From bb5a8288e624bd94feb351f4e5c4a389cf825459 Mon Sep 17 00:00:00 2001 From: Eric Rakestraw Date: Tue, 6 Oct 2026 01:11:32 +0000 Subject: [PATCH] Stop recovery when endpoint resolution changes --- cmd/releasekit/release_wrapper_test.go | 18 ++++++++++++++++-- docs/release.md | 4 ++-- docs/roadmap/remediation.md | 4 +++- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/cmd/releasekit/release_wrapper_test.go b/cmd/releasekit/release_wrapper_test.go index 723a70b..2f23831 100644 --- a/cmd/releasekit/release_wrapper_test.go +++ b/cmd/releasekit/release_wrapper_test.go @@ -17,6 +17,7 @@ func TestReleaseWrapperPreservesNativeExitStatuses(t *testing.T) { } _, source, _, _ := runtime.Caller(0) root := filepath.Clean(filepath.Join(filepath.Dir(source), "../..")) + originalPath := os.Getenv("PATH") realGit, err := exec.LookPath("git") if err != nil { t.Fatal(err) @@ -119,15 +120,28 @@ exec "$RELEASEKIT_TEST_REAL_GIT" "$@" if !strings.Contains(string(output), fmt.Sprintf("endpoint=%q", remote)) { t.Fatalf("captured endpoint missing: %s", output) } - query := exec.Command(realGit, "ls-remote", "--refs", "--", remote, "refs/tags/v1.2.3") + t.Setenv("PATH", originalPath) + // Execute the runbook's guarded recovery commands verbatim. + recovery := `test "$(git ls-remote --get-url -- "$releasekit_endpoint")" = "$releasekit_endpoint" && + git ls-remote --refs -- "$releasekit_endpoint" "refs/tags/$releasekit_version"` + query := exec.Command("sh", "-c", recovery) query.Dir = repository + query.Env = append(os.Environ(), "releasekit_endpoint="+remote, "releasekit_version=v1.2.3") found, err := query.Output() if err != nil || len(found) == 0 { - t.Fatalf("captured endpoint lost published tag: %q %v", found, err) + t.Fatalf("captured endpoint recovery failed: %q %v", found, err) } if !strings.Contains(string(found), cliGitOutput(t, repository, "rev-parse", "refs/tags/v1.2.3")) { t.Fatalf("wrong recovered tag: %s", found) } + cliGitRun(t, repository, "config", "url."+fetch+".insteadOf", remote) + blocked := exec.Command("sh", "-c", recovery) + blocked.Dir = repository + blocked.Env = query.Env + if output, err := blocked.Output(); err == nil || len(output) != 0 { + t.Fatalf("rewritten endpoint should block query: %q %v", output, err) + } + fetchQuery := exec.Command(realGit, "ls-remote", "--refs", "origin", "refs/tags/v1.2.3") fetchQuery.Dir = repository stale, err := fetchQuery.Output() diff --git a/docs/release.md b/docs/release.md index b735f22..baef48c 100644 --- a/docs/release.md +++ b/docs/release.md @@ -86,8 +86,8 @@ Check that Git URL rewriting still resolves this value to itself before querying ```sh releasekit_endpoint='COPY_CAPTURED_ENDPOINT' releasekit_version='vX.Y.Z' -test "$(git ls-remote --get-url -- "$releasekit_endpoint")" = "$releasekit_endpoint" -git ls-remote --refs -- "$releasekit_endpoint" "refs/tags/$releasekit_version" +test "$(git ls-remote --get-url -- "$releasekit_endpoint")" = "$releasekit_endpoint" && + git ls-remote --refs -- "$releasekit_endpoint" "refs/tags/$releasekit_version" ``` A failed URL-equality check or remote query leaves the outcome unresolved. diff --git a/docs/roadmap/remediation.md b/docs/roadmap/remediation.md index d06cac5..c41cabb 100644 --- a/docs/roadmap/remediation.md +++ b/docs/roadmap/remediation.md @@ -356,7 +356,9 @@ runbook checks URL resolution and queries that exact endpoint, retaining tags on failed or unresolved checks. Local wrapper fixtures use separate fetch and push repositories; a lost acknowledgment changes the configured push URL, and recovery still finds the intended object at the captured endpoint while the -fetch endpoint reports absence. Focused publication/wrapper tests pass. +fetch endpoint reports absence. The guarded runbook commands are exercised +verbatim, including refusing a query after URL rewriting changes the endpoint. +Focused publication/wrapper tests pass. ### R10 — Medium: The boundary checker bans valid command-internal composition