diff --git a/.custom-gcl.yml b/.custom-gcl.yml new file mode 100644 index 00000000..1c466a2e --- /dev/null +++ b/.custom-gcl.yml @@ -0,0 +1,5 @@ +version: v2.0.2 +destination: . +plugins: +- module: 'github.com/cschleiden/go-workflows/analyzer' + path: ./analyzer \ No newline at end of file diff --git a/.gitignore b/.gitignore index 2ceda742..cd87d76b 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,6 @@ vendor plugin.so -web/app/node_modules \ No newline at end of file +web/app/node_modules + +custom-gcl \ No newline at end of file diff --git a/.golangci.yml b/.golangci.yml index f4dcaaac..00dc2bb5 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,13 +1,52 @@ +version: "2" + run: - tests: false + allow-parallel-runners: true linters: enable: - - goworkflows - -linters-settings: - custom: - goworkflows: - path: ./plugin.so - description: go-workflows - original-url: github.com/cschleiden/go-workflows/analyzer \ No newline at end of file + - bidichk + - bodyclose + - errcheck + - errname + - errorlint + - goprintffuncname + - govet + - importas + - ineffassign + - makezero + - prealloc + - predeclared + - promlinter + - rowserrcheck + - staticcheck + - tagalign + - testifylint + - tparallel + - unconvert + - usetesting + - wastedassign + - whitespace + - unused + - goworkflows + settings: + staticcheck: + checks: + - "all" + - "-ST1003" + custom: + goworkflows: + type: "module" + original-url: "github.com/cschleiden/go-workflows/analyzer" + settings: + checkprivatereturnvalues: true + + +formatters: + enable: + - gofmt + - goimports + settings: + goimports: + local-prefixes: + - "github.com/cschleiden/go-workflows" diff --git a/.vscode/settings.json b/.vscode/settings.json index 00d83c57..cc9a2c75 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,6 @@ { // Some integration tests take quite long to run, increase the overall limit for now - "go.testFlags": ["-timeout", "120s", "-race", "-count", "1" , "-short"], + "go.testFlags": ["-timeout", "120s", "-race", "-count", "1", "-short"], "files.exclude": { "**/.git": true, "**/.svn": true, @@ -13,6 +13,9 @@ "github-actions.workflows.pinned.workflows": [], "go.testExplorer.showDynamicSubtestsInEditor": true, "go.lintTool": "golangci-lint-v2", - "Lua.diagnostics.globals": ["KEYS", "ARGV", "redis", "cjson"], - + // "go.lintFlags": ["--path-mode=abs", "--fast-only"], + // "go.alternateTools": { + // "golangci-lint-v2": "${workspaceFolder}/custom-gcl" + // }, + "Lua.diagnostics.globals": ["KEYS", "ARGV", "redis", "cjson"] } diff --git a/Makefile b/Makefile index f8ab699b..b8c7742a 100644 --- a/Makefile +++ b/Makefile @@ -9,6 +9,7 @@ GOGET=$(GOCMD) get GOMOD=$(GOCMD) mod GOFMT=gofmt GOLINT=golangci-lint +CUSTOM_GOLINT=./custom-gcl # Test parameters TEST_TIMEOUT=240s @@ -59,12 +60,16 @@ test-monoprocess: # Run all backend tests test-backends: test-redis test-mysql test-sqlite test-monoprocess -# Lint the code -lint: +custom-gcl: @echo "Checking if golangci-lint is installed..." @which $(GOLINT) > /dev/null || (echo "golangci-lint is not installed. Please install it first." && exit 1) + @echo "Building custom linter plugin..." + $(GOLINT) custom + +# Lint the code +lint: custom-gcl @echo "Running linter..." - $(GOLINT) run + $(CUSTOM_GOLINT) run # Format the code fmt: diff --git a/analyzer/README.md b/analyzer/README.md index b591a004..00d8316f 100644 --- a/analyzer/README.md +++ b/analyzer/README.md @@ -2,4 +2,18 @@ This package implements a basic analyzer for checking various common workflow error conditions. -It can be used with golangci-lint as a custom linter to provide feedback in editors or in CI runs. \ No newline at end of file +In your own .golangci.yaml configuration file, you can enable it like this: + +```yaml +version: "2" + +linters: + enable: + - goworkflows + + settings: + custom: + goworkflows: + type: module + original-url: github.com/cschleiden/go-workflows/analyzer +``` \ No newline at end of file diff --git a/analyzer/analyzer.go b/analyzer/analyzer.go index 7bc2a802..4a1a58b3 100644 --- a/analyzer/analyzer.go +++ b/analyzer/analyzer.go @@ -4,27 +4,55 @@ import ( "go/ast" "go/types" + "github.com/golangci/plugin-module-register/register" "golang.org/x/tools/go/analysis" "golang.org/x/tools/go/analysis/passes/inspect" "golang.org/x/tools/go/ast/inspector" ) -var checkPrivateReturnValues bool +func init() { + register.Plugin("goworkflows", New) +} -func New() *analysis.Analyzer { - a := &analysis.Analyzer{ - Name: "goworkflows", - Doc: "Checks for common errors when writing workflows", - Run: run, - Requires: []*analysis.Analyzer{inspect.Analyzer}, +func New(settings any) (register.LinterPlugin, error) { + // The configuration type will be map[string]any or []interface, it depends on your configuration. + // You can use https://github.com/go-viper/mapstructure to convert map to struct. + s, err := register.DecodeSettings[Settings](settings) + if err != nil { + return nil, err } - a.Flags.BoolVar(&checkPrivateReturnValues, "checkprivatereturnvalues", false, "Check return values of workflows which aren't exported") + return &GoWorkflowsPlugin{Settings: s}, nil +} + +type GoWorkflowsPlugin struct { + Settings Settings +} + +type Settings struct { + CheckPrivateReturnValues bool `json:"checkprivatereturnvalues"` +} + +func (w *GoWorkflowsPlugin) BuildAnalyzers() ([]*analysis.Analyzer, error) { + return []*analysis.Analyzer{ + { + Name: "goworkflows", + Doc: "Checks for common errors when writing workflows", + Run: w.run, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + }, + }, nil +} + +func (w *GoWorkflowsPlugin) GetLoadMode() string { + // NOTE: the mode can be `register.LoadModeSyntax` or `register.LoadModeTypesInfo`. + // - `register.LoadModeSyntax`: if the linter doesn't use types information. + // - `register.LoadModeTypesInfo`: if the linter uses types information. - return a + return register.LoadModeSyntax } -func run(pass *analysis.Pass) (interface{}, error) { +func (w *GoWorkflowsPlugin) run(pass *analysis.Pass) (interface{}, error) { inspector := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) // Expect workflows to be top level functions in a file. Therefore it should be enough to just keep track if the current @@ -84,7 +112,7 @@ func run(pass *analysis.Pass) (interface{}, error) { inWorkflow = true // Check return types - if n.Name.IsExported() || checkPrivateReturnValues { + if n.Name.IsExported() || w.Settings.CheckPrivateReturnValues { if n.Type.Results == nil || len(n.Type.Results.List) == 0 { pass.Reportf(n.Pos(), "workflow `%v` doesn't return anything. needs to return at least `error`", n.Name.Name) } else { diff --git a/analyzer/analyzer_test.go b/analyzer/analyzer_test.go index 05435670..436ccbd8 100644 --- a/analyzer/analyzer_test.go +++ b/analyzer/analyzer_test.go @@ -3,20 +3,39 @@ package analyzer import ( "testing" + "github.com/golangci/plugin-module-register/register" "github.com/stretchr/testify/require" "golang.org/x/tools/go/analysis/analysistest" ) func TestAll(t *testing.T) { - a := New() - a.Flags.Set("checkprivatereturnvalues", "true") - analysistest.Run(t, analysistest.TestData(), a, "p", "q") + newPlugin, err := register.GetPlugin("goworkflows") + require.NoError(t, err) + + plugin, err := newPlugin(map[string]any{ + "checkprivatereturnvalues": true, + }) + require.NoError(t, err) + + analyzers, err := plugin.BuildAnalyzers() + require.NoError(t, err) + + analysistest.Run(t, analysistest.TestData(), analyzers[0], "p", "q") } func TestComplex(t *testing.T) { - a := New() - a.Flags.Set("checkprivatereturnvalues", "true") - result := analysistest.Run(t, analysistest.TestData(), a, "q") + newPlugin, err := register.GetPlugin("goworkflows") + require.NoError(t, err) + + plugin, err := newPlugin(map[string]any{ + "checkprivatereturnvalues": true, + }) + require.NoError(t, err) + + analyzers, err := plugin.BuildAnalyzers() + require.NoError(t, err) + + result := analysistest.Run(t, analysistest.TestData(), analyzers[0], "q") for _, r := range result { require.NoError(t, r.Err) require.Equal(t, 1, len(r.Diagnostics)) diff --git a/analyzer/go.mod b/analyzer/go.mod new file mode 100644 index 00000000..ee47051a --- /dev/null +++ b/analyzer/go.mod @@ -0,0 +1,17 @@ +module github.com/cschleiden/go-workflows/analyzer + +go 1.24.5 + +require ( + github.com/golangci/plugin-module-register v0.1.2 + github.com/stretchr/testify v1.10.0 + golang.org/x/tools v0.37.0 +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + golang.org/x/mod v0.28.0 // indirect + golang.org/x/sync v0.17.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/analyzer/go.sum b/analyzer/go.sum new file mode 100644 index 00000000..265b3736 --- /dev/null +++ b/analyzer/go.sum @@ -0,0 +1,25 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/golangci/plugin-module-register v0.1.2 h1:e5WM6PO6NIAEcij3B053CohVp3HIYbzSuP53UAYgOpg= +github.com/golangci/plugin-module-register v0.1.2/go.mod h1:1+QGTsKBvAIvPvoY/os+G5eoqxWn70HYDm2uvUyGuVw= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U= +golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI= +golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= +golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/telemetry v0.0.0-20250908211612-aef8a434d053/go.mod h1:+nZKN+XVh4LCiA9DV3ywrzN4gumyCnKjau3NGb9SGoE= +golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE= +golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/analyzer/plugin/plugin.go b/analyzer/plugin/plugin.go deleted file mode 100644 index aa3abc5f..00000000 --- a/analyzer/plugin/plugin.go +++ /dev/null @@ -1,16 +0,0 @@ -//go:build analyzerplugin -// +build analyzerplugin - -// Custom plugin for golangci-lint -package main - -import ( - "github.com/cschleiden/go-workflows/analyzer" - "golang.org/x/tools/go/analysis" -) - -func New(conf any) ([]*analysis.Analyzer, error) { - // The configuration type will be map[string]any or []interface, it depends - // on your configuration. - return []*analysis.Analyzer{analyzer.New()}, nil -} diff --git a/go.mod b/go.mod index 48bea82b..0ca2f5c1 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/cschleiden/go-workflows -go 1.24 +go 1.24.5 toolchain go1.24.6 @@ -11,7 +11,7 @@ require ( github.com/google/uuid v1.6.0 github.com/jellydator/ttlcache/v3 v3.0.0 github.com/redis/go-redis/v9 v9.0.2 - github.com/stretchr/testify v1.9.0 + github.com/stretchr/testify v1.10.0 go.opentelemetry.io/otel v1.31.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0 @@ -31,7 +31,7 @@ require ( go.opentelemetry.io/otel/metric v1.31.0 // indirect go.opentelemetry.io/proto/otlp v1.3.1 // indirect go.uber.org/atomic v1.7.0 // indirect - golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect + golang.org/x/tools v0.37.0 // indirect google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 // indirect google.golang.org/grpc v1.67.1 // indirect lukechampine.com/uint128 v1.2.0 // indirect @@ -55,11 +55,11 @@ require ( github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/mattn/go-isatty v0.0.17 // indirect github.com/sirupsen/logrus v1.9.3 // indirect - golang.org/x/mod v0.17.0 // indirect - golang.org/x/net v0.30.0 // indirect - golang.org/x/sync v0.8.0 // indirect - golang.org/x/sys v0.26.0 // indirect - golang.org/x/text v0.19.0 // indirect + golang.org/x/mod v0.28.0 // indirect + golang.org/x/net v0.44.0 // indirect + golang.org/x/sync v0.17.0 // indirect + golang.org/x/sys v0.36.0 // indirect + golang.org/x/text v0.29.0 // indirect google.golang.org/protobuf v1.35.1 // indirect ) diff --git a/go.sum b/go.sum index ae305323..7ab4bfd1 100644 --- a/go.sum +++ b/go.sum @@ -97,8 +97,8 @@ github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY= go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0 h1:K0XaT3DwHAcV4nKLzcQvwAgSyisUghWoY20I7huthMk= @@ -119,20 +119,20 @@ go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= -golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= -golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U= +golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI= +golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= +golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= +golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= -golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= -golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= +golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= +golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= +golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE= +golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 h1:DdoeryqhaXp1LtT/emMP1BRJPHHKFi5akj/nbx/zNTA= google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s=