Skip to content

Commit ce32be7

Browse files
committed
Copy version bumper over from inspec/github-actions-testing
Signed-off-by: Clinton Wolfe <156460+clintoncwolfe@users.noreply.github.com>
1 parent 3350047 commit ce32be7

1 file changed

Lines changed: 257 additions & 0 deletions

File tree

Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
name: Version Bumper
2+
3+
description: |
4+
A reusable GitHub Action to automatically bump the version of a project based on pull request labels.
5+
It uses the `bump` gem to handle versioning and tagging.
6+
7+
This action is triggered when called by another workflow after a pull request is merged into the main branch.
8+
It determines the version bump type (major, minor, patch) based on the labels applied to the pull request.
9+
The action will automatically commit the version bump and create a new tag for the release.
10+
11+
By default, it will bump the patch version unless specified otherwise. Use Labels on your PR
12+
to control behavior. Define the 'Version Bump: Major', 'Version Bump: Minor',
13+
and "Version Bump: Skip" labels in your repository to use this action effectively.
14+
15+
The action updates version strings in the files specified by the `version_file_patterns` input parameter.
16+
You may use glob patterns. Files that do not exist will be ignored with a warning.
17+
18+
Example usage:
19+
```yaml
20+
uses: ./.github/workflows/version-bumper.yml
21+
with:
22+
version_file_patterns: |
23+
lib/*/version.rb
24+
package.json
25+
inspec.yml
26+
```
27+
28+
on:
29+
workflow_call:
30+
inputs:
31+
version_file_patterns:
32+
description: 'List of file patterns to search for version strings (one pattern per line)'
33+
required: false
34+
type: string
35+
default: 'lib/*/version.rb'
36+
target_branch:
37+
description: 'The branch to push version changes to'
38+
required: false
39+
default: 'main'
40+
type: string
41+
git_username:
42+
description: 'Git username for commits'
43+
required: false
44+
default: 'Progress CI Automation'
45+
type: string
46+
git_email:
47+
description: 'Git email for commits'
48+
required: false
49+
default: 'ci@progress.com'
50+
type: string
51+
outputs:
52+
bump_level:
53+
description: 'The level that was bumped (major, minor, patch, or skipped)'
54+
value: ${{ jobs.bump-version.outputs.bump_level }}
55+
was_skipped:
56+
description: 'Whether the version bump was skipped'
57+
value: ${{ jobs.bump-version.outputs.was_skipped }}
58+
new_version:
59+
description: 'The new version after bumping'
60+
value: ${{ jobs.bump-version.outputs.new_version }}
61+
pr_number:
62+
description: 'The PR number that was processed'
63+
value: ${{ jobs.bump-version.outputs.pr_number }}
64+
65+
jobs:
66+
bump-version:
67+
runs-on: ubuntu-latest
68+
outputs:
69+
bump_level: ${{ steps.bump-type.outputs.level || 'skipped' }}
70+
was_skipped: ${{ steps.bump-type.outputs.skip }}
71+
new_version: ${{ steps.run-bump.outputs.new_version || '' }}
72+
pr_number: ${{ steps.get-pr-number.outputs.pr_number }}
73+
permissions:
74+
contents: write
75+
pull-requests: read
76+
77+
steps:
78+
- name: Checkout code
79+
uses: actions/checkout@v4
80+
with:
81+
fetch-depth: 0
82+
83+
- name: Set up Git
84+
run: |
85+
git config --global user.name "${{ inputs.git_username }}"
86+
git config --global user.email "${{ inputs.git_email }}"
87+
88+
- name: Set up Ruby
89+
uses: ruby/setup-ruby@v1
90+
with:
91+
ruby-version: 'ruby'
92+
bundler-cache: true
93+
94+
- name: Install bump gem
95+
run: gem install bump
96+
97+
- name: Get PR number from context
98+
id: get-pr-number
99+
uses: actions/github-script@v7
100+
with:
101+
script: |
102+
// Check if we're running in the context of a PR event
103+
if (!context.payload.pull_request) {
104+
core.setFailed('This workflow must be run in a PR context');
105+
throw new Error('Not running in PR context');
106+
}
107+
108+
const prNumber = context.payload.pull_request.number;
109+
console.log(`Detected PR number from event context: ${prNumber}`);
110+
111+
if (!prNumber) {
112+
core.setFailed('Could not detect PR number from context');
113+
throw new Error('Failed to detect PR number');
114+
}
115+
116+
core.setOutput('pr_number', prNumber.toString());
117+
return prNumber.toString();
118+
result-encoding: string
119+
120+
- name: Fetch PR labels
121+
id: fetch-labels
122+
uses: actions/github-script@v7
123+
with:
124+
script: |
125+
const prNumber = parseInt(${{ steps.get-pr-number.outputs.pr_number }});
126+
127+
try {
128+
const { data: pr } = await github.rest.pulls.get({
129+
owner: context.repo.owner,
130+
repo: context.repo.repo,
131+
pull_number: prNumber
132+
});
133+
134+
const labelNames = pr.labels.map(label => label.name);
135+
console.log(`PR #${prNumber} has labels:`, labelNames);
136+
return JSON.stringify(labelNames);
137+
} catch (error) {
138+
console.log(`Error fetching PR #${prNumber}: ${error.message}`);
139+
core.setFailed(`Failed to fetch PR #${prNumber}: ${error.message}`);
140+
throw new Error(`Failed to fetch PR #${prNumber}`);
141+
}
142+
result-encoding: string
143+
144+
- name: Determine bump type from labels
145+
id: bump-type
146+
run: |
147+
# Store the labels in a variable and properly parse JSON format
148+
LABELS='${{ steps.fetch-labels.outputs.result }}'
149+
150+
# First check if we should skip the version bump entirely
151+
if echo "$LABELS" | grep -q "Version Bump: Skip"; then
152+
echo "skip=true" >> $GITHUB_OUTPUT
153+
echo "Found label: Version Bump: Skip - will skip version bump"
154+
exit 0
155+
fi
156+
157+
# Check for other version bump labels
158+
if echo "$LABELS" | grep -q "Version Bump: Major"; then
159+
echo "level=major" >> $GITHUB_OUTPUT
160+
echo "Found label: Version Bump: Major"
161+
elif echo "$LABELS" | grep -q "Version Bump: Minor"; then
162+
echo "level=minor" >> $GITHUB_OUTPUT
163+
echo "Found label: Version Bump: Minor"
164+
else
165+
echo "level=patch" >> $GITHUB_OUTPUT
166+
echo "No specific version bump label found, defaulting to patch"
167+
fi
168+
169+
echo "skip=false" >> $GITHUB_OUTPUT
170+
echo "Determined bump level: $(cat $GITHUB_OUTPUT | grep level | cut -d= -f2)"
171+
172+
- name: Build list of files to update
173+
id: file-list
174+
if: steps.bump-type.outputs.skip != 'true'
175+
run: |
176+
BUMP_FILE_ARGS=""
177+
178+
# Process each pattern from the input
179+
echo "Processing file patterns from input"
180+
echo "${{ inputs.version_file_patterns }}" | while IFS= read -r file_pattern || [ -n "$file_pattern" ]; do
181+
# Skip empty lines and comments
182+
if [ -z "$file_pattern" ] || [[ "$file_pattern" == \#* ]]; then
183+
continue
184+
fi
185+
186+
# Trim whitespace
187+
file_pattern=$(echo "$file_pattern" | xargs)
188+
echo " - Processing pattern: $file_pattern"
189+
190+
# Expand the glob pattern to actual files
191+
matched_files=$(find . -path "./$file_pattern" 2>/dev/null || echo "")
192+
193+
if [ -z "$matched_files" ]; then
194+
# Check if it's a literal file that exists
195+
if [ -f "$file_pattern" ]; then
196+
echo " - Found literal file: $file_pattern"
197+
BUMP_FILE_ARGS+=" --replace-in $file_pattern"
198+
else
199+
echo " - WARNING: No files matched pattern '$file_pattern' and file does not exist"
200+
echo " - Skipping this pattern as bump cannot process non-existent files"
201+
fi
202+
else
203+
# Add each matched file individually after verifying it exists
204+
while IFS= read -r file; do
205+
if [ -n "$file" ]; then
206+
# Remove leading ./ from the file path
207+
relative_file="${file#./}"
208+
209+
# Double-check the file exists (should always be true from find)
210+
if [ -f "$relative_file" ]; then
211+
echo " - Found file: $relative_file"
212+
BUMP_FILE_ARGS+=" --replace-in $relative_file"
213+
else
214+
echo " - WARNING: File '$relative_file' was found by glob but doesn't exist - skipping"
215+
fi
216+
fi
217+
done <<< "$matched_files"
218+
fi
219+
done
220+
221+
echo "file_args=$BUMP_FILE_ARGS" >> $GITHUB_OUTPUT
222+
223+
- name: Run version bump with custom files
224+
id: run-bump
225+
if: steps.bump-type.outputs.skip != 'true'
226+
run: |
227+
BUMP_LEVEL="${{ steps.bump-type.outputs.level }}"
228+
FILE_ARGS="${{ steps.file-list.outputs.file_args }}"
229+
230+
# Build the bump command with the level and any file arguments
231+
BUMP_COMMAND="bump $BUMP_LEVEL --tag $FILE_ARGS"
232+
233+
echo "Executing: $BUMP_COMMAND"
234+
# Execute the bump command (bump will handle committing and tagging)
235+
OUTPUT=$(eval $BUMP_COMMAND)
236+
echo "$OUTPUT"
237+
238+
# Extract the new version number from the output
239+
NEW_VERSION=$(echo "$OUTPUT" | grep -o "to [0-9]\+\.[0-9]\+\.[0-9]\+" | cut -d' ' -f2)
240+
echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT
241+
echo "Bumped to version: $NEW_VERSION"
242+
env:
243+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
244+
245+
- name: Push changes to target branch
246+
if: steps.bump-type.outputs.skip != 'true'
247+
run: |
248+
# Bump has already committed and tagged, we just need to push
249+
git push origin HEAD:${{ inputs.target_branch }}
250+
git push origin --tags
251+
env:
252+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
253+
254+
- name: Log skipped version bump
255+
if: steps.bump-type.outputs.skip == 'true'
256+
run: |
257+
echo "Version bump was skipped due to the 'Version Bump: Skip' label"

0 commit comments

Comments
 (0)