feat(server): add GitLab glab CLI support for merge request operations#592
feat(server): add GitLab glab CLI support for merge request operations#592binbandit wants to merge 10 commits intopingdotgg:mainfrom
glab CLI support for merge request operations#592Conversation
Introduce a provider-agnostic git hosting abstraction that automatically detects whether a repository is hosted on GitHub or GitLab (by inspecting the origin remote URL) and dispatches PR/MR operations to the correct CLI. Architecture changes: - Add GitHostingCli service contract (generic interface replacing the GitHub-specific GitHubCli service) - Add GitHostingCliDispatcher layer that detects github.com vs gitlab.com from the origin remote URL and routes to gh or glab accordingly - Rename GitHubCliError to GitHostingCliError (deprecated alias preserved for backwards compatibility) - Update GitManager to consume the generic GitHostingCli service - Remove standalone GitHubCli/GitLabCli layer files (logic consolidated into the dispatcher) GitLab support includes: - glab mr list (with --source-branch, --output json) - glab mr create (with --target-branch, --source-branch, --description) - glab repo view --output json (for default branch detection) - Authentication and CLI-not-found error normalization Closes pingdotgg#535 Closes pingdotgg#191
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…s and handling git@ syntax
|
@t3dotgg I know gitlab is not always the most supported platform, but this might help with user adoption. Happy to revise if you want to take another approach for this implementation |
|
@juliusmarminge I cannot add the |
|
looks mostly alright, with some exceptions: a) will conflict a bunch with #718. // Service
export class GitHostingService extends ServiceMap.Service<GitHostingService, Shape>(...)
// Layers/Github
export const makeGithubService = ...
// Layers/GitLab
export const makeGitlabService = ...then whenever we run with the |
Yes and no. I believe either direction would be viable realistically. It just comes down to preference. In terms of conflicting with the other PR. We can either try and change this to work in a way that will cause less conflict, or we could merge this before that to allow for gitlab users earlier. Then i'll contribute to your branch to fix the conflicts. Up to you with how you want to move forward. |
|
consulted mr gpt:
|
|
as for order, i need to get mine in to be able to triage faster, and i have no ability to test the glab cli rn so I'm going to merge mine first |
|
@binbandit Once this has been updated with the new changes I am happy to give this a quick test, we use GitLab for some of our stuff at work so I can try it there |
@Noojuno Good to go. Let me know how it goes 😊 |
|
@binbandit checkout pr is merged so feel free to get this up to date and we can get it merged (preferalby after someone can vouch for the glab working) |
|
@juliusmarminge @binbandit I'll give this a try to validate it when it's up to date (wasn't able to before) |
f6e24d1 to
504481d
Compare
|
@juliusmarminge This now incorporates the architecture you requested. @Noojuno It's good for you to test please |
|
@binbandit Just had a chance to give it a go, but it doesn't seem to work for me. I am using macOS with Test 1 - Creating a new branch and MRWhen creating a new branch (so screen-recording-2026-03-11-1006-compressed.mp4
It then gets put in a broken state where I can't trigger the PR creation again, even though it failed the first time.
Test 2 - Auto-detecting existing MR's when loading a branchWhen choosing a branch with an active PR that uses GitHub, we get an auto-detected "View PR" button state change, even if the PR was created externally from T3 Code. This doesn't happen on GitLab. GitHub:
GitLab:
Other feedbackThis is the more nitpicky feedback since it didn't really work for me, so I wasn't able to review the full thing. The mix in terminology between MR and PR is a little odd, imo in an ideal world we would refer to them as MR's rather than PR's when using GitLab. Similarly (and I think this one is more of a big deal), using the GitHub icon for the PR related actions when using GitLab is very confusing, I think this probably needs to be the GitLab icon. I work at an org that uses both, and I think displaying the GH icon would confuse people. Happy to test again (or give any info you may need to debug the issue), so lmk! |
Oh no! I tried it myself and it seemed to work. Let me try a fresh device to ensure there is no quirks with my setup. Thanks for testing. Your comments about the mix of |
I'm about to swap over to Windows for some other testing of a project, when I get a chance I will also give it a once over there to see if I am having the same issue |
# Conflicts: # apps/server/src/git/Layers/GitManager.ts
… changes Merge main's cross-repository PR detection (headSelector, BranchHeadContext, resolveRemoteRepositoryContext) into the GitLab hosting abstraction layer. Restore all GitLab-specific changes (Icons, platform-aware UI labels, MR URL parsing) that were stashed during the merge.
|
@Noojuno I have added the logo and the Still sussing out the glab failure though |
|
@Noojuno Just confirmed on 2 different clean machines that it works. Don't want to be the guy that says "It works on my machine"... but i think it does |
Okay good to know, I'll give it a go on Windows and then start tracking down why it isn't working for me :) |
…ests abstraction findLatestPr was calling gitHostingCli.execute() with gh-specific CLI args (pr list --head --state --json) which fail on glab since GitLab uses completely different syntax (mr list --source-branch --all --output json). Add listPullRequests method to GitHostingCliShape that accepts state filter and limit, with proper glab flag translation (--all, --closed, --merged). Add updatedAt to PullRequestSummary so the findLatestPr sort-by-recency logic works through the abstraction layer. Remove now-dead parsePullRequestList function from GitManager.
|
Made some hardening changes that might also help |
|
@binbandit Ahh I figured it out, it was (mostly) a me issue haha. I was using a GitLab PAT via We should probably handle that error state (no auth) slightly nicer than we do here and properly surface it to the user. On a similar note, the error toast has regressed slightly and now shows the full stack trace, could probs also use some polish.
Since you also changed the UI now with the MR wording and GitLab icon, it's probably also worth adding to the PR description. |
|
Should we remove the GitHub & gitlab create PR / MR options entirely if they are not authed? |
probalby wanna have disabled buttons with tooltips saying they're not authed, yeaa |
Add proactive auth status check for gh/glab CLIs with 60s TTL cache, surfaced as hostingCliAuthenticated in GitStatusResult. When not authenticated, PR/MR creation is disabled with a tooltip guiding the user to run `gh auth login` or `glab auth login`. Fix error toasts showing full Cause.pretty() fiber metadata by extracting clean tagged error messages via Cause.findErrorOption().
|
@Noojuno Would love a final check please. |
| const authStatusCache = new Map<HostingProvider, AuthCacheEntry>(); | ||
|
|
||
| /** | ||
| * Check whether the hosting CLI is authenticated by running | ||
| * `gh auth status` or `glab auth status`. | ||
| * | ||
| * Uses a per-provider in-memory cache with 60s TTL to avoid | ||
| * running the check on every status poll. | ||
| */ | ||
| function checkHostingAuthStatus(provider: HostingProvider): boolean | null { |
There was a problem hiding this comment.
use effect/Cache instead of ad-hoc implementations. do a repo search for usage examples if you need references
| statusDetails(input.cwd).pipe( | ||
| Effect.map((details) => ({ | ||
| branch: details.branch, | ||
| hostingPlatform: "github" as const, |
| runProcess( | ||
| "glab", | ||
| [ | ||
| "mr", | ||
| "list", | ||
| "--source-branch", | ||
| input.headSelector, | ||
| "--per-page", | ||
| String(input.limit ?? 1), | ||
| "--output", | ||
| "json", | ||
| ], | ||
| { cwd: input.cwd, timeoutMs: DEFAULT_TIMEOUT_MS }, | ||
| ), |
There was a problem hiding this comment.
please put these in a GitlabCli service layer. dispatcher shoould just coordinate between the different hosting services
| /** | ||
| * GitHubCliShape - Service API for executing GitHub CLI commands. | ||
| */ | ||
| export interface GitHubCliShape { |
There was a problem hiding this comment.
add a static readonly name = 'gh' or similar and use that instead of condition ? 'gh' : 'glab' in multiple places
|
The more and more i work on this. The less I am liking the solution as a whole. I think we have gone too far in the wrong direction. I apologise for anyone waiting on this. Checkout the branch where it is and use as is for now. I want to take a moment to really think about this |





Summary
Adds GitLab (
glabCLI) support for the PR/MR creation flow, alongside the existing GitHub (gh) support. The hosting provider is detected automatically from the repository'soriginremote URL — no user configuration required.Closes #535
Closes #191
Architecture
The existing
GitHubCliEffect service is kept as the GitHub-specific implementation (with Schema-validated JSON decoders, error normalization, etc.). This PR adds a provider-agnosticGitHostingCliservice that acts as the interface consumed byGitManager, with a dispatcher layer that delegates toGitHubClifor GitHub repos and implementsglabCLI calls inline for GitLab repos:Detection logic
detectHostingProvider(cwd)runsgit remote get-url origin, parses the URL hostname (supports bothhttps://andgit@formats), and matches againstgitlab.com/*.gitlab.com. Results are cached per repository path so detection only runs once percwd. Falls back to GitHub for unknown or missing remotes, preserving full backwards compatibility.Key design decisions
GitHubPullRequestSummary,GitHubRepositoryCloneUrls), JSON decoders, and theGitHubCli.test.tsunit tests from main's PR Add PR thread setup for local and worktree modes #718 are preserved as-is.execute,listOpenPullRequests,getPullRequest,getRepositoryCloneUrls,createPullRequest,getDefaultBranch,checkoutPullRequest.GitHubCliErroris a deprecated alias forGitHostingCliError(same_tag: "GitHostingCliError"), so existing error-handling code continues to work.Changes
apps/server/src/git/Services/GitHostingCli.tsGitHostingCliShape,PullRequestSummary,RepositoryCloneUrls,GitHostingClitag)apps/server/src/git/Layers/GitHostingCliDispatcher.tsGitHubClifor GitHub, implementsglabinline for GitLabapps/server/src/git/Errors.tsGitHostingCliErrorclass;GitHubCliErrorkept as deprecated aliasapps/server/src/git/Layers/GitManager.tsGitHostingCliinstead ofGitHubCli(5 call sites)apps/server/src/git/Layers/CodexTextGeneration.tsapps/server/src/serverLayers.tsGitHostingCliLivelayer withGitHubCliLiveas dependencyapps/server/src/git/Layers/GitManager.test.tsGitHostingCliError/GitHostingCli/PullRequestSummarytypesGitLab CLI commands used
glab mr list --source-branch <branch> --per-page <n> --output jsonglab mr view <id> --output jsonglab mr create --target-branch <base> --source-branch <head> --title <title> --description <body> --yesglab mr checkout <id>glab repo view <repo> --output json→http_url_to_repo,ssh_url_to_repoglab repo view --output json→default_branchTesting
bun lint— 0 errorsbun typecheck— 0 errors across all 7 packagesbun fmt:check— all files formattedNote
Add GitLab
glabCLI support for merge request operationsGitHostingCliabstraction andGitHostingCliDispatcherthat routes all PR/MR operations (listPullRequests,createPullRequest,checkoutPullRequest, etc.) to eitherghorglabbased on the repository's remote origin hostname.GitHubClireferences inGitManagerwith the newGitHostingCliinterface, making hosting-provider detection transparent to callers.hostingPlatform('github'|'gitlab') andhostingCliAuthenticated(boolean|null) fields toGitStatusResult, surfaced from the dispatcher's cached auth checks.GitActionsControl,Sidebar,pullRequestReference) to use platform-aware PR/MR labels, icons, and disabled states — including downgrading PR creation to push-only when the hosting CLI is unauthenticated.GitStatusResultmust now handle the two new required fields; theGitHostingCliErrortag replacesGitHubCliError(backward-compat aliases provided).Macroscope summarized 5273959.