-
Notifications
You must be signed in to change notification settings - Fork 48
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
output: Add the
gitlab
output. (#220)
* output: Add the `gitlab` output. This adds a new output format, `gitlab`, which produces [Code Quality reports](https://docs.gitlab.com/ee/ci/testing/code_quality.html#code-quality-report-format) for GitLab. This allows GitLab CI jobs to annotate which files need formatting. Without this, a CI job can only fail, leaving the user to sieve through the logs to find out which files they need to format. * internal/gitlab: Remove custom JSON (un)marshalling. This makes the code much simpler, at the cost of exporting an extra type. * integrationtest: Add integration test for `gitlab_output`. * output: Implement sorting using the `sort` package. The `slices` package added recently, in Go 1.23. * internal/gitlab: Add Apache 2 license header.
- Loading branch information
Showing
11 changed files
with
294 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
3 changes: 3 additions & 0 deletions
3
integrationtest/command/testdata/gitlab_output/after/correctly_formatted.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# Test case "gitlab_output" | ||
|
||
needs: "no-op" |
4 changes: 4 additions & 0 deletions
4
integrationtest/command/testdata/gitlab_output/after/needs_format.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
# Test case "gitlab_output" | ||
|
||
|
||
needs: "reformatting" |
3 changes: 3 additions & 0 deletions
3
integrationtest/command/testdata/gitlab_output/before/correctly_formatted.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# Test case "gitlab_output" | ||
|
||
needs: "no-op" |
4 changes: 4 additions & 0 deletions
4
integrationtest/command/testdata/gitlab_output/before/needs_format.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
# Test case "gitlab_output" | ||
|
||
|
||
needs: "reformatting" |
Empty file.
11 changes: 11 additions & 0 deletions
11
integrationtest/command/testdata/gitlab_output/stdout/stdout.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
[ | ||
{ | ||
"description": "Not formatted correctly, run yamlfmt to resolve.", | ||
"check_name": "yamlfmt", | ||
"fingerprint": "e9b14e45ca01a9a72fda9b8356a9ddbbaf7fe8c47116790a51cd699ae1679353", | ||
"severity": "major", | ||
"location": { | ||
"path": "needs_format.yaml" | ||
} | ||
} | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
// Copyright 2024 GitLab, Inc. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
// Package gitlab generates GitLab Code Quality reports. | ||
package gitlab | ||
|
||
import ( | ||
"crypto/sha256" | ||
"fmt" | ||
|
||
"github.com/google/yamlfmt" | ||
) | ||
|
||
// CodeQuality represents a single code quality finding. | ||
// | ||
// Documentation: https://docs.gitlab.com/ee/ci/testing/code_quality.html#code-quality-report-format | ||
type CodeQuality struct { | ||
Description string `json:"description,omitempty"` | ||
Name string `json:"check_name,omitempty"` | ||
Fingerprint string `json:"fingerprint,omitempty"` | ||
Severity Severity `json:"severity,omitempty"` | ||
Location Location `json:"location,omitempty"` | ||
} | ||
|
||
// Location is the location of a Code Quality finding. | ||
type Location struct { | ||
Path string `json:"path,omitempty"` | ||
} | ||
|
||
// NewCodeQuality creates a new CodeQuality object from a yamlfmt.FileDiff. | ||
// | ||
// If the file did not change, i.e. the diff is empty, an empty struct and false is returned. | ||
func NewCodeQuality(diff yamlfmt.FileDiff) (CodeQuality, bool) { | ||
if !diff.Diff.Changed() { | ||
return CodeQuality{}, false | ||
} | ||
|
||
return CodeQuality{ | ||
Description: "Not formatted correctly, run yamlfmt to resolve.", | ||
Name: "yamlfmt", | ||
Fingerprint: fingerprint(diff), | ||
Severity: Major, | ||
Location: Location{ | ||
Path: diff.Path, | ||
}, | ||
}, true | ||
} | ||
|
||
// fingerprint returns a 256-bit SHA256 hash of the original unformatted file. | ||
// This is used to uniquely identify a code quality finding. | ||
func fingerprint(diff yamlfmt.FileDiff) string { | ||
hash := sha256.New() | ||
|
||
fmt.Fprint(hash, diff.Diff.Original) | ||
|
||
return fmt.Sprintf("%x", hash.Sum(nil)) //nolint:perfsprint | ||
} | ||
|
||
// Severity is the severity of a code quality finding. | ||
type Severity string | ||
|
||
const ( | ||
Info Severity = "info" | ||
Minor Severity = "minor" | ||
Major Severity = "major" | ||
Critical Severity = "critical" | ||
Blocker Severity = "blocker" | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
// Copyright 2024 GitLab, Inc. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package gitlab_test | ||
|
||
import ( | ||
"encoding/json" | ||
"testing" | ||
|
||
"github.com/google/go-cmp/cmp" | ||
"github.com/google/yamlfmt" | ||
"github.com/google/yamlfmt/internal/gitlab" | ||
) | ||
|
||
func TestCodeQuality(t *testing.T) { | ||
t.Parallel() | ||
|
||
cases := []struct { | ||
name string | ||
diff yamlfmt.FileDiff | ||
wantOK bool | ||
wantFingerprint string | ||
}{ | ||
{ | ||
name: "no diff", | ||
diff: yamlfmt.FileDiff{ | ||
Path: "testcase/no_diff.yaml", | ||
Diff: &yamlfmt.FormatDiff{ | ||
Original: "a: b", | ||
Formatted: "a: b", | ||
}, | ||
}, | ||
wantOK: false, | ||
}, | ||
{ | ||
name: "with diff", | ||
diff: yamlfmt.FileDiff{ | ||
Path: "testcase/with_diff.yaml", | ||
Diff: &yamlfmt.FormatDiff{ | ||
Original: "a: b", | ||
Formatted: "a: b", | ||
}, | ||
}, | ||
wantOK: true, | ||
// SHA256 of diff.Diff.Original | ||
wantFingerprint: "05088f1c296b4fd999a1efe48e4addd0f962a8569afbacc84c44630d47f09330", | ||
}, | ||
} | ||
|
||
for _, tc := range cases { | ||
// copy tc to avoid capturing an aliased loop variable in a Goroutine. | ||
tc := tc | ||
|
||
t.Run(tc.name, func(t *testing.T) { | ||
t.Parallel() | ||
|
||
got, gotOK := gitlab.NewCodeQuality(tc.diff) | ||
if gotOK != tc.wantOK { | ||
t.Fatalf("NewCodeQuality() = (%#v, %v), want (*, %v)", got, gotOK, tc.wantOK) | ||
} | ||
if !gotOK { | ||
return | ||
} | ||
|
||
if tc.wantFingerprint != "" && tc.wantFingerprint != got.Fingerprint { | ||
t.Fatalf("NewCodeQuality().Fingerprint = %q, want %q", got.Fingerprint, tc.wantFingerprint) | ||
} | ||
|
||
data, err := json.Marshal(got) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
var gotUnmarshal gitlab.CodeQuality | ||
if err := json.Unmarshal(data, &gotUnmarshal); err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
if diff := cmp.Diff(got, gotUnmarshal); diff != "" { | ||
t.Errorf("json.Marshal() and json.Unmarshal() mismatch (-got +want):\n%s", diff) | ||
} | ||
}) | ||
} | ||
} |