Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions cmd/decolint/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,16 @@ func TestParseConfig(t *testing.T) {
{
"jsonc with comments and trailing comma",
"{\n // override severities\n \"rules\": {\n \"no-image-latest\": \"error\",\n },\n}\n",
Config{Rules: map[string]linter.Severity{"no-image-latest": linter.Error}},
Config{Rules: map[string]linter.Severity{"no-image-latest": linter.SeverityError}},
false,
},
{
"multiple rules",
`{"rules": {"no-image-latest": "error", "pin-image-digest": "warn", "require-non-root": "off"}}`,
Config{Rules: map[string]linter.Severity{
"no-image-latest": linter.Error,
"pin-image-digest": linter.Warn,
"require-non-root": linter.Off,
"no-image-latest": linter.SeverityError,
"pin-image-digest": linter.SeverityWarn,
"require-non-root": linter.SeverityOff,
}},
false,
},
Expand Down Expand Up @@ -76,7 +76,7 @@ func TestLoadConfigExplicitPath(t *testing.T) {
if err != nil {
t.Fatalf("loadConfig: %v", err)
}
want := Config{Rules: map[string]linter.Severity{"no-image-latest": linter.Error}}
want := Config{Rules: map[string]linter.Severity{"no-image-latest": linter.SeverityError}}
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("loadConfig() mismatch (-want +got):\n%s", diff)
}
Expand All @@ -94,20 +94,20 @@ func TestLoadConfigDiscovery(t *testing.T) {
{
".decolint.jsonc present",
map[string]string{".decolint.jsonc": `{"rules": {"no-image-latest": "error"}}`},
Config{Rules: map[string]linter.Severity{"no-image-latest": linter.Error}},
Config{Rules: map[string]linter.Severity{"no-image-latest": linter.SeverityError}},
},
{
".decolint.json fallback",
map[string]string{".decolint.json": `{"rules": {"no-image-latest": "warn"}}`},
Config{Rules: map[string]linter.Severity{"no-image-latest": linter.Warn}},
Config{Rules: map[string]linter.Severity{"no-image-latest": linter.SeverityWarn}},
},
{
".decolint.jsonc takes precedence over .decolint.json",
map[string]string{
".decolint.jsonc": `{"rules": {"no-image-latest": "error"}}`,
".decolint.json": `{"rules": {"no-image-latest": "warn"}}`,
},
Config{Rules: map[string]linter.Severity{"no-image-latest": linter.Error}},
Config{Rules: map[string]linter.Severity{"no-image-latest": linter.SeverityError}},
},
}
for _, tt := range tests {
Expand Down
10 changes: 5 additions & 5 deletions cmd/decolint/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import (
const progName = "decolint"

// failThreshold is the default lowest severity that causes exit code 1.
const failThreshold = linter.Error
const failThreshold = linter.SeverityError

const (
exitCodeSuccess = 0
Expand Down Expand Up @@ -97,9 +97,9 @@ func versionString() string {

// severityEmoji renders a severity for the -rules table; see the legend printed above the table.
var severityEmoji = map[linter.Severity]string{
linter.Off: "",
linter.Warn: "🟡 WARN",
linter.Error: "🔴 ERROR",
linter.SeverityOff: "",
linter.SeverityWarn: "🟡 WARN",
linter.SeverityError: "🔴 ERROR",
}

// rulesTableHeader is the header row of the -rules Markdown table.
Expand Down Expand Up @@ -218,7 +218,7 @@ Flags:
func runLint(ctx context.Context, stdout io.Writer, opts Options) (bool, error) {
threshold := failThreshold
if opts.DenyWarnings {
threshold = linter.Warn
threshold = linter.SeverityWarn
}

l := linter.New()
Expand Down
58 changes: 29 additions & 29 deletions cmd/decolint/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,12 @@ func TestRun(t *testing.T) {
// The fixture uses every ignore directive kind, each suppressing a rule that would
// otherwise fire: decolint-ignore-file (no-seccomp-unconfined), decolint-ignore-line
// (no-cap-add-all), and decolint-ignore-next-line (no-app-port).
{violationsFile, "no-bind-mount", linter.Warn},
{violationsFile, "no-host-port-format", linter.Error},
{violationsFile, "no-docker-socket-mount", linter.Warn},
{violationsFile, "no-image-latest", linter.Warn},
{violationsFile, "no-privileged-container", linter.Warn},
{violationsFile, "pin-feature-version", linter.Warn},
{violationsFile, "no-bind-mount", linter.SeverityWarn},
{violationsFile, "no-host-port-format", linter.SeverityError},
{violationsFile, "no-docker-socket-mount", linter.SeverityWarn},
{violationsFile, "no-image-latest", linter.SeverityWarn},
{violationsFile, "no-privileged-container", linter.SeverityWarn},
{violationsFile, "pin-feature-version", linter.SeverityWarn},
},
wantExitCode: 1, // no-host-port-format is an error by default
},
Expand All @@ -55,21 +55,21 @@ func TestRun(t *testing.T) {
name: "violations without platform selection",
args: []string{"testdata/e2e/violations"},
want: []firing{
{violationsFile, "no-docker-socket-mount", linter.Warn},
{violationsFile, "no-image-latest", linter.Warn},
{violationsFile, "no-privileged-container", linter.Warn},
{violationsFile, "pin-feature-version", linter.Warn},
{violationsFile, "no-docker-socket-mount", linter.SeverityWarn},
{violationsFile, "no-image-latest", linter.SeverityWarn},
{violationsFile, "no-privileged-container", linter.SeverityWarn},
{violationsFile, "pin-feature-version", linter.SeverityWarn},
},
wantExitCode: 0,
},
{
name: "violations with deny-warnings",
args: []string{"-deny-warnings", "testdata/e2e/violations"},
want: []firing{
{violationsFile, "no-docker-socket-mount", linter.Warn},
{violationsFile, "no-image-latest", linter.Warn},
{violationsFile, "no-privileged-container", linter.Warn},
{violationsFile, "pin-feature-version", linter.Warn},
{violationsFile, "no-docker-socket-mount", linter.SeverityWarn},
{violationsFile, "no-image-latest", linter.SeverityWarn},
{violationsFile, "no-privileged-container", linter.SeverityWarn},
{violationsFile, "pin-feature-version", linter.SeverityWarn},
},
wantExitCode: 1, // warnings now cross the fail threshold
},
Expand All @@ -83,12 +83,12 @@ func TestRun(t *testing.T) {
"testdata/e2e/violations",
},
want: []firing{
{violationsFile, "no-bind-mount", linter.Warn},
{violationsFile, "no-host-port-format", linter.Error},
{violationsFile, "no-docker-socket-mount", linter.Warn},
{violationsFile, "no-image-latest", linter.Error},
{violationsFile, "no-privileged-container", linter.Warn},
{violationsFile, "pin-image-digest", linter.Warn},
{violationsFile, "no-bind-mount", linter.SeverityWarn},
{violationsFile, "no-host-port-format", linter.SeverityError},
{violationsFile, "no-docker-socket-mount", linter.SeverityWarn},
{violationsFile, "no-image-latest", linter.SeverityError},
{violationsFile, "no-privileged-container", linter.SeverityWarn},
{violationsFile, "pin-image-digest", linter.SeverityWarn},
},
wantExitCode: 1,
},
Expand Down Expand Up @@ -177,13 +177,13 @@ func TestRun_Flags(t *testing.T) {
}

row := mdTableRow(t, out, "no-image-latest")
wantRow := []string{"no-image-latest", "(all)", severityEmoji[linter.Warn], severityEmoji[linter.Warn]}
wantRow := []string{"no-image-latest", "(all)", severityEmoji[linter.SeverityWarn], severityEmoji[linter.SeverityWarn]}
if diff := cmp.Diff(wantRow, row); diff != "" {
t.Errorf("no-image-latest row mismatch (-want +got):\n%s", diff)
}

row = mdTableRow(t, out, "no-bind-mount")
wantRow = []string{"no-bind-mount", "codespaces", severityEmoji[linter.Warn], severityEmoji[linter.Warn]}
wantRow = []string{"no-bind-mount", "codespaces", severityEmoji[linter.SeverityWarn], severityEmoji[linter.SeverityWarn]}
if diff := cmp.Diff(wantRow, row); diff != "" {
t.Errorf("no-bind-mount row mismatch (-want +got):\n%s", diff)
}
Expand All @@ -204,21 +204,21 @@ func TestRun_Flags(t *testing.T) {

// no-image-latest: default warn, overridden to error.
row := mdTableRow(t, out, "no-image-latest")
wantRow := []string{"no-image-latest", "(all)", severityEmoji[linter.Warn], severityEmoji[linter.Error]}
wantRow := []string{"no-image-latest", "(all)", severityEmoji[linter.SeverityWarn], severityEmoji[linter.SeverityError]}
if diff := cmp.Diff(wantRow, row); diff != "" {
t.Errorf("no-image-latest row mismatch (-want +got):\n%s", diff)
}

// pin-feature-version: default warn, overridden to off.
row = mdTableRow(t, out, "pin-feature-version")
wantRow = []string{"pin-feature-version", "(all)", severityEmoji[linter.Warn], severityEmoji[linter.Off]}
wantRow = []string{"pin-feature-version", "(all)", severityEmoji[linter.SeverityWarn], severityEmoji[linter.SeverityOff]}
if diff := cmp.Diff(wantRow, row); diff != "" {
t.Errorf("pin-feature-version row mismatch (-want +got):\n%s", diff)
}

// pin-image-digest: default off, overridden to warn.
row = mdTableRow(t, out, "pin-image-digest")
wantRow = []string{"pin-image-digest", "(all)", severityEmoji[linter.Off], severityEmoji[linter.Warn]}
wantRow = []string{"pin-image-digest", "(all)", severityEmoji[linter.SeverityOff], severityEmoji[linter.SeverityWarn]}
if diff := cmp.Diff(wantRow, row); diff != "" {
t.Errorf("pin-image-digest row mismatch (-want +got):\n%s", diff)
}
Expand Down Expand Up @@ -297,7 +297,7 @@ func TestRunLint(t *testing.T) {
opts := Options{
Paths: []string{dir},
Format: format.TextFormat{},
Config: Config{Rules: map[string]linter.Severity{"no-image-latest": linter.Error}},
Config: Config{Rules: map[string]linter.Severity{"no-image-latest": linter.SeverityError}},
}
hasIssue, runErr := runLint(t.Context(), &stdout, opts)
if runErr != nil || !hasIssue {
Expand All @@ -313,7 +313,7 @@ func TestRunLint(t *testing.T) {
opts := Options{
Paths: []string{dir},
Format: format.TextFormat{},
Config: Config{Rules: map[string]linter.Severity{"missing-container-def": linter.Off}},
Config: Config{Rules: map[string]linter.Severity{"missing-container-def": linter.SeverityOff}},
}
hasIssue, runErr := runLint(t.Context(), &stdout, opts)
if runErr != nil || hasIssue {
Expand All @@ -327,7 +327,7 @@ func TestRunLint(t *testing.T) {
var stdout bytes.Buffer
opts := Options{
Format: format.TextFormat{},
Config: Config{Rules: map[string]linter.Severity{"no-image-latst": linter.Error}},
Config: Config{Rules: map[string]linter.Severity{"no-image-latst": linter.SeverityError}},
}
hasIssue, runErr := runLint(t.Context(), &stdout, opts)
if runErr == nil || hasIssue {
Expand All @@ -346,7 +346,7 @@ func TestRunLint(t *testing.T) {
opts := Options{
Paths: []string{dir},
Format: format.TextFormat{},
Config: Config{Rules: map[string]linter.Severity{"no-bind-mount": linter.Error}},
Config: Config{Rules: map[string]linter.Severity{"no-bind-mount": linter.SeverityError}},
}
hasIssue, runErr := runLint(t.Context(), &stdout, opts)
if runErr != nil || hasIssue {
Expand Down
2 changes: 1 addition & 1 deletion format/github.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ type GitHubFormat struct{}
func (GitHubFormat) WriteIssues(w io.Writer, issues []linter.Issue) error {
for _, issue := range issues {
command := "error"
if issue.Severity == linter.Warn {
if issue.Severity == linter.SeverityWarn {
command = "warning"
}
_, err := fmt.Fprintf(
Expand Down
21 changes: 21 additions & 0 deletions format/text.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,31 @@ type TextFormat struct{}

// WriteIssues writes issues to w, one line per issue.
func (TextFormat) WriteIssues(w io.Writer, issues []linter.Issue) error {
var numErrors, numWarnings int

for _, issue := range issues {
if _, err := fmt.Fprintln(w, issue); err != nil {
return err
}

switch issue.Severity {
case linter.SeverityError:
numErrors++
case linter.SeverityWarn:
numWarnings++
}
}

if _, err := fmt.Fprintf(w, "Found %d error%s and %d warning%s.\n", numErrors, pluralize(numErrors), numWarnings, pluralize(numWarnings)); err != nil {
return err
}

return nil
}

func pluralize(n int) string {
if n == 1 {
return ""
}
return "s"
}
5 changes: 3 additions & 2 deletions format/text_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@ func testIssues() []linter.Issue {
Col: 12,
RuleID: "no-image-latest",
Message: `image "ubuntu:latest" uses the "latest" tag; pin a specific version`,
Severity: linter.Warn,
Severity: linter.SeverityWarn,
},
{
Path: ".devcontainer/devcontainer.json",
Line: 8,
Col: 3,
RuleID: "some-error-rule",
Message: "something is broken",
Severity: linter.Error,
Severity: linter.SeverityError,
},
}
}
Expand All @@ -38,6 +38,7 @@ func TestTextWriteIssues(t *testing.T) {

want := `.devcontainer/devcontainer.json:4:12: warn: image "ubuntu:latest" uses the "latest" tag; pin a specific version (no-image-latest)
.devcontainer/devcontainer.json:8:3: error: something is broken (some-error-rule)
Found 1 error and 1 warning.
`
if sb.String() != want {
t.Errorf("WriteIssues text = %q, want %q", sb.String(), want)
Expand Down
2 changes: 1 addition & 1 deletion linter/linter.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func New() *Linter {
// RegisterRule adds r to the linter, to run at the given severity.
func (l *Linter) RegisterRule(r *Rule, severity Severity) {
l.severities[r.ID] = severity
if severity == Off {
if severity == SeverityOff {
return
}
for _, t := range r.FileTypes {
Expand Down
6 changes: 3 additions & 3 deletions linter/linter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ func TestLintRulePanicIsRecovered(t *testing.T) {
t.Parallel()

l := linter.New()
l.RegisterRule(panicRule, linter.Error)
l.RegisterRule(panicRule, linter.SeverityError)
issues, err := l.Lint(t.Context(), "devcontainer.json", []byte(`{}`), linter.Devcontainer)
if err != nil {
t.Fatalf("Lint: %v", err)
Expand All @@ -239,7 +239,7 @@ func TestLintRulePanicIsRecovered(t *testing.T) {
if issues[0].RuleID != "panic-rule" {
t.Errorf("RuleID = %q, want %q", issues[0].RuleID, "panic-rule")
}
if issues[0].Severity != linter.Error {
t.Errorf("Severity = %v, want %v", issues[0].Severity, linter.Error)
if issues[0].Severity != linter.SeverityError {
t.Errorf("Severity = %v, want %v", issues[0].Severity, linter.SeverityError)
}
}
22 changes: 11 additions & 11 deletions linter/rule.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,20 +74,20 @@ type Context struct {
type Severity int

const (
// Off disables a rule; it produces no findings.
Off Severity = iota
// Warn marks a finding as a warning.
Warn
// Error marks a finding as an error.
Error
// SeverityOff disables a rule; it produces no findings.
SeverityOff Severity = iota
// SeverityWarn marks a finding as a warning.
SeverityWarn
// SeverityError marks a finding as an error.
SeverityError
)

// String returns the severity's name, as used in output and configuration files.
func (s Severity) String() string {
switch s {
case Error:
case SeverityError:
return "error"
case Warn:
case SeverityWarn:
return "warn"
default:
return "off"
Expand Down Expand Up @@ -123,11 +123,11 @@ func (s *Severity) UnmarshalJSONFrom(dec *jsontext.Decoder) error {
func ParseSeverity(name string) (Severity, error) {
switch strings.ToLower(name) {
case "off":
return Off, nil
return SeverityOff, nil
case "warn":
return Warn, nil
return SeverityWarn, nil
case "error":
return Error, nil
return SeverityError, nil
default:
return 0, fmt.Errorf("unknown severity %q (want one of: off, warn, error)", name)
}
Expand Down
14 changes: 7 additions & 7 deletions linter/rule_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@ func TestParseSeverity(t *testing.T) {
want linter.Severity
wantErr bool
}{
{"off", "off", linter.Off, false},
{"warn", "warn", linter.Warn, false},
{"error", "error", linter.Error, false},
{"mixed case", "Error", linter.Error, false},
{"off", "off", linter.SeverityOff, false},
{"warn", "warn", linter.SeverityWarn, false},
{"error", "error", linter.SeverityError, false},
{"mixed case", "Error", linter.SeverityError, false},
{"unknown", "bogus", 0, true},
{"empty", "", 0, true},
}
Expand All @@ -73,9 +73,9 @@ func TestSeverityJSON(t *testing.T) {
severity linter.Severity
want string
}{
{linter.Off, `"off"`},
{linter.Warn, `"warn"`},
{linter.Error, `"error"`},
{linter.SeverityOff, `"off"`},
{linter.SeverityWarn, `"warn"`},
{linter.SeverityError, `"error"`},
}
for _, tt := range tests {
t.Run(tt.want, func(t *testing.T) {
Expand Down
Loading
Loading