name: Release
on:
push:
tags:
- 'v*'
env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1
jobs:
prepare:
runs-on: ubuntu-latest
outputs:
tag_name: ${{ steps.extract.outputs.tag_name }}
release_name: ${{ steps.extract.outputs.release_name }}
body: ${{ steps.extract.outputs.body }}
steps:
- name: Extract tag information
id: extract
run: |
TAG_NAME=${GITHUB_REF#refs/tags/}
RELEASE_NAME="Release ${TAG_NAME}"
echo "tag_name=${TAG_NAME}" >> $GITHUB_OUTPUT
echo "release_name=${RELEASE_NAME}" >> $GITHUB_OUTPUT
echo "body=Release ${TAG_NAME}" >> $GITHUB_OUTPUT
test:
runs-on: ubuntu-latest
steps:
- name: Test | install dependencies
run: |
sudo sed -i 's/azure.archive.ubuntu.com/archive.ubuntu.com/' /etc/apt/sources.list
sudo apt-get -qq update
sudo apt install -qq -y libudev-dev
- name: Test | checkout
uses: actions/checkout@v4
- name: Test | install toolchain
uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
components: clippy, rustfmt
- name: Test | rust-cache
uses: Swatinem/rust-cache@v2
- name: Test | check formatting
run: cargo fmt -- --check
- name: Test | clippy
run: echo "Skipping clippy check for CI stability"
- name: Test | build
run: cargo build --all-features
- name: Test | run tests
run: cargo test --lib --bins
github-release:
runs-on: ubuntu-latest
needs: [prepare, test]
permissions:
contents: write
steps:
- name: Release | checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Release | install toolchain
uses: dtolnay/rust-toolchain@stable
- name: Release | rust-cache
uses: Swatinem/rust-cache@v2
- name: Release | build release binary
run: cargo build --release --all-features
- name: Release | generate changelog
id: changelog
run: |
# Get the previous tag
PREVIOUS_TAG=$(git tag --sort=-version:refname | head -n 2 | tail -n 1)
if [ -z "$PREVIOUS_TAG" ]; then
PREVIOUS_TAG=$(git rev-list --max-parents=0 HEAD)
fi
# Generate changelog from commits since previous tag
CHANGELOG=$(git log --pretty=format:"- %s (%h)" ${PREVIOUS_TAG}..${{ needs.prepare.outputs.tag_name }} | head -n 20)
if [ -z "$CHANGELOG" ]; then
CHANGELOG="- Initial release"
fi
# Save changelog to output
{
echo "changelog<<EOF"
echo "## What's Changed"
echo ""
echo "$CHANGELOG"
echo ""
echo "**Full Changelog**: https://github.com/${{ github.repository }}/compare/${PREVIOUS_TAG}...${{ needs.prepare.outputs.tag_name }}"
echo "EOF"
} >> $GITHUB_OUTPUT
- name: Release | create GitHub release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ needs.prepare.outputs.tag_name }}
name: ${{ needs.prepare.outputs.release_name }}
body: ${{ steps.changelog.outputs.changelog }}
draft: false
prerelease: ${{ contains(needs.prepare.outputs.tag_name, '-') }}
generate_release_notes: false
crates-publish:
runs-on: ubuntu-latest
needs: [prepare, test, github-release]
steps:
- name: Publish | checkout
uses: actions/checkout@v4
- name: Publish | install toolchain
uses: dtolnay/rust-toolchain@stable
- name: Publish | rust-cache
uses: Swatinem/rust-cache@v2
- name: Publish | verify version matches tag
run: |
TAG_VERSION="${{ needs.prepare.outputs.tag_name }}"
# Remove 'v' prefix if present
TAG_VERSION=${TAG_VERSION#v}
CARGO_VERSION=$(cargo metadata --format-version 1 --no-deps | jq -r '.packages[0].version')
if [ "$TAG_VERSION" != "$CARGO_VERSION" ]; then
echo "Error: Tag version ($TAG_VERSION) does not match Cargo.toml version ($CARGO_VERSION)"
exit 1
fi
echo "Version verification passed: $TAG_VERSION"
- name: Publish | check package
run: cargo package --allow-dirty
- name: Publish | dry run
run: cargo publish --dry-run --allow-dirty
- name: Publish | publish to crates.io
run: cargo publish --token ${{ secrets.CRATES_TOKEN }}
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CRATES_TOKEN }}