Documentation
¶
Overview ¶
Package lint holds the `forge lint` command group — the project linter pipeline (golangci / buf / frontend / forge-convention / scaffold / migration-safety / wire-coverage / authz-completeness …) plus the targeted single-rule flags and the --json aggregator.
It is a dir-nested command group (the devspace idiom). The substrate — the ~30 run*/collect* linter entry points and the shared linterStep table — moved here verbatim from package cli so the command is the substrate's home, not a thin shim over package-cli internals. The few genuinely cross-cutting helpers it needs (project-store loader, the forge.yaml-not-found sentinel, the user-vs-maintainer flag split) come from the leaf packages internal/cli/factory and internal/cli/cmdutil, so the group never imports internal/cli (which would cycle — internal/cli blank-imports the groups).
audit.go (now the internal/cli/audit group) reuses two of this package's collectors — CollectOptionalDepsGuardFindings and CollectConfigDepsFindings — via the exported aliases in lint_optional_deps_guard.go / lint_config_deps.go. That is a clean group→group dependency (audit → lint), no cycle.
`forge lint --suggest-buf-excepts` walks the project's .proto files, runs `buf lint`, and prints a buf.yaml `lint.except:` snippet suggesting which STANDARD rules to silence for a port-in-progress codebase.
Migration projects (a Connect API spec ported from a pre-forge repo) routinely hit the same handful of STANDARD rules: PACKAGE_VERSION_SUFFIX when the original namespace was `foo.v1` vs the buf-canonical `foo.v1`, RPC_REQUEST_STANDARD_NAME when the RPCs predate the `<Method>Request` convention, FIELD_LOWER_SNAKE_CASE when fields were snake_case-with-acronyms, etc. The hint at the end of `forge lint` (printBufLintExceptHint) already nudges the user toward the four most common offenders, but the nudge is a static list — it can't tell which rules ACTUALLY fire in the user's tree.
This command runs `buf lint` and aggregates the output by rule name, then prints the rules that fired across more than the `--threshold` (default 3) files. The heuristic is intentionally conservative: a single .proto with one violation is probably a real bug to fix; the same violation across many files is almost always a port-wide convention mismatch best resolved via except.
The output is YAML-shaped so the user can paste it directly into buf.yaml. Nothing is mutated.
FRICTION 2026-06-02: cp-forge proto port — author hit FIELD_LOWER_SNAKE_CASE on 40+ files and had to grep buf output manually to count rule occurrences before deciding what to except.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ErrProjectConfigNotFound = cmdutil.ErrProjectConfigNotFound
ErrProjectConfigNotFound is the canonical "forge.yaml not found" sentinel, re-exported from cmdutil so the lint helpers compare against the same value internal/cli's config.ErrProjectConfigNotFound aliases.
Functions ¶
func CheckAuthzCompleteness ¶
CheckAuthzCompleteness is the generate-pipeline entry point: it runs the authz completeness lint and returns a build-gating error (naming every offending RPC) when any method lacks an explicit authz decision, or nil when the project is clean / has no proto to check. It is the same check `forge lint` runs, exposed for the generate pipeline so an un-annotated RPC fails `forge generate` too (the runtime fail-closed deny is the backstop; this is the first line). When buf is unavailable the check is a no-op — generate must not hard-require buf on PATH for non-proto steps.
Types ¶
type ConfigDepsFinding ¶
type ConfigDepsFinding = configDepsFinding
ConfigDepsFinding is the exported alias for the config-deps finding, consumed by the internal/cli/audit group's auditConfigDeps roll-up. A type alias (not a new type) so audit's field access (.Role/.Package/.File/.Line/.Field/.Type) is unchanged from when both commands lived in package cli.
func CollectConfigDepsFindings ¶
func CollectConfigDepsFindings(projectDir string) ([]ConfigDepsFinding, error)
CollectConfigDepsFindings is the exported entry the audit group calls. It forwards to the package-internal collector; the audit roll-up is the only cross-package consumer.
type OptionalDepsGuardFinding ¶
type OptionalDepsGuardFinding = optionalDepsGuardFinding
OptionalDepsGuardFinding is the exported alias for the optional-deps-guard finding, consumed by the internal/cli/audit group's auditOptionalDepsGuard roll-up. A type alias (not a new type) so audit's field access (.Role/.Package/.File/.Line/.Expr/.Method) is unchanged from when both commands lived in package cli.
func CollectOptionalDepsGuardFindings ¶
func CollectOptionalDepsGuardFindings(projectDir string) ([]OptionalDepsGuardFinding, error)
CollectOptionalDepsGuardFindings is the exported entry the audit group calls. It forwards to the package-internal collector; the audit roll-up is the only cross-package consumer.
type WireCoverageFinding ¶
type WireCoverageFinding = wireCoverageFinding
WireCoverageFinding is the exported alias consumed by the internal/cli/audit group's auditWireCoverage roll-up. A type alias (not a new type) so audit's field access (.Function/.Field) is unchanged from when both commands lived in package cli. The underlying wireCoverageFinding mirrors forgeconv.Finding for the report shape without forcing a cross-package dependency. Wire coverage is its own thing — the user reads "1 unresolved field on service X" and goes to fix it; they don't need the full forgeconv-style remediation sentence.
func ScanWireGen ¶
func ScanWireGen(r io.Reader, path, projectDir string) ([]WireCoverageFinding, error)
ScanWireGen is the exported entry the audit group calls to build the wire-coverage roll-up without shelling to `forge lint`. It forwards to the package-internal scanner.