name: Release
on:
push:
tags: ['v*']
workflow_dispatch:
inputs:
dry_run:
description: 'Dry run (skip actual publish)'
type: boolean
default: true
concurrency:
group: release
cancel-in-progress: false
permissions:
contents: write
env:
CARGO_TERM_COLOR: always
jobs:
validate:
name: Validate Release
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@nightly
with:
components: rustfmt, clippy
- name: Cache cargo
uses: Swatinem/rust-cache@v2
- name: Check formatting
run: cargo fmt --all -- --check
- name: Run clippy
run: cargo clippy --all-targets --all-features -- -D warnings
- name: Run tests
run: cargo test --all-features
- name: Build docs
run: cargo doc --no-deps --all-features
env:
RUSTDOCFLAGS: -D warnings
- name: Verify version matches tag
if: startsWith(github.ref, 'refs/tags/v')
run: |
TAG_VERSION="${GITHUB_REF#refs/tags/v}"
CARGO_VERSION=$(grep '^version' Cargo.toml | head -1 | sed 's/.*"\(.*\)".*/\1/')
echo "Tag version: $TAG_VERSION"
echo "Cargo.toml version: $CARGO_VERSION"
if [[ "$TAG_VERSION" != "$CARGO_VERSION" ]]; then
echo "::error::Version mismatch! Tag is v$TAG_VERSION but Cargo.toml has $CARGO_VERSION"
exit 1
fi
publish:
name: Publish to crates.io
needs: validate
runs-on: ubuntu-latest
timeout-minutes: 30
if: startsWith(github.ref, 'refs/tags/v') && !inputs.dry_run
steps:
- uses: actions/checkout@v4
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@nightly
- name: Cache cargo
uses: Swatinem/rust-cache@v2
- name: Publish to crates.io
run: cargo publish --token ${{ secrets.CRATES_IO_TOKEN }}
github-release:
name: Create GitHub Release
needs: validate
runs-on: ubuntu-latest
timeout-minutes: 15
if: startsWith(github.ref, 'refs/tags/v')
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Extract version
id: version
run: echo "version=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
- name: Generate changelog
id: changelog
run: |
# Get previous tag
PREV_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "")
echo "## What's Changed" > CHANGELOG.md
echo "" >> CHANGELOG.md
if [[ -n "$PREV_TAG" ]]; then
git log --pretty=format:"* %s (%h)" $PREV_TAG..HEAD >> CHANGELOG.md
else
git log --pretty=format:"* %s (%h)" >> CHANGELOG.md
fi
echo "" >> CHANGELOG.md
echo "" >> CHANGELOG.md
echo "**Full Changelog**: https://github.com/${{ github.repository }}/compare/${PREV_TAG}...v${{ steps.version.outputs.version }}" >> CHANGELOG.md
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
name: v${{ steps.version.outputs.version }}
body_path: CHANGELOG.md
generate_release_notes: true
draft: false
prerelease: ${{ contains(steps.version.outputs.version, '-') }}
outputs:
version: ${{ steps.version.outputs.version }}
docs:
name: Build Documentation
needs: validate
runs-on: ubuntu-latest
timeout-minutes: 15
if: startsWith(github.ref, 'refs/tags/v')
permissions:
contents: read
pages: write
id-token: write
steps:
- uses: actions/checkout@v4
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@nightly
- name: Cache cargo
uses: Swatinem/rust-cache@v2
- name: Build documentation
run: |
cargo doc --no-deps --all-features
echo '<meta http-equiv="refresh" content="0; url=opentui/index.html">' > target/doc/index.html
- name: Upload pages artifact
uses: actions/upload-pages-artifact@v3
with:
path: target/doc
deploy-docs:
name: Deploy Documentation
needs: docs
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/v')
permissions:
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4