name: CI
on:
push:
branches: [main]
tags: ["v*"]
pull_request:
branches: [main]
env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1
jobs:
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
- 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
test:
name: Test (${{ matrix.os }})
needs: lint
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- name: Run tests
run: cargo test --all-features --verbose
publish:
name: Publish to crates.io
needs: test
if: startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Verify version matches tag
shell: bash
run: |
CARGO_VERSION="v$(cargo metadata --no-deps --format-version 1 | jq -r '.packages[0].version')"
TAG_VERSION="${GITHUB_REF#refs/tags/}"
if [ "$CARGO_VERSION" != "$TAG_VERSION" ]; then
echo "::error::Cargo.toml version ($CARGO_VERSION) does not match tag ($TAG_VERSION)"
exit 1
fi
- name: Publish to crates.io
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
run: cargo publish --allow-dirty
build:
name: Build (${{ matrix.target }})
needs: test
if: startsWith(github.ref, 'refs/tags/v')
strategy:
fail-fast: false
matrix:
include:
- target: x86_64-unknown-linux-gnu
os: ubuntu-latest
archive: tar.gz
- target: x86_64-apple-darwin
os: macos-latest
archive: tar.gz
- target: aarch64-apple-darwin
os: macos-latest
archive: tar.gz
- target: x86_64-pc-windows-msvc
os: windows-latest
archive: zip
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Install cross-compilation deps (aarch64-linux)
if: matrix.target == 'aarch64-unknown-linux-gnu'
run: |
sudo apt-get update
sudo apt-get install -y gcc-aarch64-linux-gnu
echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc" >> $GITHUB_ENV
- uses: Swatinem/rust-cache@v2
with:
key: ${{ matrix.target }}
- name: Build release binary
run: cargo build --release --target ${{ matrix.target }}
- name: Get version from tag
id: version
shell: bash
run: echo "version=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
- name: Package (unix)
if: matrix.archive == 'tar.gz'
shell: bash
run: |
STAGING="yaak-${{ steps.version.outputs.version }}-${{ matrix.target }}"
mkdir "$STAGING"
cp target/${{ matrix.target }}/release/yaak "$STAGING/"
cp README.md LICENSE* "$STAGING/" 2>/dev/null || true
tar czf "$STAGING.tar.gz" "$STAGING"
echo "ASSET=$STAGING.tar.gz" >> $GITHUB_ENV
- name: Package (windows)
if: matrix.archive == 'zip'
shell: bash
run: |
STAGING="yaak-${{ steps.version.outputs.version }}-${{ matrix.target }}"
mkdir "$STAGING"
cp target/${{ matrix.target }}/release/yaak.exe "$STAGING/"
cp README.md LICENSE* "$STAGING/" 2>/dev/null || true
7z a "$STAGING.zip" "$STAGING"
echo "ASSET=$STAGING.zip" >> $GITHUB_ENV
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.target }}
path: ${{ env.ASSET }}
release:
name: Release
needs: build
if: startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
- name: List artifacts
run: find artifacts -type f | sort
- name: Generate checksums
shell: bash
run: |
cd artifacts
find . -type f \( -name '*.tar.gz' -o -name '*.zip' \) \
-exec sh -c 'sha256sum "$1" >> ../SHA256SUMS.txt' _ {} \;
cd ..
cat SHA256SUMS.txt
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
generate_release_notes: true
files: |
artifacts/**/*.tar.gz
artifacts/**/*.zip
SHA256SUMS.txt
homebrew:
name: Update Homebrew tap
needs: release
if: startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Get version from tag
id: version
shell: bash
run: |
TAG="${GITHUB_REF#refs/tags/}"
echo "tag=$TAG" >> $GITHUB_OUTPUT
echo "version=${TAG#v}" >> $GITHUB_OUTPUT
- name: Download macOS release tarballs and compute SHA256
id: sha
shell: bash
run: |
VERSION="${{ steps.version.outputs.tag }}"
BASE_URL="https://github.com/hanneshapke/yaak/releases/download/${VERSION}"
curl -fSL -o x86_64.tar.gz "${BASE_URL}/yaak-${VERSION}-x86_64-apple-darwin.tar.gz"
curl -fSL -o aarch64.tar.gz "${BASE_URL}/yaak-${VERSION}-aarch64-apple-darwin.tar.gz"
curl -fSL -o linux_x86_64.tar.gz "${BASE_URL}/yaak-${VERSION}-x86_64-unknown-linux-gnu.tar.gz"
echo "sha_mac_x86=$(sha256sum x86_64.tar.gz | awk '{print $1}')" >> $GITHUB_OUTPUT
echo "sha_mac_arm=$(sha256sum aarch64.tar.gz | awk '{print $1}')" >> $GITHUB_OUTPUT
echo "sha_linux_x86=$(sha256sum linux_x86_64.tar.gz | awk '{print $1}')" >> $GITHUB_OUTPUT
- name: Generate Homebrew formula
shell: bash
run: |
VERSION="${{ steps.version.outputs.version }}"
TAG="${{ steps.version.outputs.tag }}"
cat > yaak.rb <<FORMULA
class Yaak < Formula
desc "Translate natural language to bash commands using an OpenAI-compatible LLM"
homepage "https://www.hanneshapke.com/yaak/"
version "${VERSION}"
license "Apache-2.0"
on_macos do
if Hardware::CPU.arm?
url "https://github.com/hanneshapke/yaak/releases/download/${TAG}/yaak-${TAG}-aarch64-apple-darwin.tar.gz"
sha256 "${{ steps.sha.outputs.sha_mac_arm }}"
else
url "https://github.com/hanneshapke/yaak/releases/download/${TAG}/yaak-${TAG}-x86_64-apple-darwin.tar.gz"
sha256 "${{ steps.sha.outputs.sha_mac_x86 }}"
end
end
on_linux do
if Hardware::CPU.intel?
url "https://github.com/hanneshapke/yaak/releases/download/${TAG}/yaak-${TAG}-x86_64-unknown-linux-gnu.tar.gz"
sha256 "${{ steps.sha.outputs.sha_linux_x86 }}"
end
end
def install
bin.install "yaak"
end
test do
assert_match "yaak", shell_output("#{bin}/yaak --version")
end
end
FORMULA
# Remove leading whitespace from heredoc
sed -i 's/^ //' yaak.rb
cat yaak.rb
- name: Push formula to Homebrew tap
uses: dmnemec/copy_file_to_another_repo_action@main
env:
API_TOKEN_GITHUB: ${{ secrets.HOMEBREW_TAP_TOKEN }}
with:
source_file: yaak.rb
destination_repo: hanneshapke/homebrew-yaak
destination_folder: Formula
destination_branch: main
user_email: github-actions[bot]@users.noreply.github.com
user_name: github-actions[bot]
commit_message: "Update yaak formula to ${{ steps.version.outputs.tag }}"