diff --git a/.github/workflows/build-main.yaml b/.github/workflows/build-main.yaml index 685be8a6..1f98a77c 100644 --- a/.github/workflows/build-main.yaml +++ b/.github/workflows/build-main.yaml @@ -15,6 +15,9 @@ jobs: steps: - name: Checkout PicImpact uses: actions/checkout@v4 + - name: Resolve build SHA + id: sha + run: echo "short=${GITHUB_SHA::12}" >> "$GITHUB_OUTPUT" - name: Login to Docker Hub uses: docker/login-action@v3 with: @@ -33,4 +36,10 @@ jobs: file: ./Dockerfile platforms: linux/amd64,linux/arm64 push: true - tags: ${{ secrets.DOCKERHUB_USERNAME }}/picimpact:latest + build-args: | + BUILD_SHA=${{ steps.sha.outputs.short }} + # Also tag with the commit so the pushed image is identifiable, and the + # running container reports the same value at GET /api/public/version. + tags: | + ${{ secrets.DOCKERHUB_USERNAME }}/picimpact:latest + ${{ secrets.DOCKERHUB_USERNAME }}/picimpact:git-${{ steps.sha.outputs.short }} diff --git a/Dockerfile b/Dockerfile index 310bd1ac..a7fc3e07 100644 --- a/Dockerfile +++ b/Dockerfile @@ -51,6 +51,11 @@ WORKDIR /app ENV NODE_ENV=production +# Git commit this image was built from, passed by the build workflow. Exposed at +# runtime via GET /api/public/version so a deploy's running commit is verifiable. +ARG BUILD_SHA=unknown +ENV BUILD_SHA=${BUILD_SHA} + RUN addgroup --system --gid 1001 nodejs RUN adduser --system --uid 1001 nextjs diff --git a/app/api/[[...route]]/route.ts b/app/api/[[...route]]/route.ts index dfeab85a..a1490099 100644 --- a/app/api/[[...route]]/route.ts +++ b/app/api/[[...route]]/route.ts @@ -5,6 +5,7 @@ import route from '~/hono' import download from '~/hono/open/download' import images from '~/hono/open/images' import cameraLens from '~/hono/open/camera-lens' +import version from '~/hono/open/version' const app = new Hono().basePath('/api') @@ -13,6 +14,7 @@ app.route('/v1', route) app.route('/public/download', download) app.route('/public/images', images) app.route('/public/camera-lens', cameraLens) +app.route('/public/version', version) app.notFound((c) => { return c.text('not found', 404) }) diff --git a/hono/open/version.ts b/hono/open/version.ts new file mode 100644 index 00000000..7e2db4d0 --- /dev/null +++ b/hono/open/version.ts @@ -0,0 +1,18 @@ +import 'server-only' + +import { Hono } from 'hono' +import { ok } from '~/hono/_lib/response' + +const app = new Hono() + +// Public deployment marker. Returns the git commit the running image was built +// from (`BUILD_SHA`, injected as a Docker build-arg by the build-main workflow). +// Lets a deploy be verified at a glance — `curl /api/public/version` reports the +// commit actually running — instead of guessing whether `:latest` is the branch +// HEAD (which has repeatedly caused half-deploy confusion). Falls back to +// "unknown" for local/dev builds where the arg isn't set. +app.get('/', (c) => { + return ok(c, { sha: process.env.BUILD_SHA ?? 'unknown' }) +}) + +export default app