feat(linter): add structured rule IDs (GL001–GL031)

Every Finding now carries a stable Rule string field with a GL### code.
The ID appears in output between the source location and the message:

  [ERROR] job "deploy" (ci.yml:14) GL003: missing required field 'script'
  [WARNING] (ci.yml) GL001: no stages defined

Rules:
  GL001 no-stages          GL002 workflow-when       GL003 missing-script
  GL004 unknown-stage      GL005 only-rules-conflict GL006 except-rules-conflict
  GL007 deprecated-only    GL008 invalid-when        GL009 delayed-no-start-in
  GL010 start-in-no-delayed GL011 invalid-parallel   GL012 invalid-retry
  GL013 invalid-retry-when GL014 invalid-allow-failure GL015 invalid-interruptible
  GL016 trigger-with-script GL017 invalid-trigger    GL018 invalid-coverage
  GL019 invalid-release    GL020 invalid-environment GL021 invalid-artifacts
  GL022 pages-public       GL023 invalid-cache       GL024 invalid-rules-when
  GL025 invalid-image      GL026 invalid-inherit     GL027 needs-unknown
  GL028 needs-stage-order  GL029 needs-cycle         GL030 unknown-dependency
  GL031 dependency-stage

Changes:
- internal/linter/rules.go: new file with all 31 constants + doc comments
- linter.Finding: add Rule string field; String() inserts it before the
  message colon when non-empty; format unchanged when Rule == ""
- All Finding{} literals in linter.go, keywords.go, needs.go,
  dependencies.go updated with the correct Rule: constant
- README.md lint rules table: new ID column added to all four sections
- CHANGELOG.md: entry in [Unreleased]

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-11 22:56:24 +02:00
parent 6d0aefca5b
commit f48bf02152
7 changed files with 202 additions and 50 deletions
+3
View File
@@ -46,6 +46,7 @@ func checkNeeds(p *model.Pipeline) []Finding {
}
findings = append(findings, Finding{
Severity: sev,
Rule: RuleNeedsUnknown,
Job: name,
File: job.File,
Line: job.Line,
@@ -63,6 +64,7 @@ func checkNeeds(p *model.Pipeline) []Finding {
if neededHasStage && neededStageIdx > jobStageIdx {
findings = append(findings, Finding{
Severity: Error,
Rule: RuleNeedsStageOrder,
Job: name,
File: job.File,
Line: job.Line,
@@ -124,6 +126,7 @@ func detectNeedsCycles(graph map[string][]string, jobs map[string]model.Job) []F
j := jobs[name]
findings = append(findings, Finding{
Severity: Error,
Rule: RuleNeedsCycle,
Job: name,
File: j.File,
Line: j.Line,