GitLab
- provider
- createProvider("gitlab")
- auth header
- Private-Token
- env vars
- GITLAB_TOKEN, GL_TOKEN, GITLAB_PAT
- anonymous reads
- public projects only
Address it
import { createProvider } from "@agntn/forges";
const gitlab = createProvider("gitlab"); // GITLAB_TOKEN, GL_TOKEN, GITLAB_PAT, then glab
const selfHosted = createProvider("gitlab", {
token: "glpat-…",
baseURL: "https://gitlab.example.com",
});
/api/v4 is added when the base URL lacks it. The token travels in Private-Token.
What it reads
| Resource | Endpoint |
|---|---|
| repos | GET /projects/:id, GET /users/:owner/projects, GET /groups/:owner/projects on 404 |
| issues | GET /projects/:id/issues, …/issues?search=, POST /projects/:id/issues, …/issues/:iid/notes |
| merge requests | GET /projects/:id/merge_requests, …/merge_requests/:iid/diffs, …/merge_requests/:iid/pipelines |
| commits | GET /projects/:id/repository/commits, …/commits/:sha, …/commits/:sha/diff |
| CI runs | GET /projects/:id/pipelines |
| threads | GET /projects/:id/merge_requests/:iid/discussions, PUT …/discussions/:id |
| templates | GET /projects/:id/templates/issues, …/templates/merge_requests |
| code | GET /search, GET /groups/:id/search, GET /projects/:id/search with scope=blobs |
owner/repo becomes a project id once and is cached per provider (gitlab.projectIdCacheMax, gitlab.projectIdCacheTtl in the config). Pagination reads x-next-page.
What comes back
number is the iid, never the global id. Thread.isOutdated is always false, the API has no such flag. Changed files of a merge request carry null counts when the diff is collapsed or oversized. Commit reads take at most 10 000 diff rows per call and filesComplete is null past that.
listComments asks for notes with an explicit ascending sort and drops two kinds: system notes about label and state churn, and inline DiffNotes, which are threads. So a short page with hasNextPage: true is normal, keep paging.
Templates come from the effective template API, group and instance inheritance already applied. When the API hides the winning source, scope is unknown and the source fields are null.
Gotchas
- Discussions answer 401 without a token, even on a public project. Repositories, issues, merge requests, commits and pipelines read fine anonymously. Discussions do not, and I did not expect that either.
- Every search call needs a token. Global and group code search also need Premium or Ultimate with advanced or exact code search on. Project search does not.
/users/:owner/projectsis a 404 for a group.repos.listfalls back to/groups/:owner/projectson that status only.- Search qualifiers are plain text.
label:bugsearches for the stringlabel:bug. - GitLab Free takes one assignee. Two are rejected before the request.
users.getcosts two requests: the username search returns a stub, the id from it reads the full profile.
Where it lives
src/providers/gitlab.ts.