Implemented Danswer versioning system. (#649)

* Web & API server versioning system. Displayed on UI.

* Remove some debugging code.

* Integrated backend version into GitHub Action & Docker build workflow using env variables.

* Fixed web container environment variable name.

* Revise Dockerfiles for GitHub Actions workflow.

* Added system information page to admin panel with version info. Updated github workflows to include tagged version, and corresponding changes in the dockerfiles and codebases for web&backend to use env variables if present. Changed to 'dev' naming scheme if no env var is present to indicate local setup. Removed version from admin panel header.

* Added missing systeminfo dir to remote repo.
This commit is contained in:
Bradley
2023-11-03 18:02:39 -07:00
committed by GitHub
parent d9581ce0ae
commit 551705ad62
14 changed files with 141 additions and 4 deletions

View File

@@ -32,3 +32,5 @@ jobs:
tags: |
danswer/danswer-backend:${{ github.ref_name }}
danswer/danswer-backend:latest
build-args: |
DANSWER_VERSION: ${{ github.ref_name }}

View File

@@ -32,3 +32,5 @@ jobs:
tags: |
danswer/danswer-web-server:${{ github.ref_name }}
danswer/danswer-web-server:latest
build-args: |
DANSWER_VERSION: ${{ github.ref_name }}

View File

@@ -1,5 +1,12 @@
FROM python:3.11.4-slim-bookworm
# Default DANSWER_VERSION build argument set here.
# This can be overridden by passing in a build arg, typically from GitHub Actions.
ARG DANSWER_VERSION=0.1.0
# Then passed to the container environment.
ENV DANSWER_VERSION=${DANSWER_VERSION}
# Install system dependencies
RUN apt-get update && \
apt-get install -y git cmake pkg-config libprotobuf-c-dev protobuf-compiler \

View File

@@ -0,0 +1,4 @@
import os
# Pulls the version from the environment variable DANSWER_VERSION, or defaults to 0.1.0dev.
__version__ = os.environ.get("DANSWER_VERSION", "") or "0.1.0dev"

View File

@@ -46,6 +46,10 @@ class AuthTypeResponse(BaseModel):
auth_type: AuthType
class VersionResponse(BaseModel):
backend_version: str
class DataRequest(BaseModel):
data: str

View File

@@ -1,9 +1,10 @@
from fastapi import APIRouter
from danswer import __version__
from danswer.configs.app_configs import AUTH_TYPE
from danswer.server.models import AuthTypeResponse
from danswer.server.models import StatusResponse
from danswer.server.models import VersionResponse
router = APIRouter()
@@ -16,3 +17,8 @@ def healthcheck() -> StatusResponse:
@router.get("/auth/type")
def get_auth_type() -> AuthTypeResponse:
return AuthTypeResponse(auth_type=AUTH_TYPE)
@router.get("/version")
def get_version() -> VersionResponse:
return VersionResponse(backend_version=__version__)

View File

@@ -1,5 +1,11 @@
FROM node:20-alpine AS base
# Default DANSWER_VERSION build argument set here.
# This can be overridden by passing in a build arg, typically from GitHub Actions.
ARG DANSWER_VERSION=0.1.0
# Then passed to the container environment.
ENV DANSWER_VERSION=${DANSWER_VERSION}
# Step 1. Install dependencies only when needed
FROM base AS deps
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.

View File

@@ -1,3 +1,9 @@
// Get Danswer Web Version
const { version: package_version } = require('./package.json'); // version from package.json
const env_version = process.env.DANSWER_VERSION; // version from env variable
// Use env version if set & valid, otherwise default to package version
const version = env_version || package_version;
/** @type {import('next').NextConfig} */
const nextConfig = {
output: "standalone",
@@ -33,6 +39,9 @@ const nextConfig = {
},
];
},
publicRuntimeConfig: {
version,
},
};
module.exports = nextConfig;

4
web/package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "qa",
"version": "0.1.0",
"version": "0.1.0dev",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "qa",
"version": "0.1.0",
"version": "0.1.0dev",
"dependencies": {
"@phosphor-icons/react": "^2.0.8",
"@tremor/react": "^3.9.2",

View File

@@ -1,6 +1,6 @@
{
"name": "qa",
"version": "0.1.0",
"version": "0.1.0dev",
"private": true,
"scripts": {
"dev": "next dev",

View File

@@ -0,0 +1,40 @@
import { NotebookIcon } from "@/components/icons/icons";
import { getWebVersion, getBackendVersion } from "@/lib/version"
const Page = async () => {
let web_version: string | null = null;
let backend_version: string | null = null;
try {
[web_version, backend_version] = await Promise.all([
getWebVersion(),
getBackendVersion(),
]);
} catch (e) {
console.log(`Version info fetch failed for system info page - ${e}`);
}
return (
<div>
<div className="border-solid border-gray-600 border-b pb-2 mb-4 flex">
<NotebookIcon size={32} />
<h1 className="text-3xl font-bold pl-2">System Information</h1>
</div>
<div>
<p className="font-bold text-lg my-auto mb-2">Danswer MIT</p>
<div className="flex mb-2">
<p className="my-auto mr-1">Backend Version: </p>
<p className="text-base my-auto text-slate-400 italic">{backend_version}</p>
</div>
<div className="flex mb-2">
<p className="my-auto mr-1">Web Version: </p>
<p className="text-base my-auto text-slate-400 italic">{web_version}</p>
</div>
</div>
</div>
);
};
export default Page;

View File

@@ -3,6 +3,8 @@ import { AuthType, OAUTH_NAME } from "@/lib/constants";
import { User } from "@/lib/types";
import { getCurrentUserSS, getAuthUrlSS, getAuthTypeSS } from "@/lib/userSS";
import { redirect } from "next/navigation";
import { getWebVersion, getBackendVersion } from "@/lib/version"
const BUTTON_STYLE =
"group relative w-64 flex justify-center " +
@@ -25,6 +27,17 @@ const Page = async () => {
console.log(`Some fetch failed for the login page - ${e}`);
}
let web_version: string | null = null;
let backend_version: string | null = null;
try {
[web_version, backend_version] = await Promise.all([
getWebVersion(),
getBackendVersion(),
]);
} catch (e) {
console.log(`Version info fetch failed for the login page - ${e}`);
}
// simply take the user to the home page if Auth is disabled
if (authType === "disabled") {
return redirect("/");
@@ -80,6 +93,9 @@ const Page = async () => {
)}
</div>
</div>
<div className="fixed bottom-4 right-4 z-50 text-slate-400 p-2">
VERSION w{ web_version } b{ backend_version }
</div>
</div>
</main>
);

View File

@@ -317,6 +317,20 @@ export async function Layout({ children }: { children: React.ReactNode }) {
},
],
},
{
name: "Info",
items: [
{
name: (
<div className="flex">
<NotebookIcon size={18} />
<div className="ml-1">System Information</div>
</div>
),
link: "/admin/systeminfo",
},
],
},
]}
/>
<div className="px-12 min-h-screen bg-gray-900 text-gray-100 w-full">

27
web/src/lib/version.ts Normal file
View File

@@ -0,0 +1,27 @@
import { buildUrl } from "./utilsSS";
import getConfig from 'next/config';
const { publicRuntimeConfig } = getConfig();
const version = publicRuntimeConfig?.version;
// Maybe improve type-safety by creating a 'VersionType' instead of generic string
export const getBackendVersion = async (): Promise<string | null> => {
try {
const res = await fetch(buildUrl("/version"));
if (!res.ok) {
//throw new Error("Failed to fetch data");
return null;
}
const data: { backend_version: string } = await res.json();
return data.backend_version as string;
} catch (e) {
console.log(`Error fetching backend version info: ${e}`);
return null;
}
};
// Frontend?
export const getWebVersion = (): string | null => {
return version;
};