Skip to content

Commit c66f261

Browse files
authored
Add a release workflow (#2125)
This new workflow will enable people working in the collector repo to simply create a new release by issuing a workflow from the github actions tab. Two inputs can be provided to the workflow: - version: Specify the <Major>.<minor> version to be tagged. - dry-run: Run the workflow, create all tags but don't push to origin. The intended usage is to make a first run with the dry-run flag on to check everything works as expected, then make a second run without the dry-run flag to create the release tags. If the version input is left unchanged a new minor version will be tagged, creating the <Major>.<minor>.x tag on the master branch, setting up the release-<Major>.<minor> branch with an empty commit and the new .0 tag on top of said commit. If a version is provided the behavior depends on what the actual value is. The workflow will check if the behavior intended by the caller is to do a major, minor or patch release by checking the provided version against the latest tag on master. In all cases, the workflow ensures the release will always have the correct version numbers.
1 parent 2551e89 commit c66f261

File tree

2 files changed

+260
-3
lines changed

2 files changed

+260
-3
lines changed

.github/workflows/release.yml

Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
name: Tag a new release
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
version:
7+
description: |
8+
The release version in <Major>.<minor> format.
9+
0.0 means new minor version on the latest major version.
10+
default: '0.0'
11+
type: string
12+
dry-run:
13+
description: Do not push anything
14+
default: true
15+
type: boolean
16+
17+
jobs:
18+
determine-version:
19+
runs-on: ubuntu-24.04
20+
21+
outputs:
22+
major: ${{ steps.final-values.outputs.major }}
23+
minor: ${{ steps.final-values.outputs.minor }}
24+
patch: ${{ steps.patch-version.outputs.value || '0' }}
25+
release-type: ${{ steps.final-values.outputs.type }}
26+
27+
steps:
28+
- uses: actions/checkout@v4
29+
with:
30+
submodules: false
31+
fetch-depth: 0
32+
33+
- name: Parse required release
34+
id: required-release
35+
run: |
36+
if [[ "${{ inputs.version }}" =~ ^([[:digit:]]+)\.([[:digit:]]+)$ ]]; then
37+
echo "major=${BASH_REMATCH[1]}" >> "$GITHUB_OUTPUT"
38+
echo "minor=${BASH_REMATCH[2]}" >> "$GITHUB_OUTPUT"
39+
else
40+
echo >&2 "Invalid version ${{ inputs.version }}. The expected format is <Major>.<minor>"
41+
exit 1
42+
fi
43+
44+
- name: Get closest tag to master
45+
id: latest-tag
46+
env:
47+
REQUIRED_MAJOR: ${{ steps.required-release.outputs.major }}
48+
run: |
49+
tag=(0 0)
50+
while read -r line; do
51+
if [[ "$line" =~ ^([[:digit:]]+)\.([[:digit:]]+)\.x$ ]]; then
52+
# If we are doing a release for a specific major
53+
# version, we want to limit ourselves to that, so we
54+
# ignore newer major versions.
55+
if ((tag[0] < BASH_REMATCH[1] && (REQUIRED_MAJOR == 0 || REQUIRED_MAJOR >= BASH_REMATCH[1]))); then
56+
tag=("${BASH_REMATCH[1]}" "${BASH_REMATCH[2]}")
57+
elif ((tag[0] == BASH_REMATCH[1] && tag[1] < BASH_REMATCH[2])); then
58+
tag=("${BASH_REMATCH[1]}" "${BASH_REMATCH[2]}")
59+
fi
60+
fi
61+
done < <(git tag --merged)
62+
63+
echo "major=${tag[0]}" >> "$GITHUB_OUTPUT"
64+
echo "minor=${tag[1]}" >> "$GITHUB_OUTPUT"
65+
66+
- name: Determine release type and version
67+
id: final-values
68+
env:
69+
LATEST_MAJOR: ${{ steps.latest-tag.outputs.major }}
70+
LATEST_MINOR: ${{ steps.latest-tag.outputs.minor }}
71+
REQUIRED_MAJOR: ${{ steps.required-release.outputs.major }}
72+
REQUIRED_MINOR: ${{ steps.required-release.outputs.minor }}
73+
run: |
74+
function add_outputs() {
75+
cat << EOF >> "$GITHUB_OUTPUT"
76+
major=$1
77+
minor=$2
78+
type=$3
79+
EOF
80+
}
81+
82+
if ((REQUIRED_MAJOR==0)); then
83+
add_outputs "${LATEST_MAJOR}" "$((LATEST_MINOR+1))" "minor"
84+
elif ((REQUIRED_MAJOR > LATEST_MAJOR)); then
85+
add_outputs "$((LATEST_MAJOR+1))" "0" "major"
86+
elif ((REQUIRED_MAJOR == LATEST_MAJOR && REQUIRED_MINOR > LATEST_MINOR)); then
87+
add_outputs "${LATEST_MAJOR}" "$((LATEST_MINOR+1))" "minor"
88+
else
89+
add_outputs "${REQUIRED_MAJOR}" "${REQUIRED_MINOR}" "patch"
90+
fi
91+
92+
- name: Get patch version
93+
id: patch-version
94+
if: steps.final-values.outputs.type == 'patch'
95+
env:
96+
MAJOR: ${{ steps.final-values.outputs.major }}
97+
MINOR: ${{ steps.final-values.outputs.minor }}
98+
run: |
99+
git checkout "release-${MAJOR}.${MINOR}"
100+
git pull --ff-only
101+
102+
patch=0
103+
while read -r line; do
104+
if [[ "$line" =~ ^${MAJOR}.${MINOR}.([[:digit:]]+)$ ]]; then
105+
if ((BASH_REMATCH[1] > patch)); then
106+
patch="${BASH_REMATCH[1]}"
107+
fi
108+
fi
109+
done < <(git tag --merged)
110+
111+
echo "value=$((patch+1))" >> "$GITHUB_OUTPUT"
112+
113+
- name: Notify tags and branches
114+
env:
115+
MAJOR: ${{ steps.final-values.outputs.major }}
116+
MINOR: ${{ steps.final-values.outputs.minor }}
117+
PATCH: ${{ steps.patch-version.outputs.value || '0' }}
118+
RELEASE_TYPE: ${{ steps.final-values.outputs.type }}
119+
run: |
120+
function notice() {
121+
echo "::notice title=$1:: $2"
122+
}
123+
124+
BRANCH="master"
125+
if [[ "${RELEASE_TYPE}" == "patch" ]]; then
126+
BRANCH="release-${MAJOR}.${MINOR}"
127+
fi
128+
129+
notice "Release type" "${RELEASE_TYPE}"
130+
notice "Tag" "${MAJOR}.${MINOR}.${PATCH}"
131+
notice "Base branch" "${BRANCH}"
132+
if [[ "${BRANCH}" == "master" ]]; then
133+
notice "Master tag" "${MAJOR}.${MINOR}.x"
134+
notice "Release branch" "release-${MAJOR}.${MINOR}"
135+
fi
136+
137+
- name: Mismatched versions
138+
if: steps.required-release.outputs.major != 0 && (
139+
steps.required-release.outputs.major != steps.final-values.outputs.major ||
140+
steps.required-release.outputs.minor != steps.final-values.outputs.minor
141+
)
142+
env:
143+
REQUIRED_MAJOR: ${{ steps.required-release.outputs.major }}
144+
REQUIRED_MINOR: ${{ steps.required-release.outputs.minor }}
145+
CALCULATED_MAJOR: ${{ steps.final-values.outputs.major }}
146+
CALCULATED_MINOR: ${{ steps.final-values.outputs.minor }}
147+
run: |
148+
cat << EOF >&2
149+
::error title='Version mismatch'::The required version did not match the one calculated. REQUIRED: ${REQUIRED_MAJOR}.${REQUIRED_MINOR}, GOT: ${CALCULATED_MAJOR}.${CALCULATED_MINOR}
150+
151+
Please review the input and retrigger the workflow.
152+
EOF
153+
154+
# Fail the workflow
155+
exit 1
156+
157+
release:
158+
runs-on: ubuntu-24.04
159+
if: ${{ !inputs.dry-run }}
160+
needs:
161+
- determine-version
162+
env:
163+
RELEASE: ${{ needs.determine-version.outputs.major }}.${{ needs.determine-version.outputs.minor }}
164+
RELEASE_TYPE: ${{ needs.determine-version.outputs.release-type }}
165+
PATCH: ${{ needs.determine-version.outputs.patch }}
166+
167+
steps:
168+
- uses: actions/checkout@v4
169+
with:
170+
submodules: false
171+
fetch-depth: 0
172+
173+
- name: Initialize mandatory git config
174+
run: |
175+
git config user.name "${{ github.event.sender.login }}"
176+
git config user.email noreply@github.com
177+
178+
- name: Create release branch
179+
if: needs.determine-version.outputs.release-type != 'patch'
180+
run: |
181+
git checkout master
182+
git pull --ff-only
183+
git tag "${RELEASE}.x"
184+
git checkout -b "release-${RELEASE}"
185+
git commit --no-verify --allow-empty -m "Empty commit to diverge ${RELEASE} from master"
186+
187+
- name: Push release branch
188+
if: needs.determine-version.outputs.release-type != 'patch'
189+
run: |
190+
git push origin "${RELEASE}.x"
191+
git push --set-upstream origin "release-${RELEASE}"
192+
193+
- name: Create release tag
194+
run: |
195+
git checkout "release-${RELEASE}"
196+
if [[ "${RELEASE_TYPE}" == "patch" ]]; then
197+
git pull --ff-only
198+
fi
199+
git tag "${RELEASE}.${PATCH}"
200+
201+
- name: Push release tag
202+
run: |
203+
git push origin "${RELEASE}.${PATCH}"
204+
205+
- name: Create tag in falcosecurity-libs
206+
run: |
207+
git submodule update --init falcosecurity-libs
208+
cd falcosecurity-libs/
209+
git tag "${RELEASE}.${PATCH}"
210+
211+
- name: Push tag in falcosecurity-libs
212+
run: |
213+
cd falcosecurity-libs/
214+
git push origin "${RELEASE}.${PATCH}"
215+
216+
- name: Send message to slack
217+
uses: rtCamp/action-slack-notify@v2
218+
env:
219+
SLACK_WEBHOOK: ${{ secrets.SLACK_COLLECTOR_ONCALL_WEBHOOK }}
220+
SLACK_CHANNEL: team-acs-collector-oncall
221+
SLACK_COLOR: success
222+
SLACK_LINK_NAMES: true
223+
SLACK_TITLE: "New release tagged"
224+
SLACKIFY_MARKDOWN: true
225+
MSG_MINIMAL: true
226+
SLACK_MESSAGE: |
227+
@acs-collector-oncall a new release has just been triggered
228+
with the following values:
229+
230+
| Name | Value |
231+
| --- | --- |
232+
| Version | ${{ env.RELEASE }}.${{ env.PATCH }} |
233+
| Release Type | ${{ env.RELEASE_TYPE }} |

docs/release.md

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,38 @@
11
# Release Process
22

33

4-
### Considerations
4+
## Considerations
55

66
- All tags created during a release should *not* be annotated. The ref to an
77
annotated tag (e.g. `refs/tags/tagname`) does not refer to the tagged commit,
88
instead referring to a non-commit object representing the annoation. This can
99
cause complications in CI builds on remote VMs.
1010

11-
**Create the collector image release branch**
11+
## Automated release
12+
13+
A workflow for automated releases can be found in the 'Actions' tab of
14+
GitHub. Once in said tab, look for the `Tag a new release` workflow in
15+
the side bar, select it and use the `Run workflow` button on the far
16+
right to trigger the tagging process, setting the `<Major>.<minor>`
17+
version for the release in the menu that pops up or leaving it as 0.0.
18+
The workflow will check the version input and adjust the major, minor
19+
and patch versions to be used before creating any necessary branches
20+
and tags. If left with the default value of 0.0, the workflow will
21+
create a new minor release.
22+
23+
The recommended workflow is to first run in dry-mode and check the tags
24+
and branches that will be used are correct in the `Summary` section of
25+
the triggered workflow, then run it again without dry-mode to create
26+
the actual release. With the tag pushed, the workflow for creating the
27+
new version of collector should be triggered on its own.
28+
29+
## Manual release
30+
31+
**Note**: This release process should only be used if the automated
32+
process fails.
33+
---
34+
35+
### Create the collector image release branch
1236

1337
1. Navigate to the local stackrox/collector git repository directory on the master branch and ensure the local checked out version is up to date.
1438

@@ -62,7 +86,7 @@ git tag "${COLLECTOR_RELEASE}.${COLLECTOR_PATCH_NUMBER}"
6286
git push origin "${COLLECTOR_RELEASE}.${COLLECTOR_PATCH_NUMBER}"
6387
```
6488

65-
**Patch releases**
89+
### Patch releases
6690

6791
There is a script at utilities/tag-bumper.py for creating new tags for patch releases.
6892
That script is out of date and will be updated.

0 commit comments

Comments
 (0)