name: Automatic Release
on:
push:
branches: [main]
permissions:
contents: write
concurrency:
group: automatic-release-main
cancel-in-progress: false
jobs:
version:
name: Version and publish source
if: ${{ !contains(github.event.head_commit.message, 'chore(release):') }}
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.version }}
revision: ${{ steps.revision.outputs.revision }}
steps:
- name: Check out Rust project only
uses: actions/checkout@v6
with:
sparse-checkout: |
.github
Cargo.toml
Cargo.lock
CHANGELOG.md
LICENSE
Makefile
README.md
docs
src
fetch-depth: 0
- name: Install stable Rust
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
- name: Install cargo-edit
run: cargo install cargo-edit --locked
- name: Bump patch version
run: cargo set-version --bump patch
- name: Verify release source
run: |
cargo fmt --all -- --check
cargo check --locked
cargo test --locked
cargo clippy --all-targets --all-features --locked -- -D warnings
cargo package --locked --allow-dirty --no-verify
- name: Read version
id: version
shell: bash
run: echo "version=$(cargo metadata --no-deps --format-version 1 | jq -r '.packages[0].version')" >> "$GITHUB_OUTPUT"
- name: Update changelog
env:
VERSION: ${{ steps.version.outputs.version }}
run: |
python3 - <<'PY'
import datetime
import os
import pathlib
import subprocess
path = pathlib.Path("CHANGELOG.md")
text = path.read_text() if path.exists() else "# Changelog\n\n## [Unreleased]\n"
previous = subprocess.run(
["git", "describe", "--tags", "--abbrev=0"],
capture_output=True,
text=True,
check=False,
).stdout.strip()
revision_range = f"{previous}..HEAD" if previous else "HEAD"
entries = subprocess.run(
["git", "log", revision_range, "--pretty=format:- %s (%h)"],
capture_output=True,
text=True,
check=True,
).stdout.strip() or "- Maintenance release"
section = (
f"## [{os.environ['VERSION']}] - "
f"{datetime.date.today().isoformat()}\n\n{entries}\n"
)
marker = "## [Unreleased]"
if marker in text:
insert_at = text.index(marker) + len(marker)
text = text[:insert_at] + "\n\n" + section + text[insert_at:]
else:
text = text.rstrip() + "\n\n" + section
path.write_text(text)
PY
- name: Commit version and tag
shell: bash
env:
VERSION: ${{ steps.version.outputs.version }}
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add Cargo.toml Cargo.lock CHANGELOG.md
git commit -m "chore(release): v${VERSION} [skip ci]"
git tag "v${VERSION}"
git push origin HEAD:main --follow-tags
- name: Capture release revision
id: revision
run: echo "revision=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
- name: Publish crate
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
run: cargo publish --locked
build:
name: Build ${{ matrix.target }}
needs: version
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- target: x86_64-unknown-linux-gnu
os: ubuntu-latest
archive: agentusage-linux-x86_64.tar.gz
- target: aarch64-unknown-linux-gnu
os: ubuntu-latest
archive: agentusage-linux-aarch64.tar.gz
cross: true
- target: aarch64-apple-darwin
os: macos-14
archive: agentusage-macos-aarch64.tar.gz
- target: x86_64-pc-windows-msvc
os: windows-latest
archive: agentusage-windows-x86_64.zip
steps:
- name: Check out released revision only
uses: actions/checkout@v6
with:
ref: ${{ needs.version.outputs.revision }}
sparse-checkout: |
Cargo.toml
Cargo.lock
CHANGELOG.md
LICENSE
README.md
src
- name: Install stable Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Install cross
if: matrix.cross == true
uses: taiki-e/install-action@cross
- name: Build Unix target
if: runner.os != 'Windows'
shell: bash
run: |
if [ "${{ matrix.cross }}" = "true" ]; then
cross build --release --locked --target "${{ matrix.target }}"
else
cargo build --release --locked --target "${{ matrix.target }}"
fi
- name: Build Windows target
if: runner.os == 'Windows'
run: cargo build --release --locked --target ${{ matrix.target }}
- name: Package Unix archive
if: runner.os != 'Windows'
shell: bash
run: |
mkdir dist
cp "target/${{ matrix.target }}/release/agentusage" dist/agentusage
cp "target/${{ matrix.target }}/release/au" dist/au
cp CHANGELOG.md LICENSE README.md dist/
tar -C dist -czf "${{ matrix.archive }}" agentusage au CHANGELOG.md LICENSE README.md
- name: Package Windows archive
if: runner.os == 'Windows'
shell: pwsh
run: |
New-Item -ItemType Directory -Force dist | Out-Null
Copy-Item "target/${{ matrix.target }}/release/agentusage.exe" dist/agentusage.exe
Copy-Item "target/${{ matrix.target }}/release/au.exe" dist/au.exe
Copy-Item CHANGELOG.md,LICENSE,README.md dist/
Compress-Archive -Path dist/agentusage.exe,dist/au.exe,dist/CHANGELOG.md,dist/LICENSE,dist/README.md -DestinationPath "${{ matrix.archive }}"
- name: Upload archive
uses: actions/upload-artifact@v6
with:
name: ${{ matrix.archive }}
path: ${{ matrix.archive }}
github-release:
name: Publish GitHub release
needs: [version, build]
runs-on: ubuntu-latest
steps:
- name: Download archives
uses: actions/download-artifact@v6
with:
path: dist
merge-multiple: true
- name: Create checksums
working-directory: dist
run: sha256sum *.tar.gz *.zip > SHA256SUMS
- name: Publish GitHub release
uses: softprops/action-gh-release@v2
with:
tag_name: v${{ needs.version.outputs.version }}
generate_release_notes: true
files: |
dist/*.tar.gz
dist/*.zip
dist/SHA256SUMS