Coordinate separate full vet pass

This commit is contained in:
2026-10-07 22:18:47 +00:00
parent 30fe4fe206
commit 1c41f7a0d1
19 changed files with 261 additions and 80 deletions

View File

@@ -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,

View File

@@ -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.

View File

@@ -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)
}
}

View File

@@ -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,
},
}
}

View File

@@ -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)
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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. |

View File

@@ -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

View File

@@ -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.

View File

@@ -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

View File

@@ -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.

View File

@@ -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"

View File

@@ -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
}

View File

@@ -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

View File

@@ -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

View File

@@ -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)
}
}