Prepare self-bootstrap dependencies through normal Go access

This commit is contained in:
2026-10-08 01:37:48 +00:00
parent 2e0d302178
commit b6a490cefc
4 changed files with 65 additions and 13 deletions

View File

@@ -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)
}

View File

@@ -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

View File

@@ -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.

View File

@@ -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}" \