Expose Markdown validation selection
This commit is contained in:
@@ -130,12 +130,20 @@ type ValidationSettings struct {
|
||||
// GoFormat checks the formatting of committed regular Go source files.
|
||||
// Omitted or false disables the check.
|
||||
GoFormat bool `json:"go_format,omitempty"`
|
||||
Markdown *MarkdownSettings `json:"markdown,omitempty"`
|
||||
ModuleDistribution ModuleDistributionSettings `json:"module_distribution,omitempty"`
|
||||
// FileSize checks the size of each committed blob. Null or omission disables
|
||||
// the check; an empty object enables the default limit.
|
||||
FileSize *FileSizeSettings `json:"file_size,omitempty"`
|
||||
}
|
||||
|
||||
// MarkdownSettings selects committed Markdown destination and fragment checks.
|
||||
type MarkdownSettings struct {
|
||||
// EnforceCheckoutBoundary fails local destinations that resolve outside
|
||||
// the candidate checkout. Submodule contents remain out of scope.
|
||||
EnforceCheckoutBoundary bool `json:"enforce_checkout_boundary,omitempty"`
|
||||
}
|
||||
|
||||
// FileSizeSettings selects the maximum committed-file size and literal exclusions.
|
||||
type FileSizeSettings struct {
|
||||
LimitBytes *int64 `json:"limit_bytes,omitempty"`
|
||||
|
||||
@@ -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,"module_distribution":{"workspace_files":true,"local_replacements":true},"file_size":{"limit_bytes":0,"exclusions":["assets"]},"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,"markdown":{"enforce_checkout_boundary":true},"module_distribution":{"workspace_files":true,"local_replacements":true},"file_size":{"limit_bytes":0,"exclusions":["assets"]},"go":{"race":true,"full_vet":true,"compile_targets":["linux/amd64","linux/arm64","darwin/arm64"]}}}`), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ func TestLoadSettingsReadsGoValidation(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if settings.Validation == nil || !settings.Validation.GoFormat || !settings.Validation.ModuleDistribution.WorkspaceFiles || !settings.Validation.ModuleDistribution.LocalReplacements || !settings.Validation.Go.Race || !settings.Validation.Go.FullVet {
|
||||
if settings.Validation == nil || !settings.Validation.GoFormat || settings.Validation.Markdown == nil || !settings.Validation.Markdown.EnforceCheckoutBoundary || !settings.Validation.ModuleDistribution.WorkspaceFiles || !settings.Validation.ModuleDistribution.LocalReplacements || !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 settings.Validation.FileSize == nil || settings.Validation.FileSize.LimitBytes == nil || *settings.Validation.FileSize.LimitBytes != 0 || len(settings.Validation.FileSize.Exclusions) != 1 || settings.Validation.FileSize.Exclusions[0] != "assets" {
|
||||
@@ -166,6 +166,35 @@ func TestLoadSettingsReadsGoValidation(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadSettingsPreservesMarkdownObjectSelection(t *testing.T) {
|
||||
repoDir := t.TempDir()
|
||||
profilePath := filepath.Join(repoDir, "releasekit.json")
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
profile string
|
||||
want bool
|
||||
}{
|
||||
{name: "omitted", profile: `{"product":"Example","validation":{}}`},
|
||||
{name: "null", profile: `{"product":"Example","validation":{"markdown":null}}`},
|
||||
{name: "empty object", profile: `{"product":"Example","validation":{"markdown":{}}}`, want: true},
|
||||
{name: "boundary enabled", profile: `{"product":"Example","validation":{"markdown":{"enforce_checkout_boundary":true}}}`, want: true},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
if err := os.WriteFile(profilePath, []byte(test.profile), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
settings, err := LoadSettings(repoDir, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got := settings.Validation != nil && settings.Validation.Markdown != nil
|
||||
if got != test.want {
|
||||
t.Fatalf("Markdown selected = %t, want %t", got, test.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadSettingsPreservesEmptyAndNullFileSize(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
|
||||
@@ -172,6 +172,11 @@ func validationSpec(settings *cli.ValidationSettings) releasekit.ValidationSpec
|
||||
CompileTargets: make([]releasekit.Target, len(settings.Go.CompileTargets)),
|
||||
},
|
||||
}
|
||||
if settings.Markdown != nil {
|
||||
result.Markdown = &releasekit.MarkdownValidationSpec{
|
||||
EnforceCheckoutBoundary: settings.Markdown.EnforceCheckoutBoundary,
|
||||
}
|
||||
}
|
||||
if settings.FileSize != nil {
|
||||
var limitBytes *int64
|
||||
if settings.FileSize.LimitBytes != nil {
|
||||
|
||||
@@ -119,6 +119,7 @@ func TestCandidateRequestMapsGoValidation(t *testing.T) {
|
||||
ExpectedPushURL: "publication-only value",
|
||||
Validation: &cli.ValidationSettings{
|
||||
GoFormat: true,
|
||||
Markdown: &cli.MarkdownSettings{EnforceCheckoutBoundary: true},
|
||||
Go: cli.GoValidationSettings{Race: true, FullVet: true, CompileTargets: []string{"linux/amd64", "darwin/arm64"}},
|
||||
ModuleDistribution: cli.ModuleDistributionSettings{WorkspaceFiles: true, LocalReplacements: true},
|
||||
FileSize: &cli.FileSizeSettings{LimitBytes: &limit, Exclusions: []string{"cache"}},
|
||||
@@ -130,6 +131,9 @@ func TestCandidateRequestMapsGoValidation(t *testing.T) {
|
||||
if !request.Validation.GoFormat {
|
||||
t.Fatalf("candidate request GoFormat = false, want true")
|
||||
}
|
||||
if request.Validation.Markdown == nil || !request.Validation.Markdown.EnforceCheckoutBoundary {
|
||||
t.Fatalf("candidate request Markdown = %#v, want boundary enforcement enabled", request.Validation.Markdown)
|
||||
}
|
||||
if !request.Validation.ModuleDistribution.WorkspaceFiles || !request.Validation.ModuleDistribution.LocalReplacements {
|
||||
t.Fatalf("candidate request module distribution = %#v, want both checks enabled", request.Validation.ModuleDistribution)
|
||||
}
|
||||
@@ -150,6 +154,58 @@ func TestCandidateRequestMapsGoValidation(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidationSpecPreservesMarkdownObjectSelection(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
settings *cli.MarkdownSettings
|
||||
want bool
|
||||
}{
|
||||
{name: "omitted"},
|
||||
{name: "empty object", settings: &cli.MarkdownSettings{}, want: true},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
got := validationSpec(&cli.ValidationSettings{Markdown: test.settings}).Markdown != nil
|
||||
if got != test.want {
|
||||
t.Fatalf("Markdown selected = %t, want %t", got, test.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCandidateCommandsRunMarkdownProfile(t *testing.T) {
|
||||
repository := newCLIRepository(t)
|
||||
profile, err := json.Marshal(cli.Settings{
|
||||
Product: "Example Product",
|
||||
Validation: &cli.ValidationSettings{
|
||||
Markdown: &cli.MarkdownSettings{},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
writeRepositoryFile(t, repository, "releasekit.json", string(profile))
|
||||
writeRepositoryFile(t, repository, "guide.md", "[missing](missing.md#top)\n")
|
||||
cliGitRun(t, repository, "add", "releasekit.json", "guide.md")
|
||||
cliGitRun(t, repository, "commit", "-m", "configure Markdown candidate validation")
|
||||
remote := filepath.Join(t.TempDir(), "origin.git")
|
||||
cliGitRun(t, repository, "init", "--bare", remote)
|
||||
cliGitRun(t, repository, "remote", "add", "origin", remote)
|
||||
cliGitRun(t, repository, "push", "origin", "HEAD:refs/heads/main")
|
||||
|
||||
for _, command := range []string{"check", "publish"} {
|
||||
t.Run(command, func(t *testing.T) {
|
||||
var stdout, stderr bytes.Buffer
|
||||
args := []string{command, "--repo", repository, "v1.2.3"}
|
||||
if status := run(context.Background(), args, &stdout, &stderr); status != 1 {
|
||||
t.Fatalf("run() status = %d; stdout=%q stderr=%q, want validation failure", status, stdout.String(), stderr.String())
|
||||
}
|
||||
if !strings.Contains(stderr.String(), "markdown-links") || !strings.Contains(stderr.String(), "guide.md:1:") || !strings.Contains(stderr.String(), "missing.md#top") {
|
||||
t.Fatalf("stderr = %q, want Markdown identity, source location, and target", stderr.String())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCandidateCommandsPresentValidationDiagnosticsOnce(t *testing.T) {
|
||||
repository := newCLIRepository(t)
|
||||
profile, err := json.Marshal(cli.Settings{Product: "Example Product"})
|
||||
|
||||
Reference in New Issue
Block a user