Skip to content

Commit 2050468

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 03fa72b commit 2050468

File tree

5 files changed

+362
-5
lines changed

5 files changed

+362
-5
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: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
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: Determine next version
62+
if: steps.check_schedule.outputs.should_release == 'true'
63+
id: next_version
64+
env:
65+
INPUT_VERSION: ${{ github.event.inputs.version }}
66+
run: |
67+
# If version is provided via workflow dispatch, validate and use it
68+
if [ -n "$INPUT_VERSION" ]; then
69+
VERSION="$INPUT_VERSION"
70+
# Validate version format strictly
71+
if ! echo "$VERSION" | grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' >/dev/null; then
72+
echo "Error: Invalid version format. Expected X.Y.Z (e.g., 1.5.1)"
73+
exit 1
74+
fi
75+
echo "VERSION=$VERSION" >> $GITHUB_OUTPUT
76+
exit 0
77+
fi
78+
79+
NEW_VERSION=$(cargo xtask next-version minor)
80+
echo "New version: $NEW_VERSION"
81+
echo "VERSION=$NEW_VERSION" >> $GITHUB_OUTPUT
82+
83+
- name: Import GPG key
84+
if: steps.check_schedule.outputs.should_release == 'true'
85+
uses: crazy-max/ghaction-import-gpg@v6
86+
with:
87+
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
88+
passphrase: ${{ secrets.GPG_PASSPHRASE }}
89+
git_user_signingkey: true
90+
git_commit_gpgsign: true
91+
git_tag_gpgsign: true
92+
93+
- name: Create release branch
94+
if: steps.check_schedule.outputs.should_release == 'true'
95+
id: create_branch
96+
env:
97+
VERSION: ${{ steps.next_version.outputs.VERSION }}
98+
run: |
99+
BRANCH_NAME="release-${VERSION}"
100+
git checkout -b "$BRANCH_NAME"
101+
echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT
102+
103+
- name: Create release commit
104+
if: steps.check_schedule.outputs.should_release == 'true'
105+
env:
106+
VERSION: ${{ steps.next_version.outputs.VERSION }}
107+
run: |
108+
dnf -y install pandoc
109+
cargo install cargo-edit
110+
cargo set-version --manifest-path crates/lib/Cargo.toml --package bootc-lib "$VERSION"
111+
cargo update --workspace
112+
cargo xtask update-generated
113+
git commit -am "Release $VERSION"
114+
115+
- name: Push branch
116+
if: steps.check_schedule.outputs.should_release == 'true'
117+
env:
118+
BRANCH_NAME: ${{ steps.create_branch.outputs.branch_name }}
119+
run: |
120+
git push origin "${BRANCH_NAME}"
121+
122+
- name: Create Pull Request
123+
if: steps.check_schedule.outputs.should_release == 'true'
124+
uses: actions/github-script@v7
125+
env:
126+
VERSION: ${{ steps.next_version.outputs.VERSION }}
127+
BRANCH_NAME: ${{ steps.create_branch.outputs.branch_name }}
128+
with:
129+
script: |
130+
const version = process.env.VERSION;
131+
const branchName = process.env.BRANCH_NAME;
132+
133+
const { data: pr } = await github.rest.pulls.create({
134+
owner: context.repo.owner,
135+
repo: context.repo.repo,
136+
title: `Release ${version}`,
137+
body: `## Release ${version}
138+
139+
This is an automated release PR created by the scheduled release workflow.
140+
141+
### Release Process
142+
143+
1. Review the changes in this PR
144+
2. Ensure all tests pass
145+
3. Merge the PR
146+
4. The release tag will be automatically created and signed when this PR is merged
147+
148+
The release workflow will automatically trigger when the tag is pushed.`,
149+
head: branchName,
150+
base: 'main',
151+
draft: false
152+
});
153+
154+
// Add the release label
155+
await github.rest.issues.addLabels({
156+
owner: context.repo.owner,
157+
repo: context.repo.repo,
158+
issue_number: pr.number,
159+
labels: ['release']
160+
});
161+
162+
console.log(`Created PR #${pr.number}: ${pr.html_url}`);

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/xtask/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ anyhow = { workspace = true }
1616
camino = { workspace = true }
1717
chrono = { workspace = true, features = ["std"] }
1818
fn-error-context = { workspace = true }
19+
serde_json = { workspace = true }
1920
tar = "0.4"
2021
toml = "0.8"
2122
tempfile = { workspace = true }

0 commit comments

Comments
 (0)