fix(ci): show the correct job's log in child pipelines, and the requested pipeline's jobs
Fixes #8495 (closed) and closes the second half of #7997 (closed).
Problem
Two bugs, both from the same root cause: glab ci view had more than one answer to "which pipeline am I looking at".
#8495 (closed) — wrong job's log in a child pipeline. Pressing Enter on a job inside a child pipeline showed the log of an unrelated job from the parent. The Enter and Ctrl+Space paths resolved a job by name via RunTraceSha(… projectID, commitSHA, curJob.Name), where projectID and commitSHA were captured once at startup and never updated on descent. Job names are only unique within one pipeline, so the lookup fell through PipelineJobWithSha's last-running → first-pending → last-job fallback chain and silently streamed a different job. The job grid rendered correctly the whole time because updateJobs read the live pipeline stack — the correct pipeline was right there, just not used to fetch logs.
#7997 (closed) — wrong pipeline's jobs. glab ci view -p 100 titled the window "Pipeline #100" while rendering jobs from whatever pipeline was newest on that commit; -b <branch> had the same split. The requested pipeline reached only the title and the --web URL. 0dabef9f stopped using commit.LastPipeline to resolve the pipeline but left curPipeline using it to decide which pipeline's jobs to render, so the original symptom survived in the grid after the title was fixed.
Fix
One source of truth: the navigation stack. run() seeds it with the pipeline that was actually requested, descending into a child pushes onto it, and every downstream request — job list, logs, cancel, retry — reads the top of it.
- Resolve jobs by ID against the current pipeline.
RunTraceShais replaced byRunTraceJob(ctx, client, w, pid, jobID, pollInterval). No name-based resolution remains, so there is no fallback chain left to pick the wrong job. - Key the cached log page by job ID. New
logsPageKeyhelper applied at all six sites. Name keying also collided for retried jobs, which share a name with the original attempt but have a new ID — they were showing the failed attempt's cached trace. - Seed the stack in
run().curPipelineno longer needs thecommit.LastPipelinefallback, which letscommitdrop out ofupdateJobs,jobsView, andinputCaptureentirely — it was only ever threaded through to reach that fallback. - Esc / q ascends only while more than one pipeline is stacked, since the requested pipeline now permanently occupies the bottom.
Also fixed, same root cause
- Cancel and retry used the startup project too, so they addressed the wrong project for multi-project triggers.
PlayOrRetryJobstookrepo string; widened topid anyto match the package convention. - A goroutine race: the trace goroutine read the
curJobglobal after being scheduled, so navigating during a fetch could stream a different job. Both trace sites now snapshot the job and project first.
Removed
PipelineJobWithSha and pipelineJobsWithSha, whose only caller was the deleted RunTraceSha. Their pipeline lookup was independently unsound: it listed project pipelines by SHA and took pipelines[0], and a child pipeline shares its parent's SHA.
Seeding the stack also removed both GetCommit calls and a redundant second GetPipeline — three fewer API requests at startup, and run() is ~25 lines shorter.
Testing
make test (4347 tests), make lint (0 issues), go test -race on the touched packages, and the full lefthook run pre-push all pass.
New coverage:
TestRunTraceJob— traces by ID against both string- and numerically-identified projects (the cross-project child case), plus its error path.Test_logsPageKey— same-name-different-pipeline, stable key, and retried-job cases.Test_curPipeline_tracksDescentIntoChildPipeline— descent into a child pipeline in another project and the return trip.Test_pipelineInfo— the seed conversion carries the pipeline and project IDs.Test_curPipeline_emptyStackreplacesTest_curPipeline_nilLastPipeline, which covered the fallback this MR deliberately removes.
The two existing TestCIView --web cases had mocked GetCommit; gomock failed them when the call disappeared, which was a useful confirmation it was really gone. The mocks are removed and both cases still assert the same output. No test cases were repurposed or dropped.
Please verify by hand before merging
Neither behaviour is reachable from a unit test — run() returns early without a TTY, and the existing tests only exercise --web. Two things worth a manual pass:
- On a pipeline with a child pipeline: descend into it, open a job's log, and confirm it is that job's own trace. Then Esc back and open a parent job with the same name.
glab ci view -p <an older pipeline ID on a SHA that has a newer one>— the grid should match the title. And Esc at the top level should still quit.
Note on ownership
@bobby060 reported #8495 (closed) with an accurate root-cause analysis and said they had a fix in progress. I built this before checking in with them — happy to close this in favour of theirs, or to have them review. The diagnosis in the issue is theirs; this MR extends it to the cancel/retry paths, the retried-job cache collision, and #7997 (closed).