GitLab adapter: pipelines, jobs, job logs and labels #139

Closed
opened 2026-10-12 07:51:39 -05:00 by deadstyle · 3 comments
Owner

Problem

A GitLab session has no [3] box data, no job logs, and no label editing until the adapter implements the optional capabilities. Parent: #137. Depends on #138 (the internal/forge/gitlab package); pick this up after #138 merges.

Acceptance criteria

  • The GitLab adapter implements forge.RunLister, forge.LogReader and forge.Labeler, so [3] lists pipelines, a pipeline shows its jobs with their stage, R opens a pipeline's page, and labels can be edited, as on Forgejo (design.md → Boxes [3]; Keymap R; ADR 0006 capabilities; architecture.md: "GitLab uses pipelines → stages → jobs, which maps to Job.Stage").
  • A pipeline's state folds into the same CIState values as #138's pipeline fold.
  • Job logs stream without sending the token to any other host.
  • Setting labels by ID works even though GitLab's API takes label names, and never creates a new label.

Plan

In internal/forge/gitlab, using #138's client:

  • ListRuns: GET /projects/:id/pipelines?per_page=100&order_by=id&sort=desc plus ref (bare branch) and sha from RunFilter. One page only, newest first, as ADR 0017's runs exception (record it in ADR 0018's consequences or a short addendum). Mapping: ID ← id, Number ← iid, Workflow ← name (falls back to source), Branch ← ref, Commit ← sha, Event ← source, StartedAt ← started_at else created_at, Duration ← duration when present (the list gains it in GitLab 19.4; older servers leave it zero, no per-pipeline GET), WebURL ← web_url. Status: #138's fold.
  • ListJobs: GET …/pipelines/:id/jobs?per_page=100 (paginated; retried jobs excluded, GitLab's default). Stage ← stage, Name, Status folded the same way, Attempt = 1. Trigger (bridge) jobs aren't in this list; leave them out.
  • JobLog: GET /projects/:id/jobs/:job_id/trace, streamed, outside any cache. Drop PRIVATE-TOKEN on any redirect hop off the API origin (as the github adapter's JobLog); test it. The log carries ANSI and section_start/section_end markers; pass the bytes through untouched (the Logs view owns rendering).
  • Labeler: ListLabels ← GET /projects/:id/labels (group labels included by default; paginated). ItemLabels reads the item's labels names and maps them to ListLabels IDs (an item label missing from the list keeps ID 0). SetLabels maps IDs → names via ListLabels (an unknown ID is an error, so GitLab never auto-creates a label), then PUT the issue or MR with labels=<comma list> (empty clears). Colours: GitLab returns #RRGGBB or a CSS colour name; pass hex through in the format the gitea adapter uses and map a CSS name to "" (the UI's default).
  • Fixtures synthesised from docs.gitlab.com examples (pipelines, jobs, labels, a trace), recorded in the testdata README.
  • Update the GitLab column rows for runs, jobs, job log and labels in docs/architecture.md.

Context

  • Gitea/GitHub equivalents: internal/forge/github/actions.go, labels.go. Capabilities: internal/forge/forge.go.
  • Facts (docs.gitlab.com, 2026-10-12): pipeline list fields id, iid, sha, ref, status, source, created_at, updated_at, web_url, name; started_at/duration only on the single GET before 19.4. Jobs list excludes retried jobs unless include_retried=true; trigger jobs live under …/trigger_jobs. Label colours may be CSS names; PUT … labels= with an unknown name creates a project label.

Out of scope

Re-run (Rerunner stays undefined, ADR 0006). The [3] box title on GitLab (asked on #141). Trigger/downstream pipelines.

## Problem A GitLab session has no `[3]` box data, no job logs, and no label editing until the adapter implements the optional capabilities. Parent: #137. **Depends on #138** (the `internal/forge/gitlab` package); pick this up after #138 merges. ## Acceptance criteria - The GitLab adapter implements `forge.RunLister`, `forge.LogReader` and `forge.Labeler`, so `[3]` lists pipelines, a pipeline shows its jobs with their stage, `R` opens a pipeline's page, and labels can be edited, as on Forgejo *(design.md → Boxes `[3]`; Keymap `R`; ADR 0006 capabilities; architecture.md: "GitLab uses pipelines → stages → jobs, which maps to `Job.Stage`")*. - A pipeline's state folds into the same `CIState` values as #138's pipeline fold. - Job logs stream without sending the token to any other host. - Setting labels by ID works even though GitLab's API takes label names, and never creates a new label. ## Plan In `internal/forge/gitlab`, using #138's client: - `ListRuns`: `GET /projects/:id/pipelines?per_page=100&order_by=id&sort=desc` plus `ref` (bare branch) and `sha` from `RunFilter`. One page only, newest first, as ADR 0017's runs exception (record it in ADR 0018's consequences or a short addendum). Mapping: `ID` ← `id`, `Number` ← `iid`, `Workflow` ← `name` (falls back to `source`), `Branch` ← `ref`, `Commit` ← `sha`, `Event` ← `source`, `StartedAt` ← `started_at` else `created_at`, `Duration` ← `duration` when present (the list gains it in GitLab 19.4; older servers leave it zero, no per-pipeline GET), `WebURL` ← `web_url`. Status: #138's fold. - `ListJobs`: `GET …/pipelines/:id/jobs?per_page=100` (paginated; retried jobs excluded, GitLab's default). `Stage` ← `stage`, `Name`, `Status` folded the same way, `Attempt` = 1. Trigger (bridge) jobs aren't in this list; leave them out. - `JobLog`: `GET /projects/:id/jobs/:job_id/trace`, streamed, outside any cache. Drop `PRIVATE-TOKEN` on any redirect hop off the API origin (as the github adapter's `JobLog`); test it. The log carries ANSI and `section_start`/`section_end` markers; pass the bytes through untouched (the Logs view owns rendering). - `Labeler`: `ListLabels` ← `GET /projects/:id/labels` (group labels included by default; paginated). `ItemLabels` reads the item's `labels` names and maps them to `ListLabels` IDs (an item label missing from the list keeps ID 0). `SetLabels` maps IDs → names via `ListLabels` (an unknown ID is an error, so GitLab never auto-creates a label), then `PUT` the issue or MR with `labels=<comma list>` (empty clears). Colours: GitLab returns `#RRGGBB` or a CSS colour name; pass hex through in the format the gitea adapter uses and map a CSS name to "" (the UI's default). - Fixtures synthesised from docs.gitlab.com examples (pipelines, jobs, labels, a trace), recorded in the testdata README. - Update the GitLab column rows for runs, jobs, job log and labels in `docs/architecture.md`. ## Context - Gitea/GitHub equivalents: `internal/forge/github/actions.go`, `labels.go`. Capabilities: `internal/forge/forge.go`. - Facts (docs.gitlab.com, 2026-10-12): pipeline list fields `id, iid, sha, ref, status, source, created_at, updated_at, web_url, name`; `started_at/duration` only on the single GET before 19.4. Jobs list excludes retried jobs unless `include_retried=true`; trigger jobs live under `…/trigger_jobs`. Label colours may be CSS names; `PUT … labels=` with an unknown name creates a project label. ## Out of scope Re-run (`Rerunner` stays undefined, ADR 0006). The `[3]` box title on GitLab (asked on #141). Trigger/downstream pipelines.
Author
Owner

🤖 Picked up through composer-lite on branch feature/139-gitlab-runs; the plan is this ticket body.

🤖 Picked up through composer-lite on branch `feature/139-gitlab-runs`; the plan is this ticket body.
Author
Owner

🤖 MR open (see the linked PR for #139).

🤖 MR open (see the linked PR for #139).
Author
Owner

🤖 merged: #148

🤖 merged: https://git.bobparsons.dev/deadstyle/lazyforge/pulls/148
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
deadstyle/lazyforge#139
No description provided.