fix(build): resolve OINK from candidate lock files - #497
Conversation
- read the OINK requirement from candidate go.mod - retain module integrity and replacement checks - allow staging upgrades before production merges
- share module identity and integrity validation - remove the migration tool version and checksum literals - accept valid blank lines in go.sum - cover supported pins and dependency boundary failures
bitflicker64
left a comment
There was a problem hiding this comment.
Blocking: no. Summary: the resolver is correct and well covered - it reads the candidate's own go.mod/go.sum through overlay_shell, refuses a tampered checksum, and its five new tests pass - so these are all cleanups: the contributor-facing docs still state the hard v1.0.0 contract this PR replaces, lock-file mismatches now abort with a traceback instead of fail(), the GO_BIN resolution split in two, and hugo.yml no longer asserts anything about the theme that master controls. Evidence: python3 -m unittest discover -s scripts -p 'test_oink_module.py' -v passes; python3 scripts/oink_module.py against the real checkout with go1.27.0 exits 0 and prints oink@v1.0.0 with Sum/GoModSum matching go.sum; substituting h1:WRONG= into a scratch go.sum refuses the build, but as an uncaught CalledProcessError.
| import subprocess | ||
| from pathlib import Path | ||
|
|
||
| MODULE = "github.com/pgsty/oink" |
There was a problem hiding this comment.
🧹 The contributor docs still state the hard pin this PR replaces.
README.md:35 ("The module graph must resolve github.com/pgsty/oink@v1.0.0"), its Chinese mirror README.md:119, and contribution.md:33 ("must contain exactly the pinned github.com/pgsty/oink@v1.0.0 dependency for this site") are prescriptive statements of the verification contract. After this diff the enforced contract is "whatever go.mod pins, verified against go.sum" - locked_version() accepts any release version. The version they name happens to match master today, but the rule they describe is the one being removed here, and they are the instructions a contributor follows before editing.
Requested change: reword those three lines to describe the lock-file contract rather than a literal version, so they do not need editing again on the 1.1 bump.
Separately, for #496 rather than this PR: NOTICE:9 ("This product includes OINK v1.0.0") is still accurate on master but is now the only place the version is written by hand with nothing checking it. An assertion against locked_version()'s return would keep it honest across the bump.
| ): | ||
| fail(f"unexpected OINK module metadata: {module!r}") | ||
| fail("Go executable is unavailable") | ||
| module = download_locked(assembly) |
There was a problem hiding this comment.
🧹 Lock-file mismatches now abort with a traceback instead of a clean fail().
The removed code ended in fail(f"unexpected OINK module metadata: {module!r}"), and fail raises SystemExit(message) (line 396) - one line, no traceback, and it printed the offending metadata. download_locked instead raises bare ValueErrors (scripts/oink_module.py:30,33,48,53,56,59) that carry no values, and command lets subprocess.CalledProcessError escape. main() (line 4513) has no handler, so both surface as tracebacks.
Reproduced by substituting h1:WRONG= for the archive hash in a scratch copy of go.sum:
subprocess.CalledProcessError: Command '['go', 'mod', 'download', '-json',
'github.com/pgsty/oink@v1.0.0']' returned non-zero exit status 1
and the go mod download JSON that would name the mismatch is discarded.
Requested change: put the actual and expected values into each ValueError message, and wrap this call so ValueError and CalledProcessError are reported through fail() like every other build error in this file.
| hugo = os.environ.get("HUGO_BIN", "hugo") | ||
| go = os.environ.get("GO_BIN", "go") | ||
| go_executable = shutil.which(go) | ||
| go_executable = shutil.which(os.environ.get("GO_BIN", "go")) |
There was a problem hiding this comment.
🧹 GO_BIN is now resolved twice, and the failure message lost the value.
This line keeps guarding line 4135, where go_executable sets the PATH Hugo inherits, so the check must stay. But the old code passed go_executable straight into the go mod download subprocess, which made it the single resolution point; oink_module.command now re-reads os.environ.get("GO_BIN", "go") raw, so the binary that validates the lock files and the binary prepended to Hugo's PATH are resolved independently and can differ. The message on line 4075 also dropped the value - it was fail(f"Go executable is unavailable: {go}") before, which told the operator what GO_BIN had been set to.
Requested change: pass the resolved go_executable into download_locked/command so one path is used end to end, and restore the value in the failure message.
| test "$(go list -m all | wc -l)" -eq 2 | ||
| test "$(go list -m -f '{{ .Path }}@{{ .Version }}' github.com/pgsty/oink)" = "github.com/pgsty/oink@v1.0.0" | ||
| test -z "$(go list -m -f '{{ with .Replace }}{{ .Path }}@{{ .Version }}{{ end }}' github.com/pgsty/oink)" | ||
| python3 scripts/oink_module.py |
There was a problem hiding this comment.
🧹 On the dispatch path this step no longer asserts anything master controls.
prepare requires test "$GITHUB_REF" = "refs/heads/master" (line 124), so this YAML is always master's, but source_sha is the resolved candidate branch head (line 159) and the build job checks that out (line 204). The five inline assertions this replaces were therefore master-authoritative against the candidate's go.mod; scripts/oink_module.py comes out of the candidate checkout, so the step is now self-attested. The following step already runs candidate versioning.py, so this is not a new execution boundary - what is lost is the one cheap master-side check that would catch accidental drift in a candidate's go.mod before a staging build.
Requested change: keep a version-independent assertion inline alongside the helper call, so master still bounds the graph while the version floats:
test "$(go list -m -f '{{ .Path }}')" = "github.com/apache/hugegraph-doc"
test "$(go list -m all | wc -l)" -eq 2
test -z "$(go list -m -f '{{ with .Replace }}{{ .Path }}{{ end }}' github.com/pgsty/oink)"
python3 scripts/oink_module.py
Purpose of the PR
The trusted
masterworkflow and the version builder both hard-code OINK 1.0.0 (including the migration tool checksum). This prevents staging a candidate pinned to a newer theme before merging that upgrade into production.Resolve the theme and migration tool from the candidate site's
go.mod/go.sumthrough one shared helper. Preserve site/module identity, checksum verification, the two-module dependency graph and the prohibition on replacements/exclusions. Accept valid blank lines ingo.sum.This prerequisite keeps master on OINK 1.0.0. It changes neither toolchain pins nor deployment destinations/guards. After it merges, #496 can use the existing trusted
masterworkflow to deploy OINK 1.1 to staging first.Validation
git diff --checkpassed.