Ship It - Automated PR and Merge Workflow
Automates shipping code changes with intelligent branch handling and post-merge monitoring.
Workflow
Task Progress:
- [ ] Step 1: Verify branch state and gather context
- [ ] Step 2: Create feature branch if on main with uncommitted changes
- [ ] Step 3: Push branch to origin
- [ ] Step 4: Create pull request
- [ ] Step 4b: Clean up PR description (remove Cursor attribution)
- [ ] Step 5: Monitor PR CI checks until all pass
- [ ] Step 6: Merge PR when green
- [ ] Step 7: Monitor post-merge Actions on main
- [ ] Step 8: Delete feature branch and sync main
Step 1: Verify Branch State
Notify user: "Checking current branch and gathering context..."
Run these commands to understand the current state:
BRANCH=$(git branch --show-current)
UNCOMMITTED=$(git status --short)
COMMITS_AHEAD=$(git log origin/main..HEAD --oneline | wc -l)
Determine workflow path:
- On main with uncommitted changes → Go to Step 2 (create feature branch, commit changes there)
- On main, clean working tree, 0 commits ahead → Nothing to ship. Stop and notify user.
- On feature branch with uncommitted changes → Ask user: "Commit changes first or stash them?"
- On feature branch, clean working tree → Proceed to Step 3
Notify user: "On branch $BRANCH with $COMMITS_AHEAD commits ahead and uncommitted changes on $UNCOMMITTED files."
Step 2: Create Feature Branch if on Main with Changes
CRITICAL: If on main AND there are uncommitted changes, create a feature branch first.
Notify user: "On main with uncommitted changes. Creating feature branch and committing changes there..."
# Generate branch name from first changed file
FIRST_FILE=$(git status --short | head -1 | awk '{print $2}')
BRANCH_NAME=$(echo "$FIRST_FILE" | sed 's/.*\///' | sed 's/[^a-zA-Z0-9]/-/g' | cut -d'-' -f1-3 | tr '[:upper:]' '[:lower:]' | sed 's/^-*//;s/-*$//')
BRANCH_NAME="${BRANCH_NAME:-feature}-$(date +%s)"
# Create branch and switch
git checkout -b "$BRANCH_NAME"
# Commit all uncommitted changes
git add .
git commit -m "Add $BRANCH_NAME changes"
Notify user: "Created branch $BRANCH_NAME and committed changes. Proceeding with ship workflow..."
Step 3: Push Branch to Origin
Notify user: "Pushing branch to origin..."
git push -u origin $(git branch --show-current)
Notify user: "Branch pushed."
Step 4: Create Pull Request
Notify user: "Creating pull request..."
# Check if PR already exists
PR_EXISTS=$(gh pr view --json number 2>/dev/null && echo "yes" || echo "no")
# If no PR exists, create one
if [ "$PR_EXISTS" = "no" ]; then
gh pr create --title "$(git log -1 --pretty=%s)" --body "$(cat <<'EOF'
## Summary
Changes from feature branch.
## Test Plan
- [ ] Tests pass
EOF
)"
fi
# Get PR URL
PR_URL=$(gh pr view --json url --jq '.url')
Notify user: "PR created: $PR_URL"
Step 4b: Clean Up PR Description
Notify user: "Cleaning up PR description..."
Remove any Cursor attribution from the PR body:
# Get current PR body and remove Cursor attribution lines
CURRENT_BODY=$(gh pr view --json body --jq '.body')
CLEANED_BODY=$(echo "$CURRENT_BODY" | sed '/Made with \[Cursor\]/d' | sed '/^$/N;/^\n$/d')
# Only update if there was a change
if [ "$CURRENT_BODY" != "$CLEANED_BODY" ]; then
gh pr edit --body "$CLEANED_BODY"
fi
Notify user: "PR description cleaned."
Step 5: Monitor PR CI Checks
Notify user: "Monitoring CI checks on PR (this may take a few minutes)..."
PR_NUM=$(gh pr view --json number --jq '.number')
Poll until all checks pass:
while true; do
sleep 15
CHECKS=$(gh pr checks $PR_NUM --json name,state,workflow -q '.[] | "\(.name): \(.state)"' 2>/dev/null)
# Check if any failed
if echo "$CHECKS" | grep -q "FAILURE"; then
echo "Checks failed: $CHECKS"
exit 1
fi
# Check if all passed
if ! echo "$CHECKS" | grep -q "IN_PROGRESS\|PENDING"; then
echo "All checks passed"
break
fi
echo "Checks running: $(echo "$CHECKS" | grep -c 'IN_PROGRESS') pending..."
done
Stop and report error if any check fails.
Step 6: Merge PR When Green
Notify user: "All checks passed. Merging PR now..."
Save branch name before merge:
FEATURE_BRANCH=$(git branch --show-current)
Merge:
gh pr merge --squash --delete-branch
Notify user: "PR merged. Remote branch deleted."
Step 7: Monitor Post-Merge Actions on Main
CRITICAL: After merge, wait for post-merge Actions on main to complete before cleanup.
Notify user: "Monitoring post-merge Actions on main branch..."
Switch to main first to monitor workflows there:
git checkout main
git fetch origin
Poll for post-merge Actions:
while true; do
sleep 10
# Get running workflows on main
RUNNING=$(gh run list --branch main --json name,status --jq '.[] | select(.status == "in_progress" or .status == "queued") | .name')
if [ -z "$RUNNING" ]; then
echo "No post-merge Actions running"
break
fi
echo "Post-merge Actions running: $RUNNING"
done
# Get final status of recent workflows
gh run list --branch main --limit 5 --json name,status,conclusion
Report each workflow's final status:
- "
<workflow>:<conclusion>"
Notify user: "All post-merge Actions completed. Proceeding with cleanup..."
Step 8: Delete Feature Branch and Sync Main
Notify user: "Deleting feature branch and syncing main..."
# Pull latest main
git pull origin main
# Delete local feature branch
git branch -D "$FEATURE_BRANCH" 2>/dev/null || echo "Branch already deleted"
# Verify clean state
git status
Notify user: "Done! Deleted branch $FEATURE_BRANCH. Ready for the next change. Current branch: main (up to date)"
Error Handling
| Scenario | Action |
|---|---|
| On main, clean, nothing to ship | Stop: "Nothing to ship. Make some changes first." |
| On main with uncommitted changes | Create feature branch, commit there, proceed |
| Push fails | Report auth error, ask user to check token |
| PR checks fail | Stop, show which check failed |
| Merge blocked by branch protection | Report requirements, do not bypass |
| Post-merge Actions fail | Report failure but proceed with cleanup (code is merged) |
| Timeout waiting | Ask user to continue or abort |
Success Output
Report when complete:
- PR URL
- PR checks summary
- Post-merge Actions summary (with status for each)
- Merge method used
- Branches deleted (remote via --delete-branch, local via git branch -D)
- Final state: "On main, up to date, ready for next change"
Final notification: "Ship complete. You're ready to work on the next change!"