Files
ReleaseKit/check.go

189 lines
7.8 KiB
Go
Raw Normal View History

2026-10-05 17:47:50 +00:00
package releasekit
import (
"context"
"io"
internalbuild "gitea.maximumdirect.net/eric/ReleaseKit/internal/build"
2026-10-05 17:47:50 +00:00
"gitea.maximumdirect.net/eric/ReleaseKit/internal/candidate"
2026-10-07 22:31:58 +00:00
"gitea.maximumdirect.net/eric/ReleaseKit/internal/publish"
2026-10-07 22:10:34 +00:00
"gitea.maximumdirect.net/eric/ReleaseKit/internal/validation"
2026-10-05 17:47:50 +00:00
)
// ErrInvalidVersion reports a version that is not stable vMAJOR.MINOR.PATCH.
var ErrInvalidVersion = candidate.ErrInvalidVersion
2026-10-07 22:31:58 +00:00
// ErrUnexpectedPushURL reports that the effective publication push URL does
// not exactly match ReleaseRequest.ExpectedPushURL.
var ErrUnexpectedPushURL = publish.ErrUnexpectedPushURL
2026-10-05 17:47:50 +00:00
// Candidate identifies one committed local source revision selected for
// validation.
type Candidate struct {
RepositoryDir string
Commit string
Version string
}
// ReleaseRequest contains the inputs used to validate and optionally build one
// local candidate. Remote is retained for publication operations; Check makes
// no remote availability claim.
2026-10-05 17:47:50 +00:00
type ReleaseRequest struct {
RepositoryDir string
Product string
Version string
Remote string
2026-10-07 22:31:58 +00:00
// ExpectedPushURL optionally requires Publish's resolved push URL to match
// this string exactly before validation or tag creation. Check ignores this
// publication-only expectation. A mismatch returns ErrUnexpectedPushURL.
ExpectedPushURL string
2026-10-07 22:10:34 +00:00
// Validation selects optional checks run against the captured candidate.
Validation ValidationSpec
// AdditionalChecks optionally runs product checks after shared validation.
2026-10-06 17:15:46 +00:00
AdditionalChecks func(context.Context, Candidate) error
Build *BuildSpec // optional selected-target build after validation.
2026-10-06 17:15:46 +00:00
Output io.Writer
2026-10-05 17:47:50 +00:00
}
2026-10-07 22:10:34 +00:00
// ValidationSpec selects optional shared validation checks. Its zero value
// preserves the current validation behavior.
type ValidationSpec struct {
Go GoValidationSpec
2026-10-07 22:39:15 +00:00
// GoFormat checks the formatting of committed regular Go source files. It
// does not modify repository contents and is disabled when false.
GoFormat bool
2026-10-07 22:45:47 +00:00
// ModuleDistribution selects checks for files that affect module-source
// distribution. Its zero value disables those checks.
ModuleDistribution ModuleDistributionValidationSpec
2026-10-07 22:58:38 +00:00
// FileSize selects a committed-file size check. Nil disables it; an empty
// object selects the 1,000,000-byte default.
FileSize *FileSizeValidationSpec
}
// FileSizeValidationSpec selects a maximum size for each committed blob.
type FileSizeValidationSpec struct {
// LimitBytes is the maximum decimal byte count. Nil selects 1,000,000;
// zero is a zero-byte limit, and negative values are invalid.
LimitBytes *int64
// Exclusions are literal repository-relative file or directory paths.
// A directory exclusion includes committed descendants.
Exclusions []string
2026-10-07 22:45:47 +00:00
}
// ModuleDistributionValidationSpec selects independent module-source
// distribution checks.
type ModuleDistributionValidationSpec struct {
// WorkspaceFiles rejects committed go.work and go.work.sum blobs, including
// nested paths and symlink entries. It does not select Go's test workspace.
WorkspaceFiles bool
2026-10-07 22:51:55 +00:00
// LocalReplacements rejects filesystem replacements in committed go.mod
// declarations, including nested modules. It does not select Go's workspace.
LocalReplacements bool
2026-10-07 22:10:34 +00:00
}
// GoValidationSpec selects checks performed by the Go toolchain.
type GoValidationSpec struct {
// Race runs the one standard Go test suite with the race detector enabled.
Race bool
2026-10-07 22:18:47 +00:00
// FullVet runs go vet after the suite instead of the test-time vet pass.
// Failures use the go-vet validation check identity.
FullVet bool
2026-10-07 22:27:31 +00:00
// CompileTargets checks that every package builds for each complete target
// pair after testing and vet. Compilation uses CGO_ENABLED=0 and does not
// produce artifacts or execute target binaries. Failures use the
// package-compile validation check identity.
CompileTargets []Target
2026-10-07 22:10:34 +00:00
}
2026-10-05 17:47:50 +00:00
// Check validates one local, committed release candidate. It requires a clean
// worktree and index on main, a committed note matching Product and Version,
2026-10-06 17:15:46 +00:00
// and an unused local tag. It always runs the offline Go test suite with the
// available compatible local Go toolchain and Go host target, then calls
2026-10-07 22:10:34 +00:00
// AdditionalChecks when supplied. Validation.Go.Race runs that one suite with
2026-10-07 22:27:31 +00:00
// the race detector, Validation.Go.FullVet runs go vet after the suite instead
// of test-time vet, and Validation.Go.CompileTargets compiles all packages for
// each selected GOOS/GOARCH pair after tests and vet with cgo disabled. Package
// compilation produces no artifacts and does not execute target binaries.
2026-10-07 22:39:15 +00:00
// Validation.GoFormat checks regular committed Go source after those selected
// Go checks and before AdditionalChecks. Its zero value disables formatting.
2026-10-07 22:45:47 +00:00
// Validation.ModuleDistribution.WorkspaceFiles rejects committed workspace
// files after the other selected shared checks. Its zero value disables the
// distribution check and does not change Go's workspace environment.
2026-10-07 22:51:55 +00:00
// Validation.ModuleDistribution.LocalReplacements parses committed go.mod
// declarations, including nested modules, and rejects filesystem replacements
// after workspace-file inspection. Its zero value disables that check.
2026-10-07 22:58:38 +00:00
// Validation.FileSize checks each selected committed blob after module
// distribution checks. Its nil value disables size checks; an empty object
// enables the default limit.
2026-10-07 22:39:15 +00:00
// The zero values of all selections preserve the ordinary suite. If
2026-10-07 22:10:34 +00:00
// Build selects targets, Check builds them after validation in temporary
// storage outside the worktree, then rechecks
2026-10-06 17:15:46 +00:00
// branch, HEAD, worktree, index, and local tag state. Success establishes local
// candidate evidence only; it does not establish remote availability.
2026-10-05 17:47:50 +00:00
func Check(ctx context.Context, request ReleaseRequest) (Candidate, error) {
2026-10-07 18:41:29 +00:00
checked, err := candidate.Check(ctx, candidateRequest(request))
2026-10-05 17:47:50 +00:00
if err != nil {
return Candidate{}, convertValidationError(err)
2026-10-05 17:47:50 +00:00
}
return Candidate{
RepositoryDir: checked.RepositoryDir,
Commit: checked.Commit,
Version: checked.Version,
}, nil
}
2026-10-07 18:41:29 +00:00
func candidateRequest(request ReleaseRequest) candidate.Request {
2026-10-07 22:58:38 +00:00
fileSize := internalFileSizeSettings(request.Validation.FileSize)
2026-10-07 22:27:31 +00:00
result := candidate.Request{
2026-10-07 22:18:47 +00:00
RepositoryDir: request.RepositoryDir,
Product: request.Product,
Version: request.Version,
Validation: validation.Settings{
2026-10-07 22:27:31 +00:00
Race: request.Validation.Go.Race,
FullVet: request.Validation.Go.FullVet,
CompileTargets: make([]validation.Target, len(request.Validation.Go.CompileTargets)),
2026-10-07 22:39:15 +00:00
GoFormat: request.Validation.GoFormat,
2026-10-07 22:45:47 +00:00
ModuleDistribution: validation.ModuleDistributionSettings{
2026-10-07 22:51:55 +00:00
WorkspaceFiles: request.Validation.ModuleDistribution.WorkspaceFiles,
LocalReplacements: request.Validation.ModuleDistribution.LocalReplacements,
2026-10-07 22:45:47 +00:00
},
2026-10-07 22:58:38 +00:00
FileSize: fileSize,
2026-10-07 22:18:47 +00:00
},
2026-10-07 18:41:29 +00:00
AdditionalChecks: candidateChecks(request.AdditionalChecks),
AfterValidate: internalbuild.CandidateCallback(internalBuildSpec(request.Build), request.Output),
Output: request.Output,
}
2026-10-07 22:27:31 +00:00
for index, target := range request.Validation.Go.CompileTargets {
result.Validation.CompileTargets[index] = validation.Target{OS: target.OS, Arch: target.Arch}
}
return result
2026-10-07 18:41:29 +00:00
}
2026-10-07 22:58:38 +00:00
func internalFileSizeSettings(spec *FileSizeValidationSpec) *validation.FileSizeSettings {
if spec == nil {
return nil
}
var limitBytes *int64
if spec.LimitBytes != nil {
limit := *spec.LimitBytes
limitBytes = &limit
}
return &validation.FileSizeSettings{
LimitBytes: limitBytes,
Exclusions: append([]string(nil), spec.Exclusions...),
}
}
func candidateChecks(checks func(context.Context, Candidate) error) func(context.Context, candidate.Candidate) error {
if checks == nil {
return nil
}
return func(ctx context.Context, captured candidate.Candidate) error {
return checks(ctx, Candidate{
RepositoryDir: captured.RepositoryDir,
Commit: captured.Commit,
Version: captured.Version,
})
}
}