From dev-core
Promote staging→main — pre-flight, version bump, changelog, PR & tag. Triggers: "promote staging" | "release" | "deploy" | "cut a release" | "--finalize" | "merge to main" | "promote to production" | "ship a release" | "tag and release" | "publish release".
npx claudepluginhub roxabi/roxabi-plugins --plugin dev-coreThis skill is limited to using the following tools:
Let: σ := staging | μ := main | V := release version (vX.Y.Z) | Q := DP(A)
Automates feature branch release pipeline: pre-flight checks, merge main, run tests, pre-landing review, version bump, changelog, bisectable commits, push, PR creation. Triggers on 'ship it', '/ship-workflow'.
Finalizes git-flow release: checks branch, runs tests, updates changelog, merges to main/develop, tags, pushes, creates GitHub release, switches to develop.
Manages git project releases: creates milestones, generates roadmaps, executes 9-stage pipeline with submodule support, closes releases. Yolo mode enables autonomous runs.
Share bugs, ideas, or general feedback.
Let: σ := staging | μ := main | V := release version (vX.Y.Z) | Q := DP(A)
σ → μ for production. Pre-flight → version → changelog → commit → preview → PR.
--finalize: post-merge tag + GitHub Release.
/promote → Full flow
/promote --skip-preview → Skip deploy preview
/promote --dry-run → Show what would be promoted, create nothing
/promote --finalize → Post-merge: tag + GitHub Release
git fetch origin staging main
git checkout staging && git pull origin staging
git log main..staging --oneline
git diff main...staging --stat
gh pr list --base staging --state open --json number,title,headRefName
| Check | Condition | Action |
|---|---|---|
| No commits | git log main..staging empty | REFUSE. Stop. |
| Open PRs on σ | gh pr list --base staging → results | WARN + Q: Continue | Wait |
| CI status | Latest σ commit | WARN if ¬passing |
gh api repos/:owner/:repo/commits/staging/check-runs \
--jq '[.check_runs[] | {name, conclusion}] | group_by(.conclusion) | map({conclusion: .[0].conclusion, count: length})'
Read references/release-artifacts.md for full procedure.
¬--skip-preview ⇒
gh workflow run deploy-preview.yml --ref staging -f target=both
sleep 5
RUN_ID=$(gh run list --workflow=deploy-preview.yml --limit=1 --json databaseId --jq '.[0].databaseId')
gh run watch $RUN_ID --exit-status
Q: Looks good — proceed | Issues — abort | Skip preview, proceed
--skip-preview ⇒ skip.
Promotion Summary
=================
Version: {$VERSION}
Commits: {N} commits ahead of main
PRs: {N} merged PRs
Files: {N} files changed
CI: passing/failing/pending
Preview: verified/skipped
--dry-run ⇒ display + stop. "Run /promote to create the promotion PR."
σ may have branch protection. Direct push fails →
git branch chore/$VERSION-changelog staginggit push origin chore/$VERSION-changeloggh pr create --base staging --head chore/$VERSION-changelog --title "chore(release): add $VERSION changelog"gh pr merge <N> --squash --delete-branchgit fetch origin staging && git reset --hard origin/staginggh pr create \
--base main --head staging \
--title "chore: promote staging to main ($VERSION)" \
--body "$(cat <<EOF
## Promotion: staging → main ($VERSION)
{changelog}
## Pre-flight
- [x] CI passing on staging
- [x] No open PRs targeting staging (or acknowledged)
- [{preview_check}] Deploy preview verified
- [x] Release notes committed to staging
---
Generated with [Claude Code](https://claude.com/claude-code) via \`/promote\`
EOF
)"
Display PR URL.
CRITICAL: Use merge commit (not squash) for promotion PR. Squash causes history divergence → conflicts + resurrected files on next promotion.
Promotion PR created: {URL}
⚠️ MERGE WITH MERGE COMMIT (not squash) to keep histories reconciled.
After merge:
1. Vercel auto-deploys to production
2. Verify production at your domain
3. Run /promote --finalize to tag + create GitHub Release
4. Run /cleanup to clean up merged branches
--finalize only)Skip Steps 1-8. Post-merge only.
9a. Verify merge:
git fetch origin main && git checkout main && git pull origin main
gh pr list --base main --head staging --state merged --limit 1 --json number,title,mergedAt
¬merged → REFUSE: "Merge the promotion PR first."
9b. Detect V:
grep -oP '## \[\Kv[0-9]+\.[0-9]+\.[0-9]+' CHANGELOG.md | head -1
Q: Use {detected} | Custom version
9c. Tag:
git tag -l "$VERSION" | grep -q "$VERSION" && echo "Tag exists — abort" && exit 1
git tag -a "$VERSION" -m "Release $VERSION"
git push origin "$VERSION"
9d. Release:
gh release create "$VERSION" --title "$VERSION" --notes "$CHANGELOG_CONTENT"
Inform: "Release $VERSION finalized. Run /cleanup to clean branches."
| Flag | Description |
|---|---|
| (none) | Full flow: pre-flight → version → changelog → commit → preview → PR |
--skip-preview | Skip deploy preview |
--dry-run | Show summary + changelog, create nothing |
--finalize | Post-merge: tag + GitHub Release |
| Scenario | Behavior |
|---|---|
| Nothing to promote | REFUSE: σ up to date with μ |
| Open PRs on σ | Warn, list, Q |
| CI failing | Warn, show failures, Q |
| Preview fails | Show error, Q |
| PR already exists | Detect via gh pr list, offer update |
--dry-run | Summary only, ¬create PR/commit |
¬merged (--finalize) | REFUSE: merge first |
Tag exists (--finalize) | REFUSE |
| Invalid version | REFUSE: ask for valid vX.Y.Z |
/dev)--finalize follow-up)/promote, never chained from a feature pipeline)/dev SKIPS this step by default (Step 4 skip logic: promote → skip)/dev-owned task lifecycle--finalize after merge). Stop./dev recovery path (standalone).$ARGUMENTS