Add CalVer build-time version tracking
- Dockerfile: ARG BUILD_VERSION=dev → ENV APP_VERSION baked into image
- ci.yml: BUILD_VERSION passed as build-arg; set to git tag name on v*
tag builds, "dev" on branch builds
- routes/api.py: GET /api/version returns {"version": APP_VERSION}
- SettingsView: fetches /api/version on mount, displays in About section
under General tab
Version source of truth is the git tag (YY.MM.DD.N CalVer).
pyproject.toml / package.json versions are no longer maintained.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -104,18 +104,21 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Generate image tags
|
- name: Generate image tags and version
|
||||||
id: tags
|
id: tags
|
||||||
run: |
|
run: |
|
||||||
TAGS="${{ env.IMAGE }}:${{ github.sha }}"
|
TAGS="${{ env.IMAGE }}:${{ github.sha }}"
|
||||||
|
BUILD_VERSION="dev"
|
||||||
if [[ "${{ github.ref }}" == "refs/heads/main" ]]; then
|
if [[ "${{ github.ref }}" == "refs/heads/main" ]]; then
|
||||||
TAGS="$TAGS,${{ env.IMAGE }}:latest"
|
TAGS="$TAGS,${{ env.IMAGE }}:latest"
|
||||||
elif [[ "${{ github.ref }}" == "refs/heads/dev" ]]; then
|
elif [[ "${{ github.ref }}" == "refs/heads/dev" ]]; then
|
||||||
TAGS="$TAGS,${{ env.IMAGE }}:dev"
|
TAGS="$TAGS,${{ env.IMAGE }}:dev"
|
||||||
elif [[ "${{ github.ref }}" == refs/tags/* ]]; then
|
elif [[ "${{ github.ref }}" == refs/tags/* ]]; then
|
||||||
TAGS="$TAGS,${{ env.IMAGE }}:latest,${{ env.IMAGE }}:${{ github.ref_name }}"
|
TAGS="$TAGS,${{ env.IMAGE }}:latest,${{ env.IMAGE }}:${{ github.ref_name }}"
|
||||||
|
BUILD_VERSION="${{ github.ref_name }}"
|
||||||
fi
|
fi
|
||||||
echo "value=$TAGS" >> $GITHUB_OUTPUT
|
echo "value=$TAGS" >> $GITHUB_OUTPUT
|
||||||
|
echo "build_version=$BUILD_VERSION" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v4
|
uses: docker/setup-buildx-action@v4
|
||||||
@@ -133,5 +136,6 @@ jobs:
|
|||||||
context: .
|
context: .
|
||||||
push: true
|
push: true
|
||||||
tags: ${{ steps.tags.outputs.value }}
|
tags: ${{ steps.tags.outputs.value }}
|
||||||
|
build-args: BUILD_VERSION=${{ steps.tags.outputs.build_version }}
|
||||||
cache-from: type=registry,ref=${{ env.IMAGE }}:cache
|
cache-from: type=registry,ref=${{ env.IMAGE }}:cache
|
||||||
cache-to: type=registry,ref=${{ env.IMAGE }}:cache,mode=max
|
cache-to: type=registry,ref=${{ env.IMAGE }}:cache,mode=max
|
||||||
|
|||||||
@@ -21,5 +21,10 @@ COPY alembic/ alembic/
|
|||||||
# Ensure Python finds the source tree (where static files live) before site-packages
|
# Ensure Python finds the source tree (where static files live) before site-packages
|
||||||
ENV PYTHONPATH=/app/src
|
ENV PYTHONPATH=/app/src
|
||||||
|
|
||||||
|
# Version is injected at build time via --build-arg BUILD_VERSION=YY.MM.DD.N
|
||||||
|
# Falls back to "dev" for local / untagged builds
|
||||||
|
ARG BUILD_VERSION=dev
|
||||||
|
ENV APP_VERSION=$BUILD_VERSION
|
||||||
|
|
||||||
EXPOSE 5000
|
EXPOSE 5000
|
||||||
CMD ["sh", "-c", "alembic upgrade head && hypercorn 'fabledassistant.app:create_app()' --bind 0.0.0.0:5000 --keep-alive 600"]
|
CMD ["sh", "-c", "alembic upgrade head && hypercorn 'fabledassistant.app:create_app()' --bind 0.0.0.0:5000 --keep-alive 600"]
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ const saving = ref(false);
|
|||||||
const saved = ref(false);
|
const saved = ref(false);
|
||||||
const exporting = ref(false);
|
const exporting = ref(false);
|
||||||
const restoring = ref(false);
|
const restoring = ref(false);
|
||||||
|
const appVersion = ref('dev');
|
||||||
const restoreFileInput = ref<HTMLInputElement | null>(null);
|
const restoreFileInput = ref<HTMLInputElement | null>(null);
|
||||||
|
|
||||||
// Migrate stored "admin" → "config"; unknown tabs fall back to "general"
|
// Migrate stored "admin" → "config"; unknown tabs fall back to "general"
|
||||||
@@ -176,6 +177,10 @@ const searchLoading = ref(false);
|
|||||||
const searchError = ref("");
|
const searchError = ref("");
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
|
try {
|
||||||
|
const v = await apiGet<{ version: string }>('/api/version')
|
||||||
|
appVersion.value = v.version
|
||||||
|
} catch { /* non-critical */ }
|
||||||
await store.fetchSettings();
|
await store.fetchSettings();
|
||||||
pushStore.checkSubscription();
|
pushStore.checkSubscription();
|
||||||
assistantName.value = store.assistantName;
|
assistantName.value = store.assistantName;
|
||||||
@@ -1031,6 +1036,11 @@ function formatUserDate(iso: string): string {
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<section class="settings-section full-width">
|
||||||
|
<h2>About</h2>
|
||||||
|
<p class="version-line">Fabled Assistant <span class="version-badge">{{ appVersion }}</span></p>
|
||||||
|
</section>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- ── Integrations ── -->
|
<!-- ── Integrations ── -->
|
||||||
@@ -2206,6 +2216,25 @@ function formatUserDate(iso: string): string {
|
|||||||
margin-right: 0.25rem;
|
margin-right: 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ── About / version ─────────────────────────────────────────── */
|
||||||
|
.version-line {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.6rem;
|
||||||
|
}
|
||||||
|
.version-badge {
|
||||||
|
font-family: ui-monospace, monospace;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
padding: 0.15rem 0.5rem;
|
||||||
|
background: var(--color-bg-secondary);
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
border-radius: 4px;
|
||||||
|
color: var(--color-text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
/* ── Groups tab ──────────────────────────────────────────────── */
|
/* ── Groups tab ──────────────────────────────────────────────── */
|
||||||
.btn-primary {
|
.btn-primary {
|
||||||
padding: 0.4rem 0.9rem;
|
padding: 0.4rem 0.9rem;
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
from quart import Blueprint, jsonify
|
from quart import Blueprint, jsonify
|
||||||
|
|
||||||
api = Blueprint("api", __name__, url_prefix="/api")
|
api = Blueprint("api", __name__, url_prefix="/api")
|
||||||
@@ -6,3 +8,8 @@ api = Blueprint("api", __name__, url_prefix="/api")
|
|||||||
@api.route("/health")
|
@api.route("/health")
|
||||||
async def health():
|
async def health():
|
||||||
return jsonify({"status": "ok"})
|
return jsonify({"status": "ok"})
|
||||||
|
|
||||||
|
|
||||||
|
@api.route("/version")
|
||||||
|
async def version():
|
||||||
|
return jsonify({"version": os.environ.get("APP_VERSION", "dev")})
|
||||||
|
|||||||
Reference in New Issue
Block a user