Skip to content

Commit 3af2c94

Browse files
committed
feat(ci): add custom linter to prevent stuttering in pkg paths
1 parent a0f696e commit 3af2c94

8 files changed

Lines changed: 84 additions & 4 deletions

File tree

.custom-gcl.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
version: v2.12.2
2+
plugins:
3+
- module: 'github.com/stackitcloud/stackit-sdk-go/tools'
4+
import: 'github.com/stackitcloud/stackit-sdk-go/tools/linters/pkgstutter'
5+
path: ./tools

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,4 @@ bin/
1515
*.vscode/
1616

1717
go.work.sum
18+
custom-gcl

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ project-tools: ## Install project tools
3737

3838
# LINT
3939
lint-golangci-lint: ## Lint Go code
40+
@golangci-lint custom
4041
@echo ">> Linting with golangci-lint"
4142
@$(SCRIPTS_BASE)/lint-golangci-lint.sh "${skip-non-generated-files}" "${service}"
4243

golang-ci.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ run:
99
concurrency: 4
1010
linters:
1111
enable:
12+
- pkgstutter # custom local linter
1213
- bodyclose
1314
- depguard
1415
- errorlint
@@ -25,6 +26,10 @@ linters:
2526
- noctx # false positive: finds errors with http.NewRequest that dont make sense
2627
- unparam # false positives
2728
settings:
29+
custom:
30+
pkgstutter:
31+
type: module
32+
description: "A custom local linter"
2833
depguard:
2934
rules:
3035
main:

scripts/lint-golangci-lint.sh

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,9 @@ lint_service() {
3434
echo ">> Linting service ${service}"
3535
cd ${SERVICES_PATH}/${service}
3636
if [ "${SKIP_NON_GENERATED_FILES}" = true ]; then
37-
golangci-lint run ${GOLANG_CI_ARGS} --skip-dirs wait # All manually maintained files are in subfolders
37+
${ROOT_DIR}/custom-gcl run ${GOLANG_CI_ARGS} --skip-dirs wait # All manually maintained files are in subfolders
3838
else
39-
golangci-lint run ${GOLANG_CI_ARGS}
39+
${ROOT_DIR}/custom-gcl run ${GOLANG_CI_ARGS}
4040
fi
4141
}
4242

@@ -48,7 +48,7 @@ else
4848
if [ "${SKIP_NON_GENERATED_FILES}" = false ]; then
4949
echo ">> Linting core"
5050
cd ${CORE_PATH}
51-
golangci-lint run ${GOLANG_CI_ARGS}
51+
${ROOT_DIR}/custom-gcl run ${GOLANG_CI_ARGS}
5252
fi
5353

5454
for service_dir in ${SERVICES_PATH}/*; do
@@ -61,7 +61,7 @@ else
6161
example=$(basename ${example_dir})
6262
echo ">> Linting example ${example}"
6363
cd ${example_dir}
64-
golangci-lint run ${GOLANG_CI_ARGS}
64+
${ROOT_DIR}/custom-gcl run ${GOLANG_CI_ARGS}
6565
done
6666
fi
6767
fi

tools/go.mod

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
module github.com/stackitcloud/stackit-sdk-go/tools
2+
3+
go 1.25.9
4+
5+
require (
6+
github.com/golangci/plugin-module-register v0.1.2
7+
golang.org/x/tools v0.45.0
8+
)

tools/go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
github.com/golangci/plugin-module-register v0.1.2 h1:e5WM6PO6NIAEcij3B053CohVp3HIYbzSuP53UAYgOpg=
2+
github.com/golangci/plugin-module-register v0.1.2/go.mod h1:1+QGTsKBvAIvPvoY/os+G5eoqxWn70HYDm2uvUyGuVw=
3+
golang.org/x/tools v0.45.0 h1:18qN3FAooORvApf5XjCXgsuayZOEtXf6JK18I3+ONa8=
4+
golang.org/x/tools v0.45.0/go.mod h1:LuUGqqaXcXMEFEruIVJVm5mgDD8vww/z/SR1gQ4uE/0=
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package pkgstutter
2+
3+
import (
4+
"strings"
5+
6+
"github.com/golangci/plugin-module-register/register"
7+
"golang.org/x/tools/go/analysis"
8+
)
9+
10+
var Analyzer = &analysis.Analyzer{
11+
Name: "pkgstutter",
12+
Doc: "Prevents stuttering package names, e.g. github.com/foo/bar/wait/wait",
13+
Run: run,
14+
}
15+
16+
func run(pass *analysis.Pass) (any, error) {
17+
pkgPath := pass.Pkg.Path()
18+
parts := strings.Split(pkgPath, "/")
19+
20+
// Check for adjacent identical parts in the path
21+
for i := 1; i < len(parts); i++ {
22+
if parts[i] == parts[i-1] {
23+
// If a stutter is found, report it at the package declaration
24+
// of the first file in the package to pinpoint the error.
25+
if len(pass.Files) > 0 {
26+
pass.Reportf(
27+
pass.Files[0].Package,
28+
"package path %q contains stuttering (%s/%s)",
29+
pkgPath, parts[i-1], parts[i],
30+
)
31+
}
32+
// Break after the first finding to avoid spamming multiple errors for the same package
33+
break
34+
}
35+
}
36+
37+
return nil, nil
38+
}
39+
40+
func init() {
41+
register.Plugin("pkgstutter", New)
42+
}
43+
44+
func New(settings any) (register.LinterPlugin, error) {
45+
return &plugin{}, nil
46+
}
47+
48+
type plugin struct{}
49+
50+
func (p *plugin) BuildAnalyzers() ([]*analysis.Analyzer, error) {
51+
return []*analysis.Analyzer{Analyzer}, nil
52+
}
53+
54+
func (p *plugin) GetLoadMode() string {
55+
return register.LoadModeTypesInfo
56+
}

0 commit comments

Comments
 (0)