Version Bump #298
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: 'Version Bump' | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| release_line: | |
| description: 'Release Line' | |
| required: true | |
| default: main | |
| type: choice | |
| options: | |
| - next | |
| - main | |
| workspace_input: | |
| description: 'Workspace (this must be a JSON array)' | |
| required: true | |
| type: string | |
| version-bump-type: | |
| description: 'Specifies the type of version update to apply.' | |
| required: true | |
| default: 'minor' | |
| type: choice | |
| options: | |
| - 'major' | |
| - 'minor' | |
| - 'patch' | |
| enable_pr_reuse: | |
| description: 'Enable PR reuse and branch renaming (experimental)' | |
| required: true | |
| default: false | |
| type: boolean | |
| concurrency: | |
| group: version-bump-${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| version-bump: | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| workspace: ${{ fromJSON(inputs.workspace_input) }} | |
| name: ${{ matrix.workspace }} version:bump | |
| steps: | |
| - name: 'Checkout community-plugins' | |
| uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4 | |
| with: | |
| fetch-depth: 1 | |
| # Beginning of yarn setup | |
| - name: use node.js 20.x | |
| uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4 | |
| with: | |
| node-version: 20.x | |
| registry-url: https://registry.npmjs.org/ # Needed for auth | |
| - name: cache all node_modules | |
| id: cache-modules | |
| uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 | |
| with: | |
| path: '**/node_modules' | |
| key: ${{ runner.os }}-node_modules-${{ hashFiles('yarn.lock', '**/package.json') }} | |
| - name: find location of global yarn cache | |
| id: yarn-cache | |
| if: steps.cache-modules.outputs.cache-hit != 'true' | |
| run: echo "dir=$(yarn config get cacheFolder)" >> $GITHUB_OUTPUT | |
| - name: cache global yarn cache | |
| uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 | |
| if: steps.cache-modules.outputs.cache-hit != 'true' | |
| with: | |
| path: ${{ steps.yarn-cache.outputs.dir }} | |
| key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }} | |
| restore-keys: | | |
| ${{ runner.os }}-yarn- | |
| - name: yarn install | |
| run: yarn install --immutable | |
| - name: workspace yarn install | |
| working-directory: ./workspaces/${{ matrix.workspace }} | |
| run: yarn install --immutable | |
| # End of yarn setup | |
| - name: 'Set release name' | |
| id: set_release_name | |
| run: node scripts/ci/set-release-name.js ${{ matrix.workspace }} ${{ inputs.release_line || 'main' }} | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| - name: 'Check current and release versions' | |
| id: check | |
| run: | | |
| if [[ "${{ steps.set_release_name.outputs.release_version }}" == "${{ steps.set_release_name.outputs.current_version }}" ]]; then | |
| echo "Backstage release version and current workspace version are the same, skipping version bump" | |
| exit 1 # Non-zero exit code fails the step and job | |
| fi | |
| - name: 'Check for existing version bump PR' | |
| id: check_existing_pr | |
| if: ${{ inputs.enable_pr_reuse }} | |
| uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7 | |
| with: | |
| github-token: ${{secrets.GH_SERVICE_ACCOUNT_TOKEN}} | |
| script: | | |
| const workspace = '${{ matrix.workspace }}'; | |
| const allOpenPRs = await github.paginate(github.rest.pulls.list, { | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| state: 'open', | |
| base: 'main', | |
| per_page: 100 | |
| }); | |
| const workspacePRs = allOpenPRs.filter(pr => | |
| pr.title.startsWith(`${workspace} - version:bump`) && | |
| pr.head.ref.startsWith(`${workspace}/`) | |
| ); | |
| if (workspacePRs.length > 0) { | |
| workspacePRs.sort((a, b) => | |
| new Date(b.created_at) - new Date(a.created_at) | |
| ); | |
| const existingPR = workspacePRs[0]; | |
| console.log(`Found ${workspacePRs.length} existing PR(s) for ${workspace}`); | |
| console.log(`Using most recently created PR #${existingPR.number}: ${existingPR.title}`); | |
| console.log(`Will reuse branch: ${existingPR.head.ref}`); | |
| core.setOutput('branch_name', existingPR.head.ref); | |
| core.setOutput('pr_number', existingPR.number); | |
| core.setOutput('has_existing_pr', 'true'); | |
| } else { | |
| console.log('No existing version bump PR found, will create new branch'); | |
| const newBranch = `${workspace}/v${{ steps.set_release_name.outputs.release_version }}`; | |
| core.setOutput('branch_name', newBranch); | |
| core.setOutput('has_existing_pr', 'false'); | |
| } | |
| - name: 'Configure git' | |
| run: | | |
| git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com" | |
| git config --global user.name "github-actions[bot]" | |
| - name: 'Create or checkout workspace branch' | |
| run: | | |
| git config --global --add safe.directory "$GITHUB_WORKSPACE" | |
| if [ "${{ inputs.enable_pr_reuse }}" == "true" ]; then | |
| BRANCH_NAME="${{ steps.check_existing_pr.outputs.branch_name }}" | |
| else | |
| BRANCH_NAME="${{ matrix.workspace }}/v${{ steps.set_release_name.outputs.release_version }}" | |
| fi | |
| BRANCH=$(git branch --list "$BRANCH_NAME") | |
| if [ ! -z "$BRANCH" ]; then | |
| git branch -D "$BRANCH_NAME" | |
| fi | |
| if [ "${{ inputs.enable_pr_reuse }}" == "true" ] && git ls-remote --exit-code --heads origin "$BRANCH_NAME" > /dev/null 2>&1; then | |
| echo "Remote branch exists, checking it out" | |
| git fetch origin "$BRANCH_NAME" | |
| git checkout -b "$BRANCH_NAME" "origin/$BRANCH_NAME" | |
| echo "Re-installing workspace dependencies..." | |
| cd workspaces/${{ matrix.workspace }} | |
| yarn install --immutable | |
| else | |
| echo "Creating new branch" | |
| git checkout -b "$BRANCH_NAME" | |
| fi | |
| - name: Run backstage-cli versions:bump --release ${{ inputs.release_line || 'main' }} command | |
| working-directory: ./workspaces/${{ matrix.workspace }} | |
| run: yarn backstage-cli versions:bump --release ${{ inputs.release_line || 'main' }} | |
| env: | |
| YARN_ENABLE_IMMUTABLE_INSTALLS: false | |
| - name: Run dedupe | |
| working-directory: ./workspaces/${{ matrix.workspace }} | |
| run: yarn dedupe | |
| - name: 'Check for changes' | |
| id: check_for_changes | |
| run: | | |
| CHANGES=$(git status --porcelain) | |
| if [ -z "$CHANGES" ]; then | |
| echo "No changes" | |
| echo "HAS_CHANGES=0" >> $GITHUB_OUTPUT | |
| else | |
| echo "Has changes" | |
| echo "HAS_CHANGES=1" >> $GITHUB_OUTPUT | |
| fi | |
| - name: 'Add changeset' | |
| if: ${{ steps.check_for_changes.outputs.HAS_CHANGES == 1 }} | |
| working-directory: ./workspaces/${{ matrix.workspace }} | |
| run: node ../../scripts/ci/generate-version-bump-changeset.js ${{ steps.set_release_name.outputs.release_version }} ${{ inputs.version-bump-type || 'minor' }} | |
| - name: Run prettier:fix | |
| working-directory: ./workspaces/${{ matrix.workspace }} | |
| run: yarn prettier:fix | |
| - name: 'Commit changes' | |
| if: ${{ steps.check_for_changes.outputs.HAS_CHANGES == 1 }} | |
| run: | | |
| git status | |
| git add -A -- :!.npmrc | |
| git commit -m "v${{ steps.set_release_name.outputs.release_version }} version bump" | |
| if [ "${{ inputs.enable_pr_reuse }}" == "true" ]; then | |
| BRANCH_NAME="${{ steps.check_existing_pr.outputs.branch_name }}" | |
| else | |
| BRANCH_NAME="${{ matrix.workspace }}/v${{ steps.set_release_name.outputs.release_version }}" | |
| fi | |
| git push origin "$BRANCH_NAME" | |
| - name: 'Create or update Pull Request' | |
| if: ${{ steps.check_for_changes.outputs.HAS_CHANGES == 1 }} | |
| uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7 | |
| with: | |
| github-token: ${{secrets.GH_SERVICE_ACCOUNT_TOKEN}} | |
| script: | | |
| const workspace = '${{ matrix.workspace }}'; | |
| const releaseVersion = '${{ steps.set_release_name.outputs.release_version }}'; | |
| const currentVersion = '${{ steps.set_release_name.outputs.current_version }}'; | |
| const enablePRReuse = ${{ inputs.enable_pr_reuse }}; | |
| let branchName, hasExistingPR; | |
| if (enablePRReuse) { | |
| branchName = '${{ steps.check_existing_pr.outputs.branch_name }}'; | |
| hasExistingPR = '${{ steps.check_existing_pr.outputs.has_existing_pr }}' === 'true'; | |
| } else { | |
| branchName = `${workspace}/v${releaseVersion}`; | |
| hasExistingPR = false; | |
| } | |
| const prBody = [ | |
| 'Backstage release v' + releaseVersion + ' has been published, this Pull Request contains the changes to upgrade ' + workspace + ' to this new release', | |
| ' ', | |
| 'Please review the changelog before approving, there may be manual changes needed:', | |
| ' ', | |
| '- Changelog: [v' + releaseVersion + '](https://github.com/backstage/backstage/blob/master/docs/releases/v' + releaseVersion + '-changelog.md)', | |
| '- Upgrade Helper: [From ' + currentVersion + ' to ' + releaseVersion + '](https://backstage.github.io/upgrade-helper/?from=' + currentVersion + '&to=' + releaseVersion + ')', | |
| ' ', | |
| 'Created by [Version Bump ${{ github.run_id }}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})', | |
| ' ' | |
| ].join('\n'); | |
| if (hasExistingPR) { | |
| const prNumber = parseInt('${{ steps.check_existing_pr.outputs.pr_number }}'); | |
| console.log(`Updating existing PR #${prNumber} to v${releaseVersion}`); | |
| const { data: existingPRData } = await github.rest.pulls.get({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| pull_number: prNumber | |
| }); | |
| const updatedTitle = workspace + ' - version:bump to v' + releaseVersion; | |
| const updatedBody = prBody + '\n\n---\n\n<details>\n<summary>Previous version bump information</summary>\n\n' + existingPRData.body + '\n\n</details>'; | |
| await github.rest.pulls.update({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| pull_number: prNumber, | |
| title: updatedTitle, | |
| body: updatedBody | |
| }); | |
| const previousVersionMatch = existingPRData.title.match(/version:bump to v([\d.]+)/); | |
| const previousVersion = previousVersionMatch ? previousVersionMatch[1] : 'unknown'; | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: prNumber, | |
| body: `Updated version bump to v${releaseVersion} (previously v${previousVersion}).\n\nThe branch has been updated with the latest changes. Please review the updated changelog and upgrade helper links in the PR description.` | |
| }); | |
| console.log(`Successfully updated PR #${prNumber} to v${releaseVersion}`); | |
| } else { | |
| const { data: newPR } = await github.rest.pulls.create({ | |
| title: workspace + ' - version:bump to v' + releaseVersion, | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| head: branchName, | |
| base: 'main', | |
| body: prBody | |
| }); | |
| console.log(`Created new PR #${newPR.number}`); | |
| } |