GitLab CLI (glab) Integration
Manage GitLab repositories, merge requests, CI/CD, variables, and runners with glab CLI.
Prerequisites
glab auth status
If authentication fails, see Authentication Setup.
General Rules
- Prefer native
glabcommands overglab api— native commands handle URL encoding via-R <repo>. - Strip ANSI codes when analyzing job logs:
| sed 's/\x1b\[[0-9;]*m//g' - Avoid TUI commands (
glab ci view) — crashes in non-interactive environments. - URL encoding: When using
glab api, encode/as%2Fin project paths. - JSON output: Use
--output=jsonor-F jsonwithjqfor scripting and automation. - API pagination: Use
glab api --paginate "endpoint?per_page=100"to auto-fetch all pages. - Repo context: glab auto-detects repo from git remote; use
-R owner/repowhen outside a repo directory.
Quick Reference
| Task | Command |
|---|---|
| Auth status | glab auth status |
| Check token expiry | glab api personal_access_tokens/self | jq '{name, expires_at}' |
| Rotate token | ~/.claude/skills/glab-cli/scripts/rotate-gitlab-token --yes |
| Create repo | glab repo create <name> --group <group> |
| Clone repo | glab repo clone <owner/repo> |
| Create MR | glab mr create |
| List MRs | glab mr list |
| View MR | glab mr view <id> |
| Add MR comment | glab mr note <id> -m "comment" |
| List pipelines | glab ci list -R <repo> |
| Pipeline info | glab ci get -p <id> -R <repo> -F json |
| Pipeline jobs | glab ci get -p <id> -R <repo> --with-job-details -F json |
| View job log | glab ci trace <job-id> -R <repo> |
| Trigger manual job | glab ci trigger <job-id> -R <repo> (find job ID from pipeline first) |
| Download artifacts (latest) | glab job artifact <branch> <job-name> -R <repo> |
| Download artifacts (by job ID) | glab api /projects/<path>/jobs/<id>/artifacts > file.zip |
| List variables | glab variable list |
| Set variable | glab variable set <key> <value> |
| List runners | glab api projects/:fullpath/runners (no native cmd) |
Authentication Setup
Check Status
glab auth status
Token Expired or Not Initialized
If you see token expired, unauthorized, or failed to authenticate:
Step 1: Create a Personal Access Token
- Open: https://gitlab.domain.com/-/user_settings/personal_access_tokens
- Create token with scopes:
apiandwrite_repository - Copy the token immediately
Step 2: Authenticate
echo "<YOUR_TOKEN>" | glab auth login --hostname gitlab.domain.com --stdin --git-protocol ssh
Step 3: Verify
glab auth status
Token Rotation (Auto-Renew Before Expiry)
IMPORTANT — Proactive Token Rotation:
When any glab command fails with token expired, 401 Unauthorized, or failed to authenticate, first attempt to rotate the token automatically before asking the user to manually create one.
Check token expiry:
glab api personal_access_tokens/self --hostname gitlab.domain.com | jq '{name, expires_at, active, scopes}'
Rotate using the bundled script:
~/.claude/skills/glab-cli/scripts/rotate-gitlab-token --hostname gitlab.domain.com --yes
- The
--yesflag skips the interactive confirmation (required for agent use). - The script rotates the current token, sets a new expiry date 1 year from today, and automatically updates
glab auth. - The old token is immediately revoked after rotation.
- Requires GitLab 16.0+ for the Rotation API.
When rotation fails (token already expired or GitLab < 16.0), fall back to the manual flow above.
Refresh OAuth Token
glab auth refresh --hostname gitlab.domain.com
Note: Only works for OAuth (interactive login), not PAT (--stdin).
Repository Management
Create Repository
IMPORTANT: Correct syntax for nested groups
# CORRECT: Use --group for the full group path, repo name separately
glab repo create my-project --group parent/child/subgroup --private
# WRONG: Don't put the full path as the repo name
glab repo create parent/child/subgroup/my-project # Will fail
Examples:
# In user namespace
glab repo create my-project
# In a group
glab repo create my-project --group my-group
# In nested group with options
glab repo create packer-lab \
--group my-org/my-team/my-subgroup \
--description "Project description" \
--private \
--defaultBranch main
After creating, glab initializes a new git repo in ./my-project/. If you already have a local repo:
rm -rf ./my-project
git remote add origin git@gitlab.domain.com:group/my-project.git
git push -u origin main
Visibility: --private | --public | --internal
Find Available Groups
glab api "groups?search=devops" | jq -r '.[] | "\(.full_path) (id: \(.id))"'
glab api groups --paginate | jq -r '.[] | .full_path'
Clone Repository
glab repo clone owner/repo
glab repo clone owner/repo target-dir
glab repo clone -g group-name # Clone all in group
Fork & View
glab repo fork owner/repo
glab repo view
glab repo view --web
Merge Request Operations
Create MR
glab mr create
glab mr create --title "feat: add feature" --description "desc"
glab mr for 123 # From issue
List MRs
glab mr list
glab mr list --assignee=@me
glab mr list --reviewer=@me
glab mr list --state merged --author @me
View & Checkout
glab mr view 123
glab mr view 123 --web
glab mr diff 123
glab mr checkout 123
Code Review (CE Compatible)
glab mr note 123 -m "LGTM! Nice work."
glab mr merge 123
glab mr merge 123 --squash
glab mr merge 123 --when-pipeline-succeeds # Auto-merge after CI passes
glab mr close 123
glab mr reopen 123
Inline Comment (via API)
glab api projects/:fullpath/merge_requests/123/discussions \
--method POST \
-f body="Comment here" \
-f "position[base_sha]=<base>" \
-f "position[head_sha]=<head>" \
-f "position[start_sha]=<start>" \
-f "position[position_type]=text" \
-f "position[new_path]=file.go" \
-f "position[new_line]=42"
Get SHA values: glab mr view 123 --json diffRefs
Premium/EE Only
glab mr approve 123 # Premium/EE only
glab mr revoke 123 # Premium/EE only
CI/CD Pipeline & Jobs
For complete reference including all commands, monitoring patterns, artifacts, and debugging workflows, see references/cicd-pipelines.md.
Triggering Jobs — Correct Workflow
IMPORTANT: Do NOT use glab ci run to trigger jobs. glab ci run creates a brand new pipeline via API, which is commonly blocked by workflow:rules (e.g., rules that only allow push or schedule sources). This results in 400 Pipeline filtered out by workflow rules errors.
Instead, always follow this sequence:
Check if a pipeline already exists (push events auto-create pipelines):
glab ci list --per-page 3 -F json | jq '.[] | {id, status, ref}'Find the target job in the latest pipeline:
glab ci get -p <pipeline-id> --with-job-details -F json | jq '.jobs[] | {id, name, status, stage}'Trigger the manual job by job ID:
glab ci trigger <job-id>
When to use glab ci run: Only when you need to create a pipeline on a branch that has no recent pipeline AND the project's workflow:rules allow API-triggered pipelines. This is rare — most projects restrict pipeline creation to push/schedule/MR events.
Most Common Operations
# List recent pipelines
glab ci list --per-page 5
# Get pipeline info as JSON
glab ci get -p <pipeline-id> -R <repo> -F json
# Get pipeline jobs
glab ci get -p <pipeline-id> -R <repo> --with-job-details -F json | jq '.jobs[] | {id, name, status, stage}'
# View job logs (strip ANSI codes)
glab ci trace <job-id> -R <repo> | sed 's/\x1b\[[0-9;]*m//g'
# Trigger manual job
glab ci trigger <job-id> -R <repo>
# Retry failed job
glab ci retry <job-id> -R <repo>
# Download artifacts (latest pipeline)
glab job artifact <branch> <job-name> -R <repo>
# Download artifacts (specific job ID, via API)
glab api /projects/<url-encoded-path>/jobs/<job-id>/artifacts > artifacts.zip
# CI lint
glab ci lint
Note: glab job artifact only downloads from the latest pipeline of a branch. For specific job IDs, use the API.
CI/CD Variables
glab variable list
glab variable get MY_VAR
glab variable set MY_VAR "value"
glab variable delete MY_VAR
# Advanced options
glab variable set MY_VAR "value" --protected # Only on protected branches
glab variable set MY_VAR "value" --masked # Hidden in logs
glab variable set MY_VAR "value" --scope production
glab variable set MY_CERT --value-file /path/to/cert.pem
# Backup and restore
glab variable export > variables.json
glab variable import < variables.json
Runner Status (via API)
No native runner command exists. Use glab api.
# List project runners
glab api projects/:fullpath/runners | jq '.[] | {id, description, status, is_shared}'
# Online only
glab api projects/:fullpath/runners --field status=online
# Runner details
glab api runners/<runner-id>
glab api runners/<runner-id>/jobs
# Group runners
glab api groups/<group-id>/runners
Errors & Debugging
For common error solutions (401, HTTP 405, namespace not found, TUI panic, URL encoding), see references/troubleshooting.md.
For CI/CD failure debugging workflow (list pipelines -> find failed jobs -> view logs -> retry), see references/cicd-pipelines.md.