From b6a490cefc5d6880454e2d6ddef20205e6e55647 Mon Sep 17 00:00:00 2001 From: Eric Rakestraw Date: Thu, 8 Oct 2026 01:37:48 +0000 Subject: [PATCH] Prepare self-bootstrap dependencies through normal Go access --- cmd/releasekit/woodpecker_test.go | 45 +++++++++++++++++-- docs/consumers/delivery.md | 4 ++ docs/roadmap/remediation.md | 27 +++++++---- .../woodpecker/releasekit-source-delivery.yml | 2 - 4 files changed, 65 insertions(+), 13 deletions(-) diff --git a/cmd/releasekit/woodpecker_test.go b/cmd/releasekit/woodpecker_test.go index 9f70baf..f0ec512 100644 --- a/cmd/releasekit/woodpecker_test.go +++ b/cmd/releasekit/woodpecker_test.go @@ -4,12 +4,15 @@ import ( "archive/zip" "errors" "fmt" + "net/http" + "net/http/httptest" "os" "os/exec" "path/filepath" "runtime" "strconv" "strings" + "sync/atomic" "testing" "gitea.maximumdirect.net/eric/ReleaseKit/cmd/releasekit/internal/cli" @@ -228,16 +231,52 @@ func TestWoodpeckerSourceDeliveryTemplateSelfBootstraps(t *testing.T) { } command := strings.ReplaceAll(block, "$${", "${") repository := newCLIRepository(t) - writeRepositoryFile(t, repository, "cmd/releasekit/main.go", "package main\n\nimport (\"fmt\"; \"os\")\n\nfunc main() {\n\tfmt.Println(\"self-bootstrap\")\n\tfmt.Printf(\"tool-args=%q\\n\", os.Args[1:])\n}\n") - cliGitRun(t, repository, "add", "cmd/releasekit/main.go") + const dependency = "example.com/releasekit-helper" + proxyDir := t.TempDir() + writeModuleProxyVersion(t, proxyDir, dependency, "v1.0.0", "module "+dependency+"\n\ngo 1.25.0\n", map[string]string{ + "helper.go": "package helper\n\nconst Message = \"prepared tool dependency\"\n", + }) + var proxyRequests atomic.Int64 + files := http.FileServer(http.Dir(proxyDir)) + proxy := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + proxyRequests.Add(1) + files.ServeHTTP(w, r) + })) + defer proxy.Close() + writeRepositoryFile(t, repository, "go.mod", "module example.com/cli-candidate\n\ngo 1.25.0\n\nrequire "+dependency+" v1.0.0\n") + writeRepositoryFile(t, repository, "cmd/releasekit/main.go", "package main\n\nimport (\"fmt\"; \"os\"; helper \""+dependency+"\")\n\nfunc main() {\n\tfmt.Println(\"self-bootstrap \" + helper.Message)\n\tfmt.Printf(\"tool-args=%q\\n\", os.Args[1:])\n}\n") + preparationEnv := map[string]string{ + "GOPROXY": proxy.URL, "GOSUMDB": "off", "GONOPROXY": "none", "GOPRIVATE": "", + "GOWORK": "off", "GOTOOLCHAIN": "local", "GOENV": "off", "GOFLAGS": "-modcacherw", + "GOMODCACHE": t.TempDir(), + } + runWoodpeckerBlock(t, "go mod download "+dependency, repository, preparationEnv) + cliGitRun(t, repository, "add", "cmd/releasekit/main.go", "go.mod", "go.sum") cliGitRun(t, repository, "commit", "-m", "add the self-bootstrapped command") commit := pushTaggedCLIRepository(t, repository) goDir, goLog := installGoBootstrapShim(t) env := templateEnvironment(repository, commit, goDir, goLog) + for key, value := range preparationEnv { + env[key] = value + } + env["GOMODCACHE"] = t.TempDir() + proxyRequests.Store(0) + env["CI_COMMIT_SHA"] = strings.Repeat("0", len(commit)) + if output, err := executeWoodpeckerBlock(command, repository, env); err == nil { + t.Fatalf("wrong commit unexpectedly bootstrapped the tool: %s", output) + } + assertNoCommands(t, goLog) + if proxyRequests.Load() != 0 { + t.Fatal("wrong commit reached tool dependency preparation") + } + env["CI_COMMIT_SHA"] = commit output := runWoodpeckerBlock(t, command, repository, env) - if !strings.Contains(output, "self-bootstrap") || !strings.Contains(output, `"`+commit+`"`) { + if !strings.Contains(output, "self-bootstrap prepared tool dependency") || !strings.Contains(output, `"`+commit+`"`) { t.Fatalf("self-bootstrap output = %q", output) } + if proxyRequests.Load() == 0 { + t.Fatal("self-bootstrap did not prepare its dependency from the local proxy") + } if !strings.Contains(output, `"ci-deliver"`) || !strings.Contains(output, `"`+repository+`"`) || !strings.Contains(output, `"v1.2.3"`) { t.Fatalf("self-bootstrapped command arguments = %q", output) } diff --git a/docs/consumers/delivery.md b/docs/consumers/delivery.md index 7c745e7..2f6f993 100644 --- a/docs/consumers/delivery.md +++ b/docs/consumers/delivery.md @@ -184,6 +184,10 @@ the executable separately and invoke the same `ci-deliver` command. For ReleaseKit's own source workflow, the maintained [source-delivery template](../../examples/woodpecker/releasekit-source-delivery.yml) checks out the tag and self-bootstraps `./cmd/releasekit` into temporary storage. +Its readonly tool build uses committed module metadata and can fetch missing +dependencies through normal Go proxy, private-module, checksum, and cache +settings. Tool bootstrap follows tag verification; offline candidate validation +and application artifact compilation retain their separate environments. The normal Go module and build caches are sufficient. Cache mounts may be added as deployment configuration when they help the runner; they are not needed by diff --git a/docs/roadmap/remediation.md b/docs/roadmap/remediation.md index 7f654b1..34b959c 100644 --- a/docs/roadmap/remediation.md +++ b/docs/roadmap/remediation.md @@ -1,12 +1,13 @@ # ReleaseKit implementation remediation -Status: One open v0.2.0 finding (9). Findings 1–8 are addressed. +Status: All nine findings addressed. The v0.2.0 review covers committed state `edd670e` and the implementation commits after `89981ab`, against the [implementation plan](implementation.md), [feature roadmap](v0.2.0.md), [model roadmap](model.md), accepted ADRs, and applicable repository policies. Both ReleaseKit and Audita had clean worktrees -when inspected. This review changes only this document. +when inspected. The review changed only this document; the resolutions below +record subsequent remediation. ## Foundation review history @@ -285,10 +286,10 @@ tool through temporary module metadata. Audita's local wrapper and workflow adopt that approach in commit `17421bc`; the inspected declarations preserve its existing dependency version, image, secret name, and release profile. -All ten feature areas are present. The three findings below identify incomplete -behavior within Markdown validation and self-bootstrap. Finding 8 imposes -unrequested existence/boundary checks on image alternative text. Finding 9 -effectively requires a prepopulated tool module cache. The review found no +All ten feature areas are present. The review identified three behavior gaps +within Markdown validation and self-bootstrap. Finding 8 imposed unrequested +existence/boundary checks on image alternative text. Finding 9 effectively +required a prepopulated tool module cache. The review found no additional public package, validator-registration framework, universal optional check, arbitrary inspection limit, or change to source-publication completion. @@ -371,6 +372,16 @@ real linked images still report missing image sources and missing link targets. ## 9. P1: ReleaseKit's source-delivery bootstrap fails with an empty module cache +**Resolution:** Addressed. Self-bootstrap uses normal Go dependency access for +its readonly tool build after tag verification. The maintained workflow test +starts with an empty module cache and a local HTTP module proxy, builds a tool +with an external dependency, and verifies execution, clean tagged source, and +temporary-output cleanup. A wrong commit stops before Go or proxy access. The +regression failed before the fix and passed afterward. The delivery guide now +describes tool dependency access and its separation from offline validation. +The uncached offline suite, `go run ./internal/apicheck`, and local documentation +link/fragment checks passed after all three fixes. + **Owners:** [source-delivery workflow](../../examples/woodpecker/releasekit-source-delivery.yml), lines 27–31; [workflow verification](../../cmd/releasekit/woodpecker_test.go), `TestWoodpeckerSourceDeliveryTemplateSelfBootstraps`; and the @@ -415,5 +426,5 @@ delivery tests retain ownership of hosted page behavior. Goldmark's renderer confirmed the alternative-text semantics. A separate temporary empty module cache reproduced finding 9. These probes are review evidence; production code and the committed test suite remain unchanged. -- Existing tests pass but omit these cases. The required fixes remain open; - their remediation and regression coverage are subsequent work. +- Existing tests passed but omitted these cases at review time. The resolutions + above record completed fixes and regression coverage. diff --git a/examples/woodpecker/releasekit-source-delivery.yml b/examples/woodpecker/releasekit-source-delivery.yml index 039f1f0..37585cf 100644 --- a/examples/woodpecker/releasekit-source-delivery.yml +++ b/examples/woodpecker/releasekit-source-delivery.yml @@ -26,8 +26,6 @@ steps: trap 'rm -rf "$${bootstrap_dir}"' EXIT export GOWORK=off export GOTOOLCHAIN=local - export GOPROXY=off - export GOSUMDB=off go build -mod=readonly -o "$${bootstrap_dir}/releasekit" ./cmd/releasekit "$${bootstrap_dir}/releasekit" ci-deliver \ --repo "$${CI_WORKSPACE}" \