name: Release binaries # Build per-platform binaries and attach them to the GitHub Release for the # pushed tag. No npm, no tokens — only the built-in GITHUB_TOKEN. Consumers # install with: curl -fsSL .../install.sh | sh on: push: tags: - 'v*' workflow_dispatch: inputs: tag: description: 'Existing tag to (re)build binaries for, e.g. v0.27.0-fork.12' required: true permissions: contents: write concurrency: release-binaries-${{ github.ref }} jobs: build: name: Build ${{ matrix.name }} runs-on: ${{ matrix.os }} timeout-minutes: 30 strategy: fail-fast: false matrix: include: - { name: Linux x64, os: ubuntu-latest, target: x86_64-unknown-linux-gnu, asset: chrome-use-linux-x64, use_zigbuild: true, ext: '' } - { name: Linux ARM64, os: ubuntu-latest, target: aarch64-unknown-linux-gnu, asset: chrome-use-linux-arm64, use_zigbuild: true, ext: '' } - { name: Linux musl x64, os: ubuntu-latest, target: x86_64-unknown-linux-musl, asset: chrome-use-linux-musl-x64, use_zigbuild: true, ext: '' } - { name: Linux musl ARM64, os: ubuntu-latest, target: aarch64-unknown-linux-musl, asset: chrome-use-linux-musl-arm64, use_zigbuild: true, ext: '' } - { name: Windows x64, os: ubuntu-latest, target: x86_64-pc-windows-gnu, asset: chrome-use-win32-x64, use_zigbuild: false, ext: '.exe' } - { name: macOS x64, os: macos-latest, target: x86_64-apple-darwin, asset: chrome-use-darwin-x64, use_zigbuild: false, ext: '' } - { name: macOS ARM64, os: macos-latest, target: aarch64-apple-darwin, asset: chrome-use-darwin-arm64, use_zigbuild: false, ext: '' } steps: - name: Checkout uses: actions/checkout@v6 with: ref: ${{ github.event.inputs.tag || github.ref }} - name: Setup Rust toolchain uses: dtolnay/rust-toolchain@stable with: targets: ${{ matrix.target }} - name: Install cross-compilation tools (Linux) if: runner.os == 'Linux' run: | sudo apt-get update sudo apt-get install -y gcc-aarch64-linux-gnu gcc-x86-64-linux-gnu mingw-w64 - name: Install cargo-zigbuild if: matrix.use_zigbuild run: | pip3 install ziglang cargo install cargo-zigbuild - name: Configure Rust linkers if: runner.os == 'Linux' run: | mkdir -p ~/.cargo cat >> ~/.cargo/config.toml << 'EOF' [target.aarch64-unknown-linux-gnu] linker = "aarch64-linux-gnu-gcc" [target.x86_64-pc-windows-gnu] linker = "x86_64-w64-mingw32-gcc" EOF - name: Cache Rust build artifacts uses: Swatinem/rust-cache@v2 with: workspaces: cli - name: Build (zigbuild) if: matrix.use_zigbuild run: cargo zigbuild --release --manifest-path cli/Cargo.toml --target ${{ matrix.target }} - name: Build (cargo) if: '!matrix.use_zigbuild' run: cargo build --release --manifest-path cli/Cargo.toml --target ${{ matrix.target }} - name: Package (.tar.gz + .sha256) shell: bash run: | set -euo pipefail mkdir -p dist src="cli/target/${{ matrix.target }}/release/chrome-use${{ matrix.ext }}" # The binary inside every archive is named `chrome-use` (or .exe); # install.sh extracts that fixed name regardless of platform. cp "$src" "dist/chrome-use${{ matrix.ext }}" chmod +x "dist/chrome-use${{ matrix.ext }}" || true ( cd dist tar czf "${{ matrix.asset }}.tar.gz" "chrome-use${{ matrix.ext }}" if command -v sha256sum >/dev/null 2>&1; then sha256sum "${{ matrix.asset }}.tar.gz" > "${{ matrix.asset }}.tar.gz.sha256" else shasum -a 256 "${{ matrix.asset }}.tar.gz" > "${{ matrix.asset }}.tar.gz.sha256" fi ) - name: Upload artifact uses: actions/upload-artifact@v7 with: name: ${{ matrix.asset }} path: dist/${{ matrix.asset }}.tar.gz* retention-days: 3 release: name: Attach binaries to GitHub Release needs: build runs-on: ubuntu-latest timeout-minutes: 10 permissions: contents: write steps: # The release job is separate from the build matrix and has no repo by # default — check it out (full history + tags) so the changelog step has a # git repo to diff. Without this, `git` failed with "not a git repository" # and the changelog came out empty. - name: Checkout uses: actions/checkout@v6 with: ref: ${{ github.event.inputs.tag || github.ref }} fetch-depth: 0 - name: Download all artifacts uses: actions/download-artifact@v8 with: path: dist merge-multiple: true - name: List assets run: ls -la dist # Build the changelog from conventional-commit subjects since the previous # tag. GitHub's built-in generate_release_notes only lists merged PRs, # which is near-empty for this commit-to-main workflow — so we render the # commit log ourselves and every release shows what actually changed. - name: Generate changelog id: changelog run: | # fetch-depth:0 gets history, but the tag refs the changelog needs # aren't always present in a detached-HEAD tag checkout — pull them in. git fetch --tags --force --quiet origin 2>/dev/null || true TAG="${{ github.event.inputs.tag || github.ref_name }}" PREV="$(git describe --tags --abbrev=0 "${TAG}^" 2>/dev/null || true)" RANGE="${TAG}" [ -n "$PREV" ] && RANGE="${PREV}..${TAG}" # Group commit subjects by conventional-commit type so the notes are # scannable ("what's new / what's fixed") instead of a flat dev log. LOG="$(git log "$RANGE" --no-merges --pretty='%s' | grep -v '^chore(release)' || true)" # NOTE: the job runs under `bash -e`. grep returning 1 (no match) and # the `[ -n "$body" ]` test returning 1 (empty section) must NOT abort # the script — otherwise a release whose commit range lacks a whole # category (e.g. only `feat`, no `fix`) dies before writing the closing # heredoc delimiter and the whole release step fails. `|| true` + # `return 0` keep section() always-succeeding. section() { # $1=header $2=grep-pattern local body; body="$(printf '%s\n' "$LOG" | grep -E "$2" | sed 's/^/- /' || true)" [ -n "$body" ] && printf '\n### %s\n%s\n' "$1" "$body" return 0 } { echo "notes<<__NOTES_EOF__" echo "## What changed" section "✨ Features" '^feat' section "🐛 Fixes" '^fix' section "🔧 Other" '^(perf|refactor|docs|build|ci|test|style|revert)' if [ -n "$PREV" ]; then echo "" echo "**Full changelog**: https://github.com/${{ github.repository }}/compare/${PREV}...${TAG}" fi echo "__NOTES_EOF__" } >> "$GITHUB_OUTPUT" - name: Attach to release uses: softprops/action-gh-release@v3 with: tag_name: ${{ github.event.inputs.tag || github.ref_name }} files: | dist/*.tar.gz dist/*.tar.gz.sha256 fail_on_unmatched_files: true # The commit-based changelog so every release shows what changed. The # first matrix job to run creates the release with these notes; # append_body:false keeps later platform jobs from duplicating them. body: ${{ steps.changelog.outputs.notes }} append_body: false