Verify releases use original Git objects

This commit is contained in:
2026-11-02 03:14:01 +00:00
parent 7e6e0cc7d1
commit 94aedacf05
5 changed files with 141 additions and 1 deletions

View File

@@ -18,6 +18,41 @@ import (
releasekit "go.maximumdirect.net/releasekit"
)
func TestBuildReplacementDerivedSourceIsNotEligibleForDelivery(t *testing.T) {
repository := newTaggedDeliveryConsumer(t)
original := buildGit(t, repository, "rev-parse", "HEAD")
mainPath := filepath.Join(repository, "cmd/app/main.go")
main, err := os.ReadFile(mainPath)
if err != nil {
t.Fatal(err)
}
writeBuildFixture(t, repository, "cmd/app/main.go", string(main)+"\n// replacement source\n")
buildGit(t, repository, "add", "cmd/app/main.go")
buildGit(t, repository, "commit", "-m", "Replacement source")
replacement := buildGit(t, repository, "rev-parse", "HEAD")
buildGit(t, repository, "reset", "--hard", original)
buildGit(t, repository, "replace", original, replacement)
buildGit(t, repository, "reset", "--hard", original)
result, err := releasekit.Build(context.Background(), releasekit.BuildRequest{
RepositoryDir: repository, Version: "v1.2.3", OutputDir: t.TempDir(),
Spec: releasekit.BuildSpec{MainPackage: "./cmd/app", BinaryName: "fixture", Targets: []releasekit.Target{{OS: runtime.GOOS, Arch: runtime.GOARCH}}},
})
if err != nil || result.SourceVerified || len(result.Artifacts) != 1 || result.CandidateCommit != original {
t.Fatalf("replacement build = %#v, error = %v", result, err)
}
buildGit(t, repository, "replace", "-d", original)
buildGit(t, repository, "reset", "--hard", original)
server, remote := newFacadeDeliveryServer(t, repository)
defer server.Close()
delivery, err := releasekit.Deliver(context.Background(), releasekit.DeliveryRequest{
RepositoryDir: repository, Product: "Fixture", Version: "v1.2.3",
Destination: facadeDeliveryDestination(server.URL), Prebuilt: &result,
})
if err == nil || delivery.State != releasekit.DeliveryNotAttempted || remote.snapshot().writes != 0 {
t.Fatalf("delivery of replacement build = %#v, error = %v", delivery, err)
}
}
func TestBuildPreservesInvalidVersionAndDeclarationFailureEvidence(t *testing.T) {
spec := releasekit.BuildSpec{MainPackage: "./cmd/app", BinaryName: "widget", Targets: []releasekit.Target{{OS: "linux", Arch: "amd64"}, {OS: "linux", Arch: "amd64"}}}
invalid, err := releasekit.Build(context.Background(), releasekit.BuildRequest{

View File

@@ -402,6 +402,29 @@ func TestDeliverPageOnlySkipsBuildAndHostedAssets(t *testing.T) {
}
}
func TestDeliverUsesOriginalNoteWithBlobReplacement(t *testing.T) {
repository := newTaggedDeliveryConsumer(t)
original := buildGit(t, repository, "rev-parse", "HEAD:docs/releases/v1.2.3.md")
replacementPath := filepath.Join(t.TempDir(), "note.md")
if err := os.WriteFile(replacementPath, []byte("# Fixture v1.2.3\n\nReplacement note.\n"), 0o600); err != nil {
t.Fatal(err)
}
replacement := buildGit(t, repository, "hash-object", "-w", replacementPath)
buildGit(t, repository, "replace", original, replacement)
server, remote := newFacadeDeliveryServer(t, repository)
defer server.Close()
result, err := releasekit.Deliver(context.Background(), releasekit.DeliveryRequest{
RepositoryDir: repository, Product: "Fixture", Version: "v1.2.3",
Destination: facadeDeliveryDestination(server.URL),
})
if err != nil || result.State != releasekit.DeliveryComplete {
t.Fatalf("delivery = %#v, error = %v", result, err)
}
if got := remote.snapshot().createdBody; got != "# Fixture v1.2.3\n\nDelivery fixture.\n" {
t.Fatalf("delivered note = %q", got)
}
}
func TestDeliverUsesCommittedModuleIdentityForSeparatePages(t *testing.T) {
repository := newTwoModuleDeliveryConsumer(t)
server, remote := newFacadeDeliveryServer(t, repository)
@@ -817,6 +840,7 @@ type facadeDeliveryData struct {
tag string
version string
noteBody string
createdBody string
pages map[string]bool
requests int
writes int
@@ -876,6 +900,7 @@ func (state *facadeDeliveryServer) serveHTTP(w http.ResponseWriter, r *http.Requ
if payload.Tag != data.tag || payload.Target != data.commit || payload.Name != "Fixture "+data.version {
data.t.Errorf("release payload = %#v", payload)
}
data.createdBody = payload.Body
data.pages[data.tag] = true
state.writeRelease(w)
case "/api/v1/repos/owner/example/releases/10/assets":

View File

@@ -38,7 +38,7 @@ the agent policy; an intermediate compilation dependency is recorded accurately.
| --- | --- | --- |
| 1 | A01; publication assertion portion of A19 | Complete — regression failed before the fix and passed afterward; focused publication and reconciliation tests passed. |
| 2 | A02, A12: common Git mechanics | Complete — focused Git, candidate, identity, and public build/publication/delivery tests passed; A02 regression evidence follows in Stage 3. |
| 3 | A02: source identity regression evidence | Pending |
| 3 | A02: source identity regression evidence | Complete — commit, tree, and blob replacement regressions and public publication, build, and delivery cases passed. |
| 4 | A03: buffered command teardown | Pending |
| 5 | A03, A18: piped lifetime and queued cancellation | Pending |
| 6 | A04 | Pending |

View File

@@ -142,3 +142,52 @@ func TestInspectTaggedSourceReportsDirtyCheckoutAndReadCommitFile(t *testing.T)
t.Fatalf("dirty status = %q", source.Status)
}
}
func TestCommittedReadsIgnoreReplacementObjects(t *testing.T) {
for _, kind := range []string{"commit", "tree", "blob"} {
t.Run(kind, func(t *testing.T) {
fixture := newRemoteFixture(t)
original := map[string]string{
"commit": fixture.commit,
"tree": gitText(t, fixture.work, "rev-parse", "HEAD^{tree}"),
"blob": gitText(t, fixture.work, "rev-parse", "HEAD:source.txt"),
}
writeRepoFile(t, fixture.work, "source.txt", "replacement")
gitRun(t, fixture.work, "add", "source.txt")
gitRun(t, fixture.work, "commit", "-m", "Replacement")
replacement := map[string]string{
"commit": gitText(t, fixture.work, "rev-parse", "HEAD"),
"tree": gitText(t, fixture.work, "rev-parse", "HEAD^{tree}"),
"blob": gitText(t, fixture.work, "rev-parse", "HEAD:source.txt"),
}
gitRun(t, fixture.work, "reset", "--hard", fixture.commit)
gitRun(t, fixture.work, "replace", original[kind], replacement[kind])
content, err := ReadCommitFile(t.Context(), fixture.work, fixture.commit, "source.txt")
if err != nil || string(content) != "candidate" {
t.Fatalf("committed content = %q, error = %v", content, err)
}
entries, err := ReadTree(t.Context(), fixture.work, fixture.commit)
if err != nil {
t.Fatal(err)
}
var object string
for _, entry := range entries {
if entry.Path == "source.txt" {
object = entry.ObjectID
}
}
if object != original["blob"] {
t.Fatalf("tree blob = %q, want %q", object, original["blob"])
}
reader := NewBlobReader(t.Context(), fixture.work)
content, err = reader.Read(t.Context(), object)
if err != nil || string(content) != "candidate" {
t.Fatalf("batch content = %q, error = %v", content, err)
}
if err := reader.Close(); err != nil {
t.Fatal(err)
}
})
}
}

View File

@@ -93,6 +93,7 @@ func TestCandidateOperationsDiscoverGoOnlyWhenLaterWorkNeedsIt(t *testing.T) {
func TestPublishPushesOnlyTheCapturedAnnotatedTag(t *testing.T) {
repo := newPublishFixture(t)
gitRun(t, repo.work, "config", "push.recurseSubmodules", "only")
gitRun(t, repo.work, "replace", repo.unrelatedCommit, repo.commit)
suiteRuns := filepath.Join(t.TempDir(), "suite-runs")
t.Setenv("RELEASEKIT_FIXTURE_SUITE_RUNS", suiteRuns)
refsBefore := gitText(t, repo.bare, "show-ref", "--heads", "--tags")
@@ -162,6 +163,36 @@ func TestPublishPushesOnlyTheCapturedAnnotatedTag(t *testing.T) {
}
}
func TestPublishRejectsReplacementDerivedCheckout(t *testing.T) {
repo := newPublishFixture(t)
writeFile(t, repo.work, "broken.go", "package fixture\nfunc broken(\n")
gitRun(t, repo.work, "add", "broken.go")
gitRun(t, repo.work, "commit", "-m", "Original source does not compile")
original := gitText(t, repo.work, "rev-parse", "HEAD")
gitRun(t, repo.work, "-c", "remote.origin.mirror=false", "push", "origin", "HEAD:refs/heads/main")
writeFile(t, repo.work, "broken.go", "package fixture\nfunc valid() {}\n")
writeFile(t, repo.work, "docs/releases/v1.2.3.md", "# Sample App v1.2.3\n\nReplacement note.\n")
gitRun(t, repo.work, "add", "broken.go", "docs/releases/v1.2.3.md")
gitRun(t, repo.work, "commit", "-m", "Replacement source")
replacement := gitText(t, repo.work, "rev-parse", "HEAD")
gitRun(t, repo.work, "reset", "--hard", original)
gitRun(t, repo.work, "replace", original, replacement)
gitRun(t, repo.work, "reset", "--hard", original)
contents, err := os.ReadFile(filepath.Join(repo.work, "broken.go"))
if err != nil || !strings.Contains(string(contents), "func valid") {
t.Fatalf("replacement checkout = %q, error = %v", contents, err)
}
result, err := releasekit.Publish(context.Background(), releasekit.ReleaseRequest{
RepositoryDir: repo.work, Product: "Sample App", Version: "v1.2.3",
})
if err == nil || !strings.Contains(err.Error(), "clean") || result.State != releasekit.PublicationNotAttempted {
t.Fatalf("publication = %#v, error = %v; want dirty source rejection", result, err)
}
if err := exec.Command("git", "-C", repo.bare, "show-ref", "--verify", "--quiet", "refs/tags/v1.2.3").Run(); err == nil {
t.Fatal("replacement-derived source was published")
}
}
func TestPublishUsesSeparateFullTagsForModulesAtOneCommit(t *testing.T) {
repo := newPublishFixture(t)
firstDir, secondDir := "integrations/first", "integrations/second"