name: CLI Release
on:
push:
tags:
- "openrouter-cli-v*.*.*"
permissions:
contents: write
concurrency:
group: cli-release-${{ github.ref }}
cancel-in-progress: true
jobs:
verify:
name: Verify CLI release
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Setup Rust (stable)
uses: dtolnay/rust-toolchain@stable
- name: Cache Rust artifacts
uses: Swatinem/rust-cache@v2
- name: Check tag matches crates/openrouter-cli/Cargo.toml version
run: |
TAG_VERSION="${GITHUB_REF_NAME#openrouter-cli-v}"
CRATE_VERSION="$(sed -n 's/^version = "\(.*\)"/\1/p' crates/openrouter-cli/Cargo.toml | head -n 1)"
if [ "$TAG_VERSION" != "$CRATE_VERSION" ]; then
echo "Tag version ($TAG_VERSION) does not match openrouter-cli version ($CRATE_VERSION)."
exit 1
fi
- name: Run CLI tests
run: cargo test -p openrouter-cli
- name: Validate CLI package contents
run: cargo package -p openrouter-cli --locked
publish-crates-io:
name: Publish openrouter-cli to crates.io
needs: verify
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Setup Rust (stable)
uses: dtolnay/rust-toolchain@stable
- name: Cache Rust artifacts
uses: Swatinem/rust-cache@v2
- name: Publish openrouter-cli (skip when already published)
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
run: |
set -euo pipefail
VERSION="${GITHUB_REF_NAME#openrouter-cli-v}"
if [ -z "${CARGO_REGISTRY_TOKEN}" ]; then
echo "CARGO_REGISTRY_TOKEN is required for CLI release publishing."
exit 1
fi
if curl -fsS "https://crates.io/api/v1/crates/openrouter-cli/${VERSION}" > /dev/null; then
echo "openrouter-cli ${VERSION} is already published; skipping cargo publish."
exit 0
fi
cargo publish -p openrouter-cli --locked --token "${CARGO_REGISTRY_TOKEN}"
build-binaries:
name: Build CLI binary (${{ matrix.target }})
needs: [verify, publish-crates-io]
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
exe_suffix: ""
- os: macos-14
target: aarch64-apple-darwin
exe_suffix: ""
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Setup Rust (stable)
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Cache Rust artifacts
uses: Swatinem/rust-cache@v2
- name: Build openrouter-cli
run: cargo build --release --locked -p openrouter-cli --target ${{ matrix.target }}
- name: Package release asset
shell: bash
run: |
set -euo pipefail
VERSION="${GITHUB_REF_NAME#openrouter-cli-v}"
TARGET="${{ matrix.target }}"
BIN="openrouter-cli${{ matrix.exe_suffix }}"
ASSET="openrouter-cli-${VERSION}-${TARGET}.tar.gz"
mkdir -p dist
cp "target/${TARGET}/release/${BIN}" "dist/${BIN}"
cp crates/openrouter-cli/README.md dist/README.md
cp LICENSE dist/LICENSE
tar -C dist -czf "${ASSET}" "${BIN}" README.md LICENSE
- name: Upload binary artifact
uses: actions/upload-artifact@v7
with:
name: openrouter-cli-${{ matrix.target }}
path: openrouter-cli-*.tar.gz
publish-release:
name: Publish CLI release assets
needs: build-binaries
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Download built artifacts
uses: actions/download-artifact@v8
with:
path: dist
- name: Build checksum manifest
shell: bash
run: |
set -euo pipefail
find dist -name '*.tar.gz' -type f -exec cp {} . \;
sha256sum openrouter-cli-*.tar.gz > SHA256SUMS
- name: Create GitHub release with assets
env:
GH_TOKEN: ${{ github.token }}
run: |
if gh release view "${GITHUB_REF_NAME}" > /dev/null 2>&1; then
echo "Release ${GITHUB_REF_NAME} already exists; uploading assets with clobber."
gh release upload "${GITHUB_REF_NAME}" \
openrouter-cli-*.tar.gz \
SHA256SUMS \
--clobber
exit 0
fi
gh release create "${GITHUB_REF_NAME}" \
openrouter-cli-*.tar.gz \
SHA256SUMS \
--generate-notes \
--verify-tag