feat: Add Windows x86_64-pc-windows-msvc binary support to release pipeline

- Add Windows target (x86_64-pc-windows-msvc) to GitHub Actions build matrix
- Install NASM via Chocolatey for BoringSSL compilation on Windows
- Implement platform-specific packaging: .zip for Windows, .tar.gz for Unix
- Update checksum computation to handle both .tar.gz and .zip formats
- Include Windows .exe binaries in GitHub Releases

Supported platforms: macOS (x86_64/aarch64), Linux (x86_64/aarch64), Windows (x86_64)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
notrealsuryansh 2026-05-05 11:37:36 +05:30
parent 615f326660
commit da0f8abeff

View file

@ -1,250 +1,270 @@
name: Release name: Release
on: on:
push: push:
tags: ["v*"] tags: ["v*"]
permissions: permissions:
contents: write contents: write
packages: write packages: write
env: env:
CARGO_TERM_COLOR: always CARGO_TERM_COLOR: always
jobs: jobs:
build: build:
name: Build ${{ matrix.target }} name: Build ${{ matrix.target }}
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
include: include:
- target: x86_64-apple-darwin - target: x86_64-apple-darwin
os: macos-latest os: macos-latest
- target: aarch64-apple-darwin - target: aarch64-apple-darwin
os: macos-latest os: macos-latest
- target: x86_64-unknown-linux-gnu - target: x86_64-unknown-linux-gnu
os: ubuntu-latest os: ubuntu-latest
- target: aarch64-unknown-linux-gnu - target: aarch64-unknown-linux-gnu
os: ubuntu-latest os: ubuntu-latest
- target: x86_64-pc-windows-msvc
steps: os: windows-latest
- uses: actions/checkout@v4
steps:
- uses: dtolnay/rust-toolchain@stable - uses: actions/checkout@v4
with:
targets: ${{ matrix.target }} - uses: dtolnay/rust-toolchain@stable
with:
- uses: Swatinem/rust-cache@v2 targets: ${{ matrix.target }}
with:
key: ${{ matrix.target }} - uses: Swatinem/rust-cache@v2
with:
# Cross-compilation support for aarch64-linux key: ${{ matrix.target }}
# boring-sys2 builds BoringSSL from C source via cmake — needs cross-compiler + cmake
- name: Install cross-compilation tools # Cross-compilation support for aarch64-linux
if: matrix.target == 'aarch64-unknown-linux-gnu' # boring-sys2 builds BoringSSL from C source via cmake — needs cross-compiler + cmake
run: | - name: Install cross-compilation tools
sudo apt-get update if: matrix.target == 'aarch64-unknown-linux-gnu'
sudo apt-get install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu cmake run: |
echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc" >> $GITHUB_ENV sudo apt-get update
echo "CC_aarch64_unknown_linux_gnu=aarch64-linux-gnu-gcc" >> $GITHUB_ENV sudo apt-get install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu cmake
echo "CXX_aarch64_unknown_linux_gnu=aarch64-linux-gnu-g++" >> $GITHUB_ENV echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc" >> $GITHUB_ENV
echo "CMAKE_SYSTEM_NAME=Linux" >> $GITHUB_ENV echo "CC_aarch64_unknown_linux_gnu=aarch64-linux-gnu-gcc" >> $GITHUB_ENV
echo "CMAKE_SYSTEM_PROCESSOR=aarch64" >> $GITHUB_ENV echo "CXX_aarch64_unknown_linux_gnu=aarch64-linux-gnu-g++" >> $GITHUB_ENV
echo "CMAKE_SYSTEM_NAME=Linux" >> $GITHUB_ENV
# BoringSSL build tools for native targets echo "CMAKE_SYSTEM_PROCESSOR=aarch64" >> $GITHUB_ENV
- name: Install cmake
if: matrix.target != 'aarch64-unknown-linux-gnu' && runner.os == 'Linux' # BoringSSL build tools for native targets
run: sudo apt-get update && sudo apt-get install -y cmake - name: Install cmake
if: matrix.target != 'aarch64-unknown-linux-gnu' && runner.os == 'Linux'
- name: Build run: sudo apt-get update && sudo apt-get install -y cmake
run: cargo build --release --target ${{ matrix.target }}
- name: Install NASM (Windows)
- name: Package if: runner.os == 'Windows'
shell: bash run: |
run: | choco install nasm -y
tag="${GITHUB_REF#refs/tags/}" echo "C:\Program Files\NASM" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
staging="webclaw-${tag}-${{ matrix.target }}"
mkdir "$staging" - name: Build
# Fail loud if any binary is missing. A silent `|| true` on the run: cargo build --release --target ${{ matrix.target }}
# copy was how v0.4.0 shipped tarballs that lacked webclaw-server —
# don't repeat that mistake. If a future binary gets renamed or - name: Package
# removed, this step should scream, not quietly publish an shell: bash
# incomplete release. run: |
cp target/${{ matrix.target }}/release/webclaw "$staging/" tag="${GITHUB_REF#refs/tags/}"
cp target/${{ matrix.target }}/release/webclaw-mcp "$staging/" staging="webclaw-${tag}-${{ matrix.target }}"
cp target/${{ matrix.target }}/release/webclaw-server "$staging/" mkdir "$staging"
cp README.md LICENSE "$staging/" # Fail loud if any binary is missing. A silent `|| true` on the
tar czf "$staging.tar.gz" "$staging" # copy was how v0.4.0 shipped tarballs that lacked webclaw-server —
echo "ASSET=$staging.tar.gz" >> $GITHUB_ENV # don't repeat that mistake. If a future binary gets renamed or
# removed, this step should scream, not quietly publish an
- name: Upload artifact # incomplete release.
uses: actions/upload-artifact@v4
with: if [[ "${{ matrix.os }}" == "windows-latest" ]]; then
name: ${{ matrix.target }} cp target/${{ matrix.target }}/release/webclaw.exe "$staging/"
path: ${{ env.ASSET }} cp target/${{ matrix.target }}/release/webclaw-mcp.exe "$staging/"
cp target/${{ matrix.target }}/release/webclaw-server.exe "$staging/"
release: cp README.md LICENSE "$staging/"
name: Release 7z a -tzip "$staging.zip" "$staging"
needs: build echo "ASSET=$staging.zip" >> $GITHUB_ENV
runs-on: ubuntu-latest else
steps: cp target/${{ matrix.target }}/release/webclaw "$staging/"
- uses: actions/checkout@v4 cp target/${{ matrix.target }}/release/webclaw-mcp "$staging/"
cp target/${{ matrix.target }}/release/webclaw-server "$staging/"
- uses: actions/download-artifact@v4 cp README.md LICENSE "$staging/"
with: tar czf "$staging.tar.gz" "$staging"
path: artifacts echo "ASSET=$staging.tar.gz" >> $GITHUB_ENV
fi
- name: Compute checksums
run: | - name: Upload artifact
cd artifacts uses: actions/upload-artifact@v4
find . -name '*.tar.gz' -exec mv {} . \; with:
sha256sum *.tar.gz > SHA256SUMS name: ${{ matrix.target }}
cat SHA256SUMS path: ${{ env.ASSET }}
- name: Create GitHub Release release:
uses: softprops/action-gh-release@v2 name: Release
with: needs: build
generate_release_notes: true runs-on: ubuntu-latest
files: | steps:
artifacts/*.tar.gz - uses: actions/checkout@v4
artifacts/SHA256SUMS
- uses: actions/download-artifact@v4
docker: with:
name: Docker path: artifacts
needs: release
runs-on: ubuntu-latest - name: Compute checksums
steps: run: |
- uses: actions/checkout@v4 cd artifacts
find . -name '*.tar.gz' -exec mv {} . \;
- uses: docker/setup-qemu-action@v3 find . -name '*.zip' -exec mv {} . \;
with: sha256sum *.tar.gz *.zip > SHA256SUMS 2>/dev/null || sha256sum * > SHA256SUMS
platforms: arm64 cat SHA256SUMS
- uses: docker/setup-buildx-action@v3 - name: Create GitHub Release
uses: softprops/action-gh-release@v2
- uses: docker/login-action@v3 with:
with: generate_release_notes: true
registry: ghcr.io files: |
username: ${{ github.actor }} artifacts/*.tar.gz
password: ${{ secrets.GITHUB_TOKEN }} artifacts/*.zip
artifacts/SHA256SUMS
# Download pre-built binaries for both architectures
- name: Download release binaries docker:
run: | name: Docker
tag="${GITHUB_REF#refs/tags/}" needs: release
for target in x86_64-unknown-linux-gnu aarch64-unknown-linux-gnu; do runs-on: ubuntu-latest
dir="webclaw-${tag}-${target}" steps:
curl -sSL "https://github.com/0xMassi/webclaw/releases/download/${tag}/${dir}.tar.gz" -o "${target}.tar.gz" - uses: actions/checkout@v4
tar xzf "${target}.tar.gz"
mkdir -p "binaries-${target}" - uses: docker/setup-qemu-action@v3
cp "${dir}/webclaw" "binaries-${target}/webclaw" with:
cp "${dir}/webclaw-mcp" "binaries-${target}/webclaw-mcp" platforms: arm64
cp "${dir}/webclaw-server" "binaries-${target}/webclaw-server"
chmod +x "binaries-${target}"/* - uses: docker/setup-buildx-action@v3
done
ls -laR binaries-*/ - uses: docker/login-action@v3
with:
# Build per-arch images with plain docker build (no buildx manifest nesting) registry: ghcr.io
- name: Build and push username: ${{ github.actor }}
run: | password: ${{ secrets.GITHUB_TOKEN }}
tag="${GITHUB_REF#refs/tags/}"
# Download pre-built binaries for both architectures
# amd64 - name: Download release binaries
docker build -f Dockerfile.ci --build-arg BINARY_DIR=binaries-x86_64-unknown-linux-gnu \ run: |
--platform linux/amd64 -t ghcr.io/0xmassi/webclaw:${tag}-amd64 --push . tag="${GITHUB_REF#refs/tags/}"
for target in x86_64-unknown-linux-gnu aarch64-unknown-linux-gnu; do
# arm64 dir="webclaw-${tag}-${target}"
docker build -f Dockerfile.ci --build-arg BINARY_DIR=binaries-aarch64-unknown-linux-gnu \ curl -sSL "https://github.com/0xMassi/webclaw/releases/download/${tag}/${dir}.tar.gz" -o "${target}.tar.gz"
--platform linux/arm64 -t ghcr.io/0xmassi/webclaw:${tag}-arm64 --push . tar xzf "${target}.tar.gz"
mkdir -p "binaries-${target}"
# Multi-arch manifest cp "${dir}/webclaw" "binaries-${target}/webclaw"
docker manifest create ghcr.io/0xmassi/webclaw:${tag} \ cp "${dir}/webclaw-mcp" "binaries-${target}/webclaw-mcp"
ghcr.io/0xmassi/webclaw:${tag}-amd64 \ cp "${dir}/webclaw-server" "binaries-${target}/webclaw-server"
ghcr.io/0xmassi/webclaw:${tag}-arm64 chmod +x "binaries-${target}"/*
docker manifest push ghcr.io/0xmassi/webclaw:${tag} done
ls -laR binaries-*/
docker manifest create ghcr.io/0xmassi/webclaw:latest \
ghcr.io/0xmassi/webclaw:${tag}-amd64 \ # Build per-arch images with plain docker build (no buildx manifest nesting)
ghcr.io/0xmassi/webclaw:${tag}-arm64 - name: Build and push
docker manifest push ghcr.io/0xmassi/webclaw:latest run: |
tag="${GITHUB_REF#refs/tags/}"
homebrew:
name: Update Homebrew # amd64
needs: [release, docker] docker build -f Dockerfile.ci --build-arg BINARY_DIR=binaries-x86_64-unknown-linux-gnu \
runs-on: ubuntu-latest --platform linux/amd64 -t ghcr.io/0xmassi/webclaw:${tag}-amd64 --push .
steps:
- name: Compute all checksums and update formula # arm64
env: docker build -f Dockerfile.ci --build-arg BINARY_DIR=binaries-aarch64-unknown-linux-gnu \
COMMITTER_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} --platform linux/arm64 -t ghcr.io/0xmassi/webclaw:${tag}-arm64 --push .
run: |
tag="${GITHUB_REF#refs/tags/}" # Multi-arch manifest
base="https://github.com/0xMassi/webclaw/releases/download/${tag}" docker manifest create ghcr.io/0xmassi/webclaw:${tag} \
ghcr.io/0xmassi/webclaw:${tag}-amd64 \
# Download all 4 tarballs and compute SHAs ghcr.io/0xmassi/webclaw:${tag}-arm64
for target in aarch64-apple-darwin x86_64-apple-darwin aarch64-unknown-linux-gnu x86_64-unknown-linux-gnu; do docker manifest push ghcr.io/0xmassi/webclaw:${tag}
curl -sSL "${base}/webclaw-${tag}-${target}.tar.gz" -o "${target}.tar.gz"
done docker manifest create ghcr.io/0xmassi/webclaw:latest \
ghcr.io/0xmassi/webclaw:${tag}-amd64 \
SHA_MAC_ARM=$(sha256sum aarch64-apple-darwin.tar.gz | cut -d' ' -f1) ghcr.io/0xmassi/webclaw:${tag}-arm64
SHA_MAC_X86=$(sha256sum x86_64-apple-darwin.tar.gz | cut -d' ' -f1) docker manifest push ghcr.io/0xmassi/webclaw:latest
SHA_LINUX_ARM=$(sha256sum aarch64-unknown-linux-gnu.tar.gz | cut -d' ' -f1)
SHA_LINUX_X86=$(sha256sum x86_64-unknown-linux-gnu.tar.gz | cut -d' ' -f1) homebrew:
name: Update Homebrew
echo "macOS arm64: $SHA_MAC_ARM" needs: [release, docker]
echo "macOS x86_64: $SHA_MAC_X86" runs-on: ubuntu-latest
echo "Linux arm64: $SHA_LINUX_ARM" steps:
echo "Linux x86_64: $SHA_LINUX_X86" - name: Compute all checksums and update formula
env:
# Generate formula COMMITTER_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}
cat > webclaw.rb << FORMULA run: |
class Webclaw < Formula tag="${GITHUB_REF#refs/tags/}"
desc "The fastest web scraper for AI agents. 67% fewer tokens. Sub-ms extraction." base="https://github.com/0xMassi/webclaw/releases/download/${tag}"
homepage "https://webclaw.io"
license "AGPL-3.0" # Download all tarballs (Linux + macOS) and compute SHAs
version "${tag#v}" for target in aarch64-apple-darwin x86_64-apple-darwin aarch64-unknown-linux-gnu x86_64-unknown-linux-gnu; do
curl -sSL "${base}/webclaw-${tag}-${target}.tar.gz" -o "${target}.tar.gz"
on_macos do done
if Hardware::CPU.arm?
url "${base}/webclaw-${tag}-aarch64-apple-darwin.tar.gz" SHA_MAC_ARM=$(sha256sum aarch64-apple-darwin.tar.gz | cut -d' ' -f1)
sha256 "${SHA_MAC_ARM}" SHA_MAC_X86=$(sha256sum x86_64-apple-darwin.tar.gz | cut -d' ' -f1)
else SHA_LINUX_ARM=$(sha256sum aarch64-unknown-linux-gnu.tar.gz | cut -d' ' -f1)
url "${base}/webclaw-${tag}-x86_64-apple-darwin.tar.gz" SHA_LINUX_X86=$(sha256sum x86_64-unknown-linux-gnu.tar.gz | cut -d' ' -f1)
sha256 "${SHA_MAC_X86}"
end echo "macOS arm64: $SHA_MAC_ARM"
end echo "macOS x86_64: $SHA_MAC_X86"
echo "Linux arm64: $SHA_LINUX_ARM"
on_linux do echo "Linux x86_64: $SHA_LINUX_X86"
if Hardware::CPU.arm?
url "${base}/webclaw-${tag}-aarch64-unknown-linux-gnu.tar.gz" # Generate formula
sha256 "${SHA_LINUX_ARM}" cat > webclaw.rb << FORMULA
else class Webclaw < Formula
url "${base}/webclaw-${tag}-x86_64-unknown-linux-gnu.tar.gz" desc "The fastest web scraper for AI agents. 67% fewer tokens. Sub-ms extraction."
sha256 "${SHA_LINUX_X86}" homepage "https://webclaw.io"
end license "AGPL-3.0"
end version "${tag#v}"
def install on_macos do
bin.install "webclaw" if Hardware::CPU.arm?
bin.install "webclaw-mcp" url "${base}/webclaw-${tag}-aarch64-apple-darwin.tar.gz"
bin.install "webclaw-server" sha256 "${SHA_MAC_ARM}"
end else
url "${base}/webclaw-${tag}-x86_64-apple-darwin.tar.gz"
test do sha256 "${SHA_MAC_X86}"
assert_match "webclaw", shell_output("#{bin}/webclaw --version") end
end end
end
FORMULA on_linux do
if Hardware::CPU.arm?
# Remove leading whitespace from heredoc url "${base}/webclaw-${tag}-aarch64-unknown-linux-gnu.tar.gz"
sed -i 's/^ //' webclaw.rb sha256 "${SHA_LINUX_ARM}"
else
# Push to homebrew tap url "${base}/webclaw-${tag}-x86_64-unknown-linux-gnu.tar.gz"
git clone "https://x-access-token:${COMMITTER_TOKEN}@github.com/0xMassi/homebrew-webclaw.git" tap sha256 "${SHA_LINUX_X86}"
cp webclaw.rb tap/Formula/webclaw.rb end
cd tap end
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com" def install
git add Formula/webclaw.rb bin.install "webclaw"
git diff --cached --quiet || git commit -m "Update webclaw to ${tag}" bin.install "webclaw-mcp"
git push bin.install "webclaw-server"
end
test do
assert_match "webclaw", shell_output("#{bin}/webclaw --version")
end
end
FORMULA
# Remove leading whitespace from heredoc
sed -i 's/^ //' webclaw.rb
# Push to homebrew tap
git clone "https://x-access-token:${COMMITTER_TOKEN}@github.com/0xMassi/homebrew-webclaw.git" tap
cp webclaw.rb tap/Formula/webclaw.rb
cd tap
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add Formula/webclaw.rb
git diff --cached --quiet || git commit -m "Update webclaw to ${tag}"
git push