go@v1
Hygiene checks for Go modules. Adopt it with:
extends: - alint://bundled/go@v1 Gated with when: facts.has_go (true if any go.mod exists
anywhere in the tree) plus a per-rule
scope_filter: { has_ancestor: go.mod } on per-file content
rules so they only apply to files inside a Go module — useful
in polyglot monorepos where Go modules sit alongside
Rust / Node / Python subdirectories. Override has_go with
your own facts: block if your project uses a non-standard
location.
go.mod is a Go-specific format (not TOML/YAML/JSON), so shape
checks use file_content_matches rather than the structured-
query family.
What it checks
Section titled "What it checks"7 rules. Each links to its section below, which explains the check and shows its definition.
| Rule | Reports |
|---|---|
go-mod-existserror | Go module: go.mod at the repo root is required. |
go-sum-existswarning | go.sum pins the hashes of every transitive dependency; commit it for reproducible builds. |
go-mod-declares-module-patherror | go.mod must declare a module <path> on its first directive. |
go-mod-declares-go-versionwarning | go.mod should declare a go <version> directive (e.g. go 1.22) so the toolchain version is explicit. |
go-sources-no-bidierror | Trojan Source (CVE-2021-42574): bidi override chars in Go sources are rejected. |
go-sources-no-zero-widtherror | Zero-width characters in Go sources are rejected (review hazard). |
go-sources-final-newlineinfo | File must end with a single \n. |
All 7 rules run only when facts.has_go holds, so the ruleset stays quiet in repositories it doesn't apply to.
go-mod-exists
Section titled "go-mod-exists"- kind:
file_exists - level:
error - when:
facts.has_go - policy: https://go.dev/ref/mod#go-mod-file
Go module: go.mod at the repo root is required.
- id: go-mod-exists when: facts.has_go kind: file_exists paths: go.mod root_only: true level: error message: "Go module: go.mod at the repo root is required." policy_url: "https://go.dev/ref/mod#go-mod-file" go-sum-exists
Section titled "go-sum-exists"go.sum pins every transitive dependency's hash — missing it means non-reproducible builds. Modules with zero deps legitimately omit go.sum; disable via level: off in that case.
- kind:
file_exists - level:
warning - when:
facts.has_go - policy: https://go.dev/ref/mod#go-sum-files
go.sum pins the hashes of every transitive dependency; commit it for reproducible builds. Modules with zero dependencies legitimately omit it.
- id: go-sum-exists when: facts.has_go kind: file_exists paths: go.sum root_only: true level: warning message: >- go.sum pins the hashes of every transitive dependency; commit it for reproducible builds. Modules with zero dependencies legitimately omit it. policy_url: "https://go.dev/ref/mod#go-sum-files" go-mod-declares-module-path
Section titled "go-mod-declares-module-path"Every go.mod starts with module <path>. Absent or empty module path means go build will refuse the module.
- kind:
file_content_matches - level:
error - when:
facts.has_go
go.mod must declare a
module <path>on its first directive.
- id: go-mod-declares-module-path when: facts.has_go kind: file_content_matches paths: go.mod pattern: '(?m)^module\s+\S+' level: error message: "go.mod must declare a `module <path>` on its first directive."go-mod-declares-go-version
Section titled "go-mod-declares-go-version"Every go.mod should declare a go <major>.<minor> toolchain floor. Missing it means the toolchain selects its default, which changes across Go releases.
- kind:
file_content_matches - level:
warning - when:
facts.has_go - policy: https://go.dev/ref/mod#go-mod-file-go
go.mod should declare a
go <version>directive (e.g.go 1.22) so the toolchain version is explicit.
- id: go-mod-declares-go-version when: facts.has_go kind: file_content_matches paths: go.mod pattern: '(?m)^go\s+\d+\.\d+' level: warning message: >- go.mod should declare a `go <version>` directive (e.g. `go 1.22`) so the toolchain version is explicit. policy_url: "https://go.dev/ref/mod#go-mod-file-go" go-sources-no-bidi
Section titled "go-sources-no-bidi"- kind:
no_bidi_controls - level:
error - when:
facts.has_go - policy: https://trojansource.codes/
Trojan Source (CVE-2021-42574): bidi override chars in Go sources are rejected.
- id: go-sources-no-bidi when: facts.has_go kind: no_bidi_controls paths: "**/*.go" scope_filter: has_ancestor: go.mod level: error message: "Trojan Source (CVE-2021-42574): bidi override chars in Go sources are rejected." policy_url: "https://trojansource.codes/" go-sources-no-zero-width
Section titled "go-sources-no-zero-width"- kind:
no_zero_width_chars - level:
error - when:
facts.has_go
Zero-width characters in Go sources are rejected (review hazard).
- id: go-sources-no-zero-width when: facts.has_go kind: no_zero_width_chars paths: "**/*.go" scope_filter: has_ancestor: go.mod level: error message: "Zero-width characters in Go sources are rejected (review hazard)."go-sources-final-newline
Section titled "go-sources-final-newline"- kind:
final_newline - level:
info - when:
facts.has_go - fix:
file_append_final_newline(applied byalint fix)
- id: go-sources-final-newline when: facts.has_go kind: final_newline paths: "**/*.go" scope_filter: has_ancestor: go.mod level: info fix: file_append_final_newline: {}The when: clauses above read these facts. Each is resolved once per run; alint facts prints what they resolved to in your repository.
facts: - id: has_go any_file_exists: [go.mod, "**/go.mod" ]Customize
Section titled "Customize"Every rule here can be overridden by id from your own .alint.yml: change its level, or set level: off to drop it. An id that doesn't exist is an error at config load, so a typo can't silently pass.
extends: - alint://bundled/go@v1 rules: - id: go-mod-exists level: off - id: go-sum-exists level: errorTo take only part of the ruleset, filter it where you extend it with only: or except::
extends: - url: alint://bundled/go@v1 except: [go-mod-exists]Bundled rulesets covers versioning and how rulesets combine.
Source
Section titled "Source"The full ruleset definition, comments included, is committed at crates/alint-dsl/rulesets/v1/go.yml in the alint repo.