Files
hermes-agent/scripts/gen-bootstrap-pins.py
teknium1 79e6f1960d fix(installer): restore executable bits on install.sh and gen-bootstrap-pins.py
The salvaged commits dropped both scripts from 100755 to 100644.
2026-09-27 03:24:39 -07:00

211 lines
7.5 KiB
Python
Executable File

#!/usr/bin/env -S bash -c 'exec "$BASH" "$(dirname "$0")/_hermes-python" "$0" "$@"'
"""Generate the bootstrap pin fragments inside the installers.
The installers bootstrap uv (and, on Windows, git) BEFORE any checkout
exists, so they cannot read pm/lock.json at run time: they are fetched
standalone (`curl | sh`, `irm | iex`) and there is no JSON parser they can
rely on that early (no jq guarantee; python is what uv installs on Windows).
Instead, this script derives a plain-data fragment from pm/lock.json — the
SAME authority the pm package manager uses for every managed tool — and
splices it between markers in each installer. The bytes are stored, the
truth is derived, and the drift test (tests/scripts/test_bootstrap_pins_fragment.py)
fails when they disagree.
Run after bumping a bootstrapped tool in pm/lock.json:
python3 scripts/gen-bootstrap-pins.py # rewrite fragments
python3 scripts/gen-bootstrap-pins.py --check # exit 1 on drift
"""
from __future__ import annotations
import argparse
import json
import sys
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parents[1]
PINS_PATH = REPO_ROOT / "pm" / "lock.json"
# Standalone installers cannot read the shared mirror layout before checkout.
sys.path.insert(0, str(REPO_ROOT))
from pm.artifact_mirror import mirror_url # noqa: E402
BEGIN_MARK = "# --- BEGIN GENERATED: bootstrap pins (scripts/gen-bootstrap-pins.py) ---"
END_MARK = "# --- END GENERATED: bootstrap pins ---"
_POSIX_TARGETS = ("linux-x64", "linux-arm64", "linux-x64-musl", "linux-arm64-musl", "darwin-x64", "darwin-arm64")
_WINDOWS_TARGETS = ("win32-x64", "win32-arm64")
def _load_lock() -> dict:
return json.loads(PINS_PATH.read_text(encoding="utf-8-sig"))
def _load_uv_pin() -> dict:
entry = _load_lock()["packages"]["uv"]
for target in _POSIX_TARGETS + _WINDOWS_TARGETS:
_validate_artifact("uv", entry, target)
return entry
def _load_git_pin() -> dict:
# Windows targets only. install.ps1 bootstraps the pinned git-for-windows
# artifact because Windows needs it to clone. macOS and Linux use the
# machine's git, so the pin table declares those targets as reasoned gaps
# and there is nothing for the sh installers to stage.
entry = _load_lock()["packages"]["git"]
for target in _WINDOWS_TARGETS:
_validate_artifact("git", entry, target)
return entry
def _validate_artifact(tool: str, entry: dict, target: str) -> None:
spec = entry["artifacts"][target] # KeyError on a missing target is the point
if spec.get("missing"):
# The table says upstream ships nothing here, but this generator was
# told to emit that target. One of the two is wrong, and a KeyError
# further down would not say which.
raise ValueError(
f"{tool} pin for {target}: the table declares a gap "
f"({spec['missing']}), so there is nothing to generate"
)
if not spec["url"].startswith("https://"):
raise ValueError(f"{tool} pin for {target}: url must be https")
if len(spec["sha256"]) != 64:
raise ValueError(f"{tool} pin for {target}: sha256 must be 64 hex chars")
def _sh_fragment(uv: dict) -> str:
lines = [
BEGIN_MARK,
"# Derived from pm/lock.json. DO NOT EDIT BY HAND:",
"# run scripts/gen-bootstrap-pins.py after a pin bump.",
f'UV_PIN_VERSION="{uv["version"]}"',
"",
"# Sets UV_PIN_URL + UV_PIN_SHA256 for a <os>-<arch> target key.",
"uv_bootstrap_pin() {",
' case "$1" in',
]
for target in _POSIX_TARGETS:
entry = uv["artifacts"][target]
lines += [
f" {target})",
f' UV_PIN_URL="{entry["url"]}"',
f' UV_PIN_MIRROR="{mirror_url(entry["sha256"])}"',
f' UV_PIN_SHA256="{entry["sha256"]}"',
" ;;",
]
lines += [
" *)",
' UV_PIN_URL=""',
' UV_PIN_SHA256=""',
" return 1",
" ;;",
" esac",
"}",
END_MARK,
]
return "\n".join(lines)
def _ps1_fragment(uv: dict, git: dict) -> str:
lines = [
BEGIN_MARK,
"# Derived from pm/lock.json. DO NOT EDIT BY HAND:",
"# run scripts/gen-bootstrap-pins.py after a pin bump.",
f'$script:UvPinVersion = "{uv["version"]}"',
"$script:UvPinFiles = @{",
]
for target in _WINDOWS_TARGETS:
entry = uv["artifacts"][target]
lines += [
f' "{target}" = @{{',
f' Url = "{entry["url"]}"',
f' MirrorUrl = "{mirror_url(entry["sha256"])}"',
f' Sha256 = "{entry["sha256"]}"',
" }",
]
lines += [
"}",
"",
f'$script:GitPinVersion = "{git["version"]}"',
"$script:GitPinFiles = @{",
]
for target in _WINDOWS_TARGETS:
entry = git["artifacts"][target]
lines += [
f' "{target}" = @{{',
f' Url = "{entry["url"]}"',
f' MirrorUrl = "{mirror_url(entry["sha256"])}"',
f' Sha256 = "{entry["sha256"]}"',
" }",
]
lines += [
"}",
END_MARK,
]
return "\n".join(lines)
def _splice(path: Path, fragment: str, check: bool) -> bool:
"""Replace the marked block in *path*. Returns True when up to date."""
raw = path.read_bytes()
had_bom = raw.startswith(b"\xef\xbb\xbf")
text = raw.decode("utf-8-sig")
begin = text.index(BEGIN_MARK) # ValueError on missing marker is the point
end = text.index(END_MARK) + len(END_MARK)
# Match the file's line endings (install.ps1 is CRLF) so the generated
# block does not leave the script with mixed newlines.
newline = "\r\n" if "\r\n" in text else "\n"
fragment = fragment.replace("\n", newline)
updated = text[:begin] + fragment + text[end:]
if updated == text:
return True
if not check:
path.write_bytes((b"\xef\xbb\xbf" if had_bom else b"") + updated.encode("utf-8"))
return False
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"--check",
action="store_true",
help="exit 1 when the committed fragments drift from pm/lock.json",
)
args = parser.parse_args()
uv = _load_uv_pin()
git = _load_git_pin()
results = {
"scripts/install.sh": _splice(
REPO_ROOT / "scripts" / "install.sh", _sh_fragment(uv), args.check
),
"scripts/install.ps1": _splice(
REPO_ROOT / "scripts" / "install.ps1", _ps1_fragment(uv, git), args.check
),
# setup-hermes.sh deliberately holds NO fragment: it runs after a
# checkout exists, so it reads the pin straight from pm/lock.json
# at run time (the pm.sh-derived bootstrap). The fragment exists
# only for the curl|sh / irm|iex installers that bootstrap BEFORE
# any checkout exists.
}
stale = [name for name, fresh in results.items() if not fresh]
if args.check and stale:
print(
"bootstrap pin fragments drifted from pm/lock.json in: "
f"{', '.join(stale)}\nrun: python3 scripts/gen-bootstrap-pins.py",
file=sys.stderr,
)
return 1
for name in stale:
print(f"updated {name}")
if not stale:
print("fragments up to date")
return 0
if __name__ == "__main__":
raise SystemExit(main())