---
roadcrew_template_name: "create-release-pr.md"
roadcrew_template_type: "command"
execution_mode: "auto-execute"
roadcrew_template_version: "v1.0"
roadcrew_last_updated: "2025-10-25"
roadcrew_min_version: "1.5.0"
roadcrew_license: "See LICENSE file in .roadcrew folder"
roadcrew_copyright: "Copyright (c) 2025 North Star Holdings, LLC"
spdx_license_identifier: "LicenseRef-RoadcrewLicense-1.0"
---

# Create Release PR

Create a release PR from dev → main with automatic issue closure tracking.

**Prerequisites:** GitHub authentication required. See `.cursorrules.md` (lines 20-25) for authentication methods and workflow requirements.

## Usage

```
/create-release-pr
/create-release-pr --version v1.2.0
/create-release-pr --title "Release: March Sprint"
```

**Optional Flags:**
- `--version <version>`: Version number for the release (e.g., v1.2.0)
- `--title <title>`: Custom PR title (defaults to "Release: [date]")
- `--milestone <milestone>`: Link to specific GitHub milestone

## What This Command Does

Creates a PR from `dev` → `main` with **automatic detection** of all issues that should auto-close when merged.

**Important:** This includes **both epic issues and their child issues** - all issues with the `pending-closure` label will be included in the release PR.

## Process

### 1. Detect Pending Issues

**Collect all issues ready for closure:**

```bash
# Find all issues with pending-closure label
PENDING_ISSUES=$(gh issue list \
  --label "pending-closure" \
  --state open \
  --json number,title \
  --jq '.[] | {number, title}')

# Count total issues
ISSUE_COUNT=$(echo "$PENDING_ISSUES" | jq -s 'length')

if [ "$ISSUE_COUNT" -eq 0 ]; then
  echo "⚠️  No issues found with 'pending-closure' label"
  echo "Tip: Issues should be labeled 'pending-closure' after epic PRs merge to dev"
  echo "Tip: Both epic issues and child issues should have the label"
  echo ""
  echo "Continue creating release PR without auto-closures? [Y/n]"
  read CONTINUE
  if [ "$CONTINUE" != "Y" ]; then
    exit 0
  fi
fi

echo "📋 Found $ISSUE_COUNT issues pending closure (including epics and child issues):"
echo "$PENDING_ISSUES" | jq -r '.[] | "  - #\(.number): \(.title)"'
echo ""
```

### 2. Verify Merged to Dev

**Ensure all issues are actually implemented in dev:**

```bash
# For each pending issue, verify it's referenced in merged PRs to dev
for ISSUE_NUM in $(echo "$PENDING_ISSUES" | jq -r '.number'); do
  # Check if issue is referenced in any merged PR to dev
  PR_CHECK=$(gh pr list \
    --base dev \
    --state merged \
    --search "#$ISSUE_NUM" \
    --json number \
    --jq '.[].number')
  
  if [ -z "$PR_CHECK" ]; then
    echo "⚠️  Warning: Issue #$ISSUE_NUM not found in any merged PR to dev"
    echo "   This issue may not be ready for closure"
  fi
done
```

### 3. Build Closure Keywords

**Generate Closes #N for all pending issues:**

```bash
# Build the closure section for PR body
CLOSES_SECTION="## Issues Resolved\n\n"

for ISSUE in $(echo "$PENDING_ISSUES" | jq -r '.number'); do
  CLOSES_SECTION="$CLOSES_SECTION""Closes #$ISSUE\n"
done

echo "✅ Auto-closure keywords prepared for $ISSUE_COUNT issues"
```

### 4. Collect Changes Summary

**Generate release notes from merged PRs:**

```bash
# Get all PRs merged to dev since last release
LAST_RELEASE_DATE=$(gh release list --limit 1 --json publishedAt --jq '.[0].publishedAt')

if [ -z "$LAST_RELEASE_DATE" ]; then
  echo "ℹ️  No previous releases found, including all merged PRs to dev"
  MERGED_PRS=$(gh pr list --base dev --state merged --limit 50 --json number,title,mergedAt)
else
  echo "ℹ️  Collecting changes since $LAST_RELEASE_DATE"
  MERGED_PRS=$(gh pr list \
    --base dev \
    --state merged \
    --search "merged:>$LAST_RELEASE_DATE" \
    --json number,title,mergedAt)
fi

# Group by type (epic, feature, bug, process)
EPICS=$(echo "$MERGED_PRS" | jq -r '.[] | select(.title | contains("Epic")) | "- #\(.number): \(.title)"')
FEATURES=$(echo "$MERGED_PRS" | jq -r '.[] | select(.title | contains("feat")) | "- #\(.number): \(.title)"')
BUGS=$(echo "$MERGED_PRS" | jq -r '.[] | select(.title | contains("fix") or contains("bug")) | "- #\(.number): \(.title)"')

# Build changes summary
CHANGES_SUMMARY="## Changes Included\n\n"

if [ -n "$EPICS" ]; then
  CHANGES_SUMMARY="$CHANGES_SUMMARY### Epics\n$EPICS\n\n"
fi

if [ -n "$FEATURES" ]; then
  CHANGES_SUMMARY="$CHANGES_SUMMARY### Features\n$FEATURES\n\n"
fi

if [ -n "$BUGS" ]; then
  CHANGES_SUMMARY="$CHANGES_SUMMARY### Bug Fixes\n$BUGS\n\n"
fi
```

### 5. Create Release PR

**Build comprehensive PR with auto-closure:**

```bash
# Determine PR title
if [ -n "$VERSION" ]; then
  PR_TITLE="Release: $VERSION"
elif [ -n "$CUSTOM_TITLE" ]; then
  PR_TITLE="$CUSTOM_TITLE"
else
  PR_TITLE="Release: $(date +%Y-%m-%d)"
fi

# Build PR body
PR_BODY="$(cat <<EOF
# $PR_TITLE

## Release Summary

This release includes $ISSUE_COUNT resolved issues and $(echo "$MERGED_PRS" | jq -s 'length') merged PRs.

$CHANGES_SUMMARY

## Issues Resolved

$CLOSES_SECTION

## Testing Evidence

- [ ] All CI checks passing
- [ ] Manual testing completed
- [ ] Documentation updated
- [ ] Breaking changes documented (if any)

## Deployment Notes

[Add any deployment-specific notes here]

---

**Auto-generated by /create-release-pr**
**Issues will auto-close when this PR merges to main**
EOF
)"

# ✨ NEW: Validate PR format before pushing
echo ""
echo "🔍 Validating PR format..."
node scripts/core/validate-pr-command.js \
  --title "$PR_TITLE" \
  --body "$PR_BODY" \
  --command "create-release-pr" \
  || echo "⚠️  Validation warnings above - you can proceed to push anyway"

# Create the PR
gh pr create \
  --base main \
  --head dev \
  --title "$PR_TITLE" \
  --body "$PR_BODY"

PR_URL=$(gh pr view --json url --jq '.url')
echo ""
echo "✅ Release PR created: $PR_URL"
echo ""
echo "📌 Next Steps:"
echo "1. Review the PR and ensure all checks pass"
echo "2. Get required approvals"
echo "3. Merge to main"
echo "4. All $ISSUE_COUNT issues will auto-close on merge"
echo ""
echo "After merge, pending-closure labels will be automatically removed."
```

### 6. Post-Merge Cleanup (Automated)

**After PR merges, clean up labels:**

This can be automated via GitHub Actions or done manually:

```bash
# Remove pending-closure labels from all closed issues
gh issue list \
  --label "pending-closure" \
  --state closed \
  --json number \
  --jq '.[].number' | \
  xargs -I {} gh issue edit {} --remove-label "pending-closure"
```

## Error Handling

- If no pending issues found: Warn but allow proceeding
- If issue not in merged PRs: Warn but don't block
- If PR creation fails: Show error and suggest manual creation
- If already on main branch: Error and exit

## Integration with Other Commands

**After `/implement-epic`:**
```
1. /implement-epic 42 (creates epic PR to dev)
2. Review and merge epic PR to dev
3. /create-release-pr (bundles all pending issues)
4. Merge release PR to main → issues auto-close ✅
```

**After `/implement-issue`:**
```
# implement-issue already creates PR to main
# No need for this command - issues will auto-close directly
```

## Configuration

**Required label:** `pending-closure`
- Created by `/implement-epic` on each issue
- Indicates issue is implemented but awaiting main merge
- Removed after PR merges to main

**Optional:** Configure GitHub Actions to auto-remove labels on PR merge

## Benefits

- ✅ **Never forget closure keywords** - Automatically detected
- ✅ **Clear tracking** - pending-closure label shows status
- ✅ **Bulk releases** - Handle multiple epics in one PR
- ✅ **Audit trail** - Comments on issues explain closure strategy
- ✅ **Flexible** - Works with custom titles, versions, milestones

## Notes

- Only works for issues labeled `pending-closure`
- **Both epic issues and child issues** must have the `pending-closure` label
- Issues must be referenced in merged PRs to dev
- Always merges dev → main (never other branches)
- Includes comprehensive release notes automatically
- Compatible with semantic versioning workflows
- Epic issues will auto-close along with their child issues when the release PR merges

