refactor: /version reads CI commit-hash.txt with layered fallbacks

Ported remarkbox's pattern over to MPS. views/version.py now tries
sources in order:

  1. /opt/make_post_sell/commit-hash.txt          (CI deploy artifact)
  2. /opt/make_post_sell/env/commit-hash.txt      (alt salt layout)
  3. <package>/../commit-hash.txt                 (relative)
  4. <package>/GIT_HASH                           (setup.py legacy)
  5. git rev-parse --short HEAD                   (dev environment)
  6. "unknown"                                    (last resort)

MPS CI's build stage already writes commit-hash.txt to the artifact
tarball (.gitlab-ci.yml:38 `echo $CI_COMMIT_SHA >> commit-hash.txt`);
salt deploys it. setup.py's GIT_HASH rewrite still runs at install
time as a redundant fallback, so any environment that hasn't migrated
to commit-hash.txt yet keeps working.

Dev environments fall through to git rev-parse, which is faster and
more accurate than the previous setup.py-rewrites-source pattern that
required the file to be in the package at runtime. With this change,
/version returns the actual deployed commit hash everywhere — no
stale GIT_HASH file in git history (the file is .gitignored as of
c383c41).
This commit is contained in:
russell@unturf.com 2026-05-14 14:49:27 -04:00
parent a352a9381d
commit d9bd95bfee
No known key found for this signature in database

View file

@ -1,16 +1,65 @@
"""/version endpoint — resolves the deployed commit hash by trying
several sources in order. Borrowed from remarkbox: deploy-time file
first, then the legacy setup.py-baked file, then a runtime
`git rev-parse` for dev, then "unknown".
The CI build writes commit-hash.txt to the artifact tarball
(.gitlab-ci.yml:42 `echo $CI_COMMIT_SHA >> commit-hash.txt`); salt
deploys it to /opt/make_post_sell/. setup.py also rewrites the
in-package GIT_HASH file at install time that still works as a
fallback for any environment we haven't migrated to commit-hash.txt
yet.
"""
import os
import subprocess
from pyramid.view import view_config
VERSION = "1.1.5"
# Read git hash baked in by setup.py at install time.
_hash_file = os.path.join(os.path.dirname(os.path.dirname(__file__)), "GIT_HASH")
try:
with open(_hash_file) as f:
GIT_HASH = f.read().strip()
except Exception:
GIT_HASH = "unknown"
_this_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
def _resolve_git_hash():
# 1. Deploy-time commit-hash.txt (preferred). Salt extracts the
# CI artifact to /opt/make_post_sell/; check a few neighbour paths.
for candidate in (
"/opt/make_post_sell/commit-hash.txt",
"/opt/make_post_sell/env/commit-hash.txt",
os.path.join(_this_dir, "..", "commit-hash.txt"),
):
try:
with open(candidate) as f:
sha = f.read().strip()
if sha:
return sha[:7]
except OSError:
continue
# 2. Legacy in-package GIT_HASH (setup.py rewrites it on install).
legacy = os.path.join(_this_dir, "GIT_HASH")
try:
with open(legacy) as f:
sha = f.read().strip()
if sha:
return sha[:7]
except OSError:
pass
# 3. Dev environment: ask git directly.
try:
return subprocess.check_output(
["git", "rev-parse", "--short", "HEAD"],
cwd=_this_dir,
stderr=subprocess.DEVNULL,
).decode().strip()
except Exception:
return "unknown"
GIT_HASH = _resolve_git_hash()
@view_config(route_name="version", renderer="json")