release: cut v0.3.1 #18
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Release | |
| on: | |
| push: | |
| tags: | |
| - "v*" | |
| workflow_dispatch: | |
| inputs: | |
| release_tag: | |
| description: "Existing release tag to build and publish (for example v0.1.7)" | |
| required: true | |
| type: string | |
| permissions: | |
| contents: write | |
| jobs: | |
| verify: | |
| runs-on: ubuntu-latest | |
| env: | |
| RELEASE_TAG: ${{ github.event_name == 'workflow_dispatch' && inputs.release_tag || github.ref_name }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6.0.2 | |
| with: | |
| ref: ${{ github.event_name == 'workflow_dispatch' && inputs.release_tag || github.ref }} | |
| persist-credentials: false | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| components: rustfmt, clippy | |
| - name: Cache cargo artifacts | |
| uses: Swatinem/rust-cache@v2 | |
| - name: Verify release build | |
| run: | | |
| cargo fmt --check | |
| cargo clippy --all-targets --all-features -- -D warnings | |
| cargo test -q --locked | |
| build: | |
| needs: verify | |
| env: | |
| RELEASE_TAG: ${{ github.event_name == 'workflow_dispatch' && inputs.release_tag || github.ref_name }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - os: ubuntu-latest | |
| target: x86_64-unknown-linux-gnu | |
| archive_ext: tar.gz | |
| - os: ubuntu-latest | |
| target: aarch64-unknown-linux-gnu | |
| archive_ext: tar.gz | |
| - os: macos-15-intel | |
| target: x86_64-apple-darwin | |
| archive_ext: tar.gz | |
| - os: macos-14 | |
| target: aarch64-apple-darwin | |
| archive_ext: tar.gz | |
| - os: windows-latest | |
| target: x86_64-pc-windows-msvc | |
| archive_ext: zip | |
| runs-on: ${{ matrix.os }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6.0.2 | |
| with: | |
| ref: ${{ github.event_name == 'workflow_dispatch' && inputs.release_tag || github.ref }} | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| targets: ${{ matrix.target }} | |
| - name: Install Linux ARM64 linker | |
| if: matrix.target == 'aarch64-unknown-linux-gnu' | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y gcc-aarch64-linux-gnu | |
| - name: Cache cargo artifacts | |
| uses: Swatinem/rust-cache@v2 | |
| - name: Build release binary | |
| env: | |
| CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: ${{ matrix.target == 'aarch64-unknown-linux-gnu' && 'aarch64-linux-gnu-gcc' || '' }} | |
| run: cargo build --locked --release --target ${{ matrix.target }} | |
| - name: Package release archive (unix) | |
| if: runner.os != 'Windows' | |
| env: | |
| TARGET: ${{ matrix.target }} | |
| TAG: ${{ env.RELEASE_TAG }} | |
| run: | | |
| mkdir -p dist | |
| cp "target/$TARGET/release/kagi" dist/kagi | |
| tar -C dist -czf "kagi-$TAG-$TARGET.tar.gz" kagi | |
| cp "target/$TARGET/release/kagi" "kagi-$TAG-$TARGET" | |
| - name: Package release archive (windows) | |
| if: runner.os == 'Windows' | |
| shell: pwsh | |
| env: | |
| TARGET: ${{ matrix.target }} | |
| TAG: ${{ env.RELEASE_TAG }} | |
| run: | | |
| New-Item -ItemType Directory -Path dist -Force | Out-Null | |
| Copy-Item "target/$env:TARGET/release/kagi.exe" "dist/kagi.exe" | |
| Compress-Archive -Path "dist/kagi.exe" -DestinationPath "kagi-$env:TAG-$env:TARGET.zip" -Force | |
| Copy-Item "target/$env:TARGET/release/kagi.exe" "kagi-$env:TAG-$env:TARGET.exe" | |
| - name: Upload packaged assets (unix) | |
| if: runner.os != 'Windows' | |
| uses: actions/upload-artifact@v7.0.0 | |
| with: | |
| name: release-${{ matrix.target }} | |
| path: | | |
| kagi-${{ env.RELEASE_TAG }}-${{ matrix.target }}.${{ matrix.archive_ext }} | |
| kagi-${{ env.RELEASE_TAG }}-${{ matrix.target }} | |
| - name: Upload packaged assets (windows) | |
| if: runner.os == 'Windows' | |
| uses: actions/upload-artifact@v7.0.0 | |
| with: | |
| name: release-${{ matrix.target }} | |
| path: | | |
| kagi-${{ env.RELEASE_TAG }}-${{ matrix.target }}.${{ matrix.archive_ext }} | |
| kagi-${{ env.RELEASE_TAG }}-${{ matrix.target }}.exe | |
| release: | |
| needs: build | |
| runs-on: ubuntu-latest | |
| env: | |
| RELEASE_TAG: ${{ github.event_name == 'workflow_dispatch' && inputs.release_tag || github.ref_name }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6.0.2 | |
| with: | |
| ref: ${{ github.event_name == 'workflow_dispatch' && inputs.release_tag || github.ref }} | |
| - name: Download packaged archives | |
| uses: actions/download-artifact@v8.0.1 | |
| with: | |
| pattern: release-* | |
| merge-multiple: true | |
| path: dist | |
| - name: Generate checksums | |
| working-directory: dist | |
| run: | | |
| sha256sum * > "kagi-${RELEASE_TAG}-checksums.txt" | |
| - name: Publish GitHub release | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| if gh release view "$RELEASE_TAG" >/dev/null 2>&1; then | |
| gh release upload "$RELEASE_TAG" dist/* --clobber | |
| else | |
| gh release create "$RELEASE_TAG" dist/* --generate-notes --verify-tag | |
| fi | |
| - name: Sync Homebrew tap and Scoop bucket | |
| id: sync_package_indexes | |
| continue-on-error: true | |
| env: | |
| REPO_SYNC_TOKEN: ${{ secrets.REPO_SYNC_TOKEN }} | |
| run: | | |
| set -euo pipefail | |
| if [ -z "${REPO_SYNC_TOKEN:-}" ]; then | |
| echo "REPO_SYNC_TOKEN is not configured; skipping Homebrew/Scoop sync." | |
| exit 0 | |
| fi | |
| SYNC_TOKEN="$(printf '%s' "$REPO_SYNC_TOKEN" | tr -d '\r\n')" | |
| export SYNC_TOKEN | |
| VERSION="${RELEASE_TAG#v}" | |
| CHECKSUM_FILE="dist/kagi-${RELEASE_TAG}-checksums.txt" | |
| BASE_URL="https://github.com/Microck/kagi-cli/releases/download/${RELEASE_TAG}" | |
| checksum_for() { | |
| local asset="$1" | |
| grep " ${asset}$" "$CHECKSUM_FILE" | awk '{print $1}' | |
| } | |
| MACOS_ARM_SHA="$(checksum_for "kagi-${RELEASE_TAG}-aarch64-apple-darwin.tar.gz")" | |
| MACOS_INTEL_SHA="$(checksum_for "kagi-${RELEASE_TAG}-x86_64-apple-darwin.tar.gz")" | |
| LINUX_ARM_SHA="$(checksum_for "kagi-${RELEASE_TAG}-aarch64-unknown-linux-gnu.tar.gz")" | |
| LINUX_INTEL_SHA="$(checksum_for "kagi-${RELEASE_TAG}-x86_64-unknown-linux-gnu.tar.gz")" | |
| WINDOWS_INTEL_SHA="$(checksum_for "kagi-${RELEASE_TAG}-x86_64-pc-windows-msvc.zip")" | |
| TAP_DIR="$(mktemp -d)" | |
| BUCKET_DIR="$(mktemp -d)" | |
| ASKPASS_SCRIPT="$(mktemp)" | |
| cat > "$ASKPASS_SCRIPT" <<'EOF' | |
| #!/usr/bin/env bash | |
| case "$1" in | |
| *Username*) printf '%s\n' "x-access-token" ;; | |
| *Password*) printf '%s\n' "$SYNC_TOKEN" ;; | |
| esac | |
| EOF | |
| chmod 700 "$ASKPASS_SCRIPT" | |
| export GIT_ASKPASS="$ASKPASS_SCRIPT" | |
| export GIT_TERMINAL_PROMPT=0 | |
| clone_with_token() { | |
| local repo="$1" | |
| local dir="$2" | |
| git clone "https://github.com/${repo}.git" "$dir" | |
| } | |
| push_with_token() { | |
| local dir="$1" | |
| git -C "$dir" push origin main | |
| } | |
| clone_with_token "Microck/homebrew-kagi" "$TAP_DIR" | |
| clone_with_token "Microck/scoop-kagi" "$BUCKET_DIR" | |
| mkdir -p "$TAP_DIR/Formula" | |
| cat > "$TAP_DIR/Formula/kagi.rb" <<EOF | |
| class Kagi < Formula | |
| desc "Agent-native Rust CLI for Kagi subscribers with JSON-first output" | |
| homepage "https://github.com/Microck/kagi-cli" | |
| version "${VERSION}" | |
| license "MIT" | |
| on_macos do | |
| if Hardware::CPU.arm? | |
| url "${BASE_URL}/kagi-${RELEASE_TAG}-aarch64-apple-darwin.tar.gz" | |
| sha256 "${MACOS_ARM_SHA}" | |
| end | |
| if Hardware::CPU.intel? | |
| url "${BASE_URL}/kagi-${RELEASE_TAG}-x86_64-apple-darwin.tar.gz" | |
| sha256 "${MACOS_INTEL_SHA}" | |
| end | |
| end | |
| on_linux do | |
| if Hardware::CPU.arm? | |
| url "${BASE_URL}/kagi-${RELEASE_TAG}-aarch64-unknown-linux-gnu.tar.gz" | |
| sha256 "${LINUX_ARM_SHA}" | |
| end | |
| if Hardware::CPU.intel? | |
| url "${BASE_URL}/kagi-${RELEASE_TAG}-x86_64-unknown-linux-gnu.tar.gz" | |
| sha256 "${LINUX_INTEL_SHA}" | |
| end | |
| end | |
| def install | |
| bin.install "kagi" | |
| end | |
| test do | |
| assert_match "Usage: kagi <COMMAND>", shell_output("#{bin}/kagi --help") | |
| end | |
| end | |
| EOF | |
| cat > "$BUCKET_DIR/kagi.json" <<EOF | |
| { | |
| "version": "${VERSION}", | |
| "description": "Agent-native Rust CLI for Kagi subscribers with JSON-first output.", | |
| "homepage": "https://github.com/Microck/kagi-cli", | |
| "license": "MIT", | |
| "architecture": { | |
| "64bit": { | |
| "url": "${BASE_URL}/kagi-${RELEASE_TAG}-x86_64-pc-windows-msvc.zip", | |
| "hash": "${WINDOWS_INTEL_SHA}" | |
| } | |
| }, | |
| "bin": "kagi.exe", | |
| "checkver": { | |
| "github": "https://github.com/Microck/kagi-cli" | |
| }, | |
| "autoupdate": { | |
| "architecture": { | |
| "64bit": { | |
| "url": "https://github.com/Microck/kagi-cli/releases/download/v\$version/kagi-v\$version-x86_64-pc-windows-msvc.zip" | |
| } | |
| } | |
| } | |
| } | |
| EOF | |
| git -C "$TAP_DIR" config user.name "Microck" | |
| git -C "$TAP_DIR" config user.email "contact@micr.dev" | |
| if ! git -C "$TAP_DIR" diff --quiet; then | |
| git -C "$TAP_DIR" add Formula/kagi.rb | |
| git -C "$TAP_DIR" commit -m "chore: update kagi formula for ${RELEASE_TAG}" | |
| push_with_token "$TAP_DIR" | |
| fi | |
| git -C "$BUCKET_DIR" config user.name "Microck" | |
| git -C "$BUCKET_DIR" config user.email "contact@micr.dev" | |
| if ! git -C "$BUCKET_DIR" diff --quiet; then | |
| git -C "$BUCKET_DIR" add kagi.json | |
| git -C "$BUCKET_DIR" commit -m "chore: update kagi manifest for ${RELEASE_TAG}" | |
| push_with_token "$BUCKET_DIR" | |
| fi | |
| - name: Report package index sync warning | |
| if: steps.sync_package_indexes.outcome == 'failure' | |
| run: | | |
| echo "::warning title=Package index sync failed::GitHub release assets were published, but Homebrew/Scoop sync did not complete. Check the release job logs." |