feat(linter): glint explain, GL042 rules:if: reachability, GL043 inherit completeness
- `glint explain <RULE>`: new subcommand printing rule description, rationale, bad-YAML example and fix for every GL001–GL043 rule. `glint explain` (no arg) lists all rules with ID, severity, title. Rule IDs are case-insensitive. - GL042 (rules:if: evaluated reachability): warns when every rules:if: condition evaluates to false given the values of variables declared in the pipeline YAML, making the job statically unreachable. Conservative: only fires when all referenced variables are declared in YAML; predefined CI_* / GITLAB_* variables are skipped to avoid false positives. - GL043 (inherit: completeness): warns when inherit: default: is declared but there is no default: block in the pipeline (dead declaration), or when the list form names fields not set in the default: block. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"git.k3nny.fr/glint/internal/linter"
|
||||
)
|
||||
|
||||
func cmdExplain(args []string) {
|
||||
if len(args) == 0 {
|
||||
printRuleList()
|
||||
return
|
||||
}
|
||||
ruleID := strings.ToUpper(args[0])
|
||||
entry, ok := linter.RuleCatalog[ruleID]
|
||||
if !ok {
|
||||
fmt.Fprintf(os.Stderr, "glint explain: unknown rule %q\n\nRun 'glint explain' to list all rules.\n", ruleID)
|
||||
os.Exit(2)
|
||||
}
|
||||
printRuleEntry(ruleID, entry)
|
||||
}
|
||||
|
||||
func printRuleEntry(id string, e linter.RuleEntry) {
|
||||
sev := strings.ToLower(string(e.Severity))
|
||||
header := fmt.Sprintf("%s [%s] %s", id, sev, e.Title)
|
||||
rule := fmt.Sprintf("\n%s\n%s\n\n%s\n",
|
||||
header,
|
||||
strings.Repeat("─", len(header)),
|
||||
e.Description,
|
||||
)
|
||||
fmt.Print(rule)
|
||||
|
||||
if e.Example != "" {
|
||||
fmt.Println("Example:")
|
||||
fmt.Println()
|
||||
for _, line := range strings.Split(e.Example, "\n") {
|
||||
fmt.Printf(" %s\n", line)
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
if e.Fix != "" {
|
||||
fmt.Println("Fix:")
|
||||
fmt.Println()
|
||||
for _, line := range strings.Split(e.Fix, "\n") {
|
||||
fmt.Printf(" %s\n", line)
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
}
|
||||
|
||||
func printRuleList() {
|
||||
ids := make([]string, 0, len(linter.RuleCatalog))
|
||||
for id := range linter.RuleCatalog {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
sort.Strings(ids)
|
||||
|
||||
fmt.Printf("glint %s — lint rules\n\n", version)
|
||||
for _, id := range ids {
|
||||
e := linter.RuleCatalog[id]
|
||||
sev := strings.ToLower(string(e.Severity))
|
||||
fmt.Printf(" %-6s [%-7s] %s\n", id, sev, e.Title)
|
||||
}
|
||||
fmt.Println("\nUse 'glint explain <RULE>' for details on a specific rule.")
|
||||
}
|
||||
@@ -39,6 +39,7 @@ Usage: glint [OPTIONS] <COMMAND>
|
||||
Commands:
|
||||
check Lint a pipeline file — exits 0 (clean) or 1 (errors found)
|
||||
graph Visualise the pipeline as a job tree or Mermaid graph
|
||||
explain Show description and fix for a lint rule (e.g. glint explain GL007)
|
||||
|
||||
Options:
|
||||
-h, --help Print help
|
||||
@@ -57,6 +58,8 @@ func main() {
|
||||
cmdCheck(os.Args[2:])
|
||||
case "graph":
|
||||
cmdGraph(os.Args[2:])
|
||||
case "explain":
|
||||
cmdExplain(os.Args[2:])
|
||||
case "-h", "--help", "help":
|
||||
fmt.Fprintf(os.Stderr, "glint %s\n\n", version)
|
||||
fmt.Fprint(os.Stderr, globalUsage)
|
||||
|
||||
Reference in New Issue
Block a user