automatic versioning on tags
This commit is contained in:
@@ -57,6 +57,20 @@ jobs:
|
|||||||
flavor: |
|
flavor: |
|
||||||
latest=false
|
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
|
- name: Build and push Docker image
|
||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
@@ -65,5 +79,7 @@ jobs:
|
|||||||
push: ${{ github.event_name != 'pull_request' }}
|
push: ${{ github.event_name != 'pull_request' }}
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
build-args: |
|
||||||
|
VERSION=${{ steps.version.outputs.version }}
|
||||||
cache-from: type=gha
|
cache-from: type=gha
|
||||||
cache-to: type=gha,mode=max
|
cache-to: type=gha,mode=max
|
||||||
|
|||||||
@@ -69,6 +69,11 @@ RUN chmod +x /usr/local/bin/docker-entrypoint.sh
|
|||||||
# Create data directory for database
|
# Create data directory for database
|
||||||
RUN mkdir -p /data
|
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
|
# Set environment variables
|
||||||
ENV PORT=8081 \
|
ENV PORT=8081 \
|
||||||
DB_PATH_PREFIX=/data \
|
DB_PATH_PREFIX=/data \
|
||||||
|
|||||||
+14
-2
@@ -28,6 +28,7 @@ function App() {
|
|||||||
const [showUpload, setShowUpload] = useState(false)
|
const [showUpload, setShowUpload] = useState(false)
|
||||||
const [showPasswordModal, setShowPasswordModal] = useState(false)
|
const [showPasswordModal, setShowPasswordModal] = useState(false)
|
||||||
const [searchFilter, setSearchFilter] = useState('')
|
const [searchFilter, setSearchFilter] = useState('')
|
||||||
|
const [version, setVersion] = useState('...')
|
||||||
|
|
||||||
// Mobile sidebar state
|
// Mobile sidebar state
|
||||||
const [showSidebar, setShowSidebar] = useState(true)
|
const [showSidebar, setShowSidebar] = useState(true)
|
||||||
@@ -51,8 +52,19 @@ function App() {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchDateRange()
|
fetchDateRange()
|
||||||
fetchConversations()
|
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(() => {
|
useEffect(() => {
|
||||||
fetchConversations()
|
fetchConversations()
|
||||||
}, [startDate, endDate])
|
}, [startDate, endDate])
|
||||||
@@ -177,10 +189,10 @@ function App() {
|
|||||||
</Dropdown.Toggle>
|
</Dropdown.Toggle>
|
||||||
<Dropdown.Menu>
|
<Dropdown.Menu>
|
||||||
<Dropdown.ItemText className="fw-semibold">
|
<Dropdown.ItemText className="fw-semibold">
|
||||||
User: {user?.username}
|
{user?.username}
|
||||||
</Dropdown.ItemText>
|
</Dropdown.ItemText>
|
||||||
<Dropdown.ItemText className="small text-muted">
|
<Dropdown.ItemText className="small text-muted">
|
||||||
Version {__APP_VERSION__}
|
Version {version}
|
||||||
</Dropdown.ItemText>
|
</Dropdown.ItemText>
|
||||||
<Dropdown.Divider />
|
<Dropdown.Divider />
|
||||||
<Dropdown.Item onClick={() => setShowPasswordModal(true)}>
|
<Dropdown.Item onClick={() => setShowPasswordModal(true)}>
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
"version": "0.1.5-dev"
|
|
||||||
}
|
|
||||||
@@ -1,22 +1,7 @@
|
|||||||
import { defineConfig } from 'vite'
|
import { defineConfig } from 'vite'
|
||||||
import react from '@vitejs/plugin-react'
|
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/
|
// https://vite.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [react()],
|
plugins: [react()],
|
||||||
define: {
|
|
||||||
__APP_VERSION__: JSON.stringify(version),
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -2,9 +2,11 @@ package internal
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -422,3 +424,19 @@ func HandleSearch(c echo.Context) error {
|
|||||||
|
|
||||||
return c.JSON(http.StatusOK, results)
|
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",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -80,6 +80,9 @@ func main() {
|
|||||||
return c.String(http.StatusOK, "OK")
|
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)
|
// Serve static files from frontend/dist if it exists (for production/Docker)
|
||||||
if _, err := os.Stat("./frontend/dist"); err == nil {
|
if _, err := os.Stat("./frontend/dist"); err == nil {
|
||||||
// Serve static assets (JS, CSS, images, etc.)
|
// Serve static assets (JS, CSS, images, etc.)
|
||||||
|
|||||||
Reference in New Issue
Block a user