From b6d63fc2d255f5e4c190f71d3efb63d2bca67647 Mon Sep 17 00:00:00 2001 From: lowcarbdev Date: Sat, 22 Nov 2025 17:03:01 -0700 Subject: [PATCH] automatic versioning on tags --- .github/workflows/docker-build.yml | 16 ++++++++++++++++ Dockerfile | 5 +++++ frontend/src/App.jsx | 16 ++++++++++++++-- frontend/version.json | 3 --- frontend/vite.config.js | 15 --------------- internal/handlers.go | 18 ++++++++++++++++++ main.go | 3 +++ 7 files changed, 56 insertions(+), 20 deletions(-) delete mode 100644 frontend/version.json diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index c0e0d2b..da638bc 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -57,6 +57,20 @@ jobs: flavor: | latest=false + - name: Determine version for build + id: version + run: | + if [[ "${{ github.ref }}" == refs/tags/v* ]]; then + # For tags, strip the 'v' prefix + VERSION="${{ github.ref_name }}" + VERSION="${VERSION#v}" + else + # For non-tags, use git commit hash with dirty flag + VERSION=$(git describe --always --dirty) + fi + echo "version=git-${VERSION}" >> $GITHUB_OUTPUT + echo "Building with version: git-${VERSION}" + - name: Build and push Docker image uses: docker/build-push-action@v5 with: @@ -65,5 +79,7 @@ jobs: push: ${{ github.event_name != 'pull_request' }} tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} + build-args: | + VERSION=${{ steps.version.outputs.version }} cache-from: type=gha cache-to: type=gha,mode=max diff --git a/Dockerfile b/Dockerfile index 96735b1..877cfc6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -69,6 +69,11 @@ RUN chmod +x /usr/local/bin/docker-entrypoint.sh # Create data directory for database RUN mkdir -p /data +# Accept version as build argument and generate version.json +# This is done late in the build to maximize cache layer reuse +ARG VERSION=dev +RUN echo "{\"version\":\"${VERSION}\"}" > /app/version.json + # Set environment variables ENV PORT=8081 \ DB_PATH_PREFIX=/data \ diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 0bce878..142eb8f 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -28,6 +28,7 @@ function App() { const [showUpload, setShowUpload] = useState(false) const [showPasswordModal, setShowPasswordModal] = useState(false) const [searchFilter, setSearchFilter] = useState('') + const [version, setVersion] = useState('...') // Mobile sidebar state const [showSidebar, setShowSidebar] = useState(true) @@ -51,8 +52,19 @@ function App() { useEffect(() => { fetchDateRange() fetchConversations() + fetchVersion() }, []) + const fetchVersion = async () => { + try { + const response = await axios.get(`${API_BASE}/version`) + setVersion(response.data.version || 'unknown') + } catch (error) { + console.error('Failed to fetch version:', error) + setVersion('unknown') + } + } + useEffect(() => { fetchConversations() }, [startDate, endDate]) @@ -177,10 +189,10 @@ function App() { - User: {user?.username} + {user?.username} - Version {__APP_VERSION__} + Version {version} setShowPasswordModal(true)}> diff --git a/frontend/version.json b/frontend/version.json deleted file mode 100644 index dfbc6a7..0000000 --- a/frontend/version.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "version": "0.1.5-dev" -} diff --git a/frontend/vite.config.js b/frontend/vite.config.js index 7bcdb43..8b0f57b 100644 --- a/frontend/vite.config.js +++ b/frontend/vite.config.js @@ -1,22 +1,7 @@ import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' -import { readFileSync } from 'fs' -import { resolve } from 'path' - -// Read version from version.json -let version = 'unknown' -try { - const versionFile = readFileSync(resolve(__dirname, 'version.json'), 'utf-8') - const versionData = JSON.parse(versionFile) - version = versionData.version -} catch (error) { - console.warn('Warning: Could not read version.json, using default version') -} // https://vite.dev/config/ export default defineConfig({ plugins: [react()], - define: { - __APP_VERSION__: JSON.stringify(version), - }, }) diff --git a/internal/handlers.go b/internal/handlers.go index 3fe92bf..45774dc 100644 --- a/internal/handlers.go +++ b/internal/handlers.go @@ -2,9 +2,11 @@ package internal import ( "database/sql" + "encoding/json" "fmt" "log/slog" "net/http" + "os" "strconv" "time" @@ -422,3 +424,19 @@ func HandleSearch(c echo.Context) error { return c.JSON(http.StatusOK, results) } + +// HandleVersion returns the application version +func HandleVersion(c echo.Context) error { + // Try to read version from version.json file first (Docker builds) + versionFile := "/app/version.json" + if data, err := os.ReadFile(versionFile); err == nil { + var versionData map[string]string + if err := json.Unmarshal(data, &versionData); err == nil { + return c.JSON(http.StatusOK, versionData) + } + } + + return c.JSON(http.StatusOK, map[string]string{ + "version": "dev", + }) +} diff --git a/main.go b/main.go index 0247632..e4cc11f 100644 --- a/main.go +++ b/main.go @@ -80,6 +80,9 @@ func main() { return c.String(http.StatusOK, "OK") }) + // Version endpoint (public, no authentication required) + e.GET("/api/version", internal.HandleVersion) + // Serve static files from frontend/dist if it exists (for production/Docker) if _, err := os.Stat("./frontend/dist"); err == nil { // Serve static assets (JS, CSS, images, etc.)