Skip to content

Commit

Permalink
feat: add severity levels for violations
Browse files Browse the repository at this point in the history
  • Loading branch information
omissis committed Aug 16, 2022
1 parent 9a184a3 commit ff99319
Show file tree
Hide file tree
Showing 49 changed files with 674 additions and 400 deletions.
6 changes: 3 additions & 3 deletions .goarkitect.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ rules:
- go.sum
- main.go
- README.md
shoulds:
musts:
- kind: exist
because: "they are mandatory for the project to work"
- name: all files in "cmd/" folder are go files
Expand All @@ -26,7 +26,7 @@ rules:
excepts:
- kind: this
filePath: .DS_Store
shoulds:
musts:
- kind: end_with
suffix: .go
because: "only go files are allowed by the project layout"
Expand All @@ -39,6 +39,6 @@ rules:
- .idea
- .tool-versions
- .vscode
shoulds:
musts:
- kind: be_gitignored
because: "every developer should be able to choose what behaviors and configurations run on their machine"
87 changes: 51 additions & 36 deletions api/config_schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,44 @@
"filePath"
]
},
"fileExpect": {
"type": "object",
"oneOf": [
{
"$ref": "#/definitions/fileShouldBeGitencrypted"
},
{
"$ref": "#/definitions/fileShouldBeGitignored"
},
{
"$ref": "#/definitions/fileShouldContainValue"
},
{
"$ref": "#/definitions/fileShouldEndWith"
},
{
"$ref": "#/definitions/fileShouldExist"
},
{
"$ref": "#/definitions/fileShouldHaveContentMatchingRegex"
},
{
"$ref": "#/definitions/fileShouldHaveContentMatching"
},
{
"$ref": "#/definitions/fileShouldHavePermissions"
},
{
"$ref": "#/definitions/fileShouldMatchGlob"
},
{
"$ref": "#/definitions/fileShouldMatchRegex"
},
{
"$ref": "#/definitions/fileShouldStartWith"
}
]
},
"fileThatAreInFolder": {
"type": "object",
"additionalProperties": false,
Expand Down Expand Up @@ -177,45 +215,22 @@
]
}
},
"musts": {
"type": "array",
"items": {
"$ref": "#/definitions/fileExpect"
}
},
"shoulds": {
"type": "array",
"items": {
"type": "object",
"oneOf": [
{
"$ref": "#/definitions/fileShouldBeGitencrypted"
},
{
"$ref": "#/definitions/fileShouldBeGitignored"
},
{
"$ref": "#/definitions/fileShouldContainValue"
},
{
"$ref": "#/definitions/fileShouldEndWith"
},
{
"$ref": "#/definitions/fileShouldExist"
},
{
"$ref": "#/definitions/fileShouldHaveContentMatchingRegex"
},
{
"$ref": "#/definitions/fileShouldHaveContentMatching"
},
{
"$ref": "#/definitions/fileShouldHavePermissions"
},
{
"$ref": "#/definitions/fileShouldMatchGlob"
},
{
"$ref": "#/definitions/fileShouldMatchRegex"
},
{
"$ref": "#/definitions/fileShouldStartWith"
}
]
"$ref": "#/definitions/fileExpect"
}
},
"coulds": {
"type": "array",
"items": {
"$ref": "#/definitions/fileExpect"
}
},
"because": {
Expand Down
3 changes: 2 additions & 1 deletion cmd/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ import (
"encoding/json"
"flag"
"fmt"
"goarkitect/internal/schema/santhosh"
"log"
"os"
"path/filepath"

"goarkitect/internal/schema/santhosh"

"github.com/mitchellh/cli"
"github.com/santhosh-tekuri/jsonschema"
)
Expand Down
24 changes: 22 additions & 2 deletions cmd/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ package cmd
import (
"flag"
"fmt"
"goarkitect/internal/config"
"log"
"os"
"path/filepath"

"goarkitect/internal/arch/rule"
"goarkitect/internal/config"

"github.com/mitchellh/cli"
)

Expand All @@ -25,6 +27,8 @@ func (vc *verifyCommand) Help() string {
}

func (vc *verifyCommand) Run(args []string) int {
exitCode := 0

vc.parseFlags()

for _, configFile := range vc.configFiles {
Expand All @@ -35,9 +39,13 @@ func (vc *verifyCommand) Run(args []string) int {
results := config.Execute(conf)

vc.printResults(results)

if vc.hasErrors(results) {
exitCode = 1
}
}

return 0
return exitCode
}

func (vc *verifyCommand) Synopsis() string {
Expand Down Expand Up @@ -86,3 +94,15 @@ func (vc *verifyCommand) printResults(results []config.RuleExecutionResult) {
}
}
}

func (vc *verifyCommand) hasErrors(results []config.RuleExecutionResult) bool {
for _, r := range results {
for _, v := range r.Violations {
if v.Severity() == rule.Error.String() {
return true
}
}
}

return false
}
48 changes: 48 additions & 0 deletions examples/.goarkitect.errors.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Each rule in this file contains a config that will trigger a warning
---
rules:
- name: mandatory files should exist
kind: file
matcher:
kind: "set"
filePaths:
- wrong.json
- .envrc.dist
- .goarkitect.yaml
- .tool-versions.dist
- go.mod
- go.sum
- main.go
- README.md
musts:
- kind: exist
because: "they are mandatory for the project to work"
- name: all files in "cmd/" folder are js files
kind: file
matcher:
kind: all
thats:
- kind: are_in_folder
folder: ./cmd
recursive: false
excepts:
- kind: this
filePath: .DS_Store
shoulds:
- kind: end_with
suffix: .js
because: "only go files are allowed by the project layout"
- name: some dev files and folders are not git ignored
kind: file
matcher:
kind: set
filePaths:
- .envrc
- .idea
- .tool-versions
- .vscode
shoulds:
- kind: be_gitignored
options:
- kind: negated
because: "every developer should be able to choose what behaviors and configurations run on their machine"
3 changes: 2 additions & 1 deletion internal/arch/file/except/this.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package except

import (
"goarkitect/internal/arch/rule"
"path/filepath"
"strings"

"goarkitect/internal/arch/rule"
)

func This(filePath string) *ThisExpression {
Expand Down
3 changes: 2 additions & 1 deletion internal/arch/file/except/this_test.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package except_test

import (
"testing"

"goarkitect/internal/arch/file"
"goarkitect/internal/arch/file/except"
"goarkitect/internal/arch/rule"
"testing"

"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package should
package expect

import (
"bytes"
"fmt"
"goarkitect/internal/arch/rule"
"os/exec"
"path/filepath"

"goarkitect/internal/arch/rule"
)

func BeGitencrypted(opts ...Option) *gitEncryptedExpression {
Expand All @@ -22,7 +23,7 @@ type gitEncryptedExpression struct {
baseExpression
}

func (e gitEncryptedExpression) Evaluate(rb rule.Builder) []rule.Violation {
func (e gitEncryptedExpression) Evaluate(rb rule.Builder) []rule.CoreViolation {
return e.evaluate(rb, e.doEvaluate, e.getViolation)
}

Expand All @@ -38,14 +39,14 @@ func (e gitEncryptedExpression) doEvaluate(rb rule.Builder, filePath string) boo
return bytes.Contains(out, []byte("not encrypted"))
}

func (e gitEncryptedExpression) getViolation(filePath string) rule.Violation {
func (e gitEncryptedExpression) getViolation(filePath string) rule.CoreViolation {
format := "file '%s' is not gitencrypted"

if e.options.negated {
format = "file '%s' is gitencrypted"
}

return rule.NewViolation(
return rule.NewCoreViolation(
fmt.Sprintf(format, filepath.Base(filePath)),
)
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package should_test
package expect_test

import (
"goarkitect/internal/arch/file"
"goarkitect/internal/arch/file/should"
"goarkitect/internal/arch/rule"
"os"
"path/filepath"
"testing"

"goarkitect/internal/arch/file"
"goarkitect/internal/arch/file/expect"
"goarkitect/internal/arch/rule"

"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
)
Expand All @@ -21,43 +22,43 @@ func Test_BeGitencrypted(t *testing.T) {
testCases := []struct {
desc string
ruleBuilder *file.RuleBuilder
options []should.Option
want []rule.Violation
options []expect.Option
want []rule.CoreViolation
}{
{
desc: "file 'encrypted.txt' should be gitencrypted",
desc: "file 'encrypted.txt' expect be gitencrypted",
ruleBuilder: file.One(filepath.Join(basePath, "test/encrypted.txt")),
want: nil,
},
{
desc: "file 'not_encrypted.txt' should be gitencrypted",
desc: "file 'not_encrypted.txt' expect be gitencrypted",
ruleBuilder: file.One(filepath.Join(basePath, "test/not_encrypted.txt")),
want: []rule.Violation{
rule.NewViolation("file 'not_encrypted.txt' is not gitencrypted"),
want: []rule.CoreViolation{
rule.NewCoreViolation("file 'not_encrypted.txt' is not gitencrypted"),
},
},
{
desc: "negated: file 'encrypted.txt' should not be gitencrypted",
desc: "negated: file 'encrypted.txt' expect not be gitencrypted",
ruleBuilder: file.One(filepath.Join(basePath, "test/encrypted.txt")),
options: []should.Option{should.Negated{}},
want: []rule.Violation{
rule.NewViolation("file 'encrypted.txt' is gitencrypted"),
options: []expect.Option{expect.Negated{}},
want: []rule.CoreViolation{
rule.NewCoreViolation("file 'encrypted.txt' is gitencrypted"),
},
},
{
desc: "negated: file 'not_encrypted.txt' should not be gitencrypted",
desc: "negated: file 'not_encrypted.txt' expect not be gitencrypted",
ruleBuilder: file.One(filepath.Join(basePath, "test/not_encrypted.txt")),
options: []should.Option{should.Negated{}},
options: []expect.Option{expect.Negated{}},
want: nil,
},
}

for _, tC := range testCases {
t.Run(tC.desc, func(t *testing.T) {
hcm := should.BeGitencrypted(tC.options...)
hcm := expect.BeGitencrypted(tC.options...)
got := hcm.Evaluate(tC.ruleBuilder)

if !cmp.Equal(got, tC.want, cmp.AllowUnexported(rule.Violation{}), cmpopts.EquateEmpty()) {
if !cmp.Equal(got, tC.want, cmp.AllowUnexported(rule.CoreViolation{}), cmpopts.EquateEmpty()) {
t.Errorf("want = %+v, got = %+v", tC.want, got)
}
})
Expand Down
Loading

0 comments on commit ff99319

Please sign in to comment.