-
Notifications
You must be signed in to change notification settings - Fork 10.2k
Use depguard linter to forbid dependencies #20721
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,7 @@ version: "2" | |
linters: | ||
default: none | ||
enable: # please keep this alphabetized | ||
- depguard # Implement forbidden dependencies | ||
- errorlint | ||
- ineffassign | ||
- nakedret | ||
|
@@ -17,6 +18,67 @@ linters: | |
- usetesting | ||
- whitespace | ||
settings: | ||
depguard: | ||
rules: | ||
api: | ||
list-mode: lax | ||
files: | ||
# Check files inside the api module/directory, but skip the server/etcdserver/api | ||
# directory. Note that this is configured this way as depguard doesn't support relative | ||
# paths. | ||
# > Should always prefix a file glob with **/ as files are matched against absolute | ||
# > paths. | ||
# Refer to: https://github.com/OpenPeeDeeP/depguard?tab=readme-ov-file#config | ||
# Could be simplified if they resolve issue: https://github.com/OpenPeeDeeP/depguard/issues/54 | ||
- '**/api/**/*.go' | ||
- '!**/server/etcdserver/api/**/*.go' | ||
deny: | ||
- pkg: go.etcd.io/etcd/api/v3$ | ||
- pkg: go.etcd.io/etcd/pkg/v3 | ||
- pkg: go.etcd.io/etcd/v3 | ||
- pkg: go.etcd.io/tests/v3 | ||
client: | ||
list-mode: lax | ||
files: | ||
- "**/client/v3/**/*.go" | ||
deny: | ||
- pkg: go.etcd.io/etcd/pkg/v3 | ||
- pkg: go.etcd.io/etcd/v3 | ||
- pkg: go.etcd.io/tests/v3 | ||
etcdctl: | ||
list-mode: lax | ||
files: | ||
- "**/etcdctl/**/*.go" | ||
deny: | ||
- pkg: go.etcd.io/etcd/v3 | ||
- pkg: go.etcd.io/tests/v3 | ||
etcdutl: | ||
list-mode: lax | ||
files: | ||
- "**/etcdutl/**/*.go" | ||
deny: | ||
- pkg: go.etcd.io/etcd/v3 | ||
- pkg: go.etcd.io/tests/v3 | ||
server: | ||
list-mode: lax | ||
files: | ||
- "**/server/**/*.go" | ||
deny: | ||
- pkg: go.etcd.io/etcd/v3 | ||
- pkg: go.etcd.io/tests/v3 | ||
pkg: | ||
list-mode: lax | ||
files: | ||
# Check files inside the pkg module/directory, but skip the pkg directories inside | ||
# client and the tools/rw-heatmaps modules. Note that this is configured this way as | ||
# depguard doesn't support relative paths. | ||
- "**/pkg/**/*.go" | ||
- "!**/client/pkg/**/*.go" | ||
- "!**/tools/rw-heatmaps/pkg/**/*.go" | ||
Comment on lines
+75
to
+77
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggest not to mixes multiple modules. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here. This is not mixing multiple modules, it's targeting only the root |
||
deny: | ||
- pkg: go.etcd.io/etcd/api/v3 | ||
- pkg: go.etcd.io/etcd/v3 | ||
- pkg: go.etcd.io/tests/v3 | ||
nakedret: | ||
# Align with https://github.com/alexkohler/nakedret/blob/v1.0.2/cmd/nakedret/main.go#L10 | ||
max-func-lines: 5 | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we separate configuration for different modules?
I suggest that we define separate rules for each module, following exactly the same way as we do now in each go.mod file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, we can separate the configuration per module. I initially had it that way, but then it looked very repetitive, and I thought of consolidating
etcdutl
,etcdtl
, andserver
as they have the same rule.However, what you're looking at is a
depguard
limitation. The path needs to be absolute (refer to OpenPeeDeeP/depguard#54).So, breaking down these two lines:
'**/api/**/*.go'
: Any go file that is in a path with/api/
. Note that this would match any files insideserver/etcdserver/api
'!**/server/etcdserver/api/**/*.go'
: Deny files that have a path like/server/etcdserver/api
. Emphasis on the exclamation mark.You can see the same configuration in other projects. i.e., this is from woodpecker: https://github.com/woodpecker-ci/woodpecker/blob/388557d94aa9fe06841a207eba4ffa4ba0ae5a05/.golangci.yaml#L109-L112
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do not get time to deep it for now. May take a close look sometime later.
But my immediate feeling is that it's more complicated & hard to maintain if we mix different modules.
repetitive
should be a minor concern. It's much much clearer and easy to maintain.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright, I'll change it, but be aware that we'll still need the negation
!
paths due to depguard's limitation with paths.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done, I did split etcdctl, etcdutl, and server, which were sharing the configuration.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How does it differ from the current setup? We need to verify FORBIDDEN_DEPENDENCY clauses, it's as easy to miss. For example we haven't really added FORBIDDEN_DEPENDENCY clauses for cache package https://github.com/etcd-io/etcd/blob/main/cache/go.mod
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
replace xxx => FORBIDDEN_DEPENDENCY
is added in module level (in go.mod files); it should be stable. Butdepguard
add rules in package & files level, which is less stable (as we may add new packages)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Either way (the current approach) or a linter, would require manual intervention when we create new packages. As far as I know, there's nothing that would magically work without us adding the forbidden imports.
Either is also prone to errors. I still can't understand why we're forbidding
go.etcd.io/etcd
. Because if I understand correctly, that package isv1
.If what we don't like about depguard is how they define the list of files. When I did my research I found that:
revive
linter hasimports-blocklist
. Unfortunately there's no way to specify it per module or per file path (like depguard).revive
..gomodguard.yaml
in each module.I'll open a PR with the gomodguard approach.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems that we can't define rules for multiple modules in .golangci.yaml. It should be fine. Adding a separate
.gomodguard.yaml
for each module is fine.Not sure I understood this. There is no golang source code in the top-level module, nor any
replace xxx => FORBIDDEN_DEPENDENCY
directives in https://github.com/etcd-io/etcd/blob/main/go.modAnyway, the PR #20748 looks good to me. To double confirm, can it clear the blocker to use go workspace as you mentioned in #20721 (comment)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a minor inconvenience whenever we have the Go workspace. With the workspace, you can run (once):
From the top-level of the repository.
With this linter configured per module, we'll still need to do
Yes, that would clear the
go.mod
replaces blocker.