name: Release
on:
push:
branches: [main]
pull_request:
types: [closed]
branches: [main]
workflow_dispatch:
concurrency:
group: version-release
cancel-in-progress: false
jobs:
release:
if: github.event_name == 'push' || github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
outputs:
version: ${{ steps.bump.outputs.version }}
tag: ${{ steps.bump.outputs.tag }}
did_bump: ${{ steps.bump.outputs.did_bump }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
fetch-tags: true
- name: Setup Rust
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: stable
cache: true
- name: Install grubble
run: cargo install grubble
- name: Clean stale tags
run: |
git fetch origin --tags
git tag -l 'v*' | while read -r tag; do
if ! git merge-base --is-ancestor "$tag" HEAD 2>/dev/null; then
git tag -d "$tag"
fi
done
- name: Bump version and create tag
id: bump
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
BEFORE=$(grep '^version' Cargo.toml | head -1 | cut -d'"' -f2)
grubble \
--git-user-name "github-actions[bot]" \
--git-user-email "41898282+github-actions[bot]@users.noreply.github.com" \
--preset rust \
--changelog \
--push \
--tag \
--update-major-tag
AFTER=$(grep '^version' Cargo.toml | head -1 | cut -d'"' -f2)
if [ "$AFTER" != "$BEFORE" ]; then
echo "did_bump=true" >> $GITHUB_OUTPUT
echo "version=$AFTER" >> $GITHUB_OUTPUT
echo "tag=v$AFTER" >> $GITHUB_OUTPUT
else
echo "did_bump=false" >> $GITHUB_OUTPUT
echo "version=$AFTER" >> $GITHUB_OUTPUT
fi
build:
name: Build binaries
needs: release
if: needs.release.outputs.did_bump == 'true'
strategy:
fail-fast: false
matrix:
include:
- target: x86_64-unknown-linux-musl
os: ubuntu-latest
name: tala-linux-x86_64
cross: true
- target: aarch64-unknown-linux-musl
os: ubuntu-latest
name: tala-linux-aarch64
cross: true
- target: aarch64-apple-darwin
os: macos-latest
name: tala-macos-aarch64
cross: false
- target: x86_64-pc-windows-msvc
os: windows-latest
name: tala-windows-x86_64.exe
cross: false
runs-on: ${{ matrix.os }}
permissions:
contents: write
steps:
- uses: actions/checkout@v4
with:
ref: ${{ needs.release.outputs.tag }}
- name: Setup Rust
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: stable
target: ${{ matrix.target }}
cache: true
- name: Set up ARM64 emulation
if: matrix.target == 'aarch64-unknown-linux-musl'
uses: docker/setup-qemu-action@v3
with:
platforms: arm64
- name: Install cross
if: matrix.cross
run: cargo install cross --git https://github.com/cross-rs/cross
- name: Build (native)
if: ${{ !matrix.cross }}
run: cargo build --release --locked --target ${{ matrix.target }}
- name: Build (cross)
if: matrix.cross
run: cross build --release --locked --target ${{ matrix.target }}
- name: Verify binary (Unix)
if: matrix.os != 'windows-latest'
run: |
expected="tala ${{ needs.release.outputs.version }}"
if [ "${{ matrix.target }}" = "aarch64-unknown-linux-musl" ]; then
actual="$(docker run --rm --platform linux/arm64 --volume "$GITHUB_WORKSPACE:/work:ro" alpine:3.22 /work/target/${{ matrix.target }}/release/tala --version)"
else
actual="$(target/${{ matrix.target }}/release/tala --version)"
fi
test "$actual" = "$expected"
- name: Verify binary (Windows)
if: matrix.os == 'windows-latest'
shell: pwsh
run: |
$expected = "tala ${{ needs.release.outputs.version }}"
$actual = (& "target/${{ matrix.target }}/release/tala.exe" --version).Trim()
if ($actual -ne $expected) { throw "Expected '$expected', got '$actual'" }
- name: Package (Unix)
if: matrix.os != 'windows-latest'
run: |
cd target/${{ matrix.target }}/release
tar czf ../../../${{ matrix.name }}.tar.gz tala
cd -
shasum -a 256 ${{ matrix.name }}.tar.gz > ${{ matrix.name }}.tar.gz.sha256
- name: Package (Windows)
if: matrix.os == 'windows-latest'
run: |
cd target/${{ matrix.target }}/release
7z a ../../../${{ matrix.name }}.zip tala.exe
cd -
- name: Verify archive (Unix)
if: matrix.os != 'windows-latest'
run: |
archive="${{ matrix.name }}.tar.gz"
grep -Fxq tala <(tar -tzf "$archive")
shasum -a 256 -c "${{ matrix.name }}.tar.gz.sha256"
extracted="$(mktemp -d)"
tar -xzf "$archive" -C "$extracted"
test -x "$extracted/tala"
if [ "${{ matrix.target }}" = "aarch64-unknown-linux-musl" ]; then
actual="$(docker run --rm --platform linux/arm64 --volume "$extracted:/work:ro" alpine:3.22 /work/tala --version)"
else
actual="$("$extracted/tala" --version)"
fi
test "$actual" = "tala ${{ needs.release.outputs.version }}"
- name: Verify archive (Windows)
if: matrix.os == 'windows-latest'
shell: pwsh
run: |
$archive = "${{ matrix.name }}.zip"
$listing = 7z l $archive | Out-String
if ($listing -notmatch "tala\.exe") { throw "Archive does not contain tala.exe" }
$extracted = Join-Path $env:RUNNER_TEMP "tala-archive"
7z x $archive "-o$extracted" -y | Out-Null
$actual = (& (Join-Path $extracted "tala.exe") --version).Trim()
$expected = "tala ${{ needs.release.outputs.version }}"
if ($actual -ne $expected) { throw "Expected '$expected', got '$actual'" }
- name: Upload Release Asset (Unix)
if: matrix.os != 'windows-latest'
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ needs.release.outputs.tag }}
files: |
${{ matrix.name }}.tar.gz
${{ matrix.name }}.tar.gz.sha256
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload Release Asset (Windows)
if: matrix.os == 'windows-latest'
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ needs.release.outputs.tag }}
files: |
${{ matrix.name }}.zip
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
publish:
name: Publish to crates.io
needs: [release, build]
if: needs.release.outputs.did_bump == 'true'
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@v4
with:
ref: ${{ needs.release.outputs.tag }}
- name: Setup Rust
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: stable
cache: true
- name: Authenticate with crates.io
uses: rust-lang/crates-io-auth-action@v1
id: auth
- name: Publish
run: cargo publish --locked
env:
CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }}
verify-install:
name: Verify cargo-binstall installation
needs: [release, build, publish]
if: needs.release.outputs.did_bump == 'true'
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Setup Rust
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: stable
cache: true
- name: Install cargo-binstall
run: cargo install cargo-binstall --locked
- name: Install released Tala binary
run: cargo binstall --no-confirm --force --disable-telemetry --strategies crate-meta-data --target x86_64-unknown-linux-musl tala-cli
- name: Verify installed version
run: test "$(tala --version)" = "tala ${{ needs.release.outputs.version }}"