feat(linter): GL033 static dead-rules detection
ci / vet, staticcheck, test, build (push) Successful in 2m12s
release / Build and publish release (push) Successful in 1m7s

Add rule GL033 that warns when every rule in a job's rules: block has
an explicit when: never, making the job permanently excluded from any
pipeline run. This is a pure static check — no if: evaluation or context
required. Only rules with literal when: never trigger it; rules with no
when: (defaults to on_success), when: manual, when: always, or
when: on_failure are treated as reachable.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-14 09:13:19 +02:00
parent 5fee51ec7d
commit 54b5850835
8 changed files with 188 additions and 1 deletions
+23
View File
@@ -95,6 +95,7 @@ func checkJobKeywords(name string, job model.Job) []Finding {
findings = append(findings, checkArtifacts(name, job)...)
findings = append(findings, checkCache(name, job)...)
findings = append(findings, checkRules(name, job)...)
findings = append(findings, checkDeadRules(name, job)...)
findings = append(findings, checkImage(name, job)...)
findings = append(findings, checkInherit(name, job)...)
return findings
@@ -476,6 +477,28 @@ func checkRules(name string, job model.Job) []Finding {
return findings
}
// checkDeadRules reports when every rule in a job's rules: block has an
// explicit when: never, making the job permanently unreachable. This is a
// provably-correct static claim: no matter which if: condition matches, the
// outcome is always "never"; and if no rule matches, the implicit fallback is
// also skip. No if: evaluation is required.
func checkDeadRules(name string, job model.Job) []Finding {
if len(job.Rules) == 0 {
return nil
}
for _, r := range job.Rules {
if r.When != "never" {
return nil
}
}
return []Finding{{
Severity: Warning,
Rule: RuleDeadRules,
Job: name,
Message: "rules: block can never activate; every rule has 'when: never' — job is permanently excluded from the pipeline",
}}
}
func checkImage(name string, job model.Job) []Finding {
if job.Image == nil {
return nil