Skip to content

Commit bccdcf4

Browse files
Add Release github action
I was asked to create a GitHub action for the mono repo, and I thought I would create one that could be used for Buildah too. I don't think we will need this for container-libs after all, we'll just be tagging there I think. So I have decided to add this here. I originally created https://github.com/TomSweeneyRedHat/ActionJackson/blob/main/.github/workflows/action_release.yml.finalhold which is based off the release GitHub action that Podman uses, but a little lighter weight. As Buildah doesn't use a RELEASE_NOTES.md like Podman does, I removed that logic from this version of the script. This will create a Release when a tag is pushed upstream, and will generate the release notes in the same way that "Generate release notes" button on the GitHub web interface does, including recognizing new contributors. That is is I did the action correctly! It also has the ability to be run fron the Actions menu, so if you ever want to redo a release, or test creating one without actually creating one, you can do so. Signed-off-by: tomsweeneyredhat <[email protected]>
1 parent e0e108c commit bccdcf4

File tree

2 files changed

+300
-0
lines changed

2 files changed

+300
-0
lines changed

.github/workflows/README.md

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# Workflow README.md
2+
## Automated Release
3+
4+
The workflow in the action_release.yml automates the process of creating a new GitHub Release whenever a version tag is pushed to the repository. It uses a series of community actions to handle code checkout, release note generation, and release creation.
5+
6+
It also allows for a manual creation of the release from the GitHub Actions page of the repository.
7+
8+
This workflow heavily leveraged the [release](https://github.com/containers/podman/blob/main/.github/workflows/release.yml) workflow in Podman. This README.md was
9+
initially generated by Gemini, and then gently massaged.
10+
11+
## 🚀 Workflow Purpose
12+
13+
The `action_release.yml` file defines a job that automatically:
14+
15+
1. Checks out the code corresponding to the pushed tag.
16+
2. Generates structured release notes by analyzing merged Pull Requests between the previous tag and the new tag.
17+
3. Creates an official GitHub Release using the newly generated notes as the release description.
18+
19+
## ⚙️ How to Use It
20+
21+
This workflow is **event-driven**, meaning you do not need to manually trigger it from the GitHub Actions tab. It is triggered by pushing a new Git tag.
22+
23+
It can also be triggered "by hand" if so desired.
24+
25+
### Triggering the Release
26+
#### Automatic Triggering
27+
To run the workflow and create a new release, follow these steps in your local repository:
28+
29+
1. **Create a New Tag:** Use the `git tag` command to create an annotated tag for your new version (e.g., `v1.2.3`).
30+
31+
```bash
32+
git tag -a v1.2.3 -m "Release version 1.2.3"
33+
```
34+
35+
2. **Push the Tag:** Push the tag to your GitHub repository.
36+
37+
```bash
38+
git push upstream v1.2.3
39+
```
40+
41+
Once the tag is pushed, GitHub will automatically detect the event and start the **Action Release** workflow.
42+
43+
#### Manual Triggering
44+
45+
In the event of a need to recreate a release, a GitHub Action can be run manually, as long as a tag exists for it. Go to `https://github.com/{ProjectName}/actions` and select
46+
the "release" workflow in the left windowpane. Once selected, a new window will come up with a `Run workflow` button. Click on this, and answer
47+
the following questions in the form:
48+
49+
* **Release Version to build and upload:** Enter the version number for the already created tag in the project to build the release from. It should be of the form `v.X.Y.Z`, or `v1.24.15`, for example.
50+
* **Build only: Do not create the release** This defaults to `true`, allowing you to do the preliminary checking to see if everything is in place to build the release. If you select `true`, the release will **NOT** be built. If you select `false`, the release will attempt to build.
51+
* **Send Mail to [email protected]** This defaults to `false`. If set to true, an email will be generated and sent to the [email protected] page announcing the new release for the project.
52+
53+
## 🔑 Permissions and Requirements
54+
55+
This workflow requires elevated permissions to interact with the GitHub API for generating notes and creating the release object.
56+
57+
To ensure the workflow runs successfully, the job configuration must include the following permissions block, granting the default `GITHUB_TOKEN` the ability to write:
58+
59+
```yaml
60+
jobs:
61+
release:
62+
runs-on: ubuntu-latest
63+
permissions:
64+
contents: write # REQUIRED to create the release and fetch content
65+
steps:
66+
# ...
67+
```
68+
69+
🧩 Key Actions Used (Inferred)
70+
The automation process relies on the following GitHub Actions:
71+
72+
Action Name Purpose
73+
actions/checkout@v5 Downloads a copy of the repository code to the runner.
74+
gableroux/[email protected] Generates the Markdown content for the release notes based on the commit history and merged PRs between tags.
75+
dawidd6/[email protected] Create and formats email if requested.
76+
77+
🛑 Troubleshooting
78+
If the workflow fails with an error, such as the AxiosError: Request failed with status code 404 (a common GitHub API error in release actions), consider the following:
79+
80+
Check Permissions: Ensure your workflow or specific job has explicitly set permissions: contents: write to allow the default GITHUB_TOKEN to create the release.
81+
82+
Verify Tags: Confirm that the tag you pushed exactly matches the pattern the workflow is listening for (e.g., v*).
83+
84+
Previous Tag Existence: For release note generation to work correctly, a previous tag must exist to define the change range. Ensure your repository has at least one prior version tag.
Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
# -----------------------------------------------------------------------------------------------------------
2+
# This is a GitHub Action that will automatically create a release after a tag is pushed into the project
3+
# by someone, or by running from the "Actions" page of the project. The interface in the Actions page will
4+
# ask for the Branch to run from, the Release Version to build, if it should create a release or just do
5+
# an initial build, and finally, if mail should be sent to the [email protected] email distribution lists.
6+
# -----------------------------------------------------------------------------------------------------------
7+
name: Release
8+
on:
9+
push:
10+
tags:
11+
- '*'
12+
13+
workflow_dispatch:
14+
inputs:
15+
version:
16+
description: 'Release version to build and upload (e.g. "v9.8.7")'
17+
required: true
18+
buildonly:
19+
description: 'Build only: Do not create release'
20+
default: "true" # 'choice' type requires string value
21+
type: choice
22+
options:
23+
- "true" # Must be quoted string, boolean value not supported.
24+
- "false"
25+
sendmail:
26+
description: 'Send Mail to [email protected]?'
27+
default: "false" # 'choice' type requires string value
28+
type: choice
29+
options:
30+
- "true" # Must be quoted string, boolean value not supported.
31+
- "false"
32+
33+
# Set the permission for this workflow
34+
permissions:
35+
contents: write
36+
37+
# -----------------------------------------------------------------------------------------------------------
38+
# Get the version of the tag from input or from a push of it.
39+
# Then figure out the prior version of the project.
40+
# -----------------------------------------------------------------------------------------------------------
41+
jobs:
42+
check:
43+
name: Check
44+
runs-on: ubuntu-latest
45+
steps:
46+
- name: Determine Version
47+
id: getversion
48+
run: |
49+
if [[ -z "${{ inputs.version }}" ]]
50+
then
51+
VERSION=${{ github.ref_name }}
52+
else
53+
VERSION=${{ inputs.version }}
54+
fi
55+
if ! grep -Eq 'v[0-9]+(\.[0-9]+(\.[0-9]+(-.+)?)?)?$' <<<"$VERSION"
56+
then
57+
echo "Unable to parse release version '$VERSION' from github event JSON, or workflow 'version' input."
58+
exit 1
59+
fi
60+
61+
if grep -Eq '.+-dev$' <<<"$VERSION"
62+
then
63+
echo "Refusing to process a "-dev" version '$VERSION'"
64+
exit 1
65+
fi
66+
prevrelease=$(curl --retry 3 --silent -m 10 --connect-timeout 5 "https://api.github.com/repos/${{ github.repository }}/releases/latest")
67+
PRIORVERSION=$(echo "$prevrelease" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/') >> $GITHUB_OUTPUT
68+
echo "version=$VERSION" >> $GITHUB_OUTPUT
69+
echo "priorversion=$PRIORVERSION" >> $GITHUB_OUTPUT
70+
echo "::notice::Building $VERSION"
71+
echo "::notice::With Prior Version $PRIORVERSION"
72+
echo "::notice::For the project ${{ github.event.repository.name }}"
73+
74+
- name: Just check, or build the release?
75+
id: buildonly
76+
run: |
77+
# The 'tag' trigger will not have a 'buildonly' input set. Handle
78+
# this case in a readable/maintainable way.
79+
if [[ -z "${{ inputs.buildonly }}" ]]
80+
then
81+
BUILDONLY="false"
82+
else
83+
BUILDONLY=${{ inputs.buildonly }}
84+
fi
85+
echo "buildonly=$BUILDONLY" >> $GITHUB_OUTPUT
86+
87+
outputs:
88+
version: ${{ steps.getversion.outputs.version }}
89+
priorversion: ${{ steps.getversion.outputs.priorversion }}
90+
buildonly: ${{ steps.buildonly.outputs.buildonly }}
91+
92+
# -----------------------------------------------------------------------------------------------------------
93+
# Start the release process
94+
# -----------------------------------------------------------------------------------------------------------
95+
release:
96+
name: Create Release
97+
runs-on: ubuntu-latest
98+
if: needs.check.outputs.buildonly == 'false'
99+
needs: [check]
100+
permissions:
101+
contents: write
102+
env:
103+
VERSION: ${{needs.check.outputs.version}}
104+
PRIORVERSION: ${{needs.check.outputs.priorversion}}
105+
106+
steps:
107+
- name: Checkout the main branch of this repository
108+
uses: actions/checkout@v5
109+
with:
110+
ref: ${{needs.check.outputs.version}}
111+
112+
- name: Touch release notes file
113+
run: |
114+
touch $VERSION-release-notes.md
115+
116+
# -----------------------------------------------------------------------------------------------------------
117+
# Create the Changelog
118+
# -----------------------------------------------------------------------------------------------------------
119+
- name: Conventional Changelog Action
120+
id: changelog
121+
env:
122+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
123+
uses: gableroux/[email protected]
124+
with:
125+
github_token: ${{ secrets.GITHUB_TOKEN }}
126+
base_tag: ${{ needs.check.outputs.priorversion }}
127+
head_tag: ${{ github.sha }}
128+
repository: ${{ github.repository }}
129+
auto_detect_new_contributors: 'true'
130+
131+
# -----------------------------------------------------------------------------------------------------------
132+
# Create the Release
133+
# -----------------------------------------------------------------------------------------------------------
134+
- name: Create release
135+
env:
136+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
137+
run: |
138+
title=$VERSION
139+
if [[ $VERSION == *-rc* ]]; then
140+
RC="--prerelease"
141+
title="${title/rc/"RC"}"
142+
else
143+
# check if this version should not be marked latest
144+
vers=${VERSION#"v"}
145+
priorversion=${PRIORVERSION#"v"}
146+
echo "::notice::With Prior Version ${priorversion}"
147+
echo "::notice::With New Version ${vers}"
148+
149+
# sort -V -C returns 0 if args are ascending version order
150+
if !(echo "${priorvers},${vers}" | tr ',' '\n' | sort -V -C)
151+
then
152+
LATEST="--latest=false"
153+
fi
154+
fi
155+
156+
# If we're generating the release notes, put the generated ones in the file.
157+
releasenotes="$VERSION-release-notes.md"
158+
echo "${{ steps.changelog.outputs.notes }}" > $releasenotes
159+
160+
gh release create $VERSION \
161+
-t $title \
162+
--notes-file $releasenotes \
163+
--verify-tag \
164+
$RC \
165+
$LATEST
166+
167+
# -----------------------------------------------------------------------------------------------------------
168+
# If requested, let's send an email notification if we're creating the release.
169+
# -----------------------------------------------------------------------------------------------------------
170+
notification:
171+
name: Email notification
172+
runs-on: ubuntu-latest
173+
needs: [check, release]
174+
if: needs.check.outputs.buildonly == 'false' && inputs.sendmail == 'true'
175+
steps:
176+
- name: Format release email
177+
id: format
178+
env:
179+
VERSION: ${{ needs.check.outputs.version }}
180+
run: |
181+
if grep -Eq '.+-rc' <<<"$VERSION"
182+
then
183+
RC_PREFIX="candidate "
184+
fi
185+
186+
echo "mail_subj=${{ github.event.repository.name }} ${RC_PREFIX}${VERSION} Released" >> $GITHUB_OUTPUT
187+
188+
cat <<EOF>email_body.txt
189+
Hi all,
190+
191+
Containers-lib ${RC_PREFIX}${VERSION} is now available. You may view the full details at
192+
https://github.com/${{ github.repository }}/releases/tag/$VERSION
193+
194+
Release ${RC_PREFIX}Notes:
195+
--------------
196+
EOF
197+
198+
echo ${{ secrets.GITHUB_TOKEN }} | gh auth login --with-token
199+
gh release view $VERSION \
200+
--repo ${{ github.repository }} --json=body --jq '.body' >> email_body.txt
201+
202+
# If the job fails, permit the operator to observe the contents if helpful.
203+
- name: Provide release e-mail contents for examination
204+
run: cat email_body.txt
205+
- name: Send release notification e-mail
206+
# Ref: https://github.com/dawidd6/action-send-mail
207+
uses: dawidd6/[email protected]
208+
with:
209+
server_address: ${{secrets.ACTION_MAIL_SERVER}}
210+
server_port: 465
211+
username: ${{secrets.ACTION_MAIL_USERNAME}}
212+
password: ${{secrets.ACTION_MAIL_PASSWORD}}
213+
subject: ${{ steps.format.outputs.mail_subj }}
214+
to: Podman List <[email protected]>
215+
from: ${{secrets.ACTION_MAIL_SENDER}}
216+
body: file://./email_body.txt

0 commit comments

Comments
 (0)