Hugo version pinning: pin in harness.yaml, auto-download, CI honors it #5
Labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Cloonar/deckle#5
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What to build
Deterministic Hugo versions per site (docs/DECISIONS.md D13, D25):
harness.yamlat the repo root carries the pinned Hugo version; the tool downloads that exact binary (with checksum verification) into a cache on first run and uses it for the preview server and all builds. The scaffolded CI workflow (later issue) reads the same pin — structure the pin so a workflow can extract it.Acceptance criteria
harness.yamlis parsed;hugo.versionselects the binary used forhugo serverand pre-flight buildshugo.binarypath override in harness.yaml bypasses download (air-gapped hosts)Blocked by
Agent Brief
Category: enhancement
Summary: Deterministic Hugo versions per site: a
harness.yamlat the site root pins the Hugo version; the harness downloads exactly that binary (checksum-verified) into a per-user cache on first run and uses it for the supervised preview server, with an explicithugo.binaryoverride for air-gapped hosts and no silent fallback to a system Hugo when a pin exists.Dependency note: this issue is hard-blocked by #1 (walking skeleton), which is merged: it provides the supervisor whose configuration already accepts a Hugo binary path (defaulting to
hugoonPATH). Beyond that this is fully independent Go-side work — no dependency on #2, #3, or #4, and it can run concurrently with them. The issue body'sdocs/DECISIONS.mdD13/D25 references point at a file that is not committed — this brief is self-contained.Current behavior:
The supervisor launches whatever binary its config names, defaulting to
hugoonPATH; the CLI never sets it, so every run uses the machine's system Hugo. Noharness.yamlexists and nothing parses one. Which Hugo version a site is edited and previewed with is whatever happens to be installed — unmanaged drift between machines and CI. The status endpoint says nothing about the Hugo version in use.Desired behavior:
harness.yaml. Read from the root of the site directory the harness is pointed at (forserve, the--sitedirectory — the root of the site's repo). Shape (exact key names below are the contract; the file will grow more keys in later issues, so unknown keys must be tolerated):The pin must be trivially extractable by a CI workflow with a one-liner (
yq, or even grep) — keep it a plain scalar at a stable key path, and note the extraction path in the README. A malformedharness.yamlis a clear startup error naming the file and the problem, never silently ignored.Resolution order. Exactly one binary is chosen at startup, and the harness logs which one and why:
hugo.binaryset — use exactly that path; verify it exists and is executable; never download. This is the air-gapped escape hatch.hugo.versionset — use the cached binary for that version, downloading it first if missing (below).harness.yaml, or no pin in it — today's behavior:hugofromPATH. This keeps existing sites working; the log states that no pin is in effect.Download and cache. When a pinned version is not cached: download the official release artifact for the pinned version matching the current OS/arch, extended edition (the edition this project targets — if no extended artifact exists for the pinned version/platform, fail with a clear error). Verify the artifact against the checksums published for that release before trusting it; on mismatch, delete the artifact and fail loudly. Extract the binary into a cache outside the repo in the per-user cache location, keyed at least by version/OS/arch, so it is reused across runs and across sites pinning the same version. Writes into the cache are atomic (download/extract to a temp path, rename into place) so an interrupted run never leaves a corrupt entry that later runs would trust.
No silent fallback. When a pin or override exists, any failure — network error, missing release asset, checksum mismatch, override path missing or not executable — refuses to start the preview with a different Hugo. The error must be actionable (what was pinned, what failed, and that
hugo.binaryexists as an offline escape hatch). Falling back quietly to a systemhugois explicitly forbidden.Visibility. The active Hugo version and how it was resolved (pinned download / explicit binary / PATH) are exposed on the status endpoint — as additive fields; the existing status fields are a frozen contract with the frontend and must not change shape — and surfaced in the shell UI (e.g. alongside the existing status pill). Changing the pin in
harness.yamland restarting the harness switches versions, visibly.Key interfaces:
harness.yamlconfig shape as above. Put its parsing in a package reusable by later issues (initscaffolding in #17 writes this file; pre-flight builds in #10 must resolve the same binary) rather than inside the CLI entry point.http.Client/URL base or a fetcher interface) so tests can serve fake archives and fake checksum files, including the mismatch path.Acceptance criteria:
harness.yamlat the site root is parsed;hugo.versionselects the binary used to launch the supervisedhugo serverhugohugo.binaryinharness.yamlbypasses download entirely and uses exactly that path; a missing/non-executable override path is a clear errorharness.yamlor no pin, behavior is unchanged (hugofromPATH) and the log says soharness.yamland the extractionharness.yamlis a clear startup error naming the problemOut of scope:
harness.yamland the customer-site CI workflow that reads the pin (#17) — this issue only structures the pin so that workflow can read itharness.yamlkeys or a general site-config system — only thehugosection