|
| 1 | +name: create release PR |
| 2 | + |
| 3 | +on: |
| 4 | + workflow_dispatch: |
| 5 | + inputs: |
| 6 | + bump: |
| 7 | + description: "Version bump level" |
| 8 | + required: true |
| 9 | + type: choice |
| 10 | + options: |
| 11 | + - patch |
| 12 | + - minor |
| 13 | + - major |
| 14 | + dry_run: |
| 15 | + description: "Dry run (preview without creating PR)" |
| 16 | + required: false |
| 17 | + default: false |
| 18 | + type: boolean |
| 19 | + |
| 20 | +concurrency: |
| 21 | + group: release-pr |
| 22 | + cancel-in-progress: false |
| 23 | + |
| 24 | +permissions: |
| 25 | + contents: write |
| 26 | + pull-requests: write |
| 27 | + |
| 28 | +env: |
| 29 | + NX_VERBOSE_LOGGING: true |
| 30 | + |
| 31 | +jobs: |
| 32 | + create-release-pr: |
| 33 | + runs-on: ubuntu-latest |
| 34 | + timeout-minutes: 15 |
| 35 | + steps: |
| 36 | + - name: Check for existing release PR |
| 37 | + uses: actions/github-script@v7 |
| 38 | + with: |
| 39 | + github-token: ${{ secrets.GITHUB_TOKEN }} |
| 40 | + script: | |
| 41 | + const { owner, repo } = context.repo; |
| 42 | + const { data: prs } = await github.rest.pulls.list({ |
| 43 | + owner, |
| 44 | + repo, |
| 45 | + state: "open", |
| 46 | + head_prefix: `${owner}:release/publish/v`, |
| 47 | + }); |
| 48 | +
|
| 49 | + const releasePRs = prs.filter(pr => pr.head.ref.startsWith("release/publish/v")); |
| 50 | + if (releasePRs.length > 0) { |
| 51 | + const existing = releasePRs.map(pr => ` - #${pr.number}: ${pr.title} (${pr.html_url})`).join("\n"); |
| 52 | + core.setFailed( |
| 53 | + `An open release PR already exists. Close or merge it before creating a new one:\n${existing}` |
| 54 | + ); |
| 55 | + } |
| 56 | +
|
| 57 | + - name: Checkout Repo |
| 58 | + uses: actions/checkout@v4 |
| 59 | + with: |
| 60 | + fetch-depth: 0 |
| 61 | + token: ${{ secrets.GITHUB_TOKEN }} |
| 62 | + |
| 63 | + - name: Setup pnpm |
| 64 | + uses: pnpm/action-setup@v4 |
| 65 | + with: |
| 66 | + version: "10.13.1" |
| 67 | + |
| 68 | + - name: Setup Node |
| 69 | + uses: actions/setup-node@v4 |
| 70 | + with: |
| 71 | + node-version: 20.x |
| 72 | + |
| 73 | + - name: Install Dependencies |
| 74 | + run: pnpm install --frozen-lockfile |
| 75 | + |
| 76 | + - name: Prepare release |
| 77 | + id: prepare |
| 78 | + run: | |
| 79 | + if [ "${{ inputs.dry_run }}" == "true" ]; then |
| 80 | + pnpm tsx scripts/release/prepare-release.ts --bump ${{ inputs.bump }} --dry-run |
| 81 | + else |
| 82 | + pnpm tsx scripts/release/prepare-release.ts --bump ${{ inputs.bump }} |
| 83 | + fi |
| 84 | +
|
| 85 | + - name: Generate AI release notes |
| 86 | + if: inputs.dry_run != true |
| 87 | + id: ai_notes |
| 88 | + run: pnpm tsx scripts/release/generate-ai-release-notes.ts "${{ steps.prepare.outputs.version }}" |
| 89 | + env: |
| 90 | + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} |
| 91 | + NOTION_API_KEY: ${{ secrets.NOTION_API_KEY }} |
| 92 | + NOTION_RELEASE_NOTES_PAGE: ${{ secrets.NOTION_RELEASE_NOTES_PAGE }} |
| 93 | + |
| 94 | + - name: Create release PR |
| 95 | + if: inputs.dry_run != true |
| 96 | + id: create_pr |
| 97 | + uses: peter-evans/create-pull-request@v8 |
| 98 | + with: |
| 99 | + token: ${{ secrets.GITHUB_TOKEN }} |
| 100 | + branch: release/publish/v${{ steps.prepare.outputs.version }} |
| 101 | + delete-branch: true |
| 102 | + commit-message: "chore: release v${{ steps.prepare.outputs.version }}" |
| 103 | + title: "chore: release v${{ steps.prepare.outputs.version }}" |
| 104 | + body: | |
| 105 | + ## Release v${{ steps.prepare.outputs.version }} |
| 106 | +
|
| 107 | + **Bump:** `${{ inputs.bump }}` |
| 108 | +
|
| 109 | + --- |
| 110 | +
|
| 111 | + ### How this release process works |
| 112 | +
|
| 113 | + 1. **This PR was created automatically** by the "Create Release PR" workflow. |
| 114 | + It bumped all `@copilotkit/*` package versions to `${{ steps.prepare.outputs.version }}` |
| 115 | + and generated AI-enhanced release notes. |
| 116 | +
|
| 117 | + 2. **CI runs on this PR** — the full test suite (unit tests, lint, type checks, build) |
| 118 | + must pass before merging. This is the review gate. |
| 119 | +
|
| 120 | + 3. **Review the release notes** in `release-notes.md` in this PR. |
| 121 | + If a Notion draft was created, you can edit the release notes there before merging. |
| 122 | +
|
| 123 | + 4. **When this PR is merged**, the `publish-release` workflow automatically: |
| 124 | + - Builds all packages |
| 125 | + - Publishes all `@copilotkit/*` packages to npm at version `${{ steps.prepare.outputs.version }}` |
| 126 | + - Creates git tag `v${{ steps.prepare.outputs.version }}` |
| 127 | + - Creates a GitHub Release with the final release notes |
| 128 | +
|
| 129 | + ### Before merging |
| 130 | +
|
| 131 | + - [ ] CI is green (tests, lint, types, build) |
| 132 | + - [ ] Version bumps look correct |
| 133 | + - [ ] Release notes are accurate (edit in Notion if a draft was created) |
| 134 | +
|
| 135 | + --- |
| 136 | +
|
| 137 | + > **Do not merge until CI is fully green.** The full test suite runs automatically on this PR. |
| 138 | + labels: release |
| 139 | + |
| 140 | + - name: Comment Notion link on PR |
| 141 | + if: inputs.dry_run != true && steps.ai_notes.outputs.notion_url |
| 142 | + uses: actions/github-script@v7 |
| 143 | + env: |
| 144 | + NOTION_URL: ${{ steps.ai_notes.outputs.notion_url }} |
| 145 | + PR_NUMBER: ${{ steps.create_pr.outputs.pull-request-number }} |
| 146 | + with: |
| 147 | + github-token: ${{ secrets.GITHUB_TOKEN }} |
| 148 | + script: | |
| 149 | + const { owner, repo } = context.repo; |
| 150 | + const prNumber = parseInt(process.env.PR_NUMBER, 10); |
| 151 | + const notionUrl = process.env.NOTION_URL; |
| 152 | +
|
| 153 | + if (prNumber && notionUrl) { |
| 154 | + await github.rest.issues.createComment({ |
| 155 | + owner, |
| 156 | + repo, |
| 157 | + issue_number: prNumber, |
| 158 | + body: `📝 **Release notes draft:** ${notionUrl}\n\nYou can edit the release notes in Notion before merging. The final content will be used for the GitHub Release.`, |
| 159 | + }); |
| 160 | + } |
0 commit comments