diff --git a/Dockerfile b/Dockerfile index 2a2f1f2..d4e9cd5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -19,11 +19,6 @@ ENV PYTHONUNBUFFERED=1 \ WORKDIR /app -# Which build is this? Read from a file IN the repo rather than a build arg, -# because Coolify does not pass a commit SHA into a Docker Compose build - an -# ARG would silently fall back to "dev" forever and the marker would never -# tell you anything. Docker's ENV also does not evaluate $(cat ...), so the -# file is simply copied and the app reads it at startup. # Dependencies first, so a code change does not reinstall the world. COPY requirements.txt . @@ -33,12 +28,22 @@ COPY app.py run.py ./ COPY boq_engine/ ./boq_engine/ COPY static/ ./static/ COPY .streamlit/ ./.streamlit/ -COPY build.txt ./build.txt # Run as a non-root user. UID 1000 matches the usual Coolify/Docker convention. RUN useradd --create-home --uid 1000 --shell /bin/bash megainfra \ && mkdir -p /data/profiles /app/out \ && chown -R megainfra:megainfra /app /data +# A fingerprint of the actual source, computed HERE at image build time. +# +# A commit SHA looked better but is self-referential and can never be right: +# the file naming the commit has to live in that commit, so it can only ever +# name the parent. A hash of the files actually in the image has no such +# problem - it is true by construction and cannot go stale. +RUN find /app -name '*.py' -o -name '*.css' -o -name '*.toml' \ + | sort | xargs sha256sum | sha256sum | cut -c1-12 > /app/build.txt \ + && chown megainfra:megainfra /app/build.txt \ + && echo "build fingerprint: $(cat /app/build.txt)" + USER megainfra ENV HOME=/home/megainfra \ diff --git a/README.md b/README.md index af3b58c..13f2658 100644 --- a/README.md +++ b/README.md @@ -8,12 +8,15 @@ disagree. Anything you disagree with, you settle **in the app** and regenerate. The consultant never has to leave the screen. -Runs entirely on your machine. Nothing is uploaded anywhere. The sidebar shows -the build SHA, so you can always see which version you are looking at. It is -read from **`build.txt` in the repo**, not a build argument, because Coolify -does not pass a commit SHA into a Docker Compose build — an `ARG` silently -falls back to `dev` forever and the marker then tells you nothing. -`./update_build_marker.sh` rewrites it; run it as part of committing. +Runs entirely on your machine. Nothing is uploaded anywhere. The sidebar shows a +**build fingerprint** — a hash of the `.py`/`.css`/`.toml` actually copied into +the image, computed during the Docker build. It reads e.g. `build d7d427a2e770`. + +A commit SHA was the obvious first choice and was wrong twice over: Coolify +does not pass a commit SHA into a Docker Compose build, so an `ARG` silently +falls back to `dev` forever; and a file naming the commit cannot live in the +commit it names, so it could only ever name the parent. A hash of the shipped +files is true by construction and cannot go stale. ![the app](docs/app-screenshot.png) diff --git a/app.py b/app.py index 68d7ea3..9773db1 100644 --- a/app.py +++ b/app.py @@ -111,13 +111,12 @@ def _fp(data: bytes) -> str: def build_sha() -> str: - """Which build is this? Shown in the sidebar so a deploy can be confirmed - without digging through Coolify's log. + """Fingerprint of the source in this image, shown in the sidebar. - Falls back through several routes because Streamlit executes this file in - an exec context where __file__ is not guaranteed - a bare reference to it - would take the whole app down. Falls back to the env var, then to a - literal, and never raises. + Computed at image build time over the .py/.css/.toml actually copied in, so + it identifies the running code by construction. Falls back through several + paths because Streamlit execs this file where __file__ is not guaranteed - + a bare reference to it would take the whole app down - and never raises. """ candidates = [] try: @@ -135,7 +134,7 @@ def build_sha() -> str: return val except OSError: continue - return os.environ.get("BOQ_BUILD_SHA") or "dev" + return "unknown" def money(v) -> str: diff --git a/build.txt b/build.txt deleted file mode 100644 index 65c4515..0000000 --- a/build.txt +++ /dev/null @@ -1 +0,0 @@ -bcba46a diff --git a/update_build_marker.sh b/update_build_marker.sh deleted file mode 100755 index 063281e..0000000 --- a/update_build_marker.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash -# Rewrite build.txt to the current commit. Run as part of committing, so the -# marker in the sidebar always names the build that is actually deployed. -# Wired up as a git post-commit hook when the repo is set up locally. -set -e -cd "$(dirname "$0")" -git rev-parse --short HEAD > build.txt