diff --git a/cmd/core-lint/main.go b/cmd/core-lint/main.go index 012cd41..2d0ba47 100644 --- a/cmd/core-lint/main.go +++ b/cmd/core-lint/main.go @@ -197,7 +197,7 @@ func addLintCommands(root *cli.Command) { if err != nil { return err } - fmt.Println(string(data)) + cli.Print("%s\n", string(data)) return nil }) diff --git a/cmd/qa/cmd_docblock.go b/cmd/qa/cmd_docblock.go index e85b55a..ad6da35 100644 --- a/cmd/qa/cmd_docblock.go +++ b/cmd/qa/cmd_docblock.go @@ -93,7 +93,7 @@ func RunDocblockCheck(paths []string, threshold float64, verbose, jsonOutput boo if err != nil { return err } - fmt.Println(string(data)) + cli.Print("%s\n", string(data)) if !result.Passed { return cli.Err("docblock coverage %.1f%% below threshold %.1f%%", result.Coverage, threshold) } diff --git a/cmd/qa/cmd_php.go b/cmd/qa/cmd_php.go index 4067325..9176fca 100644 --- a/cmd/qa/cmd_php.go +++ b/cmd/qa/cmd_php.go @@ -252,7 +252,7 @@ func addPHPAuditCommand(parent *cli.Command) { if err != nil { return err } - fmt.Println(string(data)) + cli.Print("%s\n", string(data)) if payload.HasVulnerabilities { return cli.Err("vulnerabilities found in dependencies") @@ -344,7 +344,7 @@ func addPHPSecurityCommand(parent *cli.Command) { if err != nil { return err } - fmt.Println(string(data)) + cli.Print("%s\n", string(data)) summary := result.Summary if summary.Critical > 0 || summary.High > 0 { @@ -358,7 +358,7 @@ func addPHPSecurityCommand(parent *cli.Command) { if err != nil { return err } - fmt.Println(string(data)) + cli.Print("%s\n", string(data)) summary := result.Summary if summary.Critical > 0 || summary.High > 0 { diff --git a/cmd/qa/cmd_review.go b/cmd/qa/cmd_review.go index db11664..308bf8c 100644 --- a/cmd/qa/cmd_review.go +++ b/cmd/qa/cmd_review.go @@ -212,7 +212,7 @@ func runReview() error { if err != nil { return err } - fmt.Println(string(data)) + cli.Print("%s\n", string(data)) if successfulFetches == 0 && len(fetchErrors) > 0 { return cli.Err("failed to fetch pull requests for %s", repoFullName) } diff --git a/pkg/lint/catalog.go b/pkg/lint/catalog.go index 1f09638..5494259 100644 --- a/pkg/lint/catalog.go +++ b/pkg/lint/catalog.go @@ -30,6 +30,7 @@ func LoadDir(dir string) (*Catalog, error) { if err != nil { return nil, coreerr.E("Catalog.LoadDir", "loading catalog from "+dir, err) } + sortDirEntries(entries) var rules []Rule for _, entry := range entries { @@ -56,6 +57,7 @@ func LoadFS(fsys fs.FS, dir string) (*Catalog, error) { if err != nil { return nil, coreerr.E("Catalog.LoadFS", "loading catalog from embedded "+dir, err) } + sortDirEntries(entries) var rules []Rule for _, entry := range entries { @@ -76,6 +78,12 @@ func LoadFS(fsys fs.FS, dir string) (*Catalog, error) { return &Catalog{Rules: rules}, nil } +func sortDirEntries(entries []fs.DirEntry) { + slices.SortFunc(entries, func(a, b fs.DirEntry) int { + return strings.Compare(a.Name(), b.Name()) + }) +} + // ForLanguage returns all rules that apply to the given language. func (c *Catalog) ForLanguage(lang string) []Rule { var result []Rule diff --git a/pkg/lint/catalog_test.go b/pkg/lint/catalog_test.go index 8d04f85..f2e1837 100644 --- a/pkg/lint/catalog_test.go +++ b/pkg/lint/catalog_test.go @@ -29,6 +29,38 @@ func TestLoadDir_Good(t *testing.T) { assert.NotNil(t, cat.ByID("go-mod-001")) } +func TestLoadDir_SortsFilesDeterministically(t *testing.T) { + dir := t.TempDir() + + err := os.WriteFile(filepath.Join(dir, "z.yaml"), []byte(`- id: z-rule + title: "Z rule" + severity: info + languages: [go] + pattern: 'z' + fix: "z" + detection: regex + auto_fixable: false +`), 0o644) + require.NoError(t, err) + + err = os.WriteFile(filepath.Join(dir, "a.yaml"), []byte(`- id: a-rule + title: "A rule" + severity: info + languages: [go] + pattern: 'a' + fix: "a" + detection: regex + auto_fixable: false +`), 0o644) + require.NoError(t, err) + + cat, err := LoadDir(dir) + require.NoError(t, err) + require.Len(t, cat.Rules, 2) + assert.Equal(t, "a-rule", cat.Rules[0].ID) + assert.Equal(t, "z-rule", cat.Rules[1].ID) +} + func TestLoadDir_Bad_NonexistentDir(t *testing.T) { _, err := LoadDir("/nonexistent/path/that/does/not/exist") assert.Error(t, err)