Add Gitlab CLI integration#998
Conversation
📝 WalkthroughWalkthroughAdds GitLab support to CLI auth, credential storage, verification, and read flows, with a new ChangesGitLab CLI Integration
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
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 |
There was a problem hiding this comment.
Actionable comments posted: 15
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
potpie/context-engine/adapters/inbound/cli/auth/auth_commands.py (1)
499-507: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueRedundant
gitlabbranch — identical toelse.
elif provider == "gitlab": credentials = get_integration_tokens(provider)does exactly what the trailingelsealready does for any non-linear/github provider. This adds branching with no behavioral difference; consider dropping thegitlabcase and letting it fall through toelse.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@potpie/context-engine/adapters/inbound/cli/auth/auth_commands.py` around lines 499 - 507, The provider handling in auth_commands.py has a redundant gitlab branch in the credential lookup logic. Update the provider selection in the relevant auth command function so the gitlab case is removed and gitlab falls through to the existing else path, keeping the behavior identical while simplifying the branch structure.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@potpie/context-engine/adapters/inbound/cli/auth/auth_commands.py`:
- Around line 639-643: The GitLab logout path in auth_logout() is invoking the
Typer command directly, which causes the default typer.Option value to be
treated as the instance host instead of None. Update this branch to call a plain
helper or explicitly pass None through gitlab_logout so
clear_gitlab_credentials(instance_host=instance) receives the expected value.
Use auth_logout(), gitlab_logout(), and clear_gitlab_credentials() to locate and
fix this path.
In `@potpie/context-engine/adapters/inbound/cli/auth/gitlab_auth.py`:
- Around line 26-53: The module has constants defined before the import blocks,
which triggers Ruff E402 because imports are no longer at the top of the file.
In gitlab_auth.py, move the assignments for EXIT_CANCELLED,
GITLAB_AUTO_OPEN_SECONDS, and _GITLAB_OPEN_PROMPT_PREFIX to after all adapters.*
and other module imports, or reorder the imports above those constants, so the
file keeps a clean top-level import section.
- Around line 216-266: The GitLab PAT flow is ignoring a provided --instance
unless --token is also supplied because run_gitlab_pat_auth only treats both
values as “supplied.” Update the branching in run_gitlab_pat_auth so an explicit
instance is preserved and reused when prompting for the token, instead of
falling back to _prompt_instance_url and git remote detection. Use the existing
instance_host, normalize_instance_url, _prompt_instance_url, and _prompt_pat
paths to keep the typed instance as the default target.
In `@potpie/context-engine/adapters/inbound/cli/auth/gitlab_commands.py`:
- Around line 78-97: Add a direct-call-safe GitLab logout helper so the CLI
wrapper is not used as the implementation entrypoint. The issue is that
gitlab_logout currently takes the raw typer.Option default when called directly
from auth_commands.py, and that value is passed into
clear_gitlab_credentials(instance_host=instance) instead of a real str | None.
Mirror the github_logout_impl pattern by moving the logout logic into a separate
helper, then have gitlab_logout only parse CLI options and delegate to that
helper so direct calls bypass Typer defaults cleanly.
In `@potpie/context-engine/adapters/inbound/cli/auth/gitlab_read.py`:
- Around line 55-86: The non-interactive path in run_gitlab_select_flow is
blocking use of the saved default project because the terminal check runs before
load_gitlab_read_credentials() and fetches prefs["default_project"]. Move the
guard so credentials are loaded and default_project is computed before deciding
whether stdin is a TTY, then allow the saved default_project branch to select or
synthesize the project when project_path is absent. Keep the logic centered
around run_gitlab_select_flow, load_gitlab_read_credentials, and _prompt_project
so scripted runs can use the persisted preference without requiring --project.
In `@potpie/context-engine/adapters/outbound/cli_auth/credentials_store.py`:
- Around line 1234-1253: `save_gitlab_workspace_prefs` should not create a new
metadata-only instance when the host is unknown. In
`save_gitlab_workspace_prefs`, after resolving `host` and reading `meta`, verify
that the host already exists in `instances` before updating `workspaces`; if it
does not, raise `ProviderCredentialError` instead of writing a stub entry. Keep
the existing update path for valid hosts, and reference the same metadata
helpers (`_read_gitlab_metadata`, `_norm_gitlab_host`,
`_get_active_gitlab_host`, `_write_metadata_entry`) when making the check.
- Around line 1130-1165: save_gitlab_credentials is overwriting the stored
GitLab instance record from scratch, which drops existing workspace preferences
and refreshes created_at on re-login. Update the save flow in
save_gitlab_credentials to load the current entry from _read_gitlab_metadata()
for the same inst_host, then merge the new record from
build_gitlab_integration_record with the existing one so fields like
workspaces/default_project and created_at are preserved unless explicitly
changed. Keep the final write through _write_metadata_entry using the merged
instance record.
In `@potpie/context-engine/adapters/outbound/cli_auth/gitlab_client.py`:
- Around line 30-41: normalize_instance_url currently preserves an explicit
http:// scheme, which allows the GitLab auth client to send PRIVATE-TOKEN over
cleartext. Update normalize_instance_url in gitlab_client.py so it rejects or
upgrades plain HTTP by default and only allows it when an explicit opt-in is
present for internal-only instances. Keep the HTTPS normalization path intact
and ensure any callers that rely on the returned URL from normalize_instance_url
or the GitLab client setup continue to receive an https:// URL unless HTTP is
explicitly permitted.
In `@potpie/context-engine/adapters/outbound/cli_auth/gitlab_read_client.py`:
- Around line 98-212: The three GitLab fetchers repeat the same
load-creds/request/normalize flow, so extract that shared logic into a helper
and keep the per-endpoint field mapping separate. Add a reusable internal helper
around load_gitlab_read_credentials, _api_base, _headers, and _get_json that
preserves the current “non-list response returns []” behavior, then have
fetch_gitlab_projects, fetch_gitlab_merge_requests, and fetch_gitlab_issues call
it and only transform each item into their specific dict shape.
- Around line 134-212: The GitLab read helpers do not enforce path-based project
ID encoding, so raw path_with_namespace strings can be routed incorrectly.
Update fetch_gitlab_merge_requests and fetch_gitlab_issues to normalize string
project_id values by percent-encoding them before building the
/projects/{project_id} URL, and make the contract explicit in these helpers so
future callers do not need to encode externally. After moving the encoding into
gitlab_read_client.py, remove the caller-side quote_plus handling in
gitlab_read.py to prevent double-encoding.
In `@potpie/context-engine/adapters/outbound/cli_auth/integration_verify.py`:
- Around line 118-152: The GitLab verification path in _verify_gitlab only
checks instance access via verify_instance_access, so it can succeed without the
read_api scope required by later commands. Update _verify_gitlab to also call
verify_read_api_scope (as run_gitlab_pat_auth does) after the /user check, and
return an insufficient-scopes failure when that probe fails. Keep the existing
GitLabAuthErrorKind handling and user/profile formatting intact.
In `@potpie/context-engine/tests/_auth_fakes.py`:
- Around line 228-244: The fake save_gitlab_credentials method currently stores
GitLab credentials without enforcing the same PAT check as the real
implementation. Update _auth_fakes.py’s save_gitlab_credentials to mirror the
production validation by rejecting empty or missing personal_access_token values
and raising the same ProviderCredentialError path that test_cli_gitlab.py
expects, while keeping the rest of the instance/account storage behavior
unchanged.
- Around line 273-282: Align InMemoryCredentialStore.save_gitlab_workspace_prefs
with the GitLab credential structure used by get_gitlab_credentials and
run_gitlab_select_flow. The current implementation writes prefs into
workspace_prefs["gitlab"], but get_gitlab_credentials only reads the GitLab
instance data under self.providers["gitlab"][...]["workspaces"], so the selected
default project is never seen. Update save_gitlab_workspace_prefs to persist the
default project under the GitLab provider/instance entry, or adjust
get_gitlab_credentials to merge in workspace_prefs when building the returned
credentials.
In `@potpie/context-engine/tests/unit/test_cli_gitlab.py`:
- Around line 40-53: The GitLab credentials test is too permissive because it
accepts either instance_host or provider_host, which hides regressions in the
raw credential contract. Update test_save_and_get_gitlab_credentials to assert
the specific instance_host field returned by get_gitlab_credentials, using the
existing cs.save_gitlab_credentials and cs.get_gitlab_credentials symbols to
verify the stored credential shape directly.
---
Outside diff comments:
In `@potpie/context-engine/adapters/inbound/cli/auth/auth_commands.py`:
- Around line 499-507: The provider handling in auth_commands.py has a redundant
gitlab branch in the credential lookup logic. Update the provider selection in
the relevant auth command function so the gitlab case is removed and gitlab
falls through to the existing else path, keeping the behavior identical while
simplifying the branch structure.
🪄 Autofix (Beta)
❌ Autofix failed (check again to retry)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI (base), Organization UI (inherited)
Review profile: ASSERTIVE
Plan: Pro
Run ID: 3e1af6f9-5359-4a2c-b246-66c73b2f8a69
📒 Files selected for processing (16)
potpie/context-engine/adapters/inbound/cli/auth/auth_commands.pypotpie/context-engine/adapters/inbound/cli/auth/gitlab_auth.pypotpie/context-engine/adapters/inbound/cli/auth/gitlab_commands.pypotpie/context-engine/adapters/inbound/cli/auth/gitlab_read.pypotpie/context-engine/adapters/outbound/cli_auth/credentials.pypotpie/context-engine/adapters/outbound/cli_auth/credentials_store.pypotpie/context-engine/adapters/outbound/cli_auth/gitlab_client.pypotpie/context-engine/adapters/outbound/cli_auth/gitlab_read_client.pypotpie/context-engine/adapters/outbound/cli_auth/integration_profile.pypotpie/context-engine/adapters/outbound/cli_auth/integration_verify.pypotpie/context-engine/adapters/outbound/cli_auth/provider_config.pypotpie/context-engine/domain/ports/cli_auth/credentials.pypotpie/context-engine/tests/_auth_fakes.pypotpie/context-engine/tests/unit/test_cli_gitlab.pypotpie/context-engine/tests/unit/test_cli_gitlab_commands.pypotpie/context-engine/tests/unit/test_gitlab_client.py
Address CodeRabbit feedback by hardening logout routing, preserving workspace prefs on re-login, and aligning verify/select behavior with login expectations. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
potpie/context-engine/adapters/inbound/cli/auth/gitlab_auth.py (1)
254-266: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
--tokenis silently dropped when--instanceis omitted.
suppliedrequires bothinstanceandtoken. If a user runspotpie gitlab login --token glpat-xxxwithout--instance, execution falls into theelsebranch, which unconditionally calls_prompt_pat()— discarding the token the user already passed and forcing them to re-enter it (plus needlessly reopening the PAT creation page). This is the same class of bug already fixed for--instanceat line 260, but the symmetric case for--tokenremains.🐛 Proposed fix
else: git_hint = _detect_gitlab_from_git_remote() inst_url = normalize_instance_url( _prompt_instance_url(default=instance or git_hint or "") ) or GITLAB_DEFAULT_INSTANCE - if not as_json: - _open_gitlab_pat_page(inst_url) - else: - webbrowser.open(gitlab_pat_page_url(inst_url), new=1) - pat = _prompt_pat() + pat = (token or "").strip() + if not pat: + if not as_json: + _open_gitlab_pat_page(inst_url) + else: + webbrowser.open(gitlab_pat_page_url(inst_url), new=1) + pat = _prompt_pat()🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@potpie/context-engine/adapters/inbound/cli/auth/gitlab_auth.py` around lines 254 - 266, `--token` is ignored when `--instance` is missing in the GitLab login flow. Update the branching in `gitlab_auth.py` so a provided token is preserved even if only one of the two flags is supplied, similar to the existing handling around `supplied` and `_prompt_instance_url`. In the `else` path, only call `_prompt_pat()` when `token` was not passed; otherwise reuse the trimmed `token` value. Keep the PAT page opening behavior tied to actual prompting, and use `_detect_gitlab_from_git_remote()`/`GITLAB_DEFAULT_INSTANCE` only for the missing instance case.potpie/context-engine/adapters/inbound/cli/auth/gitlab_commands.py (1)
81-105: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winScope the logout status to the target instance
get_integration_status("gitlab")only reflects the active GitLab host, not the--instancepassed to logout. If a different instance is removed, the command can still print “Logged out successfully.” even when that instance had no stored credentials. Use the instance-specific GitLab record forwas_authenticatedwheninstanceis set.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@potpie/context-engine/adapters/inbound/cli/auth/gitlab_commands.py` around lines 81 - 105, The logout flow in gitlab_commands.py currently reads authentication state only from get_integration_status("gitlab"), which can report the wrong status when --instance is provided. Update the was_authenticated logic in the logout command to use the instance-specific GitLab record when instance is set, and keep the existing fallback for the default host path. Make sure the later success message and JSON payload in the logout handler reflect the targeted instance correctly, using the same logout command flow around clear_gitlab_credentials, emit_error, and print_plain_line.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@potpie/context-engine/adapters/inbound/cli/auth/gitlab_auth.py`:
- Around line 254-266: `--token` is ignored when `--instance` is missing in the
GitLab login flow. Update the branching in `gitlab_auth.py` so a provided token
is preserved even if only one of the two flags is supplied, similar to the
existing handling around `supplied` and `_prompt_instance_url`. In the `else`
path, only call `_prompt_pat()` when `token` was not passed; otherwise reuse the
trimmed `token` value. Keep the PAT page opening behavior tied to actual
prompting, and use `_detect_gitlab_from_git_remote()`/`GITLAB_DEFAULT_INSTANCE`
only for the missing instance case.
In `@potpie/context-engine/adapters/inbound/cli/auth/gitlab_commands.py`:
- Around line 81-105: The logout flow in gitlab_commands.py currently reads
authentication state only from get_integration_status("gitlab"), which can
report the wrong status when --instance is provided. Update the
was_authenticated logic in the logout command to use the instance-specific
GitLab record when instance is set, and keep the existing fallback for the
default host path. Make sure the later success message and JSON payload in the
logout handler reflect the targeted instance correctly, using the same logout
command flow around clear_gitlab_credentials, emit_error, and print_plain_line.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI (base), Organization UI (inherited)
Review profile: ASSERTIVE
Plan: Pro
Run ID: 325804d8-359d-4701-8750-4526bfe13bf3
📒 Files selected for processing (12)
potpie/context-engine/adapters/inbound/cli/auth/auth_commands.pypotpie/context-engine/adapters/inbound/cli/auth/gitlab_auth.pypotpie/context-engine/adapters/inbound/cli/auth/gitlab_commands.pypotpie/context-engine/adapters/inbound/cli/auth/gitlab_read.pypotpie/context-engine/adapters/outbound/cli_auth/credentials_store.pypotpie/context-engine/adapters/outbound/cli_auth/gitlab_client.pypotpie/context-engine/adapters/outbound/cli_auth/gitlab_read_client.pypotpie/context-engine/adapters/outbound/cli_auth/integration_verify.pypotpie/context-engine/tests/_auth_fakes.pypotpie/context-engine/tests/unit/test_cli_gitlab.pypotpie/context-engine/tests/unit/test_cli_gitlab_commands.pypotpie/context-engine/tests/unit/test_gitlab_client.py
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
potpie/context-engine/tests/_auth_fakes.py (1)
257-272: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winStale
active_instance_hostafter clearing the active instance.If
instance_hostmatches the current active host and other instances remain,active_instance_hostis left pointing at the removed host instead of being reassigned, unlike productionclear_gitlab_credentialsincredentials_store.py(which reassigns to a remaining host when the active instance is cleared). Any test relying on this fake to exercise "logout from active instance while another remains" won't reflect real behavior, since a subsequentget_gitlab_credentials()call (no explicit host) would incorrectly resolve to a dead host and appear "not connected" even though another instance is still connected.🛠️ Proposed fix
def clear_gitlab_credentials( self, instance_host: str | None = None, ) -> None: if instance_host: gitlab = dict(self.providers.get("gitlab", {})) instances = dict(gitlab.get("instances", {})) instances.pop(instance_host, None) if instances: gitlab["instances"] = instances + if gitlab.get("active_instance_host") == instance_host: + gitlab["active_instance_host"] = next(iter(instances)) self.providers["gitlab"] = gitlab else: self.providers.pop("gitlab", None) else: self.providers.pop("gitlab", None)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@potpie/context-engine/tests/_auth_fakes.py` around lines 257 - 272, The fake clear_gitlab_credentials method leaves active_instance_host stale when removing the currently active GitLab host and other instances still exist. Update clear_gitlab_credentials in _auth_fakes.py so that when instance_host matches active_instance_host and the removed host was active, it reassigns active_instance_host to one of the remaining instance keys before returning. Keep the behavior aligned with the production clear_gitlab_credentials logic in credentials_store.py, and ensure the no-host case still clears both the stored instances and active host state.potpie/context-engine/adapters/inbound/cli/auth/gitlab_auth.py (1)
249-264: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winSupplied
--token(without--instance) is silently discarded.
suppliedrequires bothinstanceandtoken. If a user runspotpie gitlab login --token glpat-xxx(no--instance), execution falls into theelsebranch, which unconditionally calls_prompt_pat()— ignoring the token the user just passed — and also opens the PAT page browser prompt unnecessarily. This mirrors the previously-fixed--instance-dropped bug, but for--token.🛠️ Proposed fix
else: git_hint = _detect_gitlab_from_git_remote() inst_url = ( normalize_instance_url( _prompt_instance_url(default=instance or git_hint or "") ) or GITLAB_DEFAULT_INSTANCE ) - if not as_json: - _open_gitlab_pat_page(inst_url) - else: - webbrowser.open(gitlab_pat_page_url(inst_url), new=1) - pat = _prompt_pat() + pat = (token or "").strip() + if not pat: + if not as_json: + _open_gitlab_pat_page(inst_url) + else: + webbrowser.open(gitlab_pat_page_url(inst_url), new=1) + pat = _prompt_pat()🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@potpie/context-engine/adapters/inbound/cli/auth/gitlab_auth.py` around lines 249 - 264, The GitLab login flow in gitlab_auth.py is dropping a supplied --token when --instance is omitted because the current supplied check requires both values and falls through to the interactive branch. Update the login path around the supplied/else logic to treat token and instance independently: if a token is provided, preserve it instead of calling _prompt_pat(), and only open the PAT page or prompt for values that are still missing. Keep the behavior consistent in the same auth flow that uses normalize_instance_url, _open_gitlab_pat_page, and _prompt_pat().
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@potpie/context-engine/adapters/inbound/cli/auth/gitlab_auth.py`:
- Around line 249-264: The GitLab login flow in gitlab_auth.py is dropping a
supplied --token when --instance is omitted because the current supplied check
requires both values and falls through to the interactive branch. Update the
login path around the supplied/else logic to treat token and instance
independently: if a token is provided, preserve it instead of calling
_prompt_pat(), and only open the PAT page or prompt for values that are still
missing. Keep the behavior consistent in the same auth flow that uses
normalize_instance_url, _open_gitlab_pat_page, and _prompt_pat().
In `@potpie/context-engine/tests/_auth_fakes.py`:
- Around line 257-272: The fake clear_gitlab_credentials method leaves
active_instance_host stale when removing the currently active GitLab host and
other instances still exist. Update clear_gitlab_credentials in _auth_fakes.py
so that when instance_host matches active_instance_host and the removed host was
active, it reassigns active_instance_host to one of the remaining instance keys
before returning. Keep the behavior aligned with the production
clear_gitlab_credentials logic in credentials_store.py, and ensure the no-host
case still clears both the stored instances and active host state.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI (base), Organization UI (inherited)
Review profile: ASSERTIVE
Plan: Pro
Run ID: 4ff23865-5f11-4441-894f-ae1b7b9dc947
📒 Files selected for processing (14)
potpie/context-engine/adapters/inbound/cli/auth/auth_commands.pypotpie/context-engine/adapters/inbound/cli/auth/gitlab_auth.pypotpie/context-engine/adapters/inbound/cli/auth/gitlab_commands.pypotpie/context-engine/adapters/inbound/cli/auth/gitlab_read.pypotpie/context-engine/adapters/outbound/cli_auth/credentials.pypotpie/context-engine/adapters/outbound/cli_auth/credentials_store.pypotpie/context-engine/adapters/outbound/cli_auth/gitlab_client.pypotpie/context-engine/adapters/outbound/cli_auth/gitlab_read_client.pypotpie/context-engine/adapters/outbound/cli_auth/integration_verify.pypotpie/context-engine/domain/ports/cli_auth/credentials.pypotpie/context-engine/tests/_auth_fakes.pypotpie/context-engine/tests/unit/test_cli_gitlab.pypotpie/context-engine/tests/unit/test_cli_gitlab_commands.pypotpie/context-engine/tests/unit/test_gitlab_client.py
|
Note Autofix is a beta feature. Expect some limitations and changes as we gather feedback and continue to improve it. Autofix skipped. No unresolved CodeRabbit review comments with fix instructions found. |
Closes #997
Summary
potpie gitlabcommand group for PAT-based authentication and read workflows.potpie auth status --verifypotpie auth logoutProblem
Potpie already supports GitHub, Linear, Jira, and Confluence through the CLI. However, GitLab users could not:
This was particularly problematic for self-hosted GitLab users, as there was no support for custom instance URLs or a Personal Access Token (PAT) authentication flow.
Solution
CLI Surface
Files Changed
New Modules
adapters/inbound/cli/auth/gitlab_auth.pyadapters/inbound/cli/auth/gitlab_commands.pyadapters/inbound/cli/auth/gitlab_read.pyadapters/outbound/cli_auth/gitlab_client.pyadapters/outbound/cli_auth/gitlab_read_client.pyExtended Modules
auth_commands.pycredentials_store.pycredentials.pyintegration_profile.pyintegration_verify.pyprovider_config.pyTests
New Test Files
tests/unit/test_cli_gitlab.pytests/unit/test_cli_gitlab_commands.pytests/unit/test_gitlab_client.pytests/_auth_fakes.pyTest Plan
1. Verify GitLab unit tests
cd potpie/potpie/context-engine python -m pytest \ tests/unit/test_cli_gitlab.py \ tests/unit/test_cli_gitlab_commands.py \ tests/unit/test_gitlab_client.py \ -q2. Verify GitLab module coverage exceeds 80%
3. Verify existing provider CLI tests still pass
4. Manual smoke test
Notes
GitLab authentication is exposed via:
rather than the deprecated:
This PR implements Personal Access Token (PAT) authentication only. OAuth support is intentionally out of scope.