Fix commit hash not showing in Docker builds (#43)
* Fix commit hash not showing in Docker builds build_info.js computed buildHash via `git rev-parse --short HEAD` at runtime, but the final image intentionally has no git binary and no .git directory (kept lean, per .dockerignore) — so this always failed silently and the footer's version line showed "unknown" for every Docker deployment. Working correctly only for bare-metal/dev, where git + .git are actually present. Added a throwaway gitinfo build stage that reuses the main base image (no extra pull) with git installed just for this stage, reads .git from the build context (now no longer excluded — see .dockerignore), and bakes the resolved short hash into a small file that IS copied into the final image. build_info.js reads that file first, falling back to the old git-rev-parse behavior (still needed for bare-metal). Verified against a real build: `docker exec sso-manager cat /app/.build_commit` matches `git rev-parse --short HEAD` on the host, and the footer now shows the real hash instead of "unknown". Same fix already applied to proxy (theta42/proxy#133). * Support GIT_COMMIT build-arg override for submodule builds The gitinfo stage from the previous commit works for a standalone clone (.git is a real directory) but not when this repo is built as a git submodule (e.g. from theta-env): a submodule's .git is a pointer FILE, not a directory — the real object database lives in the superproject's .git/modules/, outside this repo's own directory and therefore outside Docker's build context entirely. `git rev-parse` can never resolve it from in here no matter what, so builds via theta-env still baked in "unknown" despite the earlier fix. Add an optional GIT_COMMIT build-arg that, when set, wins over the in-context git resolution. theta-env's setup.sh now computes it on the host (where the submodule DOES resolve correctly) and passes it via docker-compose.yml's build.args. Same fix in proxy: theta42/proxy#133. Verified via theta-env's actual setup.sh end to end: rebuilding with this change, `docker exec sso-manager cat /app/.build_commit` now matches `git -C sso-manager-node rev-parse --short HEAD` on the host (previously: "unknown").
This commit is contained in:
+4
-1
@@ -1,5 +1,8 @@
|
||||
# Git
|
||||
.git
|
||||
# NOTE: .git is intentionally NOT excluded — the gitinfo build stage in
|
||||
# Dockerfile.openldap reads it to bake the commit hash into the image (see
|
||||
# nodejs/utils/build_info.js), then it's discarded before the final stage.
|
||||
# It never ends up in the final image.
|
||||
.gitignore
|
||||
|
||||
# Development
|
||||
|
||||
@@ -3,6 +3,36 @@
|
||||
# self-contained single-node deployment. For production, run a dedicated
|
||||
# LDAP server and configure the app via app_* env vars / mounted secrets.js.
|
||||
|
||||
# ── Git commit hash (build-time only) ────────────────────────────────────────
|
||||
# The final image intentionally has no git binary and no .git directory (kept
|
||||
# lean, per .dockerignore), so `git rev-parse` always fails at runtime and
|
||||
# build_info.js silently fell back to "unknown". Resolve it here instead,
|
||||
# where .git IS available (build context), and bake just the short hash into
|
||||
# a file — this stage itself is discarded, only /commit.txt survives via the
|
||||
# COPY --from below. Reuses the main base image (already pulled for the real
|
||||
# build below) rather than a separate one, so this adds no extra image pull.
|
||||
#
|
||||
# GIT_COMMIT lets a caller override the resolved hash instead of computing it
|
||||
# from .git in this build context. Needed when this repo is built as a git
|
||||
# submodule (e.g. from theta-env): a submodule's .git is a pointer FILE, not
|
||||
# a directory — the real object database lives in the superproject's
|
||||
# .git/modules/, outside this repo's own directory and therefore outside
|
||||
# Docker's build context entirely, so `git rev-parse` can never resolve it
|
||||
# from in here no matter what. theta-env's setup.sh passes --build-arg
|
||||
# GIT_COMMIT=$(git -C sso-manager-node rev-parse --short HEAD), computed on
|
||||
# the host where the submodule resolves correctly.
|
||||
ARG GIT_COMMIT=""
|
||||
FROM node:20-alpine AS gitinfo
|
||||
ARG GIT_COMMIT
|
||||
WORKDIR /repo
|
||||
COPY .git ./.git
|
||||
RUN if [ -n "$GIT_COMMIT" ]; then \
|
||||
echo "$GIT_COMMIT" > /commit.txt; \
|
||||
else \
|
||||
{ apk add --no-cache git \
|
||||
&& git rev-parse --short HEAD > /commit.txt; } 2>/dev/null || echo unknown > /commit.txt; \
|
||||
fi
|
||||
|
||||
FROM node:20-alpine
|
||||
|
||||
# Install OpenLDAP and required packages.
|
||||
@@ -64,6 +94,9 @@ COPY nodejs/public ./public
|
||||
# level above the nodejs/ app dir). Without this the app crashes on startup.
|
||||
COPY tos.md /tos.md
|
||||
|
||||
# Baked commit hash from the gitinfo stage (see build_info.js).
|
||||
COPY --from=gitinfo /commit.txt ./.build_commit
|
||||
|
||||
# Copy startup script
|
||||
COPY docker-entrypoint.sh /usr/local/bin/
|
||||
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
|
||||
|
||||
@@ -1,15 +1,29 @@
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { execSync } = require('child_process');
|
||||
const { version: buildVersion } = require('../package.json');
|
||||
|
||||
let buildHash = 'unknown';
|
||||
try {
|
||||
buildHash = execSync('git rev-parse --short HEAD', { cwd: __dirname }).toString().trim();
|
||||
} catch (_) {}
|
||||
// Docker builds bake the commit hash into ../.build_commit (see the gitinfo
|
||||
// stage in Dockerfile.openldap) -- the final image has no git binary and no
|
||||
// .git directory, so `git rev-parse` below always fails there. Bare-metal/dev
|
||||
// runs have no baked file, so they fall back to asking git directly.
|
||||
function readBuildHash() {
|
||||
try {
|
||||
const baked = fs.readFileSync(path.join(__dirname, '../.build_commit'), 'utf8').trim();
|
||||
if (baked) return baked;
|
||||
} catch (_) {}
|
||||
|
||||
try {
|
||||
return execSync('git rev-parse --short HEAD', { cwd: __dirname }).toString().trim();
|
||||
} catch (_) {
|
||||
return 'unknown';
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
buildVersion,
|
||||
buildHash,
|
||||
buildHash: readBuildHash(),
|
||||
buildYear: new Date().getFullYear(),
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user