Build Binaries #98
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: Build Binaries | |
| on: | |
| push: | |
| tags: | |
| - 'v*' | |
| workflow_dispatch: | |
| inputs: | |
| tag: | |
| description: 'Tag to build (e.g., v0.12.0)' | |
| required: true | |
| type: string | |
| source_ref: | |
| description: 'Source ref to build/publish (defaults to tag; use only for release recovery)' | |
| required: false | |
| type: string | |
| permissions: {} | |
| concurrency: | |
| group: build-binaries-${{ github.event.inputs.tag || github.ref_name }} | |
| cancel-in-progress: false | |
| jobs: | |
| # Keep the public GitHub Release publication last. Binary assets are staged in | |
| # a draft release first; cleanup removes the draft if later publishing fails. | |
| build: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| env: | |
| RELEASE_TAG: ${{ github.event.inputs.tag || github.ref_name }} | |
| SOURCE_REF: ${{ github.event.inputs.source_ref || github.event.inputs.tag || github.ref_name }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 | |
| with: | |
| ref: ${{ env.SOURCE_REF }} | |
| persist-credentials: false | |
| - name: Setup Bun | |
| uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 | |
| with: | |
| bun-version: 1.3.14 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e | |
| with: | |
| node-version: '24' | |
| registry-url: 'https://registry.npmjs.org' | |
| package-manager-cache: false | |
| - name: Build binaries | |
| run: ./scripts/build-binaries.sh | |
| - name: Prepare GitHub release payload | |
| run: | | |
| set -euo pipefail | |
| mkdir -p release-assets | |
| VERSION="${RELEASE_TAG}" | |
| VERSION="${VERSION#v}" # Remove 'v' prefix | |
| node scripts/release-notes.mjs extract --version "${VERSION}" --tag "${RELEASE_TAG}" --out release-assets/RELEASE_NOTES.md | |
| node scripts/generate-coding-agent-install-lock.mjs --check | |
| cp packages/coding-agent/install-lock/package.json release-assets/pi-coding-agent-install-package.json | |
| cp packages/coding-agent/install-lock/package-lock.json release-assets/pi-coding-agent-install-package-lock.json | |
| cd packages/coding-agent/binaries | |
| binary_assets=( | |
| pi-darwin-arm64.tar.gz | |
| pi-darwin-x64.tar.gz | |
| pi-linux-x64.tar.gz | |
| pi-linux-arm64.tar.gz | |
| pi-windows-x64.zip | |
| pi-windows-arm64.zip | |
| ) | |
| for asset in "${binary_assets[@]}"; do | |
| test -f "${asset}" | |
| done | |
| cp "${binary_assets[@]}" "${GITHUB_WORKSPACE}/release-assets/" | |
| cd "${GITHUB_WORKSPACE}/release-assets" | |
| release_assets=( | |
| pi-darwin-arm64.tar.gz | |
| pi-darwin-x64.tar.gz | |
| pi-linux-x64.tar.gz | |
| pi-linux-arm64.tar.gz | |
| pi-windows-x64.zip | |
| pi-windows-arm64.zip | |
| pi-coding-agent-install-package.json | |
| pi-coding-agent-install-package-lock.json | |
| ) | |
| sha256sum "${release_assets[@]}" > SHA256SUMS | |
| - name: Upload GitHub release payload | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 | |
| with: | |
| name: release-assets-${{ env.RELEASE_TAG }} | |
| path: release-assets/* | |
| if-no-files-found: error | |
| retention-days: 14 | |
| stage-github-release: | |
| runs-on: ubuntu-latest | |
| needs: build | |
| permissions: | |
| actions: read | |
| contents: write | |
| env: | |
| GH_REPO: ${{ github.repository }} | |
| RELEASE_TAG: ${{ github.event.inputs.tag || github.ref_name }} | |
| steps: | |
| - name: Download GitHub release payload | |
| uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 | |
| with: | |
| name: release-assets-${{ env.RELEASE_TAG }} | |
| path: release-assets | |
| - name: Validate GitHub release payload | |
| run: | | |
| set -euo pipefail | |
| cd release-assets | |
| expected_assets=( | |
| pi-darwin-arm64.tar.gz | |
| pi-darwin-x64.tar.gz | |
| pi-linux-x64.tar.gz | |
| pi-linux-arm64.tar.gz | |
| pi-windows-x64.zip | |
| pi-windows-arm64.zip | |
| pi-coding-agent-install-package.json | |
| pi-coding-agent-install-package-lock.json | |
| SHA256SUMS | |
| RELEASE_NOTES.md | |
| ) | |
| for asset in "${expected_assets[@]}"; do | |
| test -f "${asset}" | |
| done | |
| sha256sum -c SHA256SUMS | |
| - name: Create draft GitHub Release and upload binaries | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| set -euo pipefail | |
| cd release-assets | |
| release_assets=( | |
| pi-darwin-arm64.tar.gz | |
| pi-darwin-x64.tar.gz | |
| pi-linux-x64.tar.gz | |
| pi-linux-arm64.tar.gz | |
| pi-windows-x64.zip | |
| pi-windows-arm64.zip | |
| pi-coding-agent-install-package.json | |
| pi-coding-agent-install-package-lock.json | |
| SHA256SUMS | |
| ) | |
| existing_release="$(gh release view "${RELEASE_TAG}" --json isDraft --jq .isDraft 2>/dev/null || true)" | |
| if [[ "${existing_release}" == "false" ]]; then | |
| echo "::error::GitHub Release ${RELEASE_TAG} is already published. Refusing to mutate a public release." | |
| exit 1 | |
| fi | |
| if [[ "${existing_release}" == "true" ]]; then | |
| gh release delete "${RELEASE_TAG}" --yes | |
| fi | |
| gh release create "${RELEASE_TAG}" \ | |
| --verify-tag \ | |
| --draft \ | |
| --title "${RELEASE_TAG}" \ | |
| --notes-file RELEASE_NOTES.md \ | |
| "${release_assets[@]}" | |
| expected_asset_names="$(printf '%s\n' "${release_assets[@]}" | sort)" | |
| actual_asset_names="$(gh release view "${RELEASE_TAG}" --json assets --jq '.assets[].name' | sort)" | |
| if [[ "${actual_asset_names}" != "${expected_asset_names}" ]]; then | |
| echo "::error::Draft GitHub Release asset set does not match expected files." | |
| diff -u <(printf '%s\n' "${expected_asset_names}") <(printf '%s\n' "${actual_asset_names}") || true | |
| exit 1 | |
| fi | |
| publish-npm: | |
| runs-on: ubuntu-latest | |
| needs: stage-github-release | |
| environment: npm-publish | |
| permissions: | |
| contents: read | |
| id-token: write | |
| env: | |
| RELEASE_TAG: ${{ github.event.inputs.tag || github.ref_name }} | |
| SOURCE_REF: ${{ github.event.inputs.source_ref || github.event.inputs.tag || github.ref_name }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 | |
| with: | |
| ref: ${{ env.SOURCE_REF }} | |
| persist-credentials: false | |
| - name: Setup Node.js | |
| uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e | |
| with: | |
| node-version: '24' | |
| registry-url: 'https://registry.npmjs.org' | |
| cache: npm | |
| - name: Install system dependencies | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y libcairo2-dev libpango1.0-dev libjpeg-dev libgif-dev librsvg2-dev fd-find ripgrep | |
| sudo ln -s "$(which fdfind)" /usr/local/bin/fd | |
| - name: Install dependencies | |
| run: npm ci --ignore-scripts | |
| # Build only — the tagged commit already passed the required "Check and test" | |
| # status gate on main (full `npm run check` + `npm test`). Re-running the whole | |
| # workspace test suite here is redundant and, on this contended publish runner, | |
| # its subprocess-heavy suites (app-server/daemon, MCP, PTY) leak handles and hang | |
| # the job. Build produces the dist artifacts `scripts/publish.mjs` bundles. | |
| - name: Build | |
| run: npm run build | |
| - name: Upgrade npm for trusted publishing | |
| run: | | |
| npm install -g npm@11.16.0 --ignore-scripts | |
| npm --version | |
| - name: Publish npm packages | |
| run: node scripts/publish.mjs | |
| publish-github-release: | |
| runs-on: ubuntu-latest | |
| needs: | |
| - stage-github-release | |
| - publish-npm | |
| permissions: | |
| contents: write | |
| env: | |
| GH_REPO: ${{ github.repository }} | |
| RELEASE_TAG: ${{ github.event.inputs.tag || github.ref_name }} | |
| steps: | |
| - name: Publish staged GitHub Release | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| set -euo pipefail | |
| existing_release="$(gh release view "${RELEASE_TAG}" --json isDraft --jq .isDraft 2>/dev/null || true)" | |
| if [[ "${existing_release}" == "" ]]; then | |
| echo "::error::Draft GitHub Release ${RELEASE_TAG} does not exist." | |
| exit 1 | |
| fi | |
| if [[ "${existing_release}" == "false" ]]; then | |
| echo "::error::GitHub Release ${RELEASE_TAG} is already published." | |
| exit 1 | |
| fi | |
| gh release edit "${RELEASE_TAG}" --draft=false | |
| cleanup-draft-github-release: | |
| runs-on: ubuntu-latest | |
| needs: | |
| - build | |
| - stage-github-release | |
| - publish-npm | |
| - publish-github-release | |
| if: ${{ always() && needs.stage-github-release.result != 'skipped' && (needs.stage-github-release.result != 'success' || needs.publish-npm.result != 'success' || needs.publish-github-release.result != 'success') }} | |
| permissions: | |
| contents: write | |
| env: | |
| GH_REPO: ${{ github.repository }} | |
| RELEASE_TAG: ${{ github.event.inputs.tag || github.ref_name }} | |
| steps: | |
| - name: Delete draft GitHub Release after failure | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| set -euo pipefail | |
| existing_release="$(gh release view "${RELEASE_TAG}" --json isDraft --jq .isDraft 2>/dev/null || true)" | |
| if [[ "${existing_release}" == "true" ]]; then | |
| gh release delete "${RELEASE_TAG}" --yes | |
| fi |