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
|
||||||
.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
|
.gitignore
|
||||||
|
|
||||||
# Development
|
# Development
|
||||||
|
|||||||
@@ -3,6 +3,36 @@
|
|||||||
# self-contained single-node deployment. For production, run a dedicated
|
# self-contained single-node deployment. For production, run a dedicated
|
||||||
# LDAP server and configure the app via app_* env vars / mounted secrets.js.
|
# 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
|
FROM node:20-alpine
|
||||||
|
|
||||||
# Install OpenLDAP and required packages.
|
# 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.
|
# level above the nodejs/ app dir). Without this the app crashes on startup.
|
||||||
COPY tos.md /tos.md
|
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 startup script
|
||||||
COPY docker-entrypoint.sh /usr/local/bin/
|
COPY docker-entrypoint.sh /usr/local/bin/
|
||||||
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
|
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
|
||||||
|
|||||||
@@ -1,15 +1,29 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
const { execSync } = require('child_process');
|
const { execSync } = require('child_process');
|
||||||
const { version: buildVersion } = require('../package.json');
|
const { version: buildVersion } = require('../package.json');
|
||||||
|
|
||||||
let buildHash = 'unknown';
|
// 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 {
|
try {
|
||||||
buildHash = execSync('git rev-parse --short HEAD', { cwd: __dirname }).toString().trim();
|
const baked = fs.readFileSync(path.join(__dirname, '../.build_commit'), 'utf8').trim();
|
||||||
|
if (baked) return baked;
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return execSync('git rev-parse --short HEAD', { cwd: __dirname }).toString().trim();
|
||||||
|
} catch (_) {
|
||||||
|
return 'unknown';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
buildVersion,
|
buildVersion,
|
||||||
buildHash,
|
buildHash: readBuildHash(),
|
||||||
buildYear: new Date().getFullYear(),
|
buildYear: new Date().getFullYear(),
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user