Inspect committed workspace files
This commit is contained in:
17
check.go
17
check.go
@@ -52,6 +52,17 @@ type ValidationSpec struct {
|
||||
// GoFormat checks the formatting of committed regular Go source files. It
|
||||
// does not modify repository contents and is disabled when false.
|
||||
GoFormat bool
|
||||
// ModuleDistribution selects checks for files that affect module-source
|
||||
// distribution. Its zero value disables those checks.
|
||||
ModuleDistribution ModuleDistributionValidationSpec
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// GoValidationSpec selects checks performed by the Go toolchain.
|
||||
@@ -79,6 +90,9 @@ type GoValidationSpec struct {
|
||||
// compilation produces no artifacts and does not execute target binaries.
|
||||
// Validation.GoFormat checks regular committed Go source after those selected
|
||||
// Go checks and before AdditionalChecks. Its zero value disables formatting.
|
||||
// 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.
|
||||
// The zero values of all selections preserve the ordinary suite. If
|
||||
// Build selects targets, Check builds them after validation in temporary
|
||||
// storage outside the worktree, then rechecks
|
||||
@@ -106,6 +120,9 @@ func candidateRequest(request ReleaseRequest) candidate.Request {
|
||||
FullVet: request.Validation.Go.FullVet,
|
||||
CompileTargets: make([]validation.Target, len(request.Validation.Go.CompileTargets)),
|
||||
GoFormat: request.Validation.GoFormat,
|
||||
ModuleDistribution: validation.ModuleDistributionSettings{
|
||||
WorkspaceFiles: request.Validation.ModuleDistribution.WorkspaceFiles,
|
||||
},
|
||||
},
|
||||
AdditionalChecks: candidateChecks(request.AdditionalChecks),
|
||||
AfterValidate: internalbuild.CandidateCallback(internalBuildSpec(request.Build), request.Output),
|
||||
|
||||
@@ -129,7 +129,15 @@ type ValidationSettings struct {
|
||||
Go GoValidationSettings `json:"go,omitempty"`
|
||||
// GoFormat checks the formatting of committed regular Go source files.
|
||||
// Omitted or false disables the check.
|
||||
GoFormat bool `json:"go_format,omitempty"`
|
||||
GoFormat bool `json:"go_format,omitempty"`
|
||||
ModuleDistribution ModuleDistributionSettings `json:"module_distribution,omitempty"`
|
||||
}
|
||||
|
||||
// ModuleDistributionSettings selects committed module-source distribution checks.
|
||||
type ModuleDistributionSettings struct {
|
||||
// WorkspaceFiles rejects committed go.work and go.work.sum blobs.
|
||||
// Omitted or false disables the check.
|
||||
WorkspaceFiles bool `json:"workspace_files,omitempty"`
|
||||
}
|
||||
|
||||
// GoValidationSettings selects checks performed by the Go toolchain.
|
||||
|
||||
@@ -144,7 +144,7 @@ func TestLoadSettingsDefaultsRemoteAndConfigPath(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","expected_push_url":"ssh://git.example/owner/repo.git","validation":{"go_format":true,"go":{"race":true,"full_vet":true,"compile_targets":["linux/amd64","linux/arm64","darwin/arm64"]}}}`), 0o600); err != nil {
|
||||
if err := os.WriteFile(profilePath, []byte(`{"product":"Example","expected_push_url":"ssh://git.example/owner/repo.git","validation":{"go_format":true,"module_distribution":{"workspace_files":true},"go":{"race":true,"full_vet":true,"compile_targets":["linux/amd64","linux/arm64","darwin/arm64"]}}}`), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -152,8 +152,8 @@ func TestLoadSettingsReadsGoValidation(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if settings.Validation == nil || !settings.Validation.GoFormat || !settings.Validation.Go.Race || !settings.Validation.Go.FullVet {
|
||||
t.Fatalf("validation settings = %#v, want Go formatting, race, and full vet enabled", settings.Validation)
|
||||
if settings.Validation == nil || !settings.Validation.GoFormat || !settings.Validation.ModuleDistribution.WorkspaceFiles || !settings.Validation.Go.Race || !settings.Validation.Go.FullVet {
|
||||
t.Fatalf("validation settings = %#v, want Go formatting, workspace inspection, race, and full vet enabled", settings.Validation)
|
||||
}
|
||||
if got, want := strings.Join(settings.Validation.Go.CompileTargets, ","), "linux/amd64,linux/arm64,darwin/arm64"; got != want {
|
||||
t.Fatalf("compile targets = %q, want %q", got, want)
|
||||
|
||||
@@ -162,6 +162,9 @@ func validationSpec(settings *cli.ValidationSettings) releasekit.ValidationSpec
|
||||
}
|
||||
result := releasekit.ValidationSpec{
|
||||
GoFormat: settings.GoFormat,
|
||||
ModuleDistribution: releasekit.ModuleDistributionValidationSpec{
|
||||
WorkspaceFiles: settings.ModuleDistribution.WorkspaceFiles,
|
||||
},
|
||||
Go: releasekit.GoValidationSpec{
|
||||
Race: settings.Go.Race,
|
||||
FullVet: settings.Go.FullVet,
|
||||
|
||||
@@ -117,8 +117,9 @@ func TestCandidateRequestMapsGoValidation(t *testing.T) {
|
||||
Product: "Example Product",
|
||||
ExpectedPushURL: "publication-only value",
|
||||
Validation: &cli.ValidationSettings{
|
||||
GoFormat: true,
|
||||
Go: cli.GoValidationSettings{Race: true, FullVet: true, CompileTargets: []string{"linux/amd64", "darwin/arm64"}},
|
||||
GoFormat: true,
|
||||
Go: cli.GoValidationSettings{Race: true, FullVet: true, CompileTargets: []string{"linux/amd64", "darwin/arm64"}},
|
||||
ModuleDistribution: cli.ModuleDistributionSettings{WorkspaceFiles: true},
|
||||
},
|
||||
}, nil)
|
||||
if !request.Validation.Go.Race || !request.Validation.Go.FullVet {
|
||||
@@ -127,6 +128,9 @@ func TestCandidateRequestMapsGoValidation(t *testing.T) {
|
||||
if !request.Validation.GoFormat {
|
||||
t.Fatalf("candidate request GoFormat = false, want true")
|
||||
}
|
||||
if !request.Validation.ModuleDistribution.WorkspaceFiles {
|
||||
t.Fatalf("candidate request workspace files = false, want true")
|
||||
}
|
||||
if request.ExpectedPushURL != "" {
|
||||
t.Fatalf("candidate request carried publication-only expected push URL %q", request.ExpectedPushURL)
|
||||
}
|
||||
|
||||
17
docs/cli.md
17
docs/cli.md
@@ -39,8 +39,11 @@ show the selected target. When `validation.go_format` is true, `check` examines
|
||||
regular committed Go files with the Go formatter after tests, vet, and package
|
||||
compilation. It includes vendor and nested-module source, ignores the worktree
|
||||
and symlink blobs, and reports unformatted paths as `go-format` without
|
||||
modifying files. Any artifact build selected by the separate `build`
|
||||
setting runs after shared validation and configured checks.
|
||||
modifying files. When `validation.module_distribution.workspace_files` is true,
|
||||
`check` reports committed `go.work` and `go.work.sum` blobs, including nested
|
||||
and symlink entries, as `workspace-files`. This inspection is independent of
|
||||
`GOWORK` and ignores uncommitted files. Any artifact build selected by the
|
||||
separate `build` setting runs after shared validation and configured checks.
|
||||
|
||||
`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,
|
||||
@@ -60,8 +63,9 @@ 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. When `validation.go.full_vet` is true,
|
||||
test-time vet is disabled and `go vet -mod=readonly ./...` runs after successful
|
||||
tests. Selected package compilation and committed Go formatting follow, then
|
||||
configured checks run in profile order. It then creates an annotated local tag
|
||||
tests. Selected package compilation and committed Go formatting follow. The
|
||||
selected workspace-file check then runs, 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
|
||||
on success. Validation
|
||||
@@ -77,8 +81,9 @@ 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. When `validation.go_format` is true,
|
||||
committed Go formatting runs after package compilation and before configured
|
||||
checks. A failure is reported as `go-format` with affected repository paths;
|
||||
parser and source-inspection causes are retained.
|
||||
checks. The selected workspace-file check then reports committed go.work and
|
||||
go.work.sum paths as `workspace-files`. Formatting failures are reported as
|
||||
`go-format` with affected paths and preserve parser or source-inspection causes.
|
||||
|
||||
The optional profile field `expected_push_url` applies only to `publish`. A
|
||||
nonempty value must exactly match the effective push URL resolved by Git;
|
||||
|
||||
@@ -45,6 +45,12 @@ symlink blobs and the worktree, and reports every selected path whose bytes
|
||||
change under formatting. It never writes formatted bytes back. Omitted or false
|
||||
disables the check. It runs after tests, selected full vet, and selected package
|
||||
compilation, before configured product checks.
|
||||
|
||||
When `validation.module_distribution.workspace_files` is true, it reports
|
||||
committed blob entries whose basename is `go.work` or `go.work.sum`, including
|
||||
nested paths and symlinks. It checks candidate distribution contents regardless
|
||||
of `GOWORK`; it does not select or change the workspace used by Go commands,
|
||||
and it does not alter existing vendor behavior. Omitted or false disables it.
|
||||
For example:
|
||||
|
||||
```json
|
||||
@@ -56,7 +62,10 @@ For example:
|
||||
"full_vet": true,
|
||||
"compile_targets": ["linux/amd64", "linux/arm64"]
|
||||
},
|
||||
"go_format": true
|
||||
"go_format": true,
|
||||
"module_distribution": {
|
||||
"workspace_files": true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -84,10 +93,14 @@ run native `go vet -mod=readonly ./...` after successful tests. They then run
|
||||
`validation.go.compile_targets` entry, in profile order. Compile failures use
|
||||
the `package-compile` identity and include the selected target. When selected,
|
||||
`validation.go_format` checks regular committed Go source with go/format after
|
||||
package compilation; unformatted paths use the `go-format` identity. Configured
|
||||
`checks` commands run afterward, once each in order, as additional product
|
||||
checks. Artifact builds selected by the separate `build` setting run after
|
||||
shared validation and product checks.
|
||||
package compilation; unformatted paths use the `go-format` identity. When
|
||||
`validation.module_distribution.workspace_files` is true, shared validation
|
||||
also reports committed go.work and go.work.sum blobs after Go formatting, using
|
||||
the workspace-files identity. Uncommitted workspace files are not inspected.
|
||||
Configured `checks` commands run afterward, once each in order, as additional
|
||||
product checks. Artifact builds selected by the separate `build` setting 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.
|
||||
|
||||
@@ -41,6 +41,12 @@ formatter built into ReleaseKit. It includes vendor and nested-module source,
|
||||
ignores symlink blobs and worktree changes, and reports unformatted paths as
|
||||
`go-format` without rewriting files. Its zero value disables the check; it runs
|
||||
after tests, selected vet, and package compilation, before `AdditionalChecks`.
|
||||
|
||||
Set `Validation.ModuleDistribution.WorkspaceFiles` to reject committed
|
||||
`go.work` and `go.work.sum` blobs, including nested and symlink entries. It
|
||||
examines the captured candidate even when `GOWORK=off`, ignores uncommitted
|
||||
files, and does not change Go test workspace selection or vendor behavior. The
|
||||
zero value disables the check; failures use `workspace-files`.
|
||||
ReleaseKit calls `AdditionalChecks` once after shared validation when supplied.
|
||||
It does not download a different toolchain automatically. If race prerequisites
|
||||
are unavailable, the `go-test` check fails rather than being skipped; a selected
|
||||
|
||||
@@ -55,6 +55,13 @@ worktree changes and symlink blobs, and reports unformatted paths as
|
||||
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, including nested paths and 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.
|
||||
|
||||
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,
|
||||
|
||||
@@ -25,6 +25,11 @@ symlink blobs, reports all unformatted paths, and stops with a contextual cause
|
||||
on parse or inspection errors. It runs before product checks and build callbacks.
|
||||
`Request.ReadCandidateTree` lets selected file checks request candidate
|
||||
contents; the Go-only path does not enumerate the tree.
|
||||
|
||||
The workspace-file check uses that same candidate inventory and reports blob
|
||||
entries named go.work or go.work.sum, including nested paths and symlink blobs.
|
||||
It does not read workspace contents or depend on the workspace selected for Go
|
||||
commands.
|
||||
`CandidateTree` gets metadata and bytes from Git objects, reads blob bytes
|
||||
lazily, and resolves destinations without consulting the worktree or
|
||||
neighboring files.
|
||||
|
||||
@@ -337,6 +337,12 @@ behavior, API/profile parity, and failure before later product/build work.
|
||||
|
||||
## Stage 6: Select committed workspace-file inspection
|
||||
|
||||
**Completion:** Complete. `Validation.ModuleDistribution.WorkspaceFiles` and
|
||||
`validation.module_distribution.workspace_files` report committed blob entries
|
||||
named `go.work` or `go.work.sum`, including nested paths and symlinks. The rule
|
||||
inspects the captured candidate independently of `GOWORK` and leaves the Go
|
||||
workspace environment and vendor behavior unchanged.
|
||||
|
||||
**Outcome:** One distribution rule works independently of the Go environment.
|
||||
|
||||
1. Add `ModuleDistributionValidationSpec.WorkspaceFiles` and the matching JSON
|
||||
|
||||
@@ -108,6 +108,52 @@ func TestCheckStopsBeforeProductAndBuildCallbacksWhenGoFormatFails(t *testing.T)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckReportsCommittedWorkspaceFilesWhenGoWorkspaceIsDisabled(t *testing.T) {
|
||||
t.Setenv("GOWORK", "off")
|
||||
repository := newRepository(t, "# Example Product v1.2.3\n\nRelease note.\n")
|
||||
writeTestFile(t, repository, "go.work", "go 1.25.0\n")
|
||||
writeTestFile(t, repository, "nested/go.work.sum", "workspace-sum\n")
|
||||
if err := os.MkdirAll(filepath.Join(repository, "linked"), 0o700); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.Symlink("../go.work", filepath.Join(repository, "linked", "go.work")); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
gitRun(t, repository, "add", "go.work", "nested/go.work.sum", "linked/go.work")
|
||||
gitRun(t, repository, "commit", "-m", "add workspace files")
|
||||
_, err := Check(context.Background(), Request{
|
||||
RepositoryDir: repository,
|
||||
Product: "Example Product",
|
||||
Version: testVersion,
|
||||
Validation: validation.Settings{ModuleDistribution: validation.ModuleDistributionSettings{
|
||||
WorkspaceFiles: true,
|
||||
}},
|
||||
})
|
||||
var failure *validation.Failure
|
||||
if !errors.As(err, &failure) || failure.Check != validation.WorkspaceFiles {
|
||||
t.Fatalf("Check error = %v, want workspace-files failure", err)
|
||||
}
|
||||
want := map[string]bool{
|
||||
"go.work": false,
|
||||
"linked/go.work": false,
|
||||
"nested/go.work.sum": false,
|
||||
}
|
||||
if len(failure.Findings) != len(want) {
|
||||
t.Fatalf("workspace findings = %#v, want %d paths", failure.Findings, len(want))
|
||||
}
|
||||
for _, finding := range failure.Findings {
|
||||
if _, ok := want[finding.Path]; !ok {
|
||||
t.Errorf("unexpected workspace path %q", finding.Path)
|
||||
}
|
||||
want[finding.Path] = true
|
||||
}
|
||||
for path, found := range want {
|
||||
if !found {
|
||||
t.Errorf("workspace path %q was not reported", path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckRunsDefaultOfflineGoValidation(t *testing.T) {
|
||||
repository := newRepository(t, "# Example Product v1.2.3\n\nRelease note.\n")
|
||||
var output bytes.Buffer
|
||||
|
||||
@@ -17,6 +17,8 @@ const (
|
||||
PackageCompile Check = "package-compile"
|
||||
// GoFormat identifies committed Go source formatting.
|
||||
GoFormat Check = "go-format"
|
||||
// WorkspaceFiles identifies committed workspace files in module source.
|
||||
WorkspaceFiles Check = "workspace-files"
|
||||
)
|
||||
|
||||
// Finding describes a validation observation at an optional source location.
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"go/format"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"gitea.maximumdirect.net/eric/ReleaseKit/internal/gotool"
|
||||
@@ -15,10 +16,16 @@ import (
|
||||
|
||||
// Settings selects shared validation behavior.
|
||||
type Settings struct {
|
||||
Race bool
|
||||
FullVet bool
|
||||
CompileTargets []Target
|
||||
GoFormat bool
|
||||
Race bool
|
||||
FullVet bool
|
||||
CompileTargets []Target
|
||||
GoFormat bool
|
||||
ModuleDistribution ModuleDistributionSettings
|
||||
}
|
||||
|
||||
// ModuleDistributionSettings selects candidate distribution checks.
|
||||
type ModuleDistributionSettings struct {
|
||||
WorkspaceFiles bool
|
||||
}
|
||||
|
||||
// Target selects one package-compilation target.
|
||||
@@ -91,14 +98,45 @@ func Run(ctx context.Context, request Request) error {
|
||||
}
|
||||
}
|
||||
}
|
||||
if request.Settings.GoFormat {
|
||||
if request.Settings.GoFormat || request.Settings.ModuleDistribution.WorkspaceFiles {
|
||||
tree, err := request.ReadCandidateTree(ctx)
|
||||
if err != nil {
|
||||
return &Failure{Check: GoFormat, Cause: err}
|
||||
check := WorkspaceFiles
|
||||
if request.Settings.GoFormat {
|
||||
check = GoFormat
|
||||
}
|
||||
return &Failure{Check: check, Cause: err}
|
||||
}
|
||||
if err := checkGoFormat(ctx, tree); err != nil {
|
||||
return err
|
||||
if request.Settings.GoFormat {
|
||||
if err := checkGoFormat(ctx, tree); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if request.Settings.ModuleDistribution.WorkspaceFiles {
|
||||
if err := checkWorkspaceFiles(tree); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkWorkspaceFiles(tree *CandidateTree) error {
|
||||
var findings []Finding
|
||||
for _, entry := range tree.Entries() {
|
||||
if entry.Type != "blob" {
|
||||
continue
|
||||
}
|
||||
switch path.Base(entry.Path) {
|
||||
case "go.work", "go.work.sum":
|
||||
findings = append(findings, Finding{
|
||||
Path: entry.Path,
|
||||
Message: "workspace file is committed in the candidate",
|
||||
})
|
||||
}
|
||||
}
|
||||
if len(findings) != 0 {
|
||||
return &Failure{Check: WorkspaceFiles, Findings: findings}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
100
internal/validation/workspace_test.go
Normal file
100
internal/validation/workspace_test.go
Normal file
@@ -0,0 +1,100 @@
|
||||
package validation
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"gitea.maximumdirect.net/eric/ReleaseKit/internal/git"
|
||||
)
|
||||
|
||||
func TestCheckWorkspaceFilesReportsCommittedBlobNames(t *testing.T) {
|
||||
tree := &CandidateTree{entries: []git.TreeEntry{
|
||||
{Path: "go.work", Type: "blob", Mode: "100644"},
|
||||
{Path: "nested/go.work.sum", Type: "blob", Mode: "100644"},
|
||||
{Path: "vendor/nested/link/go.work", Type: "blob", Mode: "120000"},
|
||||
{Path: "nested/not-go.work.backup", Type: "blob", Mode: "100644"},
|
||||
{Path: "submodule/go.work", Type: "commit", Mode: "160000"},
|
||||
}}
|
||||
err := checkWorkspaceFiles(tree)
|
||||
failure, ok := err.(*Failure)
|
||||
if !ok || failure.Check != WorkspaceFiles {
|
||||
t.Fatalf("checkWorkspaceFiles error = %v, want workspace-files failure", err)
|
||||
}
|
||||
want := map[string]bool{
|
||||
"go.work": false,
|
||||
"nested/go.work.sum": false,
|
||||
"vendor/nested/link/go.work": false,
|
||||
}
|
||||
if len(failure.Findings) != len(want) {
|
||||
t.Fatalf("workspace findings = %#v, want %d paths", failure.Findings, len(want))
|
||||
}
|
||||
for _, finding := range failure.Findings {
|
||||
if _, ok := want[finding.Path]; !ok {
|
||||
t.Errorf("unexpected workspace path %q", finding.Path)
|
||||
}
|
||||
want[finding.Path] = true
|
||||
}
|
||||
for path, found := range want {
|
||||
if !found {
|
||||
t.Errorf("workspace path %q was not reported", path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckWorkspaceFilesAllowsOtherNames(t *testing.T) {
|
||||
tree := &CandidateTree{entries: []git.TreeEntry{
|
||||
{Path: "go.mod", Type: "blob", Mode: "100644"},
|
||||
{Path: "examples/go.work.example", Type: "blob", Mode: "100644"},
|
||||
}}
|
||||
if err := checkWorkspaceFiles(tree); err != nil {
|
||||
t.Fatalf("checkWorkspaceFiles error = %v, want no findings", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunWorkspaceFilesUsesCommittedTreeWithGoWorkspaceDisabled(t *testing.T) {
|
||||
t.Setenv("GOWORK", "off")
|
||||
repository := newTreeRepository(t)
|
||||
writeTreeFile(t, repository, "go.work", []byte("go 1.25.0\n"))
|
||||
writeTreeFile(t, repository, "nested/go.work.sum", []byte("committed workspace metadata\n"))
|
||||
gitTree(t, repository, "add", ".")
|
||||
gitTree(t, repository, "commit", "-m", "capture workspace files")
|
||||
commit := strings.TrimSpace(gitTree(t, repository, "rev-parse", "HEAD"))
|
||||
writeTreeFile(t, repository, "untracked/go.work.sum", []byte("uncommitted workspace metadata\n"))
|
||||
|
||||
logPath := installGoShim(t, "", false)
|
||||
if err := Run(context.Background(), Request{RepositoryDir: repository, Commit: commit}); err != nil {
|
||||
t.Fatalf("default validation = %v, want workspace check disabled", err)
|
||||
}
|
||||
|
||||
logPath = installGoShim(t, "", false)
|
||||
err := Run(context.Background(), Request{
|
||||
RepositoryDir: repository,
|
||||
Commit: commit,
|
||||
Settings: Settings{ModuleDistribution: ModuleDistributionSettings{
|
||||
WorkspaceFiles: true,
|
||||
}},
|
||||
})
|
||||
failure, ok := err.(*Failure)
|
||||
if !ok || failure.Check != WorkspaceFiles {
|
||||
t.Fatalf("selected workspace error = %v, want workspace-files failure", err)
|
||||
}
|
||||
want := map[string]bool{"go.work": false, "nested/go.work.sum": false}
|
||||
if len(failure.Findings) != len(want) {
|
||||
t.Fatalf("workspace findings = %#v, want committed paths only", failure.Findings)
|
||||
}
|
||||
for _, finding := range failure.Findings {
|
||||
if _, ok := want[finding.Path]; !ok {
|
||||
t.Errorf("unexpected workspace path %q", finding.Path)
|
||||
}
|
||||
want[finding.Path] = true
|
||||
}
|
||||
for path, found := range want {
|
||||
if !found {
|
||||
t.Errorf("committed workspace path %q was not reported", path)
|
||||
}
|
||||
}
|
||||
if got := strings.Join(readGoShimLog(t, logPath), "\n"); !strings.Contains(got, "test -mod=readonly -count=1 ./...") {
|
||||
t.Fatalf("Go commands = %q, want ordinary test suite with GOWORK=off", got)
|
||||
}
|
||||
}
|
||||
@@ -8,8 +8,8 @@ import (
|
||||
)
|
||||
|
||||
// ValidationCheck identifies a shared validation check that failed.
|
||||
// Current checks include "go-test", "go-vet", "package-compile", and
|
||||
// "go-format".
|
||||
// Current checks include "go-test", "go-vet", "package-compile", "go-format",
|
||||
// and "workspace-files".
|
||||
type ValidationCheck string
|
||||
|
||||
// ValidationFinding describes a failed check observation at an optional
|
||||
|
||||
@@ -12,10 +12,10 @@ func TestCandidateRequestMapsGoValidation(t *testing.T) {
|
||||
Validation: ValidationSpec{Go: GoValidationSpec{
|
||||
Race: true, FullVet: true,
|
||||
CompileTargets: []Target{{OS: "linux", Arch: "amd64"}, {OS: "darwin", Arch: "arm64"}},
|
||||
}, GoFormat: true},
|
||||
}, GoFormat: true, ModuleDistribution: ModuleDistributionValidationSpec{WorkspaceFiles: true}},
|
||||
})
|
||||
if !request.Validation.Race || !request.Validation.FullVet || !request.Validation.GoFormat {
|
||||
t.Fatalf("candidate request validation = %#v, want race, full vet, and formatting enabled", request.Validation)
|
||||
if !request.Validation.Race || !request.Validation.FullVet || !request.Validation.GoFormat || !request.Validation.ModuleDistribution.WorkspaceFiles {
|
||||
t.Fatalf("candidate request validation = %#v, want race, full vet, formatting, and workspace inspection enabled", request.Validation)
|
||||
}
|
||||
want := []validation.Target{{OS: "linux", Arch: "amd64"}, {OS: "darwin", Arch: "arm64"}}
|
||||
if len(request.Validation.CompileTargets) != len(want) {
|
||||
@@ -61,3 +61,20 @@ func TestValidationErrorPreservesGoFormatIdentityAndFindings(t *testing.T) {
|
||||
t.Fatalf("converted error = %v, want parser cause preserved", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidationErrorPreservesWorkspaceFilesIdentity(t *testing.T) {
|
||||
got := convertValidationError(&validation.Failure{
|
||||
Check: validation.WorkspaceFiles,
|
||||
Findings: []validation.Finding{{
|
||||
Path: "nested/go.work.sum",
|
||||
Message: "workspace file is committed in the candidate",
|
||||
}},
|
||||
})
|
||||
var validationError *ValidationError
|
||||
if !errors.As(got, &validationError) || validationError.Check != "workspace-files" {
|
||||
t.Fatalf("converted error = %#v, want workspace-files validation identity", got)
|
||||
}
|
||||
if len(validationError.Findings) != 1 || validationError.Findings[0].Path != "nested/go.work.sum" {
|
||||
t.Fatalf("converted findings = %#v, want nested workspace path", validationError.Findings)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user