9.9 KiB
Source tag publication
Use releasekit.Publish to validate a committed candidate and push its exact
annotated source tag:
result, err := releasekit.Publish(ctx, releasekit.ReleaseRequest{
RepositoryDir: "/work/project",
ModuleDir: "integrations/example",
Product: "Example",
Version: "v1.2.3",
ExpectedPushURL: "ssh://git.example/owner/example.git",
Validation: releasekit.ValidationSpec{
Go: releasekit.GoValidationSpec{Race: true, FullVet: true},
},
AdditionalChecks: func(ctx context.Context, candidate releasekit.Candidate) error {
return runProjectChecks(ctx, candidate.ModuleDir)
},
})
if err != nil {
return err
}
fmt.Println(result.ModulePath, result.Version, result.Tag, result.CandidateCommit, result.TagObject)
ModuleDir is a repository-relative physical module directory; omit it or use
. for a root-module release. ReleaseKit reads the selected module's committed
declaration and derives the complete Git tag from its declared module path and
physical location. Version remains the stable semantic version. The note is
<module>/docs/releases/<version>.md; an on-disk /v2 directory is part of
the note path but is removed from the tag prefix only when the module path ends
in /v2. PublicationResult returns the absolute ModuleDir, declared
ModulePath, complete Tag, and existing commit and tag-object evidence.
The optional ValidationSpec.SharedFiles selects additional committed files
for enabled file checks. Its nil and empty-slice behavior is described in the
candidate check guide.
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
successful tests, with test-time vet disabled for that suite. The zero values
keep ordinary test-time vet behavior. It then calls AdditionalChecks once
when supplied. The standard suite cannot be replaced;
move product-specific behavior into AdditionalChecks and remove repeated
standard-suite commands from custom checks. A failed suite or additional check
prevents tag creation, and wrapped errors preserve their causes.
If race prerequisites are unavailable, the selected suite fails as go-test
instead of silently running without the race detector. Full-vet failures are
reported as go-vet and stop publication before tag creation.
Set Validation.Go.CompileTargets to compile packages for each complete target
pair after tests and vet. This uses CGO_ENABLED=0, produces no artifact, and
does not execute target binaries. A failure is reported as package-compile
with the selected target and prevents tag creation.
Set Validation.GoFormat to check committed regular Go source using the Go
formatter built into ReleaseKit. It uses the selected file inputs, including
vendor and explicit shared files, and ignores
worktree changes and symlink blobs, and reports unformatted paths as
go-format without changing files. The default is disabled. The check follows
tests, selected vet, and package compilation, and a failure stops publication
before product checks or tag creation.
Set Validation.ModuleDistribution.WorkspaceFiles to reject committed
go.work and go.work.sum blobs in the selected inputs, including symlink entries.
The rule reads the captured candidate regardless of GOWORK; uncommitted files
are ignored, and Go workspace selection and vendor behavior are unchanged. Its
zero value disables the check. Findings use workspace-files and stop
publication before product checks or tag creation.
Set Validation.ModuleDistribution.LocalReplacements independently to parse
selected module's committed go.mod declarations. Vendored contents remain in
scope; automatic inputs stop at independently released nested modules. Explicit
shared files can include another module's declaration. Filesystem
replace directives are reported with their source path, line, and target as
local-replacements; versioned replacements are
permitted. Committed module symlinks are resolved within the candidate, and
parse or inspection errors retain their causes. This check is independent of
GOWORK; its zero value disables it, and a failure stops publication before
product checks or tag creation.
Set Validation.FileSize to check each committed blob against a byte limit.
Nil disables the check; an empty object enables the default of 1,000,000
decimal bytes. LimitBytes uses strict greater-than, accepts zero, and rejects
negative values. Exclusions contains literal files or directories relative to
the selected module, including committed descendants of excluded directories.
Symlink blob size is its link text; gitlinks are omitted. This check measures only the
captured candidate and reports one oversized file with its measurement as
file-size; it stops publication before product checks or tag creation.
Set Validation.Markdown to check committed local Markdown destinations and
supported heading fragments during publication. Nil disables the check; an
empty object enables it with outside-checkout links reported as out of scope.
Set EnforceCheckoutBoundary to fail those links. Submodule contents remain
out of scope under either setting. Observations use the request's output writer;
failures use markdown-links and stop publication before product checks or tag
creation.
ReleaseKit-owned selections replace duplicate generic suite, vet, formatting,
distribution, and Markdown checks in configured product checks. Keep
release-note rules and consumer-specific assertions, such as maintained-example
tests, in AdditionalChecks or profile checks.
If Build selects targets, ReleaseKit verifies them after the standard suite
and additional checks, before creating the tag. The operation then rechecks the
candidate and remote guards, creates an annotated tag for the captured commit,
and pushes only that tag object.
A successful result means Git confirmed the push or an exact authoritative tag
query confirmed it after a push error. A failed inspection after local tag
creation can leave the tag ref even when its object ID is unavailable; when
the ID was established it remains in TagObject. The local tag is retained
after a failed push. PublicationState distinguishes a known failure, conflict,
confirmed publication, and unresolved outcome. Resolve an unknown result
against the captured endpoint before retrying; see the maintainer procedure
for the recovery steps. The result's endpoint is the exact URL and can contain
credentials; handle it accordingly. The CLI contract documents
command output and exit statuses.
Retain and reconcile publication evidence
Publish automatically attempts to save one private record for each invocation.
The default store is under the repository's Git common directory, so linked
worktrees share it. ReleaseRequest.PublicationRecordDir can override the
directory; relative overrides resolve from the source repository root. The CLI
equivalent is publish --record-dir DIR. ReleaseKit creates its owned storage
directories privately and creates record files with owner-only permissions;
existing directory permissions are left as they are. The caller owns storage
retention and removal.
The saved endpoint is retained exactly for later observation and may include
credentials. Treat records, PublicationEvidence, and PublicationResult as
sensitive. Store overrides in a location with suitable access controls, avoid
printing raw endpoint values, and remove records when their recovery value has
expired. Diagnostic errors and CLI output apply credential redaction; the
private record and typed evidence preserve the raw endpoint.
Recording failure is reported in PublicationResult.RecordError, separately
from Publish's return error and publication state. A successful push remains
successful if storage fails, but the saved handoff is unavailable. Callers can
still pass result.Evidence directly to ReconcilePublication when it contains
the required identity. For example:
func observe(ctx context.Context, result releasekit.PublicationResult) (releasekit.PublicationReconciliationResult, error) {
return releasekit.ReconcilePublication(ctx, result.Evidence)
}
The evidence is populated as publication identity becomes available, including partial evidence on some earlier failures. An abrupt process termination before the save point can leave no record; removed storage also removes the handoff. Records from older attempts may lack the tag object, endpoint, or other identity needed for a conclusive observation.
Use LoadPublicationEvidence(path) to read one explicit record,
ListPublicationAttempts(ctx, request) to discover readable records and
per-record diagnostics in a local directory, or ReconcilePublication(ctx, evidence) to observe a selected endpoint. Listing does not query remotes, and
neither listing nor reconciliation changes saved records. Recovery does not
infer the attempt from current HEAD, local tags, module files, or profiles.
ReconcilePublication returns a nil error for a completed published,
not_published, or conflict observation; unresolved unknown results include
a typed error that retains the underlying cause. See the
GoDoc for the exported fields and error kinds, the
CLI guide for command statuses, and the
record format guide for supported
stored and structured formats.