Keep push recovery on captured endpoint

This commit is contained in:
2026-10-06 16:04:35 +00:00
parent b240e3cfa9
commit 12e866a94a
6 changed files with 73 additions and 12 deletions

View File

@@ -12,6 +12,7 @@ import (
"time"
releasekit "gitea.maximumdirect.net/eric/ReleaseKit"
"gitea.maximumdirect.net/eric/ReleaseKit/internal/git"
)
const releaseNote = "# Sample App v1.2.3\n\nA source release.\n\nUpgrade notes stay intact.\n"
@@ -163,6 +164,47 @@ func TestPublishLostAcknowledgmentConfirmsExactRemoteObject(t *testing.T) {
}
}
func TestPublishLostAcknowledgmentWithChangedEndpointRemainsUnknown(t *testing.T) {
repo := newPublishFixture(t)
base := t.TempDir()
emptyRemote := filepath.Join(base, "empty.git")
gitRun(t, base, "init", "--bare", emptyRemote)
wrapper := installGitWrapperWithMutationMode(t, repo, "lost-ack-rewrite", emptyRemote)
t.Setenv("PATH", wrapper.bin+string(os.PathListSeparator)+os.Getenv("PATH"))
result, err := releasekit.Publish(context.Background(), releasekit.ReleaseRequest{
RepositoryDir: repo.work,
Product: "Sample App",
Version: "v1.2.3",
Validate: func(context.Context, releasekit.Candidate) error { return nil },
})
if err == nil || result.State != releasekit.PublicationUnknown || result.Phase != releasekit.PublicationReconciliation {
t.Fatalf("result = %#v, error = %v", result, err)
}
if !strings.Contains(err.Error(), "scoped URL resolves") || !strings.Contains(err.Error(), "lost acknowledgment") {
t.Fatalf("error does not preserve endpoint verification and push failures: %v", err)
}
if !errors.Is(err, git.ErrEndpointChanged) || !errors.Is(err, git.ErrRemoteStateUnreadable) {
t.Fatalf("endpoint verification error identity was not preserved: %v", err)
}
var exitErr *exec.ExitError
if !errors.As(err, &exitErr) || exitErr.ExitCode() != 77 {
t.Fatalf("push cause identity was not preserved: %v", err)
}
if queries := countLines(t, wrapper.queryLog); queries != 3 {
t.Fatalf("authoritative tag query count = %d, want no tag query after endpoint verification failed", queries)
}
if got := gitText(t, repo.work, "rev-parse", "refs/tags/v1.2.3"); got != result.TagObject {
t.Fatalf("local tag object = %s, want retained object %s", got, result.TagObject)
}
if got := gitText(t, repo.bare, "rev-parse", "refs/tags/v1.2.3"); got != result.TagObject {
t.Fatalf("original remote tag object = %s, want published object %s", got, result.TagObject)
}
if got := gitText(t, repo.bare, "rev-parse", "refs/tags/v1.2.3^{}"); got != repo.commit {
t.Fatalf("original remote tag peels to %s, want candidate commit %s", got, repo.commit)
}
}
func TestPublishConflictingRemoteTagIsNotReportedAsAbsent(t *testing.T) {
repo := newPublishFixture(t)
wrapper := installGitWrapper(t, repo, "pass")
@@ -393,6 +435,11 @@ if [ "$IS_PUSH" = "1" ]; then
STATUS=$?
: > "$PUSH_DONE"
if [ "$MODE" = "lost-ack-cancel" ] && [ "$STATUS" = "0" ]; then kill -STOP $$; fi
if [ "$MODE" = "lost-ack-rewrite" ] && [ "$STATUS" = "0" ]; then
"$REAL_GIT" config --local "url.\"$MUTATE_OBJECT\".insteadOf" %s
echo "lost acknowledgment after tag was accepted" >&2
exit 77
fi
exit "$STATUS"
fi
if [ "$MODE" = "mutate-tag" ]; then
@@ -406,7 +453,7 @@ if [ "$MODE" = "mutate-tag" ]; then
esac
fi
exec "$REAL_GIT" "$@"
`, shellQuote(realGit), shellQuote(queryLog), shellQuote(pushDone), shellQuote(mode), shellQuote(mutationObject))
`, shellQuote(realGit), shellQuote(queryLog), shellQuote(pushDone), shellQuote(mode), shellQuote(mutationObject), shellQuote(repo.bare))
if err := os.WriteFile(filepath.Join(bin, "git"), []byte(contents), 0o755); err != nil {
t.Fatal(err)
}