Skip to content

Commit 62d61d6

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 62d61d6

File tree

3 files changed

+340
-0
lines changed

3 files changed

+340
-0
lines changed

.github/workflows/release.yml

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
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=$(grep '^version' crates/lib/Cargo.toml | sed 's/version = "//;s/"//')
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+
# Check if tag already exists
65+
if git rev-parse "$TAG_NAME" >/dev/null 2>&1; then
66+
echo "Tag $TAG_NAME already exists"
67+
exit 0
68+
fi
69+
70+
# Create signed annotated tag
71+
git tag -s -m "Release $VERSION" "$TAG_NAME"
72+
73+
# Push the tag
74+
git push origin "$TAG_NAME"
75+
76+
echo "Successfully created and pushed tag $TAG_NAME"
77+
78+
# Switch to the tagged commit for building
79+
git checkout "$TAG_NAME"
80+
81+
- name: Install vendor tool
82+
run: cargo install cargo-vendor-filterer
83+
84+
- name: Cache Dependencies
85+
uses: Swatinem/rust-cache@v2
86+
with:
87+
key: "release"
88+
89+
- name: Run cargo xtask package
90+
run: cargo xtask package
91+
92+
- name: Create Release
93+
id: create_release
94+
uses: actions/create-release@v1
95+
env:
96+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
97+
with:
98+
tag_name: ${{ steps.extract_version.outputs.TAG_NAME }}
99+
release_name: Release ${{ steps.extract_version.outputs.TAG_NAME }}
100+
draft: true
101+
prerelease: false
102+
body: |
103+
## bootc ${{ steps.extract_version.outputs.version }}
104+
105+
### Changes
106+
107+
Auto-generated release notes will be populated here.
108+
109+
### Assets
110+
111+
- `bootc-${{ steps.extract_version.outputs.version }}-vendor.tar.zstd` - Vendored dependencies archive
112+
- `bootc-${{ steps.extract_version.outputs.version }}.tar.zstd` - Source archive
113+
114+
- name: Upload vendor archive
115+
uses: actions/upload-release-asset@v1
116+
env:
117+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
118+
with:
119+
upload_url: ${{ steps.create_release.outputs.upload_url }}
120+
asset_path: ./target/bootc-${{ steps.extract_version.outputs.version }}-vendor.tar.zstd
121+
asset_name: bootc-${{ steps.extract_version.outputs.version }}-vendor.tar.zstd
122+
asset_content_type: application/zstd
123+
124+
- name: Upload source archive
125+
uses: actions/upload-release-asset@v1
126+
env:
127+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
128+
with:
129+
upload_url: ${{ steps.create_release.outputs.upload_url }}
130+
asset_path: ./target/bootc-${{ steps.extract_version.outputs.version }}.tar.zstd
131+
asset_name: bootc-${{ steps.extract_version.outputs.version }}.tar.zstd
132+
asset_content_type: application/zstd
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
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+
run: |
65+
# If version is provided via workflow dispatch, validate and use it
66+
if [ -n "${{ github.event.inputs.version }}" ]; then
67+
VERSION="${{ github.event.inputs.version }}"
68+
# Validate version format strictly
69+
if ! echo "$VERSION" | grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' >/dev/null; then
70+
echo "Error: Invalid version format. Expected X.Y.Z (e.g., 1.5.1)"
71+
exit 1
72+
fi
73+
echo "VERSION=$VERSION" >> $GITHUB_OUTPUT
74+
exit 0
75+
fi
76+
77+
# Use make command to get next minor version
78+
NEW_VERSION=$(make next-minor-version)
79+
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+
# Update version and create signed commit
109+
sed -i "s/^version = \".*\"/version = \"${VERSION}\"/" crates/lib/Cargo.toml
110+
cargo update --workspace
111+
dnf -y install pandoc
112+
cargo xtask update-generated
113+
git add -A
114+
git commit -S -m "Release ${VERSION}"
115+
116+
- name: Push branch
117+
if: steps.check_schedule.outputs.should_release == 'true'
118+
env:
119+
BRANCH_NAME: ${{ steps.create_branch.outputs.branch_name }}
120+
run: |
121+
git push origin "${BRANCH_NAME}"
122+
123+
- name: Create Pull Request
124+
if: steps.check_schedule.outputs.should_release == 'true'
125+
uses: actions/github-script@v7
126+
env:
127+
VERSION: ${{ steps.next_version.outputs.VERSION }}
128+
BRANCH_NAME: ${{ steps.create_branch.outputs.branch_name }}
129+
with:
130+
script: |
131+
const version = process.env.VERSION;
132+
const branchName = process.env.BRANCH_NAME;
133+
134+
const { data: pr } = await github.rest.pulls.create({
135+
owner: context.repo.owner,
136+
repo: context.repo.repo,
137+
title: `Release ${version}`,
138+
body: `## Release ${version}
139+
140+
This is an automated release PR created by the scheduled release workflow.
141+
142+
### Release Process
143+
144+
1. Review the changes in this PR
145+
2. Ensure all tests pass
146+
3. Merge the PR
147+
4. The release tag will be automatically created and signed when this PR is merged
148+
149+
The release workflow will automatically trigger when the tag is pushed.`,
150+
head: branchName,
151+
base: 'main',
152+
draft: false
153+
});
154+
155+
// Add the release label
156+
await github.rest.issues.addLabels({
157+
owner: context.repo.owner,
158+
repo: context.repo.repo,
159+
issue_number: pr.number,
160+
labels: ['release']
161+
});
162+
163+
console.log(`Created PR #${pr.number}: ${pr.html_url}`);

Makefile

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,3 +99,48 @@ vendor:
9999
package-rpm:
100100
cargo xtask $@
101101
.PHONY: package-rpm
102+
103+
# Create a release commit with updated version
104+
# Usage: make release-commit VERSION=1.5.0
105+
release-commit:
106+
@if [ -z "$(VERSION)" ]; then \
107+
echo "Error: VERSION is required. Usage: make release-commit VERSION=1.5.0"; \
108+
exit 1; \
109+
fi
110+
@echo "Creating release commit for version $(VERSION)..."
111+
# Update version in lib/Cargo.toml
112+
sed -i 's/^version = ".*"/version = "$(VERSION)"/' crates/lib/Cargo.toml
113+
# Update Cargo.lock with new version
114+
cargo update --workspace
115+
# Run cargo xtask update-generated to update generated files
116+
cargo xtask update-generated
117+
# Stage all changes
118+
git add crates/lib/Cargo.toml Cargo.lock
119+
git add -A # Add any files updated by cargo xtask update-generated
120+
# Create commit
121+
git commit -m "Release $(VERSION)"
122+
# Create signed tag
123+
git tag -s -m "Release $(VERSION)" v$(VERSION)
124+
.PHONY: release-commit
125+
126+
# Get the next minor version by bumping the minor version from crates/lib/Cargo.toml
127+
# Outputs the new version string (e.g., "1.3.0" if current version is "1.2.4")
128+
next-minor-version:
129+
@VERSION=$$(grep '^version' crates/lib/Cargo.toml | sed 's/version = "//;s/"//'); \
130+
if [ -z "$$VERSION" ]; then \
131+
echo "Error: Could not find version in crates/lib/Cargo.toml" >&2; \
132+
exit 1; \
133+
fi; \
134+
if ! echo "$$VERSION" | grep -E '^[0-9]+\.[0-9]+\.[0-9]+$$' >/dev/null; then \
135+
echo "Error: Invalid version format in Cargo.toml: $$VERSION" >&2; \
136+
exit 1; \
137+
fi; \
138+
MAJOR=$$(echo "$$VERSION" | cut -d. -f1); \
139+
MINOR=$$(echo "$$VERSION" | cut -d. -f2); \
140+
if ! [ "$$MAJOR" -eq "$$MAJOR" ] 2>/dev/null || ! [ "$$MINOR" -eq "$$MINOR" ] 2>/dev/null; then \
141+
echo "Error: Invalid version numbers in $$VERSION" >&2; \
142+
exit 1; \
143+
fi; \
144+
NEW_MINOR=$$((MINOR + 1)); \
145+
echo "$$MAJOR.$$NEW_MINOR.0"
146+
.PHONY: next-minor-version

0 commit comments

Comments
 (0)