feat: GitLab MR creation in /ship + /document-release

Ship Step 1.5 now checks .gitlab-ci.yml for release workflows alongside
GitHub Actions. Step 8 routes to glab mr create on GitLab repos with
correct flag mapping (-b, -t, -d). Falls back to manual instructions
when no CLI is available. Document-release now reads MR body via
glab mr view -F json and updates via glab mr update on GitLab repos.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Garry Tan
2026-03-26 00:19:54 -06:00
parent 410700483a
commit da67f4865b
4 changed files with 162 additions and 48 deletions
+46 -14
View File
@@ -236,22 +236,42 @@ Then write a `## GSTACK REVIEW REPORT` section to the end of the plan file:
file you are allowed to edit in plan mode. The plan file review report is part of the
plan's living status.
## Step 0: Detect base branch
## Step 0: Detect platform and base branch
Determine which branch this PR targets. Use the result as "the base branch" in all subsequent steps.
First, detect the git hosting platform from the remote URL:
1. Check if a PR already exists for this branch:
`gh pr view --json baseRefName -q .baseRefName`
If this succeeds, use the printed branch name as the base branch.
```bash
git remote get-url origin 2>/dev/null
```
2. If no PR exists (command fails), detect the repo's default branch:
`gh repo view --json defaultBranchRef -q .defaultBranchRef.name`
- If the URL contains "github.com" → platform is **GitHub**
- If the URL contains "gitlab" → platform is **GitLab**
- Otherwise, check CLI availability:
- `gh auth status 2>/dev/null` succeeds → platform is **GitHub** (covers GitHub Enterprise)
- `glab auth status 2>/dev/null` succeeds → platform is **GitLab** (covers self-hosted)
- Neither → **unknown** (use git-native commands only)
3. If both commands fail, fall back to `main`.
Determine which branch this PR/MR targets, or the repo's default branch if no
PR/MR exists. Use the result as "the base branch" in all subsequent steps.
**If GitHub:**
1. `gh pr view --json baseRefName -q .baseRefName` — if succeeds, use it
2. `gh repo view --json defaultBranchRef -q .defaultBranchRef.name` — if succeeds, use it
**If GitLab:**
1. `glab mr view -F json 2>/dev/null` and extract the `target_branch` field — if succeeds, use it
2. `glab repo view -F json 2>/dev/null` and extract the `default_branch` field — if succeeds, use it
**Git-native fallback (if unknown platform, or CLI commands fail):**
1. `git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's|refs/remotes/origin/||'`
2. If that fails: `git rev-parse --verify origin/main 2>/dev/null` → use `main`
3. If that fails: `git rev-parse --verify origin/master 2>/dev/null` → use `master`
If all fail, fall back to `main`.
Print the detected base branch name. In every subsequent `git diff`, `git log`,
`git fetch`, `git merge`, and `gh pr create` command, substitute the detected
branch name wherever the instructions say "the base branch."
`git fetch`, `git merge`, and PR/MR creation command, substitute the detected
branch name wherever the instructions say "the base branch" or `<default>`.
---
@@ -524,14 +544,20 @@ EOF
git push
```
**PR body update (idempotent, race-safe):**
**PR/MR body update (idempotent, race-safe):**
1. Read the existing PR body into a PID-unique tempfile:
1. Read the existing PR/MR body into a PID-unique tempfile (use the platform detected in Step 0):
**If GitHub:**
```bash
gh pr view --json body -q .body > /tmp/gstack-pr-body-$$.md
```
**If GitLab:**
```bash
glab mr view -F json 2>/dev/null | python3 -c "import sys,json; print(json.load(sys.stdin).get('description',''))" > /tmp/gstack-pr-body-$$.md
```
2. If the tempfile already contains a `## Documentation` section, replace that section with the
updated content. If it does not contain one, append a `## Documentation` section at the end.
@@ -541,18 +567,24 @@ gh pr view --json body -q .body > /tmp/gstack-pr-body-$$.md
4. Write the updated body back:
**If GitHub:**
```bash
gh pr edit --body-file /tmp/gstack-pr-body-$$.md
```
**If GitLab:**
```bash
glab mr update -d "$(cat /tmp/gstack-pr-body-$$.md)"
```
5. Clean up the tempfile:
```bash
rm -f /tmp/gstack-pr-body-$$.md
```
6. If `gh pr view` fails (no PR exists): skip with message "No PR found — skipping body update."
7. If `gh pr edit` fails: warn "Could not update PR body — documentation changes are in the
6. If `gh pr view` / `glab mr view` fails (no PR/MR exists): skip with message "No PR/MR found — skipping body update."
7. If `gh pr edit` / `glab mr update` fails: warn "Could not update PR/MR body — documentation changes are in the
commit." and continue.
**Structured doc health summary (final output):**
+16 -4
View File
@@ -291,14 +291,20 @@ EOF
git push
```
**PR body update (idempotent, race-safe):**
**PR/MR body update (idempotent, race-safe):**
1. Read the existing PR body into a PID-unique tempfile:
1. Read the existing PR/MR body into a PID-unique tempfile (use the platform detected in Step 0):
**If GitHub:**
```bash
gh pr view --json body -q .body > /tmp/gstack-pr-body-$$.md
```
**If GitLab:**
```bash
glab mr view -F json 2>/dev/null | python3 -c "import sys,json; print(json.load(sys.stdin).get('description',''))" > /tmp/gstack-pr-body-$$.md
```
2. If the tempfile already contains a `## Documentation` section, replace that section with the
updated content. If it does not contain one, append a `## Documentation` section at the end.
@@ -308,18 +314,24 @@ gh pr view --json body -q .body > /tmp/gstack-pr-body-$$.md
4. Write the updated body back:
**If GitHub:**
```bash
gh pr edit --body-file /tmp/gstack-pr-body-$$.md
```
**If GitLab:**
```bash
glab mr update -d "$(cat /tmp/gstack-pr-body-$$.md)"
```
5. Clean up the tempfile:
```bash
rm -f /tmp/gstack-pr-body-$$.md
```
6. If `gh pr view` fails (no PR exists): skip with message "No PR found — skipping body update."
7. If `gh pr edit` fails: warn "Could not update PR body — documentation changes are in the
6. If `gh pr view` / `glab mr view` fails (no PR/MR exists): skip with message "No PR/MR found — skipping body update."
7. If `gh pr edit` / `glab mr update` fails: warn "Could not update PR/MR body — documentation changes are in the
commit." and continue.
**Structured doc health summary (final output):**