From 18952020921cc9507ea84ae029a8104ce252762a Mon Sep 17 00:00:00 2001 From: Silberengel Date: Thu, 21 May 2026 13:42:04 +0200 Subject: [PATCH] make it easier to query for release --- Dockerfile | 26 ++++++++++++++++++++++++-- docker-compose.prod.yml | 6 ++++++ package-lock.json | 4 ++-- package.json | 2 +- scripts/README-deploy.md | 9 +++++++++ scripts/build-and-push-prod.sh | 8 +++++++- scripts/write-build-version-json.mjs | 26 ++++++++++++++++++++++++++ 7 files changed, 75 insertions(+), 6 deletions(-) create mode 100644 scripts/write-build-version-json.mjs diff --git a/Dockerfile b/Dockerfile index 5880b60a..43f47640 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,6 +13,13 @@ ENV VITE_LANGUAGE_TOOL_URL=${VITE_LANGUAGE_TOOL_URL} ARG VITE_TRANSLATE_URL ENV VITE_TRANSLATE_URL=${VITE_TRANSLATE_URL} +ARG APP_VERSION=unknown +ARG GIT_COMMIT=unknown +ARG BUILD_TIME +ENV APP_VERSION=${APP_VERSION} +ENV GIT_COMMIT=${GIT_COMMIT} +ENV BUILD_TIME=${BUILD_TIME} + WORKDIR /app # Copy package files first @@ -21,12 +28,20 @@ RUN npm install # Copy the source code to prevent invaliding cache whenever there is a change in the code COPY . . -RUN npm run build +RUN npm run build \ + && node scripts/write-build-version-json.mjs # Step 2: Final container with Nginx and embedded config FROM nginx:alpine -RUN apk add --no-cache jq +ARG APP_VERSION=unknown +ARG GIT_COMMIT=unknown + +RUN apk add --no-cache jq wget + +LABEL org.opencontainers.image.title="imwald-jumble" \ + org.opencontainers.image.version="${APP_VERSION}" \ + org.opencontainers.image.revision="${GIT_COMMIT}" # Copy only the generated static files COPY --from=builder /app/dist /usr/share/nginx/html @@ -39,6 +54,13 @@ RUN printf "server {\n\ server_name localhost;\n\ root /usr/share/nginx/html;\n\ index index.html;\n\ +\n\ + location = /health.json {\n\ + add_header Cache-Control \"no-cache, no-store, must-revalidate\";\n\ + add_header Pragma \"no-cache\";\n\ + expires off;\n\ + try_files \$uri =404;\n\ + }\n\ \n\ # PWA: service worker + precache manifest must not be long-cached or browsers never see\n\ # updates (VersionUpdateBanner stays hidden; About shows an old APP_VERSION from precache).\n\ diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 57312b7f..9e006b24 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -35,6 +35,12 @@ services: options: max-size: "10m" max-file: "3" + healthcheck: + test: ['CMD', 'wget', '-qO-', 'http://127.0.0.1/health.json'] + interval: 30s + timeout: 5s + retries: 3 + start_period: 10s deploy: resources: limits: diff --git a/package-lock.json b/package-lock.json index e5f3caad..b400e5a9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "imwald", - "version": "23.13.5", + "version": "23.13.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "imwald", - "version": "23.13.5", + "version": "23.13.6", "license": "MIT", "dependencies": { "@asciidoctor/core": "^3.0.4", diff --git a/package.json b/package.json index 03e10443..5f92a474 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "imwald", - "version": "23.13.5", + "version": "23.13.6", "description": "Imwald — a user-friendly Nostr client focused on relay feed browsing, publications, and relay discovery", "private": true, "type": "module", diff --git a/scripts/README-deploy.md b/scripts/README-deploy.md index 81bcc071..ad361a0a 100644 --- a/scripts/README-deploy.md +++ b/scripts/README-deploy.md @@ -75,9 +75,18 @@ Images default to `:latest`; to pin a version, set image tags in `docker-compose # Status docker compose -f docker-compose.prod.yml ps +# Release running in the app container (JSON) +curl -sS http://127.0.0.1:8089/health.json | jq . +# Same via Docker healthcheck file: +docker exec imwald-jumble wget -qO- http://127.0.0.1/health.json | jq . +# Image labels (after pull; matches package.json at build time) +docker inspect imwald-jumble --format 'version={{index .Config.Labels "org.opencontainers.image.version"}} commit={{index .Config.Labels "org.opencontainers.image.revision"}}' + # Logs docker compose -f docker-compose.prod.yml logs -f # Stop docker compose -f docker-compose.prod.yml down ``` + +`health.json` is baked into each app image build (`version`, `gitCommit`, `builtAt`). Public site: `https:///health.json` if Apache proxies `/` to port 8089. diff --git a/scripts/build-and-push-prod.sh b/scripts/build-and-push-prod.sh index 4f5cbcb1..d4fe545e 100755 --- a/scripts/build-and-push-prod.sh +++ b/scripts/build-and-push-prod.sh @@ -35,12 +35,18 @@ READ_ALOUD_TTS_URL="${READ_ALOUD_TTS_URL:-/api/piper-tts}" LANGUAGE_TOOL_URL="${LANGUAGE_TOOL_URL-/api/languagetool}" TRANSLATE_URL="${TRANSLATE_URL-/api/translate}" -echo "Building main app (version: $VERSION, VITE_PROXY_SERVER=$PROXY_ORIGIN, VITE_READ_ALOUD_TTS_URL=$READ_ALOUD_TTS_URL, VITE_LANGUAGE_TOOL_URL=$LANGUAGE_TOOL_URL, VITE_TRANSLATE_URL=$TRANSLATE_URL)" +GIT_COMMIT="$(git rev-parse --short HEAD 2>/dev/null || echo unknown)" +BUILD_TIME="$(date -u +%Y-%m-%dT%H:%M:%SZ)" + +echo "Building main app (version: $VERSION, commit: $GIT_COMMIT, VITE_PROXY_SERVER=$PROXY_ORIGIN, VITE_READ_ALOUD_TTS_URL=$READ_ALOUD_TTS_URL, VITE_LANGUAGE_TOOL_URL=$LANGUAGE_TOOL_URL, VITE_TRANSLATE_URL=$TRANSLATE_URL)" docker build \ --build-arg "VITE_PROXY_SERVER=$PROXY_ORIGIN" \ --build-arg "VITE_READ_ALOUD_TTS_URL=$READ_ALOUD_TTS_URL" \ --build-arg "VITE_LANGUAGE_TOOL_URL=$LANGUAGE_TOOL_URL" \ --build-arg "VITE_TRANSLATE_URL=$TRANSLATE_URL" \ + --build-arg "APP_VERSION=$VERSION" \ + --build-arg "GIT_COMMIT=$GIT_COMMIT" \ + --build-arg "BUILD_TIME=$BUILD_TIME" \ -t "$IMAGE_APP:latest" -t "$IMAGE_APP:$VERSION" . echo "Building NIP-66 monitor (version: $VERSION)" diff --git a/scripts/write-build-version-json.mjs b/scripts/write-build-version-json.mjs new file mode 100644 index 00000000..878b1d25 --- /dev/null +++ b/scripts/write-build-version-json.mjs @@ -0,0 +1,26 @@ +/** + * Writes dist/health.json after `npm run build` (Docker image release metadata). + * Env: APP_VERSION, GIT_COMMIT, BUILD_TIME (ISO); falls back to package.json + unknown. + */ +import { readFileSync, writeFileSync } from 'node:fs' +import { dirname, join } from 'node:path' +import { fileURLToPath } from 'node:url' + +const root = join(dirname(fileURLToPath(import.meta.url)), '..') +const pkg = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8')) +const version = process.env.APP_VERSION?.trim() || pkg.version +const gitCommit = process.env.GIT_COMMIT?.trim() || 'unknown' +const builtAt = process.env.BUILD_TIME?.trim() || new Date().toISOString() + +const payload = { + status: 'ok', + name: pkg.name, + version, + gitTag: `v${version}`, + gitCommit, + builtAt +} + +const out = join(root, 'dist', 'health.json') +writeFileSync(out, `${JSON.stringify(payload)}\n`) +console.log(`Wrote ${out}: v${version} (${gitCommit})`)