package web // The CI pages: the runs of a repository, a run with its jobs, a job with its // steps and its log (actions.go). A state is always a word as well as a // symbol and a colour. // CheckIcon is the symbol of a state: a mark in the colour of its kind, with // the state as its text for a screen reader. templ CheckIcon(kind, state string) { } templ Actions(l Layout, p ActionsPage) { @Shell(l, nil) { if len(p.Runs) == 0 {

no runs yet: a push that matches a workflow of .github/workflows starts one.

} else { for _, r := range p.Runs { @RunListRow(r) }
workflowcommitrefactortimecreated
} if p.More != "" {

} } } // RunListRow is a row of the list of runs; its id is the target of the live // updates. templ RunListRow(r RunRow) { @CheckIcon(r.Kind, r.State) { r.Workflow } { r.Number } if r.Event != "" { { r.Event } } if r.Attempt > 1 { attempt { itoa(int(r.Attempt)) } } { r.Short } { r.Title } { r.Ref } { r.Actor } { r.State }{ " " }{ r.Duration } if r.WhenAt != "" { } } // RecentActionsBlock is the "Recent actions" section of the summary rail; its // id is the target of the live updates. Without a run it is hidden. templ RecentActionsBlock(a RecentActions) {

Recent actions View all

} templ RunDetail(l Layout, p RunPage) { @Shell(l, runRail(p)) { @LiveSlot("run-notice")
@RunBody(p)
if p.Artifacts != nil { @RunArtifacts(*p.Artifacts) } @RunSummaries(p.Summaries, p.SummariesMore) } } // runBodyFragment is the body of the run page as a live fragment. templ runBodyFragment(p RunPage) {
@RunBody(p)
} // RunBody is the jobs of a run, replaced by the live updates. templ RunBody(p RunPage) {

@CheckIcon(p.Kind, p.State){ p.Workflow } { p.Number }

if p.Error != "" {
{ p.Error }
} if len(p.Annotations) > 0 {

Annotations { itoa(len(p.Annotations)) }

@AnnotationList(p.Annotations, p.AnnotationsMore, false)
} } // AnnotationList is a list of annotations: the level as a mark with its word, // the job and the step, the title and the message, and file:line. Everything // comes from the workflow and is text. Without grouped, each item names its job (the // run page); grouped lists the annotations by step instead (the job page). templ AnnotationList(anns []AnnotationView, more bool, grouped bool) { if more {

more annotations exist than are shown.

} } // JobAnnotations is the annotations of the job page, replaced by the live // updates; the element exists even when there is none, so that the first one // has something to replace. templ JobAnnotations(anns []AnnotationView, more bool) {
if len(anns) > 0 {

Annotations { itoa(len(anns)) }

@AnnotationList(anns, more, true) }
} // RunArtifacts is the artifacts of a run, with the space the artifacts of // the repository use. Names come from the workflow and are text. templ RunArtifacts(a ArtifactsView) {

Artifacts { itoa(len(a.Rows)) }

for _, r := range a.Rows { }
namesizejobexpires
{ r.Name } { r.Size } if r.JobHref != "" { { r.Job } } else { { r.Job } } if r.ExpiresAt != "" { } else { { r.Expires } } if r.DeleteAction != "" {
}

{ a.Usage }

} // RunSummaries is the job summaries of a run, one block per step that wrote // one. The Markdown is rendered by the README renderer: raw HTML dropped, URLs // filtered, images shown as their alt text. templ RunSummaries(sums []JobSummaries, more bool) { for _, j := range sums {

if j.JobHref != "" { { j.Job } } else { { j.Job } } summary

for _, s := range j.Steps {

{ s.Step }

if s.Note != "" {

{ s.Note }

} else { @templ.Raw(s.HTML) }
} if j.More {

the job has more summaries than the first 20, which are shown.

}
} if more {

more summaries are not shown: the summaries of the page are over { humanSize(maxSummaryPageBytes) }.

} } templ runRail(p RunPage) {

Run

result
{ p.State }
event
{ p.Event }
commit
{ p.Short }
ref
{ p.Ref }
actor
{ p.Actor }
attempt
{ itoa(int(p.Attempt)) } of { itoa(int(p.Latest)) }
if len(p.Attempts) > 0 { }
if p.CancelAction != "" || p.RerunAction != "" {

Actions

if p.CancelAction != "" {
} if p.RerunAction != "" {
} if p.CanRerunFailed {
}
} } // JobHead is the title of the job page with its state and its error, replaced // by the live updates. total is the number of lines the log holds. templ JobHead(j JobRow, total int64) {

@CheckIcon(j.Kind, j.State){ j.Name } { j.State }{ " " }{ j.Duration }

if j.Error != "" {

{ j.Error }

}
} // JobSteps is the steps of the job in the rail, replaced by the live updates. templ JobSteps(steps []StepView) {
    for _, s := range steps {
  1. @CheckIcon(s.Kind, s.State) if s.LogEnd > s.LogStart { { s.Name } } else { { s.Name } } { s.Duration }
  2. }
} templ Job(l Layout, p JobPage) { @Shell(l, jobRail(p)) { @LiveSlot("job-notice") @JobHead(p.JobRow, p.Total) @JobAnnotations(p.Annotations, p.AnnotationsMore)
if p.Note != "" {

{ p.Note }

} if p.Prev != "" {

}
    for _, ln := range p.Lines { @LogLine(ln) }
if p.Next != "" {

}
} } // LogLine is a line of the log. Group and error markers of the runner show as // headings and errors, never as raw text. templ LogLine(ln LogLineView) {
  • { ln.Text }
  • } templ jobRail(p JobPage) {

    Steps

    @JobSteps(p.Steps)

    Job

    run
    { p.RunName }
    if p.Runner != "" {
    runner
    { p.Runner }
    } if p.Isolation != "" {
    isolation
    { p.Isolation }
    }
    lines
    { itoa64(p.Total) }

    raw log

    } // CommitChecks is the combined state of the checks of a commit, as a mark // with a count, linking to the page that explains it. templ CommitChecks(c ChecksSummary) { if c.Total > 0 { @CheckIcon(c.Kind, c.State) { c.Label } } } // MRChecksSum is the state of the checks of a merge request in the bar above // the diff, a link to the checks of the rail. The event stream replaces it // with the checks section (same event, fragments.go). templ MRChecksSum(c ChecksSummary) { if c.Total > 0 { @CheckIcon(c.Kind, c.State) { c.Label } } else { no checks } } // MRChecks is the checks section of the rail of a merge request. templ MRChecks(c ChecksSection) {

    Checks { itoa(len(c.Rows)) }

    if len(c.Rows) == 0 {

    no checks

    } else { } if c.Note != "" {

    note: { c.Note }

    }
    }