feat: add GitLab git provider - #25396
Conversation
|
/coder-agents-review |
There was a problem hiding this comment.
Well-structured GitLab provider with strong VCR-based testing infrastructure, clean nested-group handling, and correct SCP remote parsing. The implementation is proportional to the problem and the test-to-code ratio is solid. Pariston tried to build a case that the wrong framing was chosen and couldn't: "Framing #1 (missing implementation) is correct."
Severity count: 1 P2, 8 P3, 5 Nit.
The P2 is a memory safety gap: FetchPullRequestDiff reads the entire HTTP response body into memory before the MaxDiffSize check runs, while the GitHub provider bounds reads with io.LimitReader. This is a library constraint, not a logic error, but it removes the memory cap that the package contract implies.
Several findings converge on silent error handling: resolveExternalAuth swallows Config.Git() constructor errors (9 reviewers flagged this independently), and three zero-valued fields (Additions, Deletions, Commits) are stored as known-valid in the database without being tracked in CODAGT-440.
Two test gaps: CompareTimeout (added as a P2 review fix) has no test, and the FetchBranchDiff integration test has a leniency guard that silently passes in VCR replay mode.
🤖 This review was automatically generated with Coder Agents.
| // be incorrect. To fix this, this function would need | ||
| // access to the external auth configs. | ||
| gp := gitprovider.New("github", "", nil) | ||
| gp, _ := gitprovider.New("github", "", nil) |
There was a problem hiding this comment.
P3 [DEREM-7] This hardcodes "github" as the fallback provider. With GitLab now supported, any GitLab-hosted GitRemoteOrigin stored in ChatDiffStatus will fail ParseRepositoryOrigin (host mismatch) and produce no branch URL. The pre-existing TODO acknowledges the GitHub Enterprise gap; GitLab widens it.
This needs a human decision: either accept the gap and document it, or iterate over configured providers. The fix requires access to external auth configs at the db2sdk layer, which is a broader refactor.
(Mafuuu P3, Ryosuke P3)
🤖
There was a problem hiding this comment.
Acknowledged. This is pre-existing behavior unrelated to the GitLab provider addition; the db2sdk.go fallback to "github" needs a broader refactor (iterating over configured providers). Out of scope for this PR.
🤖 Generated with Coder Agents
|
/coder-agents-review |
There was a problem hiding this comment.
Strong progress. 11 of 13 R1 posted findings addressed, all fixes verified by the panel. The P2 unbounded-read fix (DEREM-3) is correct and matches the GitHub pattern. The commit stats population (DEREM-6) is a genuine improvement. The error logging (DEREM-5) makes misconfiguration diagnosable. Chopper on the fix quality: "Error handling is thorough. Every library call goes through wrapError."
Severity count (new this round): 1 P2, 2 P3, 2 Nit.
The P2 is a regression from the DEREM-3 fix: switching to raw HTTP for FetchPullRequestDiff lost the library's *gitlab.ErrorResponse wrapping, so wrapError can no longer detect 429/403 rate limits on that path. The gitsync worker relies on *RateLimitError to back off; without it, a rate-limited GitLab instance gets hammered. Kite independently confirmed this and provided a concrete fix.
R1 contested findings: DEREM-7 (hardcoded "github" in db2sdk) was closed by the panel (4/7 accept), though 3 reviewers noted that a deferral without a ticket is a drop. This needs a human decision: file a ticket or explicitly accept the gap. DEREM-15 (context.Background) was closed 7/7; the author's defense was verified.
🤖 This review was automatically generated with Coder Agents.
|
/coder-agents-review |
There was a problem hiding this comment.
Pull request overview
Adds a GitLab implementation for coderd/externalauth/gitprovider so chat diff/gitsync flows can resolve GitLab repositories, merge requests, and diffs.
Changes:
- Introduces GitLab provider logic for MR status, diff fetching, branch-to-MR resolution, and URL parsing/building.
- Updates provider construction to return errors and threads context through gitsync provider resolution.
- Adds GitLab unit/integration tests with go-vcr cassettes and supporting dependency/config updates.
Reviewed changes
Copilot reviewed 29 out of 30 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
.gitattributes |
Marks GitLab cassette YAML as generated. |
.github/workflows/typos.toml |
Excludes GitLab testdata from typo checks. |
go.mod |
Adds GitLab client and go-vcr dependencies. |
go.sum |
Updates dependency checksums. |
coderd/database/db2sdk/db2sdk.go |
Adapts to new gitprovider.New signature. |
coderd/exp_chats.go |
Updates chat diff provider resolution and missing-token handling. |
coderd/externalauth/externalauth.go |
Changes Config.Git to return provider construction errors. |
coderd/externalauth/gitprovider/gitprovider.go |
Adds GitLab provider dispatch and shared rate-limit padding. |
coderd/externalauth/gitprovider/github.go |
Moves shared rate-limit constant out of GitHub provider. |
coderd/externalauth/gitprovider/github_test.go |
Updates tests for new constructor return values. |
coderd/externalauth/gitprovider/gitlab.go |
Implements GitLab git provider. |
coderd/externalauth/gitprovider/gitlab_test.go |
Adds GitLab unit tests. |
coderd/externalauth/gitprovider/gitlab_integration_test.go |
Adds VCR-backed GitLab integration tests. |
coderd/externalauth/gitprovider/testdata/gitlab_cassettes/** |
Adds recorded GitLab API fixtures. |
coderd/x/gitsync/gitsync.go |
Passes context into provider resolution. |
coderd/x/gitsync/gitsync_test.go |
Updates resolver mocks for context-aware signature. |
coderd/x/gitsync/worker_test.go |
Updates worker test resolver mocks. |
Comments suppressed due to low confidence (1)
coderd/externalauth/gitprovider/gitlab.go:400
- This has the same self-hosted subpath problem as repository-origin parsing: if the GitLab instance is served under a path prefix, the prefix remains in
u.Pathand becomes part of the parsed owner. For a PR likehttps://gitlab.example.com/gitlab/group/repo/-/merge_requests/1, the provider would return ownergitlab/group, causing follow-up API calls and generated URLs to target the wrong project path.
path := strings.TrimPrefix(u.Path, "/")
path = strings.TrimSuffix(path, "/")
// Find "-/merge_requests/NUMBER" in the path.
const mrMarker = "-/merge_requests/"
idx := strings.Index(path, mrMarker)
if idx < 0 {
return PRRef{}, false
}
// Everything before the marker (minus trailing slash) is the project path.
projPath := path[:idx]
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
All prior findings addressed. The P2 rate-limit regression (DEREM-25) is fixed correctly: FetchPullRequestDiff now checks 403/429 status codes and parses retry headers before the generic error path, matching the GitHub provider's pattern. The oversize test gap (DEREM-28) is filled. The duplicate test (DEREM-26) is removed. Clock injection (DEREM-27) is applied to the existing rate limit tests. Kite on the overall quality: "Well-decomposed PR. The constructor error propagation fix is the right structural change; it forces every caller to handle failure explicitly."
Severity count (new this round): 1 P3, 1 Nit.
The P3 is a test coverage gap in the approval/review-state mapping. The Nit is the same time.Now() pattern in the newly-added 429OnRawDiffEndpoint test that DEREM-27 fixed in its siblings.
Across three rounds, 18 findings were posted, 16 fixed, 2 contested and closed by panel vote. The implementation is solid, well-tested, and proportional to the problem. Chopper: "Error handling and diagnostic signal are solid across the new GitLab provider."
🤖 This review was automatically generated with Coder Agents.
Documentation CheckUpdates Needed
Automated review via Coder Agents |
# Conflicts: # scripts/develop/main.go # scripts/develop/main_test.go
|
Third item addressed in Generated by Coder Agent |
|
/coder-agents-review |
|
Review posted | Chat Review historydeep-review v0.5.0 | Round 5 | Last posted: Round 5, 24 findings (2 P2, 15 P3, 7 Nit), APPROVE. Review Finding inventoryFindings
Contested and acknowledgedDEREM-7 (P3, db2sdk.go:1956) - ChatDiffStatus hardcodes "github" provider
DEREM-15 (Nit, gitlab_test.go:43) - context.Background() instead of t.Context()
Round logRound 1Panel. 1 P2, 8 P3, 5 Nit new. 10 dropped. Reviewed against 6d7fb07..37ed044. Round 2Panel. R1: 11 addressed, 2 contested (DEREM-7 closed 4/7, DEREM-15 closed 7/7). 1 P2, 1 P3, 1 Nit (Netero) + 1 P3, 1 Nit (panel) new. Reviewed against 6d7fb07..afe59a5. Round 3Panel. R2: 5 addressed. 1 P3, 1 Nit new. 1 dropped (scope creep). Reviewed against 2732378..b7d5be2. Round 4BLOCKED. DEREM-31 silent. No review. Round 5Panel. R3: DEREM-31 addressed, DEREM-30 addressed. 2 P3 new. Reviewed against a4afb9d..f642982. About deep-reviewCRF = Coder Review Finding (P0-P4, Nit, Note)
|
There was a problem hiding this comment.
Review blocked. DEREM-31 (P3, FetchPullRequestStatus tests never assert Approved, ReviewerCount, or ChangesRequested) has not been addressed or responded to. The thread was resolved in the GitHub UI without a reply or code change.
DEREM-30 (Nit, time.Now in 429OnRawDiffEndpoint) was fixed in 85e3d3b.
Further review is blocked until DEREM-31 receives a response: fix the test gap, contest the finding with a technical defense, or acknowledge it with a reason.
🤖 This review was automatically generated with Coder Agents.
|
|
/coder-agents-review |
There was a problem hiding this comment.
All prior findings addressed across five rounds. DEREM-31 (approval test assertions) fixed in f642982. The test suite is comprehensive: ~1,400 lines of tests against ~750 lines of implementation, covering edge cases (HeadSHA fallback, oversize diffs, compare timeout, rate limits on both library and raw HTTP paths, self-hosted URL routing), VCR integration tests against four fixture MRs, and newly-added approval field assertions. Mafu-san: "The pattern repeated with DEREM-3, where the fix propagated to FetchBranchDiff too, not just the cited location."
Severity count (new this round): 2 P3.
Both are minor gaps in the new code. CRF-1: ListMergeRequestDiffs fetches one page (100 entries) without pagination, so MRs with 100+ files get undercounted additions/deletions while ChangedFiles is correct. CRF-2: FetchBranchDiff drops the response body on non-200 errors while its sibling FetchPullRequestDiff includes up to 8 KiB of the body for diagnostics.
Across 5 rounds: 20 findings posted, 18 fixed, 2 contested/closed by panel. The implementation is solid and well-tested.
🤖 This review was automatically generated with Coder Agents.
| // The commits endpoint does not return per-commit stats, so we | ||
| // count +/- lines from the unified diff returned by this endpoint. | ||
| var additions, deletions int32 | ||
| diffs, _, err := g.client.MergeRequests.ListMergeRequestDiffs( |
There was a problem hiding this comment.
P3 [CRF-1] ListMergeRequestDiffs fetches with PerPage: 100 but does not paginate. For MRs with 100+ files, Additions and Deletions are summed from only the first page, while ChangedFiles (from mr.ChangesCount) is correct. This creates an inconsistency: "150 files changed, +200/-50" when the real totals could be higher.
The GitHub provider reads additions/deletions directly from the pull request API response (no pagination needed). Here, the MR API does not expose aggregate counts, so file diffs must be iterated. Fix: either paginate until resp.NextPage == 0, or add a comment documenting the 100-file cap so future readers know it is intentional.
(Mafuuu P3)
🤖
| if rlErr := checkRateLimitError(resp, g.clock, "RateLimit-Reset"); rlErr != nil { | ||
| return "", rlErr | ||
| } | ||
| return "", g.wrapError( |
There was a problem hiding this comment.
P3 [CRF-2] Non-200 errors from the compare endpoint produce "unexpected status 404" with no response body. The sibling FetchPullRequestDiff (line 253) reads up to 8 KiB of the body and includes it: "unexpected status 404: Branch not found". GitLab's error bodies contain actionable information (project not found, branch not found, insufficient permissions). The asymmetry makes branch-diff failures harder to diagnose.
Fix: mirror the pattern from FetchPullRequestDiff:
body, _ := io.ReadAll(io.LimitReader(resp.Body, 8192))
return "", g.wrapError(
xerrors.Errorf("unexpected status %d: %s", resp.StatusCode, strings.TrimSpace(string(body))),
"compare branches",
)(Mafuuu P3, Chopper P3)
🤖
Fixes CODAGT-146
Adds a GitLab provider implementation for
coderd/externalauth/gitprovider, enabling gitsync (chatd PR sync) to work with GitLab repositories.Note: while this is a 2.7K line PR, a significant portion of this is tests and fixtures.
Still TODO
Changes
Providerinterface for GitLab using the officialgitlab.com/gitlab-org/api/client-golibraryOAuthToken(Bearer) auth, matching Coder's external auth flowNew()constructor instead of panicking on invalid URLsCompareTimeoutflag inFetchBranchDiffto avoid silently returning incomplete diffsKnown limitations
Approved,ChangesRequested, andReviewerCounthave semantic gaps vs the GitHub provider due to GitLab API differences. Tracked in CODAGT-440.Review-driven changes
Changes made after deep review:
PrivateTokentoOAuthToken(would have caused 401s for all OAuth-configured GitLab integrations)panicin constructor to proper error return, propagated throughNew()andConfig.Git()CompareTimeoutcheck to prevent silent incomplete diffsRateLimitPaddingtogitprovider.gofor discoverabilitystrings.BuilderinFetchBranchDiffcmp.Or,strings.CutPrefix,newGitLabcasing,context→actionparam renameNote
This PR was authored by a human with assistance from Coder Agents.