Repository navigation
Add GitLab provider support - #6
Conversation
- GitlabProvider for GitLab REST API v4 (gitlab.com and self-hosted via url) - MR summary notes, inline diff discussions, comment deletion, approval - Global and per-repo gitlab config blocks with env var support - Fall back to refs/merge-requests/<iid>/head for forks and deleted branches - Wizard detects GitLab remotes and validates tokens per instance - Tests for provider, config, wizard and review cycle
|
Inline diff discussions only set |
|
Hehe, nice to see you again. Fix those two and it's in. |
|
Fix for the approve_pr masking, narrow the caught statuses so a real auth failure doesn't get swallowed as a benign self-approve: def approve_pr(self, repo_slug: str, pr_id: int) -> bool:
resp = self._request_raw('POST', f'{self._mr_url(repo_slug, pr_id)}/approve')
if resp.status_code in (403, 405):
logger.warning('Cannot approve MR !%d (self-approve or approvals disabled): %s', pr_id, resp.text[:200])
return False
resp.raise_for_status()
logger.info('Approved MR !%d', pr_id)
return True401 means the token itself is bad. That should raise like every other call here, not look like a self-approve. |
|
For the inline positioning bug (gitlab.py:576-578): reviewd doesn't parse diff hunks today, so the minimal correct fix is to classify the target line against the unified diff you already generate for the AI, then set the position accordingly: if is_added_line:
position['new_line'] = target_line
elif is_removed_line:
position['old_line'] = target_line
else: # context line, present on both sides
position['old_line'] = old_line_for(target_line)
position['new_line'] = target_lineIf a line can't be classified confidently, fall back to |
…ures on approve - Read the MR diff (once per head commit) to map each new-file line to its old line. Unchanged lines now send both old_line and new_line, as GitLab requires; added lines keep new_line only. Renamed files use the diff's old_path. Files GitLab collapsed (empty diff) fall back to new_line only. - approve_pr no longer treats every 401 as "can't approve". GitLab also answers 401 for self-approval, so a 401 is only swallowed when the token still works against /user. A 403 insufficient_scope (read-only token) now raises too.
|
A intrat Nanoclaw pă fir 😎 am cerut review de pe telegram :)) |
Lines outside the MR diff, and lines in files GitLab collapsed, no longer get a guessed old_line/new_line. They're posted as file-level comments that start with "Line N:", without the suggestion block (it needs a line to apply to).
Regarding the approve_pr error: Before switching to your version, though, I checked what gitlab.com actually returns, and it's a bit annoying:
So with only 403/405 caught, every re-review of an MR reviewd had already approved would blow up. What I did instead (06083fb): on a 401, reviewd makes a quick |
|
Should be ok now. Mersi de raspuns :) |



What this does
reviewd could only review pull requests on GitHub and BitBucket. This PR adds GitLab merge requests.
GitLab works like the other two providers. Point a repo at it with
provider: gitlab, and reviewd polls for open MRs, runs the review, and posts the result: a summary comment, plus inline comments on the changed lines. If auto-approve is enabled, it also approves the MR.reviewd initalso detects GitLab remotes, so the setup wizard writes this config for you.Expand E4 under "Manual testing" to see what a review looks like on a GitLab MR.
Where to look
Most of the new code is in
providers/gitlab.py. It is written in the same style as the GitHub provider./diffsAPI, once per head commit) to work these out. Added lines only get the new line number. Findings always refer to lines in the new file, so they never target a deleted line. If a line isn't in the diff, or GitLab collapsed the file, reviewd doesn't guess: it posts a file-level comment that starts withLine N:and leaves out the suggestion block./user. If the token is also rejected there, or a 403 says its scope is too narrow, reviewd raises the error instead of hiding it.Smaller changes:
refs/merge-requests/<iid>/head, the ref GitLab keeps for every MR.reviewer.pygets one line for this.providers/base.py. This touches the GitHub and BitBucket providers but doesn't change how they behave.config.example.yamland CLAUDE.md.The commits are meant to be read one at a time. The first adds the feature, and the rest are follow-ups from a self-review.
How it was tested
Automated
There are 67 new tests, and the whole suite passes (129 tests). The GitLab API is mocked with
respx, so nothing calls the real GitLab.${ENV_VAR}tokens, a per-repo block overriding the global one, a clear error when no GitLab config exists, and GitHub, GitLab and BitBucket repos in one config.critical_taskis skipped on GitLab.Manual testing
Run by hand against a throwaway project on gitlab.com. Each check has its screenshot in a collapsed section; click a title to expand it.
Setup and listing
✅ E1 ·
reviewd initdetects the GitLab repo and writes a working config✅ E2 ·
reviewd lslists open MRs with the right authors, branches and draft stateReviewing
✅ E3 ·
--dry-runprints the review and posts nothing✅ E4 · A real review posts the summary note and an inline comment on the right line
✅ E5 · The bot marker in comments is invisible in the GitLab UI
✅ E6 · Pushing a new commit triggers a re-review that replaces the old comments
Filtering
✅ E7 · Draft MRs are skipped until tagged
[review]✅ E13 · Pagination: with a smaller page size, MRs spread over several pages are all listed
Not tested with more than 100 real open MRs.
Branch edge cases
✅ E9 · An MR whose source branch was deleted still gets reviewed
✅ E8 · An MR from a fork gets reviewed
❌ E8b · An MR from a fork whose branch name also exists in this repo (known issue, see below)
The main repo's branch gets reviewed instead of the fork's.
Tokens and approvals
✅ E12 · A read-only token fails with a clear error
✅ E10 · A bot account auto-approves an MR
✅ E11 · Trying to approve your own MR is handled gracefully
Known limitations
critical_taskstays BitBucket-only for now. GitLab has no direct equivalent.Not in this PR
There's a pagination bug in the GitHub and BitBucket providers. After the first page, httpx drops the query string from the next-page URL. GitHub repos with more than 100 open PRs could probably loop forever, and BitBucket only ever reads the first page. The GitLab provider avoids this. I can send the fix for the other two as its own PR.
🤖 Generated with Claude Code