Why this matters
helm is the canonical midsize, modular Go OSS monorepo with the
standard CNCF/Go shape: ~530 production .go files, one root
go.mod, ~150k LoC, a single 240-line Makefile that drives the dev
workflow, one ~120-line .golangci.yml enabling 17 linters + 2
formatters, one ~50-line scripts/validate-license.sh, and the
standard GitHub Actions + OWNERS + dependabot CNCF-project shape.
It’s neither the kubernetes-style verify-script empire nor the golang/go-style minimal-tooling extreme. It has the typical CNCF shape: golangci-lint config, license-header bash, OWNERS file, GHA workflows, dependabot, goreleaser. That’s the population alint needs to convert.
The structural-validation surface is deliberately small: ~22
distinct checks across the Makefile (test-style,
test-source-headers, tidy), the .golangci.yml formatters
block, scripts/validate-license.sh, the GitHub Actions workflows,
and the on-disk metadata files (OWNERS, .github/env,
dependabot.yml, .goreleaser.yaml, AGENTS.md, ADOPTERS.md, KEYS,
code-of-conduct).
Headline catch
helm has mature, well-curated tooling. golangci-lint orchestrates
17 linters; scripts/validate-license.sh walks the tree for the
Apache-2 + Helm-Authors header literals; goreleaser handles the
release matrix. What’s missing is the structural floor underneath
all of it.
Running the alint config against the live tree surfaces real findings the existing pipeline doesn’t catch:
-
Zero-width character (U+200B) in
internal/plugin/plugin.go:80— line 80, column 70 contains zero-width spaces inside a comment block. Caught by the bundledgo-sources-no-zero-widthrule (Trojan-Source CVE-2021-42574 defence).validate-license.shdoesn’t look at character-class hygiene; golangci-lint doesn’t either by default. Net-new structural finding alint catches that no existing tool in helm’s pipeline does. -
5 GitHub workflows declare
permissions: read-allorpermissions: {}instead ofcontents: read—codeql-analysis.yml,govulncheck.yml,release.yml,scorecards.yml,stale.yaml. The OpenSSF Scorecard Token-Permissions check would surface these as well, but only on its weekly cron. The bundledgha-workflow-contents-readrule catches them on every PR. -
Plus a structural drift the bundled
oss-no-trailing-whitespacerule catches:.golangci.ymlline 43 has trailing whitespace.
The pitch:
“helm is the typical CNCF-shape Go monorepo: one Makefile orchestrates lint via golangci-lint and a 50-line bash script that greps for license headers. alint replaces the orchestration layer with one declarative file, keeps golangci-lint as the deep- Go AST workhorse, and adds a structural floor (Trojan-Source defence, GitHub Actions hardening, top-level-file conventions) that helm doesn’t currently enforce.”
Where alint earns its keep here
The complementary case study to kubernetes/golang-go: those two anchor the extremes (mega-monorepo with a bespoke verify-script empire vs canonical-with-minimal-tooling). helm anchors the centre — the population alint actually needs to win.
In numbers:
- ~70 % of helm’s structural surface maps directly to existing
alint rules (license header, formatters-block shape,
.github/envpinning, OWNERS shape, top-level files, GHA hardening, hygiene floor) - ~10 % shells out via
command:to existing tools (golangci-lint,gofmt,go mod tidy,misspell,govulncheck) - ~20 % is out of alint’s scope by design (depguard, gomodguard, revive, modernize, sloglint — the Go-AST-aware checks alint isn’t trying to do)
One declarative 58-rule config replaces the Makefile’s test-style
orchestration plus the scripts/validate-license.sh 50-liner plus
the half-dozen shape-implicit assertions buried inside
.golangci.yml and the release pipeline.
helm also surfaces the v0.10 *_path_contains set-membership
shorthand as a v0.10 design candidate (now 3 sources: helm + deno
- bazel) — a cleaner abstraction over the YAML-array-
*_path_equalspitfall (#17 in CONFIG-AUTHORING.md) than thefile_content_matchesworkaround.
Future story angles
- Helm-chart structural invariants for
pkg/chart/testdata/’s ~80 fixture charts — a futurehelm/chart-structure@v1bundled ruleset (apiVersion/version/appVersion+ valid semver) sits one layer above whathelm lintitself covers - The
agent-context@v1overlay is a natural fit — helm ships AGENTS.md but doesn’t enforce its shape today alint suggestagainst the live tree — the repo is small enough (~530 .go files) that the suggester would terminate quickly; likely candidates are per-pkg/*/test-coverage thresholds,cmd/helm/subcommand-package conventions,internal/visibility discipline