Skip to content

Version Bump

Version Bump #298

Workflow file for this run

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}`);
}