Skip to content

Commit

Permalink
feat: toolcahin directive pattern
Browse files Browse the repository at this point in the history
  • Loading branch information
ldez committed Dec 3, 2024
1 parent 4990fb6 commit 5df57c2
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 32 deletions.
10 changes: 10 additions & 0 deletions cmd/gomoddirectives/gomoddirectives.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ type config struct {
ToolForbidden bool
GoDebugForbidden bool
GoVersionPattern string
ToolchainPattern string
}

func main() {
Expand All @@ -42,6 +43,7 @@ func main() {
flag.BoolVar(&cfg.ReplaceAllowLocal, "local", false, "Allow local replace directives")
flag.BoolVar(&cfg.RetractAllowNoExplanation, "retract-no-explanation", false, "Allow to use retract directives without explanation")
flag.BoolVar(&cfg.ToolchainForbidden, "toolchain", false, "Forbid the use of toolchain directive")
flag.StringVar(&cfg.ToolchainPattern, "toolchain-pattern", "", "Pattern to validate toolchain directive")
flag.BoolVar(&cfg.ToolForbidden, "tool", false, "Forbid the use of tool directives")
flag.BoolVar(&cfg.GoDebugForbidden, "godebug", false, "Forbid the use of godebug directives")
flag.StringVar(&cfg.GoVersionPattern, "goversion", "", "Pattern to validate go min version directive")
Expand Down Expand Up @@ -73,6 +75,14 @@ func main() {
}
}

if cfg.ToolchainPattern != "" {
var err error
opts.ToolchainPattern, err = regexp.Compile(cfg.ToolchainPattern)
if err != nil {
log.Fatal(err)
}
}

results, err := gomoddirectives.Analyze(opts)
if err != nil {
log.Fatal(err)
Expand Down
30 changes: 22 additions & 8 deletions gomoddirectives.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const (
reasonRetract = "a comment is mandatory to explain why the version has been retracted"
reasonExclude = "exclude directive is not allowed"
reasonToolchain = "toolchain directive is not allowed"
reasonToolchainPattern = "toolchain directive (%s) doesn't match the pattern '%s'"
reasonTool = "tool directive is not allowed"
reasonGoDebug = "godebug directive is not allowed"
reasonGoVersion = "go directive (%s) doesn't match the pattern '%s'"
Expand Down Expand Up @@ -52,6 +53,7 @@ type Options struct {
ExcludeForbidden bool
RetractAllowNoExplanation bool
ToolchainForbidden bool
ToolchainPattern *regexp.Regexp
ToolForbidden bool
GoDebugForbidden bool
GoVersionPattern *regexp.Regexp
Expand Down Expand Up @@ -120,6 +122,26 @@ func checkGoVersionDirectives(file *modfile.File, opts Options) []Result {
return []Result{NewResult(file, file.Go.Syntax, fmt.Sprintf(reasonGoVersion, file.Go.Version, opts.GoVersionPattern.String()))}
}

func checkToolchainDirective(file *modfile.File, opts Options) []Result {
if file.Toolchain == nil {
return nil
}

if opts.ToolchainForbidden {
return []Result{NewResult(file, file.Toolchain.Syntax, reasonToolchain)}
}

if opts.ToolchainPattern == nil {
return nil
}

if !opts.ToolchainPattern.MatchString(file.Toolchain.Name) {
return []Result{NewResult(file, file.Toolchain.Syntax, fmt.Sprintf(reasonToolchainPattern, file.Toolchain.Name, opts.ToolchainPattern.String()))}
}

return nil
}

func checkRetractDirectives(file *modfile.File, opts Options) []Result {
if opts.RetractAllowNoExplanation {
return nil
Expand Down Expand Up @@ -211,14 +233,6 @@ func checkReplaceDirective(opts Options, r *modfile.Replace) string {
return fmt.Sprintf("%s: %s", reasonReplace, r.Old.Path)
}

func checkToolchainDirective(file *modfile.File, opts Options) []Result {
if !opts.ToolchainForbidden || file.Toolchain == nil {
return nil
}

return []Result{NewResult(file, file.Toolchain.Syntax, reasonToolchain)}
}

func checkGoDebugDirectives(file *modfile.File, opts Options) []Result {
if !opts.GoDebugForbidden {
return nil
Expand Down
74 changes: 51 additions & 23 deletions gomoddirectives_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,25 +251,6 @@ func TestAnalyzeFile(t *testing.T) {
ToolForbidden: false,
},
},
{
desc: "toolchain: don't allow",
modulePath: "toolchain/go.mod",
opts: Options{
ToolchainForbidden: true,
},
expected: []Result{{
Reason: "toolchain directive is not allowed",
Start: token.Position{Filename: "go.mod", Offset: 0, Line: 5, Column: 1},
End: token.Position{Filename: "go.mod", Offset: 0, Line: 5, Column: 19},
}},
},
{
desc: "toolchain: allow",
modulePath: "toolchain/go.mod",
opts: Options{
ToolchainForbidden: false,
},
},
{
desc: "godebug: don't allow",
modulePath: "godebug/go.mod",
Expand Down Expand Up @@ -319,10 +300,10 @@ func TestAnalyzeFile(t *testing.T) {
desc: "goversion: pattern not matched",
modulePath: "goversion_family/go.mod",
opts: Options{
GoVersionPattern: regexp.MustCompile(`\d\.\d+\.0`),
GoVersionPattern: regexp.MustCompile(`\d\.\d+\.0$`),
},
expected: []Result{{
Reason: "go directive (1.22) doesn't match the pattern '\\d\\.\\d+\\.0'",
Reason: "go directive (1.22) doesn't match the pattern '\\d\\.\\d+\\.0$'",
Start: token.Position{Filename: "go.mod", Offset: 0, Line: 3, Column: 1},
End: token.Position{Filename: "go.mod", Offset: 0, Line: 3, Column: 8},
}},
Expand All @@ -331,7 +312,53 @@ func TestAnalyzeFile(t *testing.T) {
desc: "goversion: no Go version",
modulePath: "empty/go.mod",
opts: Options{
GoVersionPattern: regexp.MustCompile(`\d\.\d+(\.0)?`),
GoVersionPattern: regexp.MustCompile(`\d\.\d+(\.0)?$`),
},
},
{
desc: "toolchain: don't allow",
modulePath: "toolchain/go.mod",
opts: Options{
ToolchainForbidden: true,
ToolchainPattern: regexp.MustCompile(`go\d\.\d+\.\d+$`),
},
expected: []Result{{
Reason: "toolchain directive is not allowed",
Start: token.Position{Filename: "go.mod", Offset: 0, Line: 5, Column: 1},
End: token.Position{Filename: "go.mod", Offset: 0, Line: 5, Column: 19},
}},
},
{
desc: "toolchain: allow",
modulePath: "toolchain/go.mod",
opts: Options{
ToolchainForbidden: false,
},
},
{
desc: "toolchain: pattern match",
modulePath: "toolchain/go.mod",
opts: Options{
ToolchainPattern: regexp.MustCompile(`go\d\.\d+\.\d+$`),
},
},
{
desc: "toolchain: pattern not matched",
modulePath: "toolchain/go.mod",
opts: Options{
ToolchainPattern: regexp.MustCompile(`go\d\.22\.\d+$`),
},
expected: []Result{{
Reason: "toolchain directive (go1.23.3) doesn't match the pattern 'go\\d\\.22\\.\\d+$'",
Start: token.Position{Filename: "go.mod", Offset: 0, Line: 5, Column: 1},
End: token.Position{Filename: "go.mod", Offset: 0, Line: 5, Column: 19},
}},
},
{
desc: "toolchain: no Go version",
modulePath: "empty/go.mod",
opts: Options{
ToolchainPattern: regexp.MustCompile(`go\d\.22\.\d+$`),
},
},
{
Expand All @@ -346,9 +373,10 @@ func TestAnalyzeFile(t *testing.T) {
ExcludeForbidden: true,
RetractAllowNoExplanation: false,
ToolchainForbidden: true,
ToolchainPattern: regexp.MustCompile(`go\d\.\d+\.\d+$`),
ToolForbidden: true,
GoDebugForbidden: true,
GoVersionPattern: regexp.MustCompile(`\d\.\d+(\.0)?`),
GoVersionPattern: regexp.MustCompile(`\d\.\d+(\.0)?$`),
},
},
}
Expand Down
9 changes: 8 additions & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ linters-settings:
# Default: false
toolchain-forbidden: true

# Defines a pattern to validate `toolchain` directive.
# Default: '' (no match)
toolchain-pattern: 'go1\.22\.\d+$'

# Forbid the use of the `tool` directives.
# Default: false
tool-forbidden: true
Expand All @@ -44,7 +48,7 @@ linters-settings:

# Defines a pattern to validate `go` minimum version directive.
# Default: '' (no match)
go-version-pattern: '\d\.\d+(\.0)?'
go-version-pattern: '1\.\d+(\.0)?$'
```
### As a CLI
Expand All @@ -70,6 +74,8 @@ Flags:
Forbid the use of tool directives
-toolchain
Forbid the use of toolchain directive
-toolchain-pattern string
Pattern to validate toolchain directive
```

## Details
Expand Down Expand Up @@ -149,6 +155,7 @@ tool (
### [`toolchain`](https://golang.org/ref/mod#go-mod-file-toolchain) directive

- Ban `toolchain` directive.
- Use a regular expression to constraint the Go minimum version.

```go
module example.com/foo
Expand Down

0 comments on commit 5df57c2

Please sign in to comment.