Check expected publication push URL

This commit is contained in:
2026-10-07 22:31:58 +00:00
parent cabc56b134
commit 368043984e
13 changed files with 186 additions and 31 deletions

View File

@@ -71,6 +71,12 @@ after tests and full vet and before configured checks. Each failure is reported
as `package-compile` with its selected target; this does not create build
artifacts or execute target binaries.
The optional profile field `expected_push_url` applies only to `publish`. A
nonempty value must exactly match the effective push URL resolved by Git;
mismatches fail during preflight before Go validation or tag creation. Git URL
rewrites are resolved first, and the push URL is used when fetch and push URLs
differ. `check` ignores this field.
The command returns status 0 for confirmed publication, 1 for a known
publication failure or conflict, 2 for usage, profile, or invalid-version
errors, and 3 when the push outcome remains unknown. Unknown status must be

View File

@@ -10,6 +10,7 @@ The current profile fields are:
| --- | --- | --- |
| `product` | Yes | Product name used by release operations. |
| `remote` | No | Selected Git remote; defaults to `origin`. |
| `expected_push_url` | No | Exact effective push URL required by `publish`; ignored by local `check`. |
| `checks` | No | Ordered additional child commands, each with an argument vector and optional environment map. |
| `validation` | No | Optional shared checks selected by ReleaseKit. |
| `build` | No | Optional main package, binary name, and selected `GOOS/GOARCH` target strings. |
@@ -58,6 +59,13 @@ environment variable containing the Gitea token; it defaults to
`RELEASEKIT_GITEA_TOKEN`. The CLI reads this variable only for `deliver` and
passes its value explicitly to the library operation.
`expected_push_url`, when nonempty, is compared exactly with Git's resolved
effective push URL during `publish`, before candidate validation. It applies
to the push destination when fetch and push URLs differ, and Git URL rewrites
such as `insteadOf` are resolved before comparison. A mismatch fails preflight;
omitting the field keeps the selected remote's existing behavior. `check` is
local-only and ignores this publication expectation.
The `check` and `publish` commands run one
`go test -mod=readonly -count=1 ./...` suite with Go module and toolchain network
access disabled, adding `-race` when `validation.go.race` is true. When

View File

@@ -5,9 +5,10 @@ annotated source tag:
```go
result, err := releasekit.Publish(ctx, releasekit.ReleaseRequest{
RepositoryDir: "/work/project",
Product: "Example",
Version: "v1.2.3",
RepositoryDir: "/work/project",
Product: "Example",
Version: "v1.2.3",
ExpectedPushURL: "ssh://git.example/owner/example.git",
Validation: releasekit.ValidationSpec{
Go: releasekit.GoValidationSpec{Race: true, FullVet: true},
},
@@ -23,6 +24,12 @@ fmt.Println(result.Version, result.CandidateCommit, result.TagObject)
Publication captures a clean candidate on `main`, validates its committed
release note, and checks the selected remote before running candidate checks.
Set `ExpectedPushURL` to require the effective push destination to match an
exact string. Git URL rewrites are resolved first, and the push URL is compared
when fetch and push URLs differ. A mismatch fails preflight before Go validation,
callbacks, or tag creation; detect it with
`errors.Is(err, releasekit.ErrUnexpectedPushURL)`. `Check` ignores this
publication-only field.
It runs one `go test -mod=readonly -count=1 ./...` suite with the available
compatible local Go toolchain. Set `Validation.Go.Race` to run that suite with
`-race`. Set `Validation.Go.FullVet` to run `go vet -mod=readonly ./...` after

View File

@@ -80,7 +80,7 @@ not a separate release matrix:
| Runtime version lookup and injected application version | `buildinfo`, `internal/runtimeinfo`, `internal/versioncontract`, `internal/build` | `TestVersionInConsumerExecutable`, `TestVersion` |
| Candidate capture, shared validation, public diagnostics, additional checks, and rechecks | `releasekit.Check`, `releasekit.Publish`, `internal/candidate`, `internal/validation`, `cmd/releasekit/internal/cli` | `TestRunUsesOneSuiteWithSelectedRaceSetting`, `TestRunReportsRacePrerequisiteFailureWithoutFallback`, `TestRunExecutesRaceEnabledSuiteOnHost`, `TestRunCompilesTargetsAfterTestAndVetInSelectionOrder`, `TestRunAttributesTargetCompileFailureAndStops`, `TestRunCompilesSourceOnlyAndExecutablePackagesWithoutArtifacts`, `TestCandidateRequestMapsGoValidation`, `TestLoadSettingsReadsGoValidation`, `TestCheckUsesSelectedRaceSuiteBeforeAdditionalChecks`, `TestCheckCapturesCandidateAndCallsAdditionalChecksOnce`, `TestCheckReturnsPublicValidationError`, `TestPublishPushesOnlyTheCapturedAnnotatedTag`, `TestPublishFailedStandardSuiteSkipsAdditionalChecksAndTagCreation`, `TestPublishAdditionalCheckFailurePreservesCauseAndPreventsTagCreation`, `TestCheckCommandBindsConfiguredChecksToCapturedCandidate`, `TestPublishCommandPublishesConfiguredCandidate`, `TestCandidateCommandsPresentValidationDiagnosticsOnce`, `TestDeliverPageOnlyCreatesAndReusesRelease` |
| Shared Go settings for validation and builds | `internal/gotool`, `internal/process`, `internal/candidate`, `internal/build` | `TestDefaultValidationDoesNotInheritGOFLAGSRunFilter`, `TestNativeAndStaticCommandsUseOwnedBaseline`, `TestPersistentSettingsPreserveCachesAndNativeCTools`, `TestBuildIgnoresAmbientAndPersistentArchitectureTuning` |
| Authoritative endpoint and exact annotated tag publication | `releasekit.Publish`, `internal/publish`, `internal/git` | `TestPublishPushesOnlyTheCapturedAnnotatedTag`, `TestResolveUsesPushURLAndQueriesWithoutMutation` |
| Authoritative endpoint and exact annotated tag publication | `releasekit.Publish`, `internal/publish`, `internal/git` | `TestPublishPushesOnlyTheCapturedAnnotatedTag`, `TestPublishRejectsUnexpectedPushURLBeforeValidation`, `TestPublishComparesExpectedURLWithResolvedInsteadOfDestination`, `TestPublishExpectedPushURLUsesPushURLWhenFetchAndPushDiffer`, `TestCheckIgnoresExpectedPushURL`, `TestResolveUsesPushURLAndQueriesWithoutMutation` |
| Publication partial and uncertain recovery | `internal/publish`, `internal/git` | `TestPublishRejectedPushKeepsLocalTagAndReportsNotPublished`, `TestPublishLostAcknowledgmentConfirmsExactRemoteObject`, `TestPublishLostAcknowledgmentWithChangedEndpointRemainsUnknown`, `TestPublishUnavailableReconciliationRemainsUnknownAndPreservesPushError` |
| Cross-builds, version smoke checks, empty target sets, and partial outputs | `releasekit.Build`, `internal/build`, `buildinfo` | `TestBuildCreatesSelectedTargetsAndInjectsVersion`, `TestBuildWithNoTargetsDoesNotInvokeCompiler`, `TestBuildReturnsPartialArtifactsAfterTargetFailure` |
| Page-only and managed or prebuilt binary delivery | `releasekit.Deliver`, `internal/delivery`, `internal/gitea`, CLI | `TestDeliverPageOnlySkipsBuildAndHostedAssets`, `TestDeliverManagedBuildUploadsArtifactsAndCleansTemporaryOutput`, `TestDeliverBinaryConsumerWithBuildinfoAndSelectedTargets`, `TestDeliverPrebuiltArtifactsRemainCallerOwned`, `TestDeliverManagedBuildFailureCleansTemporaryOutputBeforeHostedWrites`, `TestDeliverManagedBuildCancellationCleansTemporaryOutput`, `TestDeliverBuildsTaggedArtifactsAndReusesAssets`, `TestDeliverRejectsTamperedAssetsAndStaleReceiptBeforeRemoteWrites` |

View File

@@ -295,6 +295,11 @@ Reuse the existing Go environment tests for controls already protected there.
## Stage 4: Compare the optional expected push URL
**Completion:** Complete. `ReleaseRequest.ExpectedPushURL` and profile
`expected_push_url` require an exact match with Git's resolved effective push
URL before validation. Mismatches return `ErrUnexpectedPushURL` with expected
and resolved values; `Check` ignores the publication-only expectation.
**Outcome:** Publication rejects a different effective destination before validation.
1. Add `ExpectedPushURL` and `expected_push_url`, with an empty value selecting