name: Release
on:
push:
tags: ["v*"]
env:
CARGO_TERM_COLOR: always
permissions:
contents: read
jobs:
verify-version:
name: Verify Version
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd - name: Check tag matches Cargo.toml version
run: |
TAG="${GITHUB_REF#refs/tags/v}"
CRATE_VER=$(grep '^version' Cargo.toml | head -1 | sed 's/.*"\(.*\)"/\1/')
if [ "$TAG" != "$CRATE_VER" ]; then
echo "::error::Tag v$TAG does not match Cargo.toml version $CRATE_VER"
exit 1
fi
echo "✅ Tag v$TAG matches Cargo.toml ($CRATE_VER)"
test:
name: Test (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd - uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 - run: cargo test
build:
name: Build ${{ matrix.target }}
needs: [verify-version, test]
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- target: x86_64-unknown-linux-gnu
os: ubuntu-latest
- target: aarch64-unknown-linux-gnu
os: ubuntu-24.04-arm
- target: x86_64-apple-darwin
os: macos-latest
- target: aarch64-apple-darwin
os: macos-14
- target: x86_64-pc-windows-msvc
os: windows-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd - uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 with:
targets: ${{ matrix.target }}
- uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 with:
key: release-${{ matrix.target }}
- name: Build
run: cargo build --release --target ${{ matrix.target }}
- name: Package (unix)
if: runner.os != 'Windows'
run: |
mkdir hunch-${{ matrix.target }}
cp target/${{ matrix.target }}/release/hunch hunch-${{ matrix.target }}/
cp README.md LICENSE hunch-${{ matrix.target }}/
tar czf hunch-${{ matrix.target }}.tar.gz hunch-${{ matrix.target }}
- name: Package (windows)
if: runner.os == 'Windows'
shell: bash
run: |
mkdir hunch-${{ matrix.target }}
cp target/${{ matrix.target }}/release/hunch.exe hunch-${{ matrix.target }}/
cp README.md LICENSE hunch-${{ matrix.target }}/
7z a hunch-${{ matrix.target }}.zip hunch-${{ matrix.target }}/
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a with:
name: hunch-${{ matrix.target }}
path: |
hunch-${{ matrix.target }}.tar.gz
hunch-${{ matrix.target }}.zip
if-no-files-found: ignore
publish:
name: Publish to crates.io
needs: build
runs-on: ubuntu-latest
environment: crates-io
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd - uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 - name: Publish (idempotent on "already uploaded")
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
run: |
set -o pipefail
if cargo publish 2>&1 | tee /tmp/cargo-publish.log; then
echo "✅ Published successfully"
exit 0
fi
# Distinguish "this version is already on crates.io" (idempotent
# success on re-run) from any other failure mode (auth, network,
# missing metadata, semver drift, etc.).
if grep -qE 'crate version .* is already uploaded|already exists on crates.io' /tmp/cargo-publish.log; then
echo "::warning::Version already on crates.io — treating as idempotent success"
exit 0
fi
echo "::error::cargo publish failed for a non-idempotent reason — see log above"
exit 1
github-release:
name: GitHub Release
needs: build
runs-on: ubuntu-latest
permissions:
contents: write
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c with:
path: artifacts
merge-multiple: true
- name: Extract release notes
id: notes
run: |
TAG="${GITHUB_REF#refs/tags/v}"
if [ -f RELEASE_NOTES.md ] && [ -s RELEASE_NOTES.md ]; then
cp RELEASE_NOTES.md release_notes.md
echo "generate=false" >> $GITHUB_OUTPUT
else
awk "/^## \\[${TAG}\\]/{found=1; next} /^## \\[/{if(found) exit} found" CHANGELOG.md > release_notes.md
if [ ! -s release_notes.md ]; then
echo "generate=true" >> $GITHUB_OUTPUT
else
echo "generate=false" >> $GITHUB_OUTPUT
fi
fi
- name: Create GitHub Release
uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda with:
generate_release_notes: ${{ steps.notes.outputs.generate == 'true' }}
body_path: ${{ steps.notes.outputs.generate != 'true' && 'release_notes.md' || '' }}
files: |
artifacts/*.tar.gz
artifacts/*.zip
homebrew:
name: Update Homebrew Tap
needs: github-release
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c with:
path: artifacts
merge-multiple: true
- name: Compute SHA256 checksums
id: sha
run: |
VERSION="${GITHUB_REF#refs/tags/v}"
echo "version=${VERSION}" >> $GITHUB_OUTPUT
echo "aarch64_mac=$(sha256sum artifacts/hunch-aarch64-apple-darwin.tar.gz | cut -d' ' -f1)" >> $GITHUB_OUTPUT
echo "x86_64_mac=$(sha256sum artifacts/hunch-x86_64-apple-darwin.tar.gz | cut -d' ' -f1)" >> $GITHUB_OUTPUT
echo "aarch64_linux=$(sha256sum artifacts/hunch-aarch64-unknown-linux-gnu.tar.gz | cut -d' ' -f1)" >> $GITHUB_OUTPUT
echo "x86_64_linux=$(sha256sum artifacts/hunch-x86_64-unknown-linux-gnu.tar.gz | cut -d' ' -f1)" >> $GITHUB_OUTPUT
- name: Checkout Homebrew tap
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd with:
repository: lijunzh/homebrew-hunch
token: ${{ secrets.HOMEBREW_TAP_TOKEN }}
path: homebrew-hunch
- name: Write updated formula
run: |
VERSION="${{ steps.sha.outputs.version }}"
cat > homebrew-hunch/Formula/hunch.rb << 'FORMULA'
class Hunch < Formula
desc "Fast, offline media filename parser — extract metadata from messy filenames"
homepage "https://github.com/lijunzh/hunch"
version "VERSION_PLACEHOLDER"
license "MIT"
on_macos do
if Hardware::CPU.arm?
url "https://github.com/lijunzh/hunch/releases/download/vVERSION_PLACEHOLDER/hunch-aarch64-apple-darwin.tar.gz"
sha256 "SHA_AARCH64_MAC"
else
url "https://github.com/lijunzh/hunch/releases/download/vVERSION_PLACEHOLDER/hunch-x86_64-apple-darwin.tar.gz"
sha256 "SHA_X86_64_MAC"
end
end
on_linux do
if Hardware::CPU.arm?
url "https://github.com/lijunzh/hunch/releases/download/vVERSION_PLACEHOLDER/hunch-aarch64-unknown-linux-gnu.tar.gz"
sha256 "SHA_AARCH64_LINUX"
else
url "https://github.com/lijunzh/hunch/releases/download/vVERSION_PLACEHOLDER/hunch-x86_64-unknown-linux-gnu.tar.gz"
sha256 "SHA_X86_64_LINUX"
end
end
def install
bin.install Dir["**/hunch"].first
end
test do
output = shell_output("#{bin}/hunch 'The.Matrix.1999.1080p.BluRay.x264-GROUP.mkv'")
assert_match '"title": "The Matrix"', output
assert_match '"year": 1999', output
end
end
FORMULA
# Strip leading whitespace from heredoc
sed -i 's/^ //' homebrew-hunch/Formula/hunch.rb
# Substitute placeholders
sed -i "s/VERSION_PLACEHOLDER/${VERSION}/g" homebrew-hunch/Formula/hunch.rb
sed -i "s/SHA_AARCH64_MAC/${{ steps.sha.outputs.aarch64_mac }}/g" homebrew-hunch/Formula/hunch.rb
sed -i "s/SHA_X86_64_MAC/${{ steps.sha.outputs.x86_64_mac }}/g" homebrew-hunch/Formula/hunch.rb
sed -i "s/SHA_AARCH64_LINUX/${{ steps.sha.outputs.aarch64_linux }}/g" homebrew-hunch/Formula/hunch.rb
sed -i "s/SHA_X86_64_LINUX/${{ steps.sha.outputs.x86_64_linux }}/g" homebrew-hunch/Formula/hunch.rb
- name: Push formula update
run: |
cd homebrew-hunch
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add Formula/hunch.rb
git commit -m "hunch v${{ steps.sha.outputs.version }}"
git push