From 64454c3d5f0e4aa5c6757224ca2c73bcf3ef968d Mon Sep 17 00:00:00 2001 From: dyoshikawa Date: Sat, 31 Oct 2026 10:58:15 +0900 Subject: [PATCH] ci: build release binaries with Bun's AOT compiler Compile the binaries with `bun compile` (Bun canary) on a native runner per target, since AOT cannot cross-compile. Locally on linux-x64 the AOT binary starts in ~40 ms instead of ~220 ms with --bytecode and is 104 MB instead of 163 MB, and passes the whole E2E suite. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01XcU6mYjWis3a6YpqCW2xgB --- .github/actions/build-bun-binary/action.yml | 38 +++++++++ .github/workflows/e2e-binaries.yml | 94 +++++---------------- .github/workflows/publish-assets.yml | 80 +++++++++++++++--- 3 files changed, 128 insertions(+), 84 deletions(-) create mode 100644 .github/actions/build-bun-binary/action.yml diff --git a/.github/actions/build-bun-binary/action.yml b/.github/actions/build-bun-binary/action.yml new file mode 100644 index 000000000..238f16931 --- /dev/null +++ b/.github/actions/build-bun-binary/action.yml @@ -0,0 +1,38 @@ +name: Build Bun binary +description: >- + Compile the rulesync CLI into a standalone executable for the runner's own + platform with Bun's ahead-of-time compiler. Install the dependencies first. + +inputs: + outfile: + description: Path of the executable to write (e.g. dist-bun/rulesync-linux-x64) + required: true + +runs: + using: composite + steps: + - name: Switch Bun to canary + # AOT compilation (`bun compile`) has only shipped in canary builds so + # far. Canary moves daily and cannot be pinned, so the exact revision is + # printed for every build. + shell: bash + run: | + bun upgrade --canary + bun --revision + + - name: Compile binary + # `bun compile` is `bun build --compile --aot`: the code is compiled to + # machine code at build time, which starts much faster and makes a + # smaller executable than --bytecode. AOT cannot cross-compile, so this + # builds for the runner's platform only. --sourcemap is left out because + # an AOT binary already reports the positions of its sources. + shell: bash + env: + OUTFILE: ${{ inputs.outfile }} + run: bun compile --no-script --minify --outfile="$OUTFILE" ./src/cli/index.ts + + - name: Smoke test binary + shell: bash + env: + OUTFILE: ${{ inputs.outfile }} + run: '"$OUTFILE" --version' diff --git a/.github/workflows/e2e-binaries.yml b/.github/workflows/e2e-binaries.yml index 40d8e777e..9ce3b4baa 100644 --- a/.github/workflows/e2e-binaries.yml +++ b/.github/workflows/e2e-binaries.yml @@ -21,90 +21,33 @@ permissions: contents: read # Required for checking out code jobs: - build: - name: Build binaries - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - - - name: Setup mise - uses: jdx/mise-action@6be3c7ed4c21d4f76b208d351d95aac273ebb76d # v5.2.4 - with: - experimental: true - - - name: Install dependencies - # Bun is not supported by the setup action. The tracked .npmrc still - # routes this installation through Takumi Guard. - run: bun install - - - name: Build binaries - # --bytecode precompiles the bundle so the binaries start faster. It is - # left off the Windows target: that binary segfaults at startup when built - # with --bytecode (Bun 1.3.14). - run: | - mkdir -p dist-bun - bun build --compile --minify --sourcemap --bytecode --target=bun-linux-x64 --outfile=dist-bun/rulesync-linux-x64 ./src/cli/index.ts - bun build --compile --minify --sourcemap --bytecode --target=bun-linux-arm64 --outfile=dist-bun/rulesync-linux-arm64 ./src/cli/index.ts - bun build --compile --minify --sourcemap --bytecode --target=bun-darwin-arm64 --outfile=dist-bun/rulesync-darwin-arm64 ./src/cli/index.ts - bun build --compile --minify --sourcemap --bytecode --target=bun-darwin-x64 --outfile=dist-bun/rulesync-darwin-x64 ./src/cli/index.ts - bun build --compile --minify --sourcemap --target=bun-windows-x64 --outfile=dist-bun/rulesync-windows-x64.exe ./src/cli/index.ts - - - name: Cache binaries - uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 - with: - path: dist-bun - key: rulesync-binaries-${{ github.sha }} - enableCrossOsArchive: true - e2e: - name: E2E Test (${{ matrix.os }}) + name: E2E Test (${{ matrix.target }}) runs-on: ${{ matrix.os }} - needs: build strategy: # Keep every platform leg running even if one fails, so a single # platform-specific breakage does not mask the others' results. fail-fast: false matrix: - os: [ubuntu-latest, macos-latest, windows-latest] + # Bun's AOT compiler only targets the platform it runs on, so each + # binary is built and tested on a native runner of its own. + include: + - target: linux-x64 + os: ubuntu-latest + - target: linux-arm64 + os: ubuntu-24.04-arm + - target: darwin-arm64 + os: macos-latest + - target: darwin-x64 + os: macos-15-intel + - target: windows-x64 + os: windows-latest + ext: .exe steps: - name: Checkout code uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - - name: Restore cached binaries - uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 - with: - path: dist-bun - key: rulesync-binaries-${{ github.sha }} - enableCrossOsArchive: true - fail-on-cache-miss: true - - - name: Make binary executable (Unix) - if: runner.os != 'Windows' - env: - MATRIX_OS: ${{ matrix.os }} - run: | - if [ "$MATRIX_OS" = "ubuntu-latest" ]; then - chmod +x dist-bun/rulesync-linux-x64 - BINARY_PATH=dist-bun/rulesync-linux-x64 - elif [ "$MATRIX_OS" = "macos-latest" ]; then - # macos-latest is Apple Silicon (arm64) - chmod +x dist-bun/rulesync-darwin-arm64 - BINARY_PATH=dist-bun/rulesync-darwin-arm64 - else - echo "Unexpected matrix OS for Unix binary selection: $MATRIX_OS" >&2 - exit 1 - fi - echo "BINARY_PATH=$BINARY_PATH" >> "$GITHUB_ENV" - - - name: Set binary path (Windows) - if: runner.os == 'Windows' - shell: bash - run: | - echo "BINARY_PATH=dist-bun/rulesync-windows-x64.exe" >> "$GITHUB_ENV" - - name: Setup mise uses: jdx/mise-action@6be3c7ed4c21d4f76b208d351d95aac273ebb76d # v5.2.4 with: @@ -116,7 +59,12 @@ jobs: - name: Install dependencies run: pnpm install --ignore-scripts + - name: Build binary + uses: ./.github/actions/build-bun-binary + with: + outfile: dist-bun/rulesync-${{ matrix.target }}${{ matrix.ext }} + - name: Test binary env: - RULESYNC_CMD: ${{ env.BINARY_PATH }} + RULESYNC_CMD: dist-bun/rulesync-${{ matrix.target }}${{ matrix.ext }} run: pnpm test:e2e diff --git a/.github/workflows/publish-assets.yml b/.github/workflows/publish-assets.yml index 3d001d1cd..024cf678f 100644 --- a/.github/workflows/publish-assets.yml +++ b/.github/workflows/publish-assets.yml @@ -19,9 +19,68 @@ permissions: contents: write # Required for uploading release assets jobs: + build-binaries: + name: Build binary (${{ matrix.target }}) + runs-on: ${{ matrix.os }} + if: >- + github.event_name == 'workflow_dispatch' || + ( + github.event.pull_request.merged == true && + startsWith(github.event.pull_request.head.ref, 'release/v') + ) + permissions: + contents: read # Required for checking out code + strategy: + matrix: + # Bun's AOT compiler only targets the platform it runs on, so each + # binary is built on a native runner of its own. + include: + - target: linux-x64 + os: ubuntu-latest + - target: linux-arm64 + os: ubuntu-24.04-arm + - target: darwin-arm64 + os: macos-latest + - target: darwin-x64 + os: macos-15-intel + - target: windows-x64 + os: windows-latest + ext: .exe + + steps: + - name: Checkout code + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + # Build the same commit the build-assets job tags. + ref: ${{ github.event.pull_request.merge_commit_sha || 'main' }} + + - name: Setup mise + uses: jdx/mise-action@6be3c7ed4c21d4f76b208d351d95aac273ebb76d # v5.2.4 + with: + experimental: true + + - name: Install dependencies + # Bun is not supported by the setup action. The tracked .npmrc still + # routes this installation through Takumi Guard. + run: bun install + + - name: Build binary + uses: ./.github/actions/build-bun-binary + with: + outfile: dist-bun/rulesync-${{ matrix.target }}${{ matrix.ext }} + + - name: Upload binary + uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 + with: + name: rulesync-${{ matrix.target }} + path: dist-bun/rulesync-${{ matrix.target }}${{ matrix.ext }} + if-no-files-found: error + retention-days: 1 + build-assets: name: Build and upload assets runs-on: ubuntu-latest + needs: build-binaries if: >- github.event_name == 'workflow_dispatch' || ( @@ -59,17 +118,16 @@ jobs: # routes this installation through Takumi Guard. run: bun install - - name: Build binaries - # --bytecode precompiles the bundle so the binaries start faster. It is - # left off the Windows target: that binary segfaults at startup when built - # with --bytecode (Bun 1.3.14). - run: | - mkdir -p dist-bun - bun build --compile --minify --sourcemap --bytecode --target=bun-linux-x64 --outfile=dist-bun/rulesync-linux-x64 ./src/cli/index.ts - bun build --compile --minify --sourcemap --bytecode --target=bun-linux-arm64 --outfile=dist-bun/rulesync-linux-arm64 ./src/cli/index.ts - bun build --compile --minify --sourcemap --bytecode --target=bun-darwin-arm64 --outfile=dist-bun/rulesync-darwin-arm64 ./src/cli/index.ts - bun build --compile --minify --sourcemap --bytecode --target=bun-darwin-x64 --outfile=dist-bun/rulesync-darwin-x64 ./src/cli/index.ts - bun build --compile --minify --sourcemap --target=bun-windows-x64 --outfile=dist-bun/rulesync-windows-x64.exe ./src/cli/index.ts + - name: Download binaries + uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2 + with: + pattern: rulesync-* + path: dist-bun + merge-multiple: true + + - name: Make binaries executable + # Artifacts do not keep the executable bit. + run: chmod +x dist-bun/rulesync-* - name: Generate checksums run: |