Skip to content

Commit 4fc8741

Browse files
committed
build: Add actions to automate release
This adds two github actions, "Create Release PR" and "release". The first is scheduled to run every 3 weeks to automatically create a release PR that bumps the versions. The "release" action is triggered when the release PR is merged. It will create a draft release with the tars attached. Assited-by: Claude Code Signed-off-by: ckyrouac <[email protected]>
1 parent 6607f10 commit 4fc8741

File tree

2 files changed

+284
-0
lines changed

2 files changed

+284
-0
lines changed

.github/workflows/release.yml

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
name: Release
2+
3+
on:
4+
pull_request:
5+
types: [closed]
6+
7+
permissions:
8+
contents: write
9+
10+
jobs:
11+
release:
12+
name: Create Release
13+
if: |
14+
(github.event_name == 'pull_request' &&
15+
github.event.pull_request.merged == true &&
16+
contains(github.event.pull_request.labels.*.name, 'release'))
17+
runs-on: ubuntu-latest
18+
container: quay.io/coreos-assembler/fcos-buildroot:testing-devel
19+
steps:
20+
- name: Checkout repository
21+
uses: actions/checkout@v4
22+
with:
23+
fetch-depth: 0
24+
token: ${{ secrets.GITHUB_TOKEN }}
25+
26+
- name: Extract version
27+
id: extract_version
28+
run: |
29+
# Extract version from crates/lib/Cargo.toml
30+
VERSION=$(cargo read-manifest --manifest-path crates/lib/Cargo.toml | jq -r '.version')
31+
32+
# Validate version format
33+
if ! echo "$VERSION" | grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' >/dev/null; then
34+
echo "Error: Invalid version format in Cargo.toml: $VERSION"
35+
exit 1
36+
fi
37+
38+
echo "Extracted version: $VERSION"
39+
echo "version=$VERSION" >> $GITHUB_OUTPUT
40+
echo "TAG_NAME=v$VERSION" >> $GITHUB_OUTPUT
41+
42+
- name: Install deps
43+
run: ./ci/installdeps.sh
44+
45+
- name: Mark git checkout as safe
46+
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
47+
48+
- name: Import GPG key
49+
if: github.event_name != 'push'
50+
uses: crazy-max/ghaction-import-gpg@v6
51+
with:
52+
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
53+
passphrase: ${{ secrets.GPG_PASSPHRASE }}
54+
git_user_signingkey: true
55+
git_commit_gpgsign: true
56+
git_tag_gpgsign: true
57+
58+
- name: Create and push tag
59+
if: github.event_name != 'push'
60+
run: |
61+
VERSION="${{ steps.extract_version.outputs.version }}"
62+
TAG_NAME="v$VERSION"
63+
64+
if git rev-parse "$TAG_NAME" >/dev/null 2>&1; then
65+
echo "Tag $TAG_NAME already exists"
66+
exit 0
67+
fi
68+
69+
git tag -s -m "Release $VERSION" "$TAG_NAME"
70+
git push origin "$TAG_NAME"
71+
72+
echo "Successfully created and pushed tag $TAG_NAME"
73+
74+
git checkout "$TAG_NAME"
75+
76+
- name: Install vendor tool
77+
run: cargo install cargo-vendor-filterer
78+
79+
- name: Cache Dependencies
80+
uses: Swatinem/rust-cache@v2
81+
with:
82+
key: "release"
83+
84+
- name: Run cargo xtask package
85+
run: cargo xtask package
86+
87+
- name: Create Release
88+
id: create_release
89+
uses: actions/create-release@v1
90+
env:
91+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
92+
with:
93+
tag_name: ${{ steps.extract_version.outputs.TAG_NAME }}
94+
release_name: Release ${{ steps.extract_version.outputs.TAG_NAME }}
95+
draft: true
96+
prerelease: false
97+
body: |
98+
## bootc ${{ steps.extract_version.outputs.version }}
99+
100+
### Changes
101+
102+
Auto-generated release notes will be populated here.
103+
104+
### Assets
105+
106+
- `bootc-${{ steps.extract_version.outputs.version }}-vendor.tar.zstd` - Vendored dependencies archive
107+
- `bootc-${{ steps.extract_version.outputs.version }}.tar.zstd` - Source archive
108+
109+
- name: Upload vendor archive
110+
uses: actions/upload-release-asset@v1
111+
env:
112+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
113+
with:
114+
upload_url: ${{ steps.create_release.outputs.upload_url }}
115+
asset_path: ./target/bootc-${{ steps.extract_version.outputs.version }}-vendor.tar.zstd
116+
asset_name: bootc-${{ steps.extract_version.outputs.version }}-vendor.tar.zstd
117+
asset_content_type: application/zstd
118+
119+
- name: Upload source archive
120+
uses: actions/upload-release-asset@v1
121+
env:
122+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
123+
with:
124+
upload_url: ${{ steps.create_release.outputs.upload_url }}
125+
asset_path: ./target/bootc-${{ steps.extract_version.outputs.version }}.tar.zstd
126+
asset_name: bootc-${{ steps.extract_version.outputs.version }}.tar.zstd
127+
asset_content_type: application/zstd
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
name: Create Release PR
2+
3+
on:
4+
schedule:
5+
# Run every 3 weeks on Monday at 8:00 AM UTC
6+
# Note: GitHub Actions doesn't support "every 3 weeks" directly,
7+
# so we use a workaround by running weekly and checking if it's been 3 weeks
8+
- cron: '0 8 * * 1'
9+
workflow_dispatch:
10+
inputs:
11+
version:
12+
description: 'Version to release (e.g., 1.5.1). Leave empty to auto-increment.'
13+
required: false
14+
type: string
15+
16+
permissions:
17+
contents: write
18+
pull-requests: write
19+
20+
jobs:
21+
create-release-pr:
22+
runs-on: ubuntu-latest
23+
container: quay.io/coreos-assembler/fcos-buildroot:testing-devel
24+
steps:
25+
- name: Checkout repository
26+
uses: actions/checkout@v4
27+
with:
28+
fetch-depth: 0
29+
token: ${{ secrets.GITHUB_TOKEN }}
30+
31+
- name: Install deps
32+
run: ./ci/installdeps.sh
33+
34+
- name: Mark git checkout as safe
35+
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
36+
37+
- name: Check if it's time for a release
38+
id: check_schedule
39+
run: |
40+
# For manual workflow dispatch, always proceed
41+
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
42+
echo "should_release=true" >> $GITHUB_OUTPUT
43+
exit 0
44+
fi
45+
46+
# For scheduled runs, check if it's been 3 weeks since the last release
47+
LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0")
48+
LAST_TAG_DATE=$(git log -1 --format=%ct "$LAST_TAG" 2>/dev/null || echo "0")
49+
CURRENT_DATE=$(date +%s)
50+
DAYS_SINCE_RELEASE=$(( (CURRENT_DATE - LAST_TAG_DATE) / 86400 ))
51+
52+
echo "Days since last release: $DAYS_SINCE_RELEASE"
53+
54+
# Release if it's been at least 21 days (3 weeks)
55+
if [ $DAYS_SINCE_RELEASE -ge 21 ]; then
56+
echo "should_release=true" >> $GITHUB_OUTPUT
57+
else
58+
echo "should_release=false" >> $GITHUB_OUTPUT
59+
fi
60+
61+
- name: Import GPG key
62+
if: steps.check_schedule.outputs.should_release == 'true'
63+
uses: crazy-max/ghaction-import-gpg@v6
64+
with:
65+
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
66+
passphrase: ${{ secrets.GPG_PASSPHRASE }}
67+
git_user_signingkey: true
68+
git_commit_gpgsign: true
69+
git_tag_gpgsign: true
70+
71+
- name: Create release commit
72+
id: create_commit
73+
if: steps.check_schedule.outputs.should_release == 'true'
74+
env:
75+
INPUT_VERSION: ${{ github.event.inputs.version }}
76+
run: |
77+
dnf -y install pandoc
78+
cargo install cargo-edit
79+
80+
# If version is provided via workflow dispatch, validate and use it
81+
if [ -n "$INPUT_VERSION" ]; then
82+
VERSION="$INPUT_VERSION"
83+
# Validate version format strictly
84+
if ! echo "$VERSION" | grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' >/dev/null; then
85+
echo "Error: Invalid version format. Expected X.Y.Z (e.g., 1.5.1)"
86+
exit 1
87+
fi
88+
cargo set-version --manifest-path crates/lib/Cargo.toml --package bootc-lib "$VERSION"
89+
else
90+
# default to bump the minor since that is most common
91+
cargo set-version --manifest-path crates/lib/Cargo.toml --package bootc-lib --bump minor
92+
VERSION=$(cargo read-manifest --manifest-path crates/lib/Cargo.toml | jq -r '.version')
93+
fi
94+
95+
cargo update --workspace
96+
cargo xtask update-generated
97+
git commit -am "Release $VERSION"
98+
echo "VERSION=$VERSION" >> $GITHUB_OUTPUT
99+
100+
- name: Create release branch
101+
if: steps.check_schedule.outputs.should_release == 'true'
102+
id: create_branch
103+
env:
104+
VERSION: ${{ steps.create_commit.outputs.VERSION }}
105+
run: |
106+
BRANCH_NAME="release-${VERSION}"
107+
git checkout -b "$BRANCH_NAME"
108+
echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT
109+
110+
- name: Push branch
111+
if: steps.check_schedule.outputs.should_release == 'true'
112+
env:
113+
BRANCH_NAME: ${{ steps.create_branch.outputs.branch_name }}
114+
run: |
115+
git push origin "${BRANCH_NAME}"
116+
117+
- name: Create Pull Request
118+
if: steps.check_schedule.outputs.should_release == 'true'
119+
uses: actions/github-script@v7
120+
env:
121+
VERSION: ${{ steps.create_commit.outputs.VERSION }}
122+
BRANCH_NAME: ${{ steps.create_branch.outputs.branch_name }}
123+
with:
124+
script: |
125+
const version = process.env.VERSION;
126+
const branchName = process.env.BRANCH_NAME;
127+
128+
const { data: pr } = await github.rest.pulls.create({
129+
owner: context.repo.owner,
130+
repo: context.repo.repo,
131+
title: `Release ${version}`,
132+
body: `## Release ${version}
133+
134+
This is an automated release PR created by the scheduled release workflow.
135+
136+
### Release Process
137+
138+
1. Review the changes in this PR
139+
2. Ensure all tests pass
140+
3. Merge the PR
141+
4. The release tag will be automatically created and signed when this PR is merged
142+
143+
The release workflow will automatically trigger when the tag is pushed.`,
144+
head: branchName,
145+
base: 'main',
146+
draft: false
147+
});
148+
149+
// Add the release label
150+
await github.rest.issues.addLabels({
151+
owner: context.repo.owner,
152+
repo: context.repo.repo,
153+
issue_number: pr.number,
154+
labels: ['release']
155+
});
156+
157+
console.log(`Created PR #${pr.number}: ${pr.html_url}`);

0 commit comments

Comments
 (0)