Files
glint/internal/graph/tree.go
T

114 lines
2.6 KiB
Go
Raw Permalink Normal View History

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, ", ") + "]"
}