From 551705ad6280f72826b6b17bcf19db6d84ad67b9 Mon Sep 17 00:00:00 2001 From: Bradley Date: Fri, 3 Nov 2023 18:02:39 -0700 Subject: [PATCH] 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. --- ...er-build-push-backend-container-on-tag.yml | 2 + ...docker-build-push-web-container-on-tag.yml | 2 + backend/Dockerfile | 7 ++++ backend/danswer/__init__.py | 4 ++ backend/danswer/server/models.py | 4 ++ backend/danswer/server/state.py | 8 +++- web/Dockerfile | 6 +++ web/next.config.js | 9 +++++ web/package-lock.json | 4 +- web/package.json | 2 +- web/src/app/admin/systeminfo/page.tsx | 40 +++++++++++++++++++ web/src/app/auth/login/page.tsx | 16 ++++++++ web/src/components/admin/Layout.tsx | 14 +++++++ web/src/lib/version.ts | 27 +++++++++++++ 14 files changed, 141 insertions(+), 4 deletions(-) create mode 100644 web/src/app/admin/systeminfo/page.tsx create mode 100644 web/src/lib/version.ts diff --git a/.github/workflows/docker-build-push-backend-container-on-tag.yml b/.github/workflows/docker-build-push-backend-container-on-tag.yml index 7a33f54e634a..a1cc303bfb4d 100644 --- a/.github/workflows/docker-build-push-backend-container-on-tag.yml +++ b/.github/workflows/docker-build-push-backend-container-on-tag.yml @@ -32,3 +32,5 @@ jobs: tags: | danswer/danswer-backend:${{ github.ref_name }} danswer/danswer-backend:latest + build-args: | + DANSWER_VERSION: ${{ github.ref_name }} diff --git a/.github/workflows/docker-build-push-web-container-on-tag.yml b/.github/workflows/docker-build-push-web-container-on-tag.yml index fbad072675a7..a1f20f941007 100644 --- a/.github/workflows/docker-build-push-web-container-on-tag.yml +++ b/.github/workflows/docker-build-push-web-container-on-tag.yml @@ -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 }} diff --git a/backend/Dockerfile b/backend/Dockerfile index 397bd7213b3c..4f6730b7a795 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -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 \ diff --git a/backend/danswer/__init__.py b/backend/danswer/__init__.py index e69de29bb2d1..7c68536b5e62 100644 --- a/backend/danswer/__init__.py +++ b/backend/danswer/__init__.py @@ -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" \ No newline at end of file diff --git a/backend/danswer/server/models.py b/backend/danswer/server/models.py index 1cdc40090cc0..2b145873acde 100644 --- a/backend/danswer/server/models.py +++ b/backend/danswer/server/models.py @@ -46,6 +46,10 @@ class AuthTypeResponse(BaseModel): auth_type: AuthType +class VersionResponse(BaseModel): + backend_version: str + + class DataRequest(BaseModel): data: str diff --git a/backend/danswer/server/state.py b/backend/danswer/server/state.py index cbc9500e0859..4393a1ef3d11 100644 --- a/backend/danswer/server/state.py +++ b/backend/danswer/server/state.py @@ -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__) diff --git a/web/Dockerfile b/web/Dockerfile index 0f571fc87c4f..316b8da77e7b 100644 --- a/web/Dockerfile +++ b/web/Dockerfile @@ -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. diff --git a/web/next.config.js b/web/next.config.js index 0338bc30bd03..96ed9bf6aad3 100644 --- a/web/next.config.js +++ b/web/next.config.js @@ -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; diff --git a/web/package-lock.json b/web/package-lock.json index 60e134ff4b5a..89e408caf7fd 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -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", diff --git a/web/package.json b/web/package.json index 29e81f4a9ca1..eafa73f1cd51 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "qa", - "version": "0.1.0", + "version": "0.1.0dev", "private": true, "scripts": { "dev": "next dev", diff --git a/web/src/app/admin/systeminfo/page.tsx b/web/src/app/admin/systeminfo/page.tsx new file mode 100644 index 000000000000..7f1b946e0809 --- /dev/null +++ b/web/src/app/admin/systeminfo/page.tsx @@ -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 ( +
+
+ +

System Information

+
+ +
+

Danswer MIT

+
+

Backend Version:

+

{backend_version}

+
+
+

Web Version:

+

{web_version}

+
+
+ +
+ ); +}; + +export default Page; diff --git a/web/src/app/auth/login/page.tsx b/web/src/app/auth/login/page.tsx index 24a84481f1cb..52cad640c30c 100644 --- a/web/src/app/auth/login/page.tsx +++ b/web/src/app/auth/login/page.tsx @@ -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 () => { )} +
+ VERSION w{ web_version } b{ backend_version } +
); diff --git a/web/src/components/admin/Layout.tsx b/web/src/components/admin/Layout.tsx index 45d8aac6c155..c0c8e9cf1b51 100644 --- a/web/src/components/admin/Layout.tsx +++ b/web/src/components/admin/Layout.tsx @@ -317,6 +317,20 @@ export async function Layout({ children }: { children: React.ReactNode }) { }, ], }, + { + name: "Info", + items: [ + { + name: ( +
+ +
System Information
+
+ ), + link: "/admin/systeminfo", + }, + ], + }, ]} />
diff --git a/web/src/lib/version.ts b/web/src/lib/version.ts new file mode 100644 index 000000000000..cb2a00a428bc --- /dev/null +++ b/web/src/lib/version.ts @@ -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 => { + 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; +};