Enable race testing in validation
This commit is contained in:
@@ -112,14 +112,25 @@ type CheckCommand struct {
|
||||
Env map[string]string `json:"env,omitempty"`
|
||||
}
|
||||
|
||||
// Settings contains the product, remote, and check commands read from a
|
||||
// releasekit.json profile.
|
||||
// Settings contains the product, remote, shared validation selections, and
|
||||
// check commands read from a releasekit.json profile.
|
||||
type Settings struct {
|
||||
Product string `json:"product"`
|
||||
Remote string `json:"remote,omitempty"`
|
||||
Checks []CheckCommand `json:"checks,omitempty"`
|
||||
Build *BuildSettings `json:"build,omitempty"`
|
||||
Delivery *DeliverySettings `json:"delivery,omitempty"`
|
||||
Product string `json:"product"`
|
||||
Remote string `json:"remote,omitempty"`
|
||||
Checks []CheckCommand `json:"checks,omitempty"`
|
||||
Validation *ValidationSettings `json:"validation,omitempty"`
|
||||
Build *BuildSettings `json:"build,omitempty"`
|
||||
Delivery *DeliverySettings `json:"delivery,omitempty"`
|
||||
}
|
||||
|
||||
// ValidationSettings selects shared candidate checks in a release profile.
|
||||
type ValidationSettings struct {
|
||||
Go GoValidationSettings `json:"go,omitempty"`
|
||||
}
|
||||
|
||||
// GoValidationSettings selects checks performed by the Go toolchain.
|
||||
type GoValidationSettings struct {
|
||||
Race bool `json:"race,omitempty"`
|
||||
}
|
||||
|
||||
// DeliverySettings selects the hosted Gitea release destination.
|
||||
|
||||
@@ -141,6 +141,22 @@ func TestLoadSettingsDefaultsRemoteAndConfigPath(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadSettingsReadsRaceValidation(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 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
settings, err := LoadSettings(repoDir, "")
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExampleProfileLoads(t *testing.T) {
|
||||
repoDir, err := filepath.Abs(filepath.Join("..", "..", "..", ".."))
|
||||
if err != nil {
|
||||
|
||||
@@ -139,6 +139,7 @@ func candidateRequest(invocation cli.Invocation, settings cli.Settings, output i
|
||||
Product: settings.Product,
|
||||
Version: invocation.Version,
|
||||
Remote: settings.Remote,
|
||||
Validation: validationSpec(settings.Validation),
|
||||
Build: configuredBuildSpec(settings.Build),
|
||||
Output: output,
|
||||
}
|
||||
@@ -154,6 +155,15 @@ func candidateRequest(invocation cli.Invocation, settings cli.Settings, output i
|
||||
return request
|
||||
}
|
||||
|
||||
func validationSpec(settings *cli.ValidationSettings) releasekit.ValidationSpec {
|
||||
if settings == nil {
|
||||
return releasekit.ValidationSpec{}
|
||||
}
|
||||
return releasekit.ValidationSpec{
|
||||
Go: releasekit.GoValidationSpec{Race: settings.Go.Race},
|
||||
}
|
||||
}
|
||||
|
||||
func candidateFailureText(err error) string {
|
||||
return err.Error()
|
||||
}
|
||||
|
||||
@@ -49,6 +49,7 @@ func TestUnknownCommand(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCheckCommandBindsConfiguredChecksToCapturedCandidate(t *testing.T) {
|
||||
raceSelected := racePrerequisitesAvailable()
|
||||
repository := newCLIRepository(t)
|
||||
profilePath := filepath.Join(t.TempDir(), "profile with spaces.json")
|
||||
orderPath := filepath.Join(t.TempDir(), "check-order")
|
||||
@@ -73,6 +74,9 @@ func TestCheckCommandBindsConfiguredChecksToCapturedCandidate(t *testing.T) {
|
||||
Remote: "origin",
|
||||
Checks: []cli.CheckCommand{check("first"), check("second")},
|
||||
}
|
||||
if raceSelected {
|
||||
settings.Validation = &cli.ValidationSettings{Go: cli.GoValidationSettings{Race: true}}
|
||||
}
|
||||
contents, err := json.Marshal(settings)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -82,6 +86,11 @@ func TestCheckCommandBindsConfiguredChecksToCapturedCandidate(t *testing.T) {
|
||||
}
|
||||
|
||||
var stdout, stderr bytes.Buffer
|
||||
raceMarker := ""
|
||||
if raceSelected {
|
||||
raceMarker = filepath.Join(t.TempDir(), "race-enabled")
|
||||
t.Setenv("RELEASEKIT_RACE_MARKER", raceMarker)
|
||||
}
|
||||
args := []string{"check", "--repo", repository, "--config", profilePath, "v1.2.3"}
|
||||
if status := run(context.Background(), args, &stdout, &stderr); status != 0 {
|
||||
t.Fatalf("run() status = %d; stdout=%q stderr=%q", status, stdout.String(), stderr.String())
|
||||
@@ -96,6 +105,23 @@ func TestCheckCommandBindsConfiguredChecksToCapturedCandidate(t *testing.T) {
|
||||
if want := fmt.Sprintf("local candidate v1.2.3 at %s is valid\n", commit); stdout.String() != want {
|
||||
t.Fatalf("stdout = %q, want %q", stdout.String(), want)
|
||||
}
|
||||
if raceSelected {
|
||||
if _, err := os.Stat(raceMarker); err != nil {
|
||||
t.Fatalf("race-enabled profile did not run: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCandidateRequestMapsRaceValidation(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},
|
||||
},
|
||||
}, nil)
|
||||
if !request.Validation.Go.Race {
|
||||
t.Fatalf("candidate request validation = %#v, want Go race enabled", request.Validation)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCandidateCommandsPresentValidationDiagnosticsOnce(t *testing.T) {
|
||||
@@ -165,6 +191,7 @@ func TestCandidateFailurePresentationRetainsCallbackContext(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPublishCommandPublishesConfiguredCandidate(t *testing.T) {
|
||||
raceSelected := racePrerequisitesAvailable()
|
||||
repository := newCLIRepository(t)
|
||||
writeRepositoryFile(t, repository, "docs/releases/v1.2.3.md", "# Example Product v1.2.3\n\n## Compatibility\n\nNo action required.\n")
|
||||
writeRepositoryFile(t, repository, "scripts/check-release-note.sh", `#!/bin/sh
|
||||
@@ -189,6 +216,9 @@ printf '%s %s\n' "$version" "$candidate" >> "$RELEASEKIT_COMPATIBILITY_LOG"
|
||||
Env: map[string]string{"RELEASEKIT_COMPATIBILITY_LOG": checkLog},
|
||||
}},
|
||||
}
|
||||
if raceSelected {
|
||||
settings.Validation = &cli.ValidationSettings{Go: cli.GoValidationSettings{Race: true}}
|
||||
}
|
||||
contents, err := json.Marshal(settings)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -204,6 +234,11 @@ printf '%s %s\n' "$version" "$candidate" >> "$RELEASEKIT_COMPATIBILITY_LOG"
|
||||
cliGitRun(t, repository, "push", "--set-upstream", "origin", "main")
|
||||
|
||||
var stdout, stderr bytes.Buffer
|
||||
raceMarker := ""
|
||||
if raceSelected {
|
||||
raceMarker = filepath.Join(t.TempDir(), "race-enabled")
|
||||
t.Setenv("RELEASEKIT_RACE_MARKER", raceMarker)
|
||||
}
|
||||
args := []string{"publish", "--repo", repository, "v1.2.3"}
|
||||
if status := run(context.Background(), args, &stdout, &stderr); status != 0 {
|
||||
t.Fatalf("run() status = %d; stdout=%q stderr=%q", status, stdout.String(), stderr.String())
|
||||
@@ -226,6 +261,11 @@ printf '%s %s\n' "$version" "$candidate" >> "$RELEASEKIT_COMPATIBILITY_LOG"
|
||||
if got, want := strings.Count(string(checkResult), "v1.2.3 "+commit), 1; got != want {
|
||||
t.Fatalf("product compatibility check ran %d times, want %d", got, want)
|
||||
}
|
||||
if raceSelected {
|
||||
if _, err := os.Stat(raceMarker); err != nil {
|
||||
t.Fatalf("race-enabled profile did not run: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildCommandWritesValidatedReceiptForConfiguredArtifacts(t *testing.T) {
|
||||
@@ -343,12 +383,52 @@ func newCLIRepository(t *testing.T) string {
|
||||
}
|
||||
write("go.mod", "module example.com/cli-candidate\n\ngo 1.25.0\n")
|
||||
write("candidate_test.go", "package candidate\nimport \"testing\"\nfunc TestFixture(t *testing.T) {}\n")
|
||||
write("race_candidate_test.go", `//go:build race
|
||||
|
||||
package candidate
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRaceBuildSelection(t *testing.T) {
|
||||
marker := os.Getenv("RELEASEKIT_RACE_MARKER")
|
||||
if marker == "" {
|
||||
return
|
||||
}
|
||||
if err := os.WriteFile(marker, []byte("race enabled"), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
`)
|
||||
write("docs/releases/v1.2.3.md", "# Example Product v1.2.3\n\nRelease note.\n")
|
||||
cliGitRun(t, repository, "add", "--all")
|
||||
cliGitRun(t, repository, "commit", "-m", "candidate")
|
||||
return repository
|
||||
}
|
||||
|
||||
func racePrerequisitesAvailable() bool {
|
||||
supported := runtime.GOOS == "darwin" && (runtime.GOARCH == "amd64" || runtime.GOARCH == "arm64") ||
|
||||
(runtime.GOOS == "freebsd" || runtime.GOOS == "linux") && (runtime.GOARCH == "amd64" || runtime.GOARCH == "arm64" || runtime.GOARCH == "ppc64le") ||
|
||||
runtime.GOOS == "windows" && runtime.GOARCH == "amd64"
|
||||
if !supported {
|
||||
return false
|
||||
}
|
||||
cgo, err := exec.Command("go", "env", "CGO_ENABLED").Output()
|
||||
if err != nil || strings.TrimSpace(string(cgo)) != "1" {
|
||||
return false
|
||||
}
|
||||
cc, err := exec.Command("go", "env", "CC").Output()
|
||||
if err != nil || len(strings.Fields(string(cc))) == 0 {
|
||||
return false
|
||||
}
|
||||
if _, err := exec.LookPath(strings.Fields(string(cc))[0]); err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func TestDeliverPageOnlyCreatesAndReusesRelease(t *testing.T) {
|
||||
repository := newCLIRepository(t)
|
||||
state := &deliveryServerState{}
|
||||
|
||||
Reference in New Issue
Block a user