f48bf02152
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>
51 lines
1.2 KiB
Go
51 lines
1.2 KiB
Go
package linter
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"git.k3nny.fr/glint/internal/model"
|
|
)
|
|
|
|
func checkDependencies(p *model.Pipeline) []Finding {
|
|
stageIndex := make(map[string]int, len(p.Stages))
|
|
for i, s := range p.Stages {
|
|
stageIndex[s] = i
|
|
}
|
|
|
|
var findings []Finding
|
|
for name, job := range p.Jobs {
|
|
if len(job.Dependencies) == 0 {
|
|
continue
|
|
}
|
|
jobStageIdx, jobHasStage := stageIndex[job.Stage]
|
|
for _, dep := range job.Dependencies {
|
|
depJob, exists := p.Jobs[dep]
|
|
if !exists {
|
|
findings = append(findings, Finding{
|
|
Severity: Error,
|
|
Rule: RuleUnknownDependency,
|
|
Job: name,
|
|
File: job.File,
|
|
Line: job.Line,
|
|
Message: fmt.Sprintf("'dependencies' references unknown job %q", dep),
|
|
})
|
|
continue
|
|
}
|
|
if len(p.Stages) > 0 && jobHasStage && depJob.Stage != "" {
|
|
depIdx, depHasStage := stageIndex[depJob.Stage]
|
|
if depHasStage && depIdx >= jobStageIdx {
|
|
findings = append(findings, Finding{
|
|
Severity: Error,
|
|
Rule: RuleDependencyStage,
|
|
Job: name,
|
|
File: job.File,
|
|
Line: job.Line,
|
|
Message: fmt.Sprintf("'dependencies' job %q must be in an earlier stage (in %q, current job is in %q)", dep, depJob.Stage, job.Stage),
|
|
})
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return findings
|
|
}
|