Skip to main content

Gitlab Mr Workflow

GitLab MR lifecycle: branch, commit, open, CI, merge.

Skill metadata​

SourceBundled (installed by default)
Pathskills/gitlab/gitlab-mr-workflow
Version1.1.0
AuthorAlice Agent
LicenseMIT
Platformslinux, macos, windows
TagsGitLab, Merge-Requests, CI/CD, Git, Automation, Merge
Related skillsgitlab-auth, gitlab-code-review

Reference: full SKILL.md​

info

The following is the complete skill definition that Alice loads when this skill is triggered. This is what the agent sees as instructions when the skill is active.

GitLab Merge Request Workflow

Complete guide for managing the MR lifecycle. Each section shows the glab way first, then the git + curl fallback for machines without glab.

Prerequisites​

  • Authenticated with GitLab (see gitlab-auth skill)
  • Inside a git repository with a GitLab remote

Quick Auth Detection​

# Determine which method to use throughout this workflow
if command -v glab &>/dev/null && glab auth status &>/dev/null; then
AUTH="glab"
else
AUTH="git"
# Ensure we have a token for API calls
if [ -z "$GITLAB_TOKEN" ]; then
if _alice_env="${ALICE_HOME:-$HOME/.alice}/.env"; [ -f "$_alice_env" ] && grep -q "^GITLAB_TOKEN=" "$_alice_env"; then
GITLAB_TOKEN=$(grep "^GITLAB_TOKEN=" "$_alice_env" | head -1 | cut -d= -f2 | tr -d '\n\r')
elif grep -q "github.com" ~/.git-credentials 2>/dev/null; then
GITLAB_TOKEN=$(grep "${GITLAB_HOST:-gitlab\.com}" ~/.git-credentials 2>/dev/null | head -1 | sed 's|https://[^:]*:\([^@]*\)@.*|\1|')
fi
fi
fi
echo "Using: $AUTH"

Extracting Owner/Repo from the Git Remote​

Many curl commands need owner/repo. Extract it from the git remote:

# Works for both HTTPS and SSH remote URLs
REMOTE_URL=$(git remote get-url origin)
OWNER_REPO=$(echo "$REMOTE_URL" | sed -E 's|.*${GITLAB_HOST:-gitlab\.com}[:/]||; s|\.git$||')
OWNER=$(echo "$OWNER_REPO" | cut -d/ -f1)
REPO=$(echo "$OWNER_REPO" | cut -d/ -f2)
echo "Owner: $OWNER, Repo: $REPO"

1. Branch Creation​

This part is pure git — identical either way:

# Make sure you're up to date
git fetch origin
git checkout main && git pull origin main

# Create and switch to a new branch
git checkout -b feat/add-user-authentication

Branch naming conventions:

  • feat/description — new features
  • fix/description — bug fixes
  • refactor/description — code restructuring
  • docs/description — documentation
  • ci/description — CI/CD changes

2. Making Commits​

Use the agent's file tools (write_file, patch) to make changes, then commit:

# Stage specific files
git add src/auth.py src/models/user.py tests/test_auth.py

# Commit with a conventional commit message
git commit -m "feat: add JWT-based user authentication

- Add login/register endpoints
- Add User model with password hashing
- Add auth middleware for protected routes
- Add unit tests for auth flow"

Commit message format (Conventional Commits):

type(scope): short description

Longer explanation if needed. Wrap at 72 characters.

Types: feat, fix, refactor, docs, test, ci, chore, perf

3. Pushing and Creating an MR​

Push the Branch (same either way)​

git push -u origin HEAD

Create the MR​

With glab:

glab mr create \
--title "feat: add JWT-based user authentication" \
--body "## Summary
- Adds login and register API endpoints
- JWT token generation and validation

## Test Plan
- [ ] Unit tests pass

Closes #42"

Options: --draft, --reviewer user1,user2, --label "enhancement", --base develop

With git + curl:

BRANCH=$(git branch --show-current)

curl -s -X POST \
-H "PRIVATE-TOKEN: $GITLAB_TOKEN" \
-H "Accept: application/vnd.github.v3+json" \
https://\${GITLAB_HOST:-gitlab.com}/api/v4/projects/$OWNER/$REPO/merge_requests \
-d "{
\"title\": \"feat: add JWT-based user authentication\",
\"body\": \"## Summary\nAdds login and register API endpoints.\n\nCloses #42\",
\"head\": \"$BRANCH\",
\"base\": \"main\"
}"

The response JSON includes the MR number — save it for later commands.

To create as a draft, add "draft": true to the JSON body.

4. Monitoring CI Status​

Check CI Status​

With glab:

# One-shot check
glab mr ci

# Watch until all checks finish (polls every 10s)
glab mr ci --watch

With git + curl:

# Get the latest commit SHA on the current branch
SHA=$(git rev-parse HEAD)

# Query the combined status
curl -s \
-H "PRIVATE-TOKEN: $GITLAB_TOKEN" \
https://\${GITLAB_HOST:-gitlab.com}/api/v4/projects/$OWNER/$REPO/repository/commits/$SHA/status \
| python3 -c "
import sys, json
data = json.load(sys.stdin)
print(f\"Overall: {data['state']}\")
for s in data.get('statuses', []):
print(f\" {s['context']}: {s['state']} - {s.get('description', '')}\")"

# Also check GitLab CI check runs (separate endpoint)
curl -s \
-H "PRIVATE-TOKEN: $GITLAB_TOKEN" \
https://\${GITLAB_HOST:-gitlab.com}/api/v4/projects/$OWNER/$REPO/repository/commits/$SHA/check-runs \
| python3 -c "
import sys, json
data = json.load(sys.stdin)
for cr in data.get('check_runs', []):
print(f\" {cr['name']}: {cr['status']} / {cr['conclusion'] or 'pending'}\")"

Poll Until Complete (git + curl)​

# Simple polling loop — check every 30 seconds, up to 10 minutes
SHA=$(git rev-parse HEAD)
for i in $(seq 1 20); do
STATUS=$(curl -s \
-H "PRIVATE-TOKEN: $GITLAB_TOKEN" \
https://\${GITLAB_HOST:-gitlab.com}/api/v4/projects/$OWNER/$REPO/repository/commits/$SHA/status \
| python3 -c "import sys,json; print(json.load(sys.stdin)['state'])")
echo "Check $i: $STATUS"
if [ "$STATUS" = "success" ] || [ "$STATUS" = "failure" ] || [ "$STATUS" = "error" ]; then
break
fi
sleep 30
done

5. Auto-Fixing CI Failures​

When CI fails, diagnose and fix. This loop works with either auth method.

Step 1: Get Failure Details​

With glab:

# List recent workflow runs on this branch
glab ci list --branch $(git branch --show-current) --limit 5

# View failed logs
glab ci view <RUN_ID> --log-failed

With git + curl:

BRANCH=$(git branch --show-current)

# List workflow runs on this branch
curl -s \
-H "PRIVATE-TOKEN: $GITLAB_TOKEN" \
"https://\${GITLAB_HOST:-gitlab.com}/api/v4/projects/$OWNER/$REPO/actions/runs?branch=$BRANCH&per_page=5" \
| python3 -c "
import sys, json
runs = json.load(sys.stdin)['workflow_runs']
for r in runs:
print(f\"Run {r['id']}: {r['name']} - {r['conclusion'] or r['status']}\")"

# Get failed job logs (download as zip, extract, read)
RUN_ID=<run_id>
curl -s -L \
-H "PRIVATE-TOKEN: $GITLAB_TOKEN" \
https://\${GITLAB_HOST:-gitlab.com}/api/v4/projects/$OWNER/$REPO/actions/runs/$RUN_ID/logs \
-o /tmp/ci-logs.zip
cd /tmp && unzip -o ci-logs.zip -d ci-logs && cat ci-logs/*.txt

Step 2: Fix and Push​

After identifying the issue, use file tools (patch, write_file) to fix it:

git add <fixed_files>
git commit -m "fix: resolve CI failure in <check_name>"
git push

Step 3: Verify​

Re-check CI status using the commands from Section 4 above.

Auto-Fix Loop Pattern​

When asked to auto-fix CI, follow this loop:

  1. Check CI status → identify failures
  2. Read failure logs → understand the error
  3. Use read_file + patch/write_file → fix the code
  4. git add . && git commit -m "fix: ..." && git push
  5. Wait for CI → re-check status
  6. Repeat if still failing (up to 3 attempts, then ask the user)

6. Merging​

With glab:

# Squash merge + delete branch (cleanest for feature branches)
glab mr merge --squash --delete-branch

# Enable auto-merge (merges when all checks pass)
glab mr merge --auto --squash --delete-branch

With git + curl:

MR_NUMBER=<number>

# Merge the MR via API (squash)
curl -s -X PUT \
-H "PRIVATE-TOKEN: $GITLAB_TOKEN" \
https://\${GITLAB_HOST:-gitlab.com}/api/v4/projects/$OWNER/$REPO/merge_requests/$MR_NUMBER/merge \
-d "{
\"merge_method\": \"squash\",
\"commit_title\": \"feat: add user authentication (#$MR_NUMBER)\"
}"

# Delete the remote branch after merge
BRANCH=$(git branch --show-current)
git push origin --delete $BRANCH

# Switch back to main locally
git checkout main && git pull origin main
git branch -d $BRANCH

Merge methods: "merge" (merge commit), "squash", "rebase"

Enable Auto-Merge (curl)​

# Auto-merge requires the repo to have it enabled in settings.
# This uses the GraphQL API since REST doesn't support auto-merge.
PR_NODE_ID=$(curl -s \
-H "PRIVATE-TOKEN: $GITLAB_TOKEN" \
https://\${GITLAB_HOST:-gitlab.com}/api/v4/projects/$OWNER/$REPO/merge_requests/$MR_NUMBER \
| python3 -c "import sys,json; print(json.load(sys.stdin)['node_id'])")

curl -s -X POST \
-H "PRIVATE-TOKEN: $GITLAB_TOKEN" \
https://\${GITLAB_HOST:-gitlab.com}/api/v4/graphql \
-d "{\"query\": \"mutation { enablePullRequestAutoMerge(input: {pullRequestId: \\\"$PR_NODE_ID\\\", mergeMethod: SQUASH}) { clientMutationId } }\"}"

7. Complete Workflow Example​

# 1. Start from clean main
git checkout main && git pull origin main

# 2. Branch
git checkout -b fix/login-redirect-bug

# 3. (Agent makes code changes with file tools)

# 4. Commit
git add src/auth/login.py tests/test_login.py
git commit -m "fix: correct redirect URL after login

Preserves the ?next= parameter instead of always redirecting to /dashboard."

# 5. Push
git push -u origin HEAD

# 6. Create MR (picks glab or curl based on what's available)
# ... (see Section 3)

# 7. Monitor CI (see Section 4)

# 8. Merge when green (see Section 6)

Useful MR Commands Reference​

Actionglabgit + curl
List my PRsglab mr list --author @mecurl -s -H "PRIVATE-TOKEN: $GITLAB_TOKEN" "https://\${GITLAB_HOST:-gitlab.com}/api/v4/projects/$OWNER/$REPO/merge_requests?state=open"
View MR diffglab mr diffgit diff main...HEAD (local) or curl -H "Accept: application/vnd.github.diff" ...
Add commentglab mr comment N --body "..."curl -X POST .../issues/N/comments -d '{"body":"..."}'
Request reviewglab mr edit N --add-reviewer usercurl -X POST .../merge_requests/N/requested_reviewers -d '{"reviewers":["user"]}'
Close MRglab mr close Ncurl -X PATCH .../merge_requests/N -d '{"state":"closed"}'
Check out someone's MRglab mr checkout Ngit fetch origin merge-requests/N/head:mr-N && git checkout mr-N