feat(cli)!: subcommand CLI, graph tree mode, local include resolution
BREAKING CHANGES: - `glint <file>` removed; use `glint check <file>` - `--graph <mode>` removed; use `glint graph [mode]` - `--graph-out` renamed to `--out` on `glint graph` feat(cli): ruff-style subcommands — `glint check` and `glint graph [mode]` feat(graph): `glint graph tree` — terminal job tree with context annotations feat(graph): context flags (--branch/--tag/--source/--var) on `glint graph` feat(resolver): recursive local include resolution from disk fix(resolver): extends unknown base emits warning instead of fatal error fix(model): script/before_script/after_script accept block scalar string form test(linter): Samba project CI fixtures as integration tests chore(build): fix .gitignore to not exclude cmd/glint/ directory docs: update CHANGELOG, README, ROADMAP for v0.2.0 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
package graph
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"git.k3nny.fr/glint/internal/cicontext"
|
||||
"git.k3nny.fr/glint/internal/model"
|
||||
)
|
||||
|
||||
// Tree returns a tree-command-style text representation of pipeline jobs
|
||||
// grouped by stage.
|
||||
//
|
||||
// Hidden template jobs (names starting with ".") are excluded.
|
||||
// When ctx is non-nil and non-empty, each job is annotated with its evaluated
|
||||
// state ([skipped] or [manual]); active jobs carry no annotation.
|
||||
// Without a context, job-type annotations ([manual], [delayed], [trigger]) are
|
||||
// shown instead.
|
||||
func Tree(p *model.Pipeline, ctx *cicontext.Context) string {
|
||||
var sb strings.Builder
|
||||
|
||||
// Collect visible jobs.
|
||||
var visible []string
|
||||
for name := range p.Jobs {
|
||||
if !strings.HasPrefix(name, ".") {
|
||||
visible = append(visible, name)
|
||||
}
|
||||
}
|
||||
sort.Strings(visible)
|
||||
|
||||
// Group by stage.
|
||||
byStage := make(map[string][]string)
|
||||
for _, name := range visible {
|
||||
stage := p.Jobs[name].Stage
|
||||
if stage == "" {
|
||||
stage = "test"
|
||||
}
|
||||
byStage[stage] = append(byStage[stage], name)
|
||||
}
|
||||
|
||||
// Build ordered stage list: declared first, then extras.
|
||||
seen := make(map[string]bool)
|
||||
var stages []string
|
||||
for _, s := range p.Stages {
|
||||
if len(byStage[s]) > 0 && !seen[s] {
|
||||
stages = append(stages, s)
|
||||
seen[s] = true
|
||||
}
|
||||
}
|
||||
for _, name := range visible {
|
||||
stage := p.Jobs[name].Stage
|
||||
if stage == "" {
|
||||
stage = "test"
|
||||
}
|
||||
if !seen[stage] {
|
||||
stages = append(stages, stage)
|
||||
seen[stage] = true
|
||||
}
|
||||
}
|
||||
|
||||
sb.WriteString("pipeline\n")
|
||||
|
||||
for si, stage := range stages {
|
||||
lastStage := si == len(stages)-1
|
||||
stagePrefix, childPrefix := branchChars(lastStage)
|
||||
|
||||
sb.WriteString(stagePrefix + stage + "\n")
|
||||
|
||||
jobs := byStage[stage]
|
||||
sort.Strings(jobs)
|
||||
for ji, name := range jobs {
|
||||
lastJob := ji == len(jobs)-1
|
||||
jobBranch, _ := branchChars(lastJob)
|
||||
sb.WriteString(childPrefix + jobBranch + jobLabel(p.Jobs[name], name, ctx) + "\n")
|
||||
}
|
||||
}
|
||||
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func branchChars(last bool) (branch, continuation string) {
|
||||
if last {
|
||||
return "└── ", " "
|
||||
}
|
||||
return "├── ", "│ "
|
||||
}
|
||||
|
||||
func jobLabel(job model.Job, name string, ctx *cicontext.Context) string {
|
||||
if ctx != nil && !ctx.IsEmpty() {
|
||||
switch cicontext.EvalJob(job, ctx) {
|
||||
case cicontext.JobSkipped:
|
||||
return name + " [skipped]"
|
||||
case cicontext.JobManual:
|
||||
return name + " [manual]"
|
||||
}
|
||||
return name
|
||||
}
|
||||
// No context: annotate by job type.
|
||||
var tags []string
|
||||
if job.When == "manual" {
|
||||
tags = append(tags, "manual")
|
||||
}
|
||||
if job.When == "delayed" {
|
||||
tags = append(tags, "delayed")
|
||||
}
|
||||
if job.Trigger != nil {
|
||||
tags = append(tags, "trigger")
|
||||
}
|
||||
if len(tags) == 0 {
|
||||
return name
|
||||
}
|
||||
return name + " [" + strings.Join(tags, ", ") + "]"
|
||||
}
|
||||
Reference in New Issue
Block a user