diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index d0632c49..90fc22ea 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -9,7 +9,7 @@ permissions: pull-requests: write jobs: - test-go: + benchmark: runs-on: ubuntu-latest steps: - name: Check out code diff --git a/.github/workflows/compile.yml b/.github/workflows/compile.yml index f87487f1..d8e1a3e3 100644 --- a/.github/workflows/compile.yml +++ b/.github/workflows/compile.yml @@ -13,6 +13,8 @@ on: types: - published +permissions: read-all + jobs: build: runs-on: ubuntu-latest diff --git a/.github/workflows/deps.yml b/.github/workflows/deps.yml index 1b78ba0a..329795b8 100644 --- a/.github/workflows/deps.yml +++ b/.github/workflows/deps.yml @@ -1,8 +1,7 @@ name: "Dependency Review" on: [pull_request] -permissions: - contents: read +permissions: read-all jobs: dependency-review: diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 68ef27b9..f0c4f0da 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -11,8 +11,10 @@ on: types: - published +permissions: read-all + jobs: - amd64: + docker-amd64: runs-on: ubuntu-latest steps: - name: Check out code @@ -27,7 +29,7 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - - name: Build and push + - name: Build id: docker_build uses: docker/build-push-action@v6 with: @@ -36,7 +38,7 @@ jobs: platforms: linux/amd64 push: false - arm64: + docker-arm64: if: github.event_name != 'pull_request' runs-on: ubuntu-latest steps: @@ -52,7 +54,7 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - - name: Build and push + - name: Build id: docker_build uses: docker/build-push-action@v6 with: diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 40869d33..649d3362 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -1,6 +1,8 @@ name: Spellcheck on: [pull_request] +permissions: read-all + jobs: spellcheck: name: Spellcheck diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml index 0a6feb88..6e81eab9 100644 --- a/.github/workflows/examples.yml +++ b/.github/workflows/examples.yml @@ -8,8 +8,10 @@ on: branches: - main +permissions: read-all + jobs: - test-go: + examples: runs-on: ubuntu-latest steps: - name: Check out code diff --git a/.github/workflows/go-mod-tidy.yml b/.github/workflows/go-mod-tidy.yml index ae71e963..f7f1156a 100644 --- a/.github/workflows/go-mod-tidy.yml +++ b/.github/workflows/go-mod-tidy.yml @@ -8,6 +8,8 @@ on: branches: - main +permissions: read-all + jobs: go-mod-tidy: runs-on: ubuntu-latest diff --git a/.github/workflows/goreleaser.yml b/.github/workflows/goreleaser.yml index cb343147..f7b12414 100644 --- a/.github/workflows/goreleaser.yml +++ b/.github/workflows/goreleaser.yml @@ -8,6 +8,8 @@ on: branches: - main +permissions: read-all + jobs: goreleaser-config: runs-on: ubuntu-latest diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 7e3d0a88..d2a4fdf4 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -8,6 +8,8 @@ on: branches: - main +permissions: read-all + jobs: golangci-lint: runs-on: ubuntu-latest diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index fb38aaa1..89200a0c 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -19,7 +19,7 @@ concurrency: cancel-in-progress: true jobs: - build: + pages: runs-on: ubuntu-latest steps: - name: Checkout diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 35d62f26..2dec35c2 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,6 +8,8 @@ on: branches: - main +permissions: read-all + jobs: test-go: runs-on: ubuntu-latest diff --git a/cmd/pint/scan.go b/cmd/pint/scan.go index 6b773490..918ff597 100644 --- a/cmd/pint/scan.go +++ b/cmd/pint/scan.go @@ -65,8 +65,6 @@ func checkRules(ctx context.Context, workers int, isOffline bool, gen *config.Pr go func() { for _, entry := range entries { switch { - case entry.State == discovery.Excluded: - continue case entry.PathError != nil && entry.State == discovery.Removed: continue case entry.Rule.Error.Err != nil && entry.State == discovery.Removed: diff --git a/internal/discovery/discovery.go b/internal/discovery/discovery.go index 77c359a6..9dad4676 100644 --- a/internal/discovery/discovery.go +++ b/internal/discovery/discovery.go @@ -45,8 +45,6 @@ func (c ChangeType) String() string { return "removed" case Moved: return "moved" - case Excluded: - return "excluded" default: return "---" } @@ -63,7 +61,6 @@ const ( Modified Removed Moved - Excluded ) type Path struct { diff --git a/internal/discovery/git_branch.go b/internal/discovery/git_branch.go index 7d24c235..7ebf801d 100644 --- a/internal/discovery/git_branch.go +++ b/internal/discovery/git_branch.go @@ -105,7 +105,7 @@ func (f GitBranchFinder) Find(allEntries []Entry) (entries []Entry, err error) { case me.hasBefore && me.hasAfter: switch { case me.isIdentical && !me.wasMoved: - me.after.State = Excluded + me.after.State = Noop me.after.ModifiedLines = []int{} slog.Debug( "Rule content was not modified on HEAD, identical rule present before", diff --git a/internal/discovery/git_branch_test.go b/internal/discovery/git_branch_test.go index 2f2a8990..c1513919 100644 --- a/internal/discovery/git_branch_test.go +++ b/internal/discovery/git_branch_test.go @@ -206,7 +206,7 @@ groups: finder: discovery.NewGitBranchFinder(git.RunGit, git.NewPathFilter(includeAll, nil, nil), "main", 4), entries: []discovery.Entry{ { - State: discovery.Excluded, + State: discovery.Noop, Path: discovery.Path{ Name: "rules.yml", SymlinkTarget: "rules.yml", @@ -472,7 +472,7 @@ groups: Rule: mustParse(6, "- record: up:count:2a\n expr: count(up)\n"), }, { - State: discovery.Excluded, + State: discovery.Noop, Path: discovery.Path{ Name: "rules.yml", SymlinkTarget: "rules.yml", @@ -535,7 +535,7 @@ groups: Rule: mustParse(1, "- alert: rule1\n expr: sum(foo) by(job)\n for: 0s\n"), }, { - State: discovery.Excluded, + State: discovery.Noop, Path: discovery.Path{ Name: "rules.yml", SymlinkTarget: "rules.yml", @@ -566,7 +566,7 @@ groups: finder: discovery.NewGitBranchFinder(git.RunGit, git.NewPathFilter(includeAll, nil, includeAll), "main", 4), entries: []discovery.Entry{ { - State: discovery.Excluded, + State: discovery.Noop, Path: discovery.Path{ Name: "rules.yml", SymlinkTarget: "rules.yml", @@ -606,7 +606,7 @@ groups: finder: discovery.NewGitBranchFinder(git.RunGit, git.NewPathFilter(includeAll, nil, includeAll), "main", 4), entries: []discovery.Entry{ { - State: discovery.Excluded, + State: discovery.Noop, Path: discovery.Path{ Name: "rules.yml", SymlinkTarget: "rules.yml", @@ -650,7 +650,7 @@ groups: finder: discovery.NewGitBranchFinder(git.RunGit, git.NewPathFilter(includeAll, nil, includeAll), "main", 4), entries: []discovery.Entry{ { - State: discovery.Excluded, + State: discovery.Noop, Path: discovery.Path{ Name: "rules.yml", SymlinkTarget: "rules.yml", @@ -659,7 +659,7 @@ groups: Rule: mustParse(1, "- alert: rule1\n expr: sum(foo) by(job)\n"), }, { - State: discovery.Excluded, + State: discovery.Noop, Path: discovery.Path{ Name: "rules.yml", SymlinkTarget: "rules.yml", @@ -756,7 +756,7 @@ groups: finder: discovery.NewGitBranchFinder(git.RunGit, git.NewPathFilter(includeAll, nil, includeAll), "main", 4), entries: []discovery.Entry{ { - State: discovery.Excluded, + State: discovery.Noop, Path: discovery.Path{ Name: "rules.yml", SymlinkTarget: "rules.yml", @@ -765,7 +765,7 @@ groups: Rule: mustParse(1, "- alert: rule1\n expr: sum(foo) by(job)\n"), }, { - State: discovery.Excluded, + State: discovery.Noop, Path: discovery.Path{ Name: "rules.yml", SymlinkTarget: "rules.yml", @@ -887,7 +887,7 @@ groups: finder: discovery.NewGitBranchFinder(git.RunGit, git.NewPathFilter(includeAll, nil, includeAll), "main", 4), entries: []discovery.Entry{ { - State: discovery.Excluded, + State: discovery.Noop, Path: discovery.Path{ Name: "rules.yml", SymlinkTarget: "rules.yml", @@ -1127,6 +1127,83 @@ groups: }, }, }, + { + title: "add two dups", + setup: func(t *testing.T) { + commitFile(t, "rules.yml", ` +- alert: rule1 + expr: sum(foo) by(job) +- alert: rule2 + expr: sum(foo) by(job) +- alert: rule3 + expr: sum(foo) by(job) +`, "v1") + + _, err := git.RunGit("checkout", "-b", "v2") + require.NoError(t, err, "git checkout v2") + + commitFile(t, "rules.yml", ` +- alert: rule1 + expr: sum(foo) by(job) +- alert: rule2 + expr: sum(foo) by(job) +- alert: rule3 + expr: sum(foo) by(job) +- alert: rule1 + expr: sum(foo) by(job) +- alert: rule1 + expr: sum(foo) by(job) +`, "v2") + }, + finder: discovery.NewGitBranchFinder(git.RunGit, git.NewPathFilter(includeAll, nil, includeAll), "main", 4), + entries: []discovery.Entry{ + { + State: discovery.Noop, + Path: discovery.Path{ + Name: "rules.yml", + SymlinkTarget: "rules.yml", + }, + ModifiedLines: []int{}, + Rule: mustParse(1, "- alert: rule1\n expr: sum(foo) by(job)\n"), + }, + { + State: discovery.Noop, + Path: discovery.Path{ + Name: "rules.yml", + SymlinkTarget: "rules.yml", + }, + ModifiedLines: []int{}, + Rule: mustParse(3, "- alert: rule2\n expr: sum(foo) by(job)\n"), + }, + { + State: discovery.Noop, + Path: discovery.Path{ + Name: "rules.yml", + SymlinkTarget: "rules.yml", + }, + ModifiedLines: []int{}, + Rule: mustParse(5, "- alert: rule3\n expr: sum(foo) by(job)\n"), + }, + { + State: discovery.Added, + Path: discovery.Path{ + Name: "rules.yml", + SymlinkTarget: "rules.yml", + }, + ModifiedLines: []int{8, 9}, + Rule: mustParse(7, "- alert: rule1\n expr: sum(foo) by(job)\n"), + }, + { + State: discovery.Added, + Path: discovery.Path{ + Name: "rules.yml", + SymlinkTarget: "rules.yml", + }, + ModifiedLines: []int{10, 11}, + Rule: mustParse(9, "- alert: rule1\n expr: sum(foo) by(job)\n"), + }, + }, + }, } for _, tc := range testCases { diff --git a/internal/reporter/gitlab_test.go b/internal/reporter/gitlab_test.go index 120bb6be..de83b167 100644 --- a/internal/reporter/gitlab_test.go +++ b/internal/reporter/gitlab_test.go @@ -5,6 +5,7 @@ import ( "log/slog" "net/http" "net/http/httptest" + "strings" "testing" "time" @@ -175,6 +176,199 @@ func TestGitLabReporter(t *testing.T) { return err }, }, + { + description: "list merge requests failed", + branch: "fakeBranch", + token: "fakeToken", + timeout: time.Minute, + project: 123, + maxComments: 50, + reports: []reporter.Report{fooReport}, + httpHandler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch r.URL.Path { + case "/api/v4/user": + if r.Method == http.MethodGet { + _, _ = w.Write([]byte(`{"id": 123}`)) + } + case "/api/v4/projects/123/merge_requests": + w.WriteHeader(500) + _, _ = w.Write([]byte("Mock error")) + default: + w.WriteHeader(200) + if r.Method == http.MethodGet { + _, _ = w.Write([]byte(`[]`)) + } + } + }), + errorHandler: func(err error) error { + if strings.HasSuffix(err.Error(), ": Mock error") { + return nil + } + return err + }, + }, + { + description: "user request failed", + branch: "fakeBranch", + token: "fakeToken", + timeout: time.Minute, + project: 123, + maxComments: 50, + reports: []reporter.Report{fooReport}, + httpHandler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch r.URL.Path { + case "/api/v4/user": + w.WriteHeader(500) + _, _ = w.Write([]byte("Mock error")) + default: + w.WriteHeader(200) + if r.Method == http.MethodGet { + _, _ = w.Write([]byte(`[]`)) + } + } + }), + errorHandler: func(err error) error { + if strings.HasSuffix(err.Error(), ": Mock error") { + return nil + } + return err + }, + }, + { + description: "too many comments", + branch: "fakeBranch", + token: "fakeToken", + timeout: time.Second, + project: 123, + maxComments: 1, + reports: []reporter.Report{ + { + Path: discovery.Path{ + SymlinkTarget: "foo.txt", + Name: "foo.txt", + }, + ModifiedLines: []int{1}, + Rule: mockRules[0], + Problem: checks.Problem{ + Reporter: "foo", + Text: "foo error", + Details: "foo details", + Lines: parser.LineRange{First: 1, Last: 3}, + Severity: checks.Fatal, + Anchor: checks.AnchorAfter, + }, + }, + { + Path: discovery.Path{ + SymlinkTarget: "foo.txt", + Name: "foo.txt", + }, + ModifiedLines: []int{2}, + Rule: mockRules[0], + Problem: checks.Problem{ + Reporter: "foo", + Text: "foo error", + Details: "foo details", + Lines: parser.LineRange{First: 1, Last: 3}, + Severity: checks.Fatal, + Anchor: checks.AnchorAfter, + }, + }, + { + Path: discovery.Path{ + SymlinkTarget: "foo.txt", + Name: "foo.txt", + }, + ModifiedLines: []int{3}, + Rule: mockRules[0], + Problem: checks.Problem{ + Reporter: "foo", + Text: "foo error", + Details: "foo details", + Lines: parser.LineRange{First: 1, Last: 3}, + Severity: checks.Fatal, + Anchor: checks.AnchorAfter, + }, + }, + }, + httpHandler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(200) + switch r.URL.Path { + case "/api/v4/user": + if r.Method == http.MethodGet { + _, _ = w.Write([]byte(`{"id": 123}`)) + } + case "/api/v4/projects/123/merge_requests": + if r.Method == http.MethodGet { + _, _ = w.Write([]byte(`[{"iid":1}]`)) + } + case "/api/v4/projects/123/merge_requests/1/diffs": + if r.Method == http.MethodGet { + _, _ = w.Write([]byte(`[{"diff":"` + fooDiff + `","new_path":"foo.txt","old_path":"foo.txt"}]`)) + } + case "/api/v4/projects/123/merge_requests/1/versions": + if r.Method == http.MethodGet { + _, _ = w.Write([]byte(`[ +{"id": 2,"head_commit_sha": "head","base_commit_sha": "base","start_commit_sha": "start"}, +{"id": 1,"head_commit_sha": "head","base_commit_sha": "base","start_commit_sha": "start"} +]`)) + } + case "/api/v4/projects/123/merge_requests/1/discussions": + if r.Method == http.MethodGet { + _, _ = w.Write([]byte(`[]`)) + } else { + _, _ = w.Write([]byte(`{}`)) + } + } + }), + errorHandler: func(err error) error { + return err + }, + }, + { + description: "no diff", + branch: "fakeBranch", + token: "fakeToken", + timeout: time.Second, + project: 123, + maxComments: 1, + reports: []reporter.Report{fooReport}, + httpHandler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(200) + switch r.URL.Path { + case "/api/v4/user": + if r.Method == http.MethodGet { + _, _ = w.Write([]byte(`{"id": 123}`)) + } + case "/api/v4/projects/123/merge_requests": + if r.Method == http.MethodGet { + _, _ = w.Write([]byte(`[{"iid":1}]`)) + } + case "/api/v4/projects/123/merge_requests/1/diffs": + if r.Method == http.MethodGet { + _, _ = w.Write([]byte(`[]`)) + } + case "/api/v4/projects/123/merge_requests/1/versions": + if r.Method == http.MethodGet { + _, _ = w.Write([]byte(`[ +{"id": 2,"head_commit_sha": "head","base_commit_sha": "base","start_commit_sha": "start"}, +{"id": 1,"head_commit_sha": "head","base_commit_sha": "base","start_commit_sha": "start"} +]`)) + } + case "/api/v4/projects/123/merge_requests/1/discussions": + if r.Method == http.MethodGet { + _, _ = w.Write([]byte(`[]`)) + } else { + + _, _ = w.Write([]byte(`ERROR`)) + t.FailNow() + } + } + }), + errorHandler: func(err error) error { + return err + }, + }, } for _, tc := range testCases { @@ -196,7 +390,7 @@ func TestGitLabReporter(t *testing.T) { if err == nil { summary := reporter.NewSummary(tc.reports) err = reporter.Submit(context.Background(), summary, r) - require.NoError(t, err) + require.NoError(t, tc.errorHandler(err)) } require.NoError(t, tc.errorHandler(err)) })