Coordinate separate full vet pass
This commit is contained in:
17
check.go
17
check.go
@@ -46,6 +46,9 @@ type ValidationSpec struct {
|
||||
type GoValidationSpec struct {
|
||||
// Race runs the one standard Go test suite with the race detector enabled.
|
||||
Race bool
|
||||
// FullVet runs go vet after the suite instead of the test-time vet pass.
|
||||
// Failures use the go-vet validation check identity.
|
||||
FullVet bool
|
||||
}
|
||||
|
||||
// Check validates one local, committed release candidate. It requires a clean
|
||||
@@ -53,7 +56,8 @@ type GoValidationSpec struct {
|
||||
// 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
|
||||
// AdditionalChecks when supplied. Validation.Go.Race runs that one suite with
|
||||
// the race detector; the zero value preserves the ordinary native suite. If
|
||||
// the race detector, and Validation.Go.FullVet runs go vet after the suite
|
||||
// instead of test-time vet. Their zero values preserve the ordinary suite. If
|
||||
// Build selects targets, Check builds them after validation in temporary
|
||||
// storage outside the worktree, then rechecks
|
||||
// branch, HEAD, worktree, index, and local tag state. Success establishes local
|
||||
@@ -72,10 +76,13 @@ func Check(ctx context.Context, request ReleaseRequest) (Candidate, error) {
|
||||
|
||||
func candidateRequest(request ReleaseRequest) candidate.Request {
|
||||
return candidate.Request{
|
||||
RepositoryDir: request.RepositoryDir,
|
||||
Product: request.Product,
|
||||
Version: request.Version,
|
||||
Validation: validation.Settings{Race: request.Validation.Go.Race},
|
||||
RepositoryDir: request.RepositoryDir,
|
||||
Product: request.Product,
|
||||
Version: request.Version,
|
||||
Validation: validation.Settings{
|
||||
Race: request.Validation.Go.Race,
|
||||
FullVet: request.Validation.Go.FullVet,
|
||||
},
|
||||
AdditionalChecks: candidateChecks(request.AdditionalChecks),
|
||||
AfterValidate: internalbuild.CandidateCallback(internalBuildSpec(request.Build), request.Output),
|
||||
Output: request.Output,
|
||||
|
||||
@@ -130,7 +130,8 @@ type ValidationSettings struct {
|
||||
|
||||
// GoValidationSettings selects checks performed by the Go toolchain.
|
||||
type GoValidationSettings struct {
|
||||
Race bool `json:"race,omitempty"`
|
||||
Race bool `json:"race,omitempty"`
|
||||
FullVet bool `json:"full_vet,omitempty"`
|
||||
}
|
||||
|
||||
// DeliverySettings selects the hosted Gitea release destination.
|
||||
|
||||
@@ -141,10 +141,10 @@ func TestLoadSettingsDefaultsRemoteAndConfigPath(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadSettingsReadsRaceValidation(t *testing.T) {
|
||||
func TestLoadSettingsReadsGoValidation(t *testing.T) {
|
||||
repoDir := t.TempDir()
|
||||
profilePath := filepath.Join(repoDir, "releasekit.json")
|
||||
if err := os.WriteFile(profilePath, []byte(`{"product":"Example","validation":{"go":{"race":true}}}`), 0o600); err != nil {
|
||||
if err := os.WriteFile(profilePath, []byte(`{"product":"Example","validation":{"go":{"race":true,"full_vet":true}}}`), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -152,8 +152,8 @@ func TestLoadSettingsReadsRaceValidation(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if settings.Validation == nil || !settings.Validation.Go.Race {
|
||||
t.Fatalf("validation settings = %#v, want Go race enabled", settings.Validation)
|
||||
if settings.Validation == nil || !settings.Validation.Go.Race || !settings.Validation.Go.FullVet {
|
||||
t.Fatalf("validation settings = %#v, want Go race and full vet enabled", settings.Validation)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -160,7 +160,10 @@ func validationSpec(settings *cli.ValidationSettings) releasekit.ValidationSpec
|
||||
return releasekit.ValidationSpec{}
|
||||
}
|
||||
return releasekit.ValidationSpec{
|
||||
Go: releasekit.GoValidationSpec{Race: settings.Go.Race},
|
||||
Go: releasekit.GoValidationSpec{
|
||||
Race: settings.Go.Race,
|
||||
FullVet: settings.Go.FullVet,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -112,15 +112,15 @@ func TestCheckCommandBindsConfiguredChecksToCapturedCandidate(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCandidateRequestMapsRaceValidation(t *testing.T) {
|
||||
func TestCandidateRequestMapsGoValidation(t *testing.T) {
|
||||
request := candidateRequest(cli.Invocation{Repository: "repository", Version: "v1.2.3"}, cli.Settings{
|
||||
Product: "Example Product",
|
||||
Validation: &cli.ValidationSettings{
|
||||
Go: cli.GoValidationSettings{Race: true},
|
||||
Go: cli.GoValidationSettings{Race: true, FullVet: true},
|
||||
},
|
||||
}, nil)
|
||||
if !request.Validation.Go.Race {
|
||||
t.Fatalf("candidate request validation = %#v, want Go race enabled", request.Validation)
|
||||
if !request.Validation.Go.Race || !request.Validation.Go.FullVet {
|
||||
t.Fatalf("candidate request validation = %#v, want Go race and full vet enabled", request.Validation)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
17
docs/cli.md
17
docs/cli.md
@@ -25,13 +25,16 @@ version. The repository defaults to the current directory, and the profile
|
||||
defaults to `<repository>/releasekit.json`. A successful command prints the
|
||||
captured local commit and version. It checks the committed release note and
|
||||
local Git state, runs the standard offline Go test suite (with `-race` when
|
||||
`validation.go.race` is true), and then runs any configured additional checks
|
||||
in profile order. It does not inspect remote availability or publish a tag.
|
||||
`validation.go.race` is true), and runs a separate `go vet -mod=readonly ./...`
|
||||
pass after successful tests when `validation.go.full_vet` is true. It then runs
|
||||
configured additional checks in profile order. It does not inspect remote
|
||||
availability or publish a tag.
|
||||
|
||||
`check` writes errors and diagnostics to standard error. It returns status 0
|
||||
after local validation, 1 after a candidate validation failure, and 2 for usage,
|
||||
profile, or invalid-version errors. Shared validation failures identify the
|
||||
failed check and retain useful findings and cause context in one diagnostic.
|
||||
failed check and retain useful findings and cause context in one diagnostic;
|
||||
separate full-vet failures use the `go-vet` identity.
|
||||
Unsupported argument sequences print usage to standard error.
|
||||
|
||||
Publish a source tag:
|
||||
@@ -43,9 +46,11 @@ go run ./cmd/releasekit publish [--repo DIR] [--config FILE] VERSION
|
||||
`publish` reads the same product, remote, validation, and additional-check
|
||||
settings as `check`. It checks the authoritative remote and validates the
|
||||
candidate: the single standard offline Go test suite runs first, with `-race`
|
||||
when `validation.go.race` is true, followed by configured checks in profile
|
||||
order. It then creates an annotated local tag and pushes only its exact
|
||||
tag object. It prints the confirmed version, candidate commit, and tag object
|
||||
when `validation.go.race` is true. When `validation.go.full_vet` is true,
|
||||
test-time vet is disabled and `go vet -mod=readonly ./...` runs after successful
|
||||
tests. Configured checks then run in profile order. It then creates an annotated
|
||||
local tag and pushes only its exact tag object. It prints the confirmed version,
|
||||
candidate commit, and tag object
|
||||
on success. Validation
|
||||
progress and failed publication state, including the captured publication
|
||||
endpoint for recovery, are written to standard error. A signal
|
||||
|
||||
@@ -28,12 +28,15 @@ target requires the package and binary name; each target is written as one
|
||||
|
||||
The optional `validation` object selects shared checks implemented by ReleaseKit.
|
||||
`validation.go.race` enables the race detector for the single standard Go test
|
||||
suite. When omitted or false, the ordinary native suite runs. For example:
|
||||
suite. `validation.go.full_vet` runs a separate native `go vet` pass after that
|
||||
suite succeeds; ReleaseKit disables test-time vet for that suite to avoid
|
||||
running vet twice. When `full_vet` is omitted or false, the test command keeps
|
||||
its existing vet behavior. Both settings are independent. For example:
|
||||
|
||||
```json
|
||||
{
|
||||
"product": "Example",
|
||||
"validation": {"go": {"race": true}}
|
||||
"validation": {"go": {"race": true, "full_vet": true}}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -46,11 +49,13 @@ passes its value explicitly to the library operation.
|
||||
|
||||
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. They then run
|
||||
access disabled, adding `-race` when `validation.go.race` is true. When
|
||||
`validation.go.full_vet` is true, they add `-vet=off` to that test command and
|
||||
run native `go vet -mod=readonly ./...` after successful tests. They then run
|
||||
each configured `checks` command once, in order, as an additional product
|
||||
check. Selected-target builds run after the suite and product checks. Do not
|
||||
repeat the standard suite in `checks`; doing so runs it twice. The `build`
|
||||
command uses the selected build settings and writes binaries to `dist` unless
|
||||
check. Selected-target builds run after shared validation and product checks.
|
||||
Do not repeat the standard suite or vet in `checks`; doing so repeats work. The
|
||||
`build` command uses the selected build settings and writes binaries to `dist` unless
|
||||
`--output` selects another path.
|
||||
The `deliver` command uses the delivery destination and token. By default it
|
||||
passes selected targets to the delivery facade, which builds tagged-source
|
||||
|
||||
@@ -9,7 +9,7 @@ result, err := releasekit.Check(ctx, releasekit.ReleaseRequest{
|
||||
Product: "Example",
|
||||
Version: "v1.2.3",
|
||||
Validation: releasekit.ValidationSpec{
|
||||
Go: releasekit.GoValidationSpec{Race: true},
|
||||
Go: releasekit.GoValidationSpec{Race: true, FullVet: true},
|
||||
},
|
||||
AdditionalChecks: func(ctx context.Context, candidate releasekit.Candidate) error {
|
||||
return runProjectChecks(ctx, candidate.RepositoryDir)
|
||||
@@ -28,11 +28,14 @@ product name and exact version as complete title elements. No local tag with
|
||||
that version may already exist.
|
||||
|
||||
ReleaseKit runs one `go test -mod=readonly -count=1 ./...` suite with the
|
||||
available compatible local Go toolchain, then calls `AdditionalChecks` once
|
||||
when supplied. Set `Validation.Go.Race` to run that suite with `-race`; the zero
|
||||
value keeps the ordinary native suite. ReleaseKit does not download a different
|
||||
toolchain automatically. If the race prerequisite is unavailable, the `go-test`
|
||||
check fails rather than being skipped. The suite uses the Go host target,
|
||||
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 the suite. It calls `AdditionalChecks` once after shared validation when
|
||||
supplied. Their zero values retain the ordinary native test behavior. ReleaseKit
|
||||
does not download a different toolchain automatically. If race prerequisites
|
||||
are unavailable, the `go-test` check fails rather than being skipped; a selected
|
||||
full-vet failure is reported as `go-vet`. The suite uses the Go host target,
|
||||
disables workspace and module network access, and ignores ambient `GOFLAGS`,
|
||||
experiment, and CPU feature overrides. Effective cache and native cgo/C-tool
|
||||
settings are preserved, including values from Go's persistent environment
|
||||
|
||||
@@ -9,7 +9,7 @@ result, err := releasekit.Publish(ctx, releasekit.ReleaseRequest{
|
||||
Product: "Example",
|
||||
Version: "v1.2.3",
|
||||
Validation: releasekit.ValidationSpec{
|
||||
Go: releasekit.GoValidationSpec{Race: true},
|
||||
Go: releasekit.GoValidationSpec{Race: true, FullVet: true},
|
||||
},
|
||||
AdditionalChecks: func(ctx context.Context, candidate releasekit.Candidate) error {
|
||||
return runProjectChecks(ctx, candidate.RepositoryDir)
|
||||
@@ -25,13 +25,16 @@ Publication captures a clean candidate on `main`, validates its committed
|
||||
release note, and checks the selected remote before running candidate checks.
|
||||
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`; its zero value keeps the ordinary native suite. It then calls
|
||||
`AdditionalChecks` once when supplied. The standard suite cannot be replaced;
|
||||
`-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.
|
||||
instead of silently running without the race detector. Full-vet failures are
|
||||
reported as `go-vet` and stop publication before tag creation.
|
||||
|
||||
If `Build` selects targets, ReleaseKit verifies them after the standard suite
|
||||
and additional checks, before creating the tag. The operation then rechecks the
|
||||
|
||||
@@ -46,7 +46,7 @@ these implemented packages:
|
||||
| `internal/tempdir` | Disposable system temporary storage outside source worktrees. |
|
||||
| `internal/process` | Cancellable child-process execution with per-command environment overlays and removals. |
|
||||
| `internal/preparation` | Coordination for expected-commit tag checkout and Go module dependency preparation. |
|
||||
| `internal/validation` | Shared Go validation with optional race selection, structured check failures, and lazy committed-file access for captured candidates; `internal/candidate` retains capture, callback sequencing, and local-state rechecks. |
|
||||
| `internal/validation` | Shared Go validation with optional race and full-vet selections, structured check failures, and lazy committed-file access for captured candidates; `internal/candidate` retains capture, callback sequencing, and local-state rechecks. |
|
||||
| `internal/publish` | Publication guards, one validation run, annotated tag creation, push, and recovery state. |
|
||||
| `internal/runtimeinfo` | Private version lookup used by the runtime facade. |
|
||||
| `internal/versioncontract` | Shared stable-version rules and linker-symbol identity. |
|
||||
|
||||
@@ -9,8 +9,12 @@ adds its endpoint guards, tag creation, push, and recovery around that same
|
||||
candidate path.
|
||||
|
||||
The executor runs one offline Go suite, optionally with the race detector when
|
||||
selected. Its request carries the captured repository directory and commit,
|
||||
output writer, and internal settings. `Request.ReadCandidateTree` lets a
|
||||
selected. When full vet is selected, it disables test-time vet for that suite
|
||||
and runs native `go vet -mod=readonly ./...` after the suite succeeds, using the
|
||||
same discovered Go tool and native environment. Test and vet failures retain
|
||||
their separate `go-test` and `go-vet` identities and underlying causes. Its
|
||||
request carries the captured repository directory and commit, output writer,
|
||||
and internal settings. `Request.ReadCandidateTree` lets a
|
||||
selected file check request candidate contents; the Go-only path does not
|
||||
enumerate the tree.
|
||||
`CandidateTree` gets metadata and bytes from Git objects, reads blob bytes
|
||||
|
||||
@@ -18,9 +18,11 @@ handles symlinks, parent traversal, cancellation, and absolute in-checkout paths
|
||||
The root error types and CLI presentation preserve findings and cause context.
|
||||
`buildinfo` retains its independent runtime dependency graph.
|
||||
|
||||
The public validation specification and its JSON settings remain unimplemented.
|
||||
The consumer CI template still selects an independent tool version. This sequence
|
||||
completes every remaining release-roadmap capability and Audita's CI migration.
|
||||
The shared validation specification and profile settings are implemented
|
||||
incrementally with each capability; remaining selections are not yet
|
||||
implemented. The consumer CI template still selects an independent tool
|
||||
version. This sequence completes every remaining release-roadmap capability and
|
||||
Audita's CI migration.
|
||||
It ends with local verification and committed changes. Architecture review,
|
||||
hosted deployment, release tagging, and decisions about further testing follow
|
||||
this implementation.
|
||||
@@ -250,6 +252,11 @@ hosts and a controlled Go command for portable prerequisite-failure coverage.
|
||||
|
||||
## Stage 2: Coordinate a separate full vet pass
|
||||
|
||||
**Completion:** Complete. `ReleaseRequest.Validation.Go.FullVet` and profile
|
||||
`validation.go.full_vet` disable test-time vet for the one native test suite and
|
||||
run native `go vet -mod=readonly ./...` afterward. Vet failures retain the
|
||||
`go-vet` identity, and the selection is shared by the API and profile paths.
|
||||
|
||||
**Outcome:** Full vet replaces test-time vet overlap through one declaration.
|
||||
|
||||
1. Add `Go.FullVet` and `validation.go.full_vet` through the same conversion path.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Validation API and profile model
|
||||
|
||||
Status: Shared validation foundation and Go race selection implemented;
|
||||
remaining v0.2.0 capabilities and their settings are future work.
|
||||
Status: Shared validation foundation, Go race selection, and separate full vet
|
||||
implemented; remaining v0.2.0 capabilities and their settings are future work.
|
||||
|
||||
This roadmap describes the shared model that enables the optional validation
|
||||
capabilities in the [v0.2.0 roadmap](v0.2.0.md). It owns their common API,
|
||||
@@ -18,9 +18,9 @@ Selected executable builds follow validation. The CLI maps profile `checks`
|
||||
commands to the additional-check callback.
|
||||
|
||||
This provides a common procedure and a product-specific extension point. The
|
||||
request and profile now have a structured selection for race testing; full vet,
|
||||
package compilation, formatting, Markdown, module-distribution, and file-size
|
||||
checks will use the same model as their capabilities are added.
|
||||
request and profile have structured selections for race testing and a separate
|
||||
full vet pass. Package compilation, formatting, Markdown, module-distribution,
|
||||
and file-size checks will use the same model as their capabilities are added.
|
||||
|
||||
The model should let consumers declare their choices once while ReleaseKit owns
|
||||
execution and coordination. This follows [ADR 0002](../adr/0002-routine-releases.md):
|
||||
@@ -125,15 +125,18 @@ failure-stopping behavior of the candidate workflow.
|
||||
## Implementation
|
||||
|
||||
The [implementation plan](implementation.md) records the completed foundation
|
||||
and current rollout. The facade exposes `ReleaseRequest.Validation.Go.Race`, and
|
||||
profiles accept `validation.go.race`; both select `-race` for the one base Go
|
||||
suite. Each remaining setting will be introduced with its first working
|
||||
capability. Individual checkers, module-selected CI tooling, and the publication
|
||||
endpoint expectation remain future work in the [v0.2.0 roadmap](v0.2.0.md).
|
||||
and current rollout. The facade exposes `ReleaseRequest.Validation.Go.Race` and
|
||||
`FullVet`, and profiles accept `validation.go.race` and
|
||||
`validation.go.full_vet`. Race selection adds `-race` to the one base Go suite.
|
||||
Full-vet selection disables test-time vet and runs native
|
||||
`go vet -mod=readonly ./...` after successful tests. Each remaining setting
|
||||
will be introduced with its first working capability. Individual checkers,
|
||||
module-selected CI tooling, and the publication endpoint expectation remain
|
||||
future work in the [v0.2.0 roadmap](v0.2.0.md).
|
||||
|
||||
The target state above describes the complete model across those capabilities.
|
||||
Foundation completion establishes their shared owners and conversion boundaries,
|
||||
but does not mean the optional checks are implemented or that v0.2.0 is complete.
|
||||
The rollout establishes shared owners and conversion boundaries incrementally;
|
||||
not all optional checks are implemented, and v0.2.0 is not complete.
|
||||
|
||||
## Adoption evidence
|
||||
|
||||
|
||||
@@ -11,6 +11,8 @@ type Check string
|
||||
const (
|
||||
// GoTest identifies the base Go test suite.
|
||||
GoTest Check = "go-test"
|
||||
// GoVet identifies the separate full vet pass.
|
||||
GoVet Check = "go-vet"
|
||||
)
|
||||
|
||||
// Finding describes a validation observation at an optional source location.
|
||||
|
||||
@@ -11,31 +11,41 @@ import (
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestRunUsesOneSuiteWithSelectedRaceSetting(t *testing.T) {
|
||||
func TestRunUsesOneSuiteWithGoValidationSettings(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
race bool
|
||||
name string
|
||||
race bool
|
||||
fullVet bool
|
||||
}{
|
||||
{name: "default"},
|
||||
{name: "race", race: true},
|
||||
{name: "full vet", fullVet: true},
|
||||
{name: "race and full vet", race: true, fullVet: true},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
logPath := installGoShim(t, false)
|
||||
logPath := installGoShim(t, "", false)
|
||||
err := Run(context.Background(), Request{
|
||||
RepositoryDir: t.TempDir(),
|
||||
Settings: Settings{Race: test.race},
|
||||
Settings: Settings{Race: test.race, FullVet: test.fullVet},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Run() error = %v", err)
|
||||
}
|
||||
|
||||
commands := readGoShimLog(t, logPath)
|
||||
var tests []string
|
||||
for _, command := range commands {
|
||||
var tests, vets []string
|
||||
var testPosition, vetPosition int
|
||||
for i, command := range commands {
|
||||
if strings.HasPrefix(command, "test ") {
|
||||
tests = append(tests, command)
|
||||
testPosition = i
|
||||
}
|
||||
if strings.HasPrefix(command, "vet ") {
|
||||
vets = append(vets, command)
|
||||
vetPosition = i
|
||||
}
|
||||
}
|
||||
if len(tests) != 1 {
|
||||
@@ -50,12 +60,33 @@ func TestRunUsesOneSuiteWithSelectedRaceSetting(t *testing.T) {
|
||||
if got := strings.Contains(command, " -race "); got != test.race {
|
||||
t.Errorf("Go test command %q has race=%t, want %t", tests[0], got, test.race)
|
||||
}
|
||||
if got := strings.Contains(command, " -vet=off "); got != test.fullVet {
|
||||
t.Errorf("Go test command %q disables test-time vet=%t, want %t", tests[0], got, test.fullVet)
|
||||
}
|
||||
wantVets := 0
|
||||
if test.fullVet {
|
||||
wantVets = 1
|
||||
}
|
||||
if len(vets) != wantVets {
|
||||
t.Fatalf("Go vet invocations = %q, want %d", vets, wantVets)
|
||||
}
|
||||
if test.fullVet {
|
||||
vetCommand := " " + vets[0] + " "
|
||||
for _, argument := range []string{"vet", "-mod=readonly", "./..."} {
|
||||
if !strings.Contains(vetCommand, " "+argument+" ") {
|
||||
t.Errorf("Go vet command %q does not contain %q", vets[0], argument)
|
||||
}
|
||||
}
|
||||
if vetPosition <= testPosition {
|
||||
t.Errorf("Go vet command %q did not follow the test suite %q", vets[0], tests[0])
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunReportsRacePrerequisiteFailureWithoutFallback(t *testing.T) {
|
||||
logPath := installGoShim(t, true)
|
||||
logPath := installGoShim(t, "race", false)
|
||||
err := Run(context.Background(), Request{
|
||||
RepositoryDir: t.TempDir(),
|
||||
Settings: Settings{Race: true},
|
||||
@@ -80,6 +111,69 @@ func TestRunReportsRacePrerequisiteFailureWithoutFallback(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunStopsAfterGoTestFailure(t *testing.T) {
|
||||
logPath := installGoShim(t, "test", false)
|
||||
err := Run(context.Background(), Request{
|
||||
RepositoryDir: t.TempDir(),
|
||||
Settings: Settings{FullVet: true},
|
||||
})
|
||||
var failure *Failure
|
||||
if !errors.As(err, &failure) || failure.Check != GoTest {
|
||||
t.Fatalf("Run() error = %v, want a go-test validation failure", err)
|
||||
}
|
||||
for _, command := range readGoShimLog(t, logPath) {
|
||||
if strings.HasPrefix(command, "vet ") {
|
||||
t.Fatalf("Go vet ran after Go test failed: %q", command)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunAttributesFullVetFailure(t *testing.T) {
|
||||
logPath := installGoShim(t, "vet", false)
|
||||
err := Run(context.Background(), Request{
|
||||
RepositoryDir: t.TempDir(),
|
||||
Settings: Settings{FullVet: true},
|
||||
})
|
||||
var failure *Failure
|
||||
if !errors.As(err, &failure) || failure.Check != GoVet {
|
||||
t.Fatalf("Run() error = %v, want a go-vet validation failure", err)
|
||||
}
|
||||
var exitError *exec.ExitError
|
||||
if !errors.As(err, &exitError) {
|
||||
t.Fatalf("Run() error = %v, want the command failure preserved", err)
|
||||
}
|
||||
commands := readGoShimLog(t, logPath)
|
||||
var validationCommands []string
|
||||
for _, command := range commands {
|
||||
if strings.HasPrefix(command, "test ") || strings.HasPrefix(command, "vet ") {
|
||||
validationCommands = append(validationCommands, command)
|
||||
}
|
||||
}
|
||||
if len(validationCommands) != 2 || !strings.HasPrefix(validationCommands[0], "test ") || !strings.HasPrefix(validationCommands[1], "vet ") {
|
||||
t.Fatalf("validation command sequence = %q, want test followed by vet", validationCommands)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunPreservesFullVetCancellation(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("the controlled Go command fixture uses a POSIX shell")
|
||||
}
|
||||
installGoShim(t, "", true)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
||||
defer cancel()
|
||||
err := Run(ctx, Request{
|
||||
RepositoryDir: t.TempDir(),
|
||||
Settings: Settings{FullVet: true},
|
||||
})
|
||||
var failure *Failure
|
||||
if !errors.As(err, &failure) || failure.Check != GoVet {
|
||||
t.Fatalf("Run() error = %v, want a go-vet validation failure", err)
|
||||
}
|
||||
if !errors.Is(err, context.DeadlineExceeded) {
|
||||
t.Fatalf("Run() error = %v, want deadline cause preserved", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunExecutesRaceEnabledSuiteOnHost(t *testing.T) {
|
||||
if !raceSupported(runtime.GOOS, runtime.GOARCH) {
|
||||
t.Skipf("Go race detector is not supported on %s/%s", runtime.GOOS, runtime.GOARCH)
|
||||
@@ -112,7 +206,7 @@ func TestRaceFixture(t *testing.T) {}
|
||||
}
|
||||
}
|
||||
|
||||
func installGoShim(t *testing.T, failRace bool) string {
|
||||
func installGoShim(t *testing.T, failure string, delayVet bool) string {
|
||||
t.Helper()
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("the controlled Go command fixture uses a POSIX shell")
|
||||
@@ -131,17 +225,33 @@ func installGoShim(t *testing.T, failRace bool) string {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
failBlock := ""
|
||||
if failRace {
|
||||
failBlock = `
|
||||
vetDelay := ""
|
||||
if delayVet {
|
||||
vetDelay = `
|
||||
if [ "${1-}" = "vet" ]; then
|
||||
exec sleep 5
|
||||
fi
|
||||
`
|
||||
}
|
||||
failBlock := fmt.Sprintf(`
|
||||
if [ "${1-}" = "test" ]; then
|
||||
for argument in "$@"; do
|
||||
if [ "$argument" = "-race" ]; then
|
||||
if [ "%s" = "race" ] && [ "$argument" = "-race" ]; then
|
||||
echo "race detector prerequisite unavailable" >&2
|
||||
exit 2
|
||||
fi
|
||||
done
|
||||
`
|
||||
}
|
||||
if [ "%s" = "test" ]; then
|
||||
echo "Go test failed" >&2
|
||||
exit 2
|
||||
fi
|
||||
fi
|
||||
if [ "${1-}" = "vet" ] && [ "%s" = "vet" ]; then
|
||||
echo "Go vet failed" >&2
|
||||
exit 2
|
||||
fi
|
||||
%s
|
||||
`, failure, failure, failure, vetDelay)
|
||||
script := fmt.Sprintf(`#!/bin/sh
|
||||
set -eu
|
||||
printf '%%s\n' "$*" >> "$RELEASEKIT_GO_LOG"
|
||||
|
||||
@@ -10,7 +10,8 @@ import (
|
||||
|
||||
// Settings selects shared validation behavior.
|
||||
type Settings struct {
|
||||
Race bool
|
||||
Race bool
|
||||
FullVet bool
|
||||
}
|
||||
|
||||
// Request identifies the captured source and output used by shared validation.
|
||||
@@ -37,10 +38,19 @@ func Run(ctx context.Context, request Request) error {
|
||||
if request.Settings.Race {
|
||||
args = append(args, "-race")
|
||||
}
|
||||
if request.Settings.FullVet {
|
||||
args = append(args, "-vet=off")
|
||||
}
|
||||
args = append(args, "./...")
|
||||
_, err = tool.Run(ctx, request.RepositoryDir, gotool.Native, gotool.Target{}, request.Output, args...)
|
||||
if err != nil {
|
||||
return &Failure{Check: GoTest, Cause: err}
|
||||
}
|
||||
if request.Settings.FullVet {
|
||||
_, err = tool.Run(ctx, request.RepositoryDir, gotool.Native, gotool.Target{}, request.Output, "vet", "-mod=readonly", "./...")
|
||||
if err != nil {
|
||||
return &Failure{Check: GoVet, Cause: err}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -48,9 +48,10 @@ type PublicationResult struct {
|
||||
State PublicationState // publication outcome for this invocation
|
||||
}
|
||||
|
||||
// Publish always runs one offline Go test suite for the captured candidate;
|
||||
// Validation.Go.Race enables the race detector for that suite. It then calls
|
||||
// AdditionalChecks when supplied. It optionally builds selected
|
||||
// Publish always runs one offline Go test suite for the captured candidate.
|
||||
// Validation.Go.Race enables the race detector for that suite, and
|
||||
// Validation.Go.FullVet runs go vet afterward instead of test-time vet. It then
|
||||
// calls AdditionalChecks when supplied. It optionally builds selected
|
||||
// targets in temporary storage outside the worktree and pushes the exact
|
||||
// annotated tag object to the selected remote. It returns a result with errors: preflight
|
||||
// failures are not_attempted, failed authoritative guards are conflict, and
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
)
|
||||
|
||||
// ValidationCheck identifies a shared validation check that failed.
|
||||
// Current checks include "go-test".
|
||||
// Current checks include "go-test" and "go-vet".
|
||||
type ValidationCheck string
|
||||
|
||||
// ValidationFinding describes a failed check observation at an optional
|
||||
|
||||
@@ -1,12 +1,29 @@
|
||||
package releasekit
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
func TestCandidateRequestMapsRaceValidation(t *testing.T) {
|
||||
"gitea.maximumdirect.net/eric/ReleaseKit/internal/validation"
|
||||
)
|
||||
|
||||
func TestCandidateRequestMapsGoValidation(t *testing.T) {
|
||||
request := candidateRequest(ReleaseRequest{
|
||||
Validation: ValidationSpec{Go: GoValidationSpec{Race: true}},
|
||||
Validation: ValidationSpec{Go: GoValidationSpec{Race: true, FullVet: true}},
|
||||
})
|
||||
if !request.Validation.Race {
|
||||
t.Fatalf("candidate request validation = %#v, want race enabled", request.Validation)
|
||||
if !request.Validation.Race || !request.Validation.FullVet {
|
||||
t.Fatalf("candidate request validation = %#v, want race and full vet enabled", request.Validation)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidationErrorPreservesGoVetIdentity(t *testing.T) {
|
||||
cause := errors.New("vet failed")
|
||||
got := convertValidationError(&validation.Failure{Check: validation.GoVet, Cause: cause})
|
||||
var validationError *ValidationError
|
||||
if !errors.As(got, &validationError) || validationError.Check != "go-vet" {
|
||||
t.Fatalf("converted error = %#v, want go-vet validation identity", got)
|
||||
}
|
||||
if !errors.Is(got, cause) {
|
||||
t.Fatalf("converted error = %v, want cause preserved", got)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user