From d780a9352b9d84f2bb673e594ee2e21c316c52f1 Mon Sep 17 00:00:00 2001 From: James Grugett Date: Fri, 15 May 2026 14:34:51 -0700 Subject: [PATCH 1/3] Use baseline Bun target for Windows builds --- .github/workflows/cli-release-build.yml | 2 +- .github/workflows/npm-app-release-build.yml | 2 +- cli/scripts/build-binary.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/cli-release-build.yml b/.github/workflows/cli-release-build.yml index 758794d880..8b23b94af8 100644 --- a/.github/workflows/cli-release-build.yml +++ b/.github/workflows/cli-release-build.yml @@ -321,7 +321,7 @@ jobs: shell: bash env: VERBOSE: true - OVERRIDE_TARGET: bun-windows-x64 + OVERRIDE_TARGET: bun-windows-x64-baseline OVERRIDE_PLATFORM: win32 OVERRIDE_ARCH: x64 diff --git a/.github/workflows/npm-app-release-build.yml b/.github/workflows/npm-app-release-build.yml index 486716d0de..691a41a6e4 100644 --- a/.github/workflows/npm-app-release-build.yml +++ b/.github/workflows/npm-app-release-build.yml @@ -53,7 +53,7 @@ jobs: arch: arm64 - os: windows-latest target: win32-x64 - bun_target: bun-windows-x64 + bun_target: bun-windows-x64-baseline platform: win32 arch: x64 runs-on: ${{ matrix.os }} diff --git a/cli/scripts/build-binary.ts b/cli/scripts/build-binary.ts index 5888808b41..9a10a53249 100644 --- a/cli/scripts/build-binary.ts +++ b/cli/scripts/build-binary.ts @@ -95,7 +95,7 @@ function getTargetInfo(): TargetInfo { arch: 'arm64', }, 'win32-x64': { - bunTarget: 'bun-windows-x64', + bunTarget: 'bun-windows-x64-baseline', platform: 'win32', arch: 'x64', }, From ad6c0e5ac6f410e507a14269adf4d221d1b28acd Mon Sep 17 00:00:00 2001 From: James Grugett Date: Fri, 15 May 2026 14:42:38 -0700 Subject: [PATCH 2/3] Fix Windows baseline Bun compile in CI --- cli/scripts/build-binary.ts | 84 ++++++++++++++++++++++++++++++++++++- 1 file changed, 83 insertions(+), 1 deletion(-) diff --git a/cli/scripts/build-binary.ts b/cli/scripts/build-binary.ts index 9a10a53249..367ac8f95f 100644 --- a/cli/scripts/build-binary.ts +++ b/cli/scripts/build-binary.ts @@ -12,7 +12,7 @@ import { rmSync, writeFileSync, } from 'fs' -import { tmpdir } from 'os' +import { homedir, tmpdir } from 'os' import { dirname, join } from 'path' import { fileURLToPath } from 'url' @@ -28,6 +28,8 @@ const OVERRIDE_PLATFORM = process.env.OVERRIDE_PLATFORM as | NodeJS.Platform | undefined const OVERRIDE_ARCH = process.env.OVERRIDE_ARCH ?? undefined +const OVERRIDE_COMPILE_EXECUTABLE_PATH = + process.env.BUN_COMPILE_EXECUTABLE_PATH const __filename = fileURLToPath(import.meta.url) const __dirname = dirname(__filename) @@ -111,6 +113,82 @@ function getTargetInfo(): TargetInfo { return target } +async function getCompileExecutablePath( + targetInfo: TargetInfo, +): Promise { + if (OVERRIDE_COMPILE_EXECUTABLE_PATH) { + return OVERRIDE_COMPILE_EXECUTABLE_PATH + } + + // Bun 1.3.11 can fail to extract this target from inside `bun build` on + // Windows runners. Pre-fetch the official release zip and point Bun at the + // extracted runtime so release and Freebuff CI builds still produce the + // baseline binary. + if ( + process.platform !== 'win32' || + targetInfo.bunTarget !== 'bun-windows-x64-baseline' + ) { + return undefined + } + + const cacheRoot = join( + process.env.BUN_INSTALL ?? join(homedir(), '.bun'), + 'install', + 'cache', + ) + const runtimeDir = join( + cacheRoot, + `${targetInfo.bunTarget}-v${Bun.version}-runtime`, + ) + const runtimePath = join(runtimeDir, 'bun.exe') + + if (existsSync(runtimePath)) { + return runtimePath + } + + rmSync(runtimeDir, { recursive: true, force: true }) + mkdirSync(runtimeDir, { recursive: true }) + + const zipPath = join(runtimeDir, `${targetInfo.bunTarget}.zip`) + const downloadUrl = `https://github.com/oven-sh/bun/releases/download/bun-v${Bun.version}/${targetInfo.bunTarget}.zip` + + logAlways(`Downloading ${targetInfo.bunTarget}: ${downloadUrl}`) + const response = await fetch(downloadUrl) + if (!response.ok) { + throw new Error( + `Failed to download ${targetInfo.bunTarget}: ${response.status} ${response.statusText}`, + ) + } + writeFileSync(zipPath, new Uint8Array(await response.arrayBuffer())) + + runCommand( + 'powershell.exe', + [ + '-NoProfile', + '-NonInteractive', + '-ExecutionPolicy', + 'Bypass', + '-Command', + `Expand-Archive -LiteralPath '${zipPath.replaceAll("'", "''")}' -DestinationPath '${runtimeDir.replaceAll("'", "''")}' -Force`, + ], + { env: process.env }, + ) + + const extractedRuntimePath = join( + runtimeDir, + 'bun-windows-x64-baseline', + 'bun.exe', + ) + if (!existsSync(extractedRuntimePath)) { + throw new Error( + `Downloaded ${targetInfo.bunTarget}, but bun.exe was not found at ${extractedRuntimePath}`, + ) + } + + writeFileSync(runtimePath, readFileSync(extractedRuntimePath)) + return runtimePath +} + async function main() { const [, , binaryNameArg, version] = process.argv const binaryName = binaryNameArg ?? 'codecane' @@ -122,6 +200,7 @@ async function main() { log(`Building ${binaryName} @ ${version}`) const targetInfo = getTargetInfo() + const compileExecutablePath = await getCompileExecutablePath(targetInfo) const binDir = join(cliRoot, 'bin') if (!existsSync(binDir)) { @@ -172,6 +251,9 @@ async function main() { '--compile', '--production', // Required so compiled binaries use the production JSX runtime (avoids jsxDEV crashes). `--target=${targetInfo.bunTarget}`, + ...(compileExecutablePath + ? [`--compile-executable-path=${compileExecutablePath}`] + : []), `--outfile=${outputFile}`, '--sourcemap=none', ...defineFlags.flatMap(([key, value]) => ['--define', `${key}=${value}`]), From 52f17e7d6159d5cb95ed0a1e31c11d9cfc437161 Mon Sep 17 00:00:00 2001 From: James Grugett Date: Fri, 15 May 2026 14:55:31 -0700 Subject: [PATCH 3/3] Move Bun baseline runtime setup into CI --- .../setup-bun-compile-runtime/action.yml | 51 ++++++++++++ .github/workflows/cli-release-build.yml | 5 ++ .github/workflows/freebuff-e2e.yml | 5 ++ cli/scripts/build-binary.ts | 83 +------------------ 4 files changed, 64 insertions(+), 80 deletions(-) create mode 100644 .github/actions/setup-bun-compile-runtime/action.yml diff --git a/.github/actions/setup-bun-compile-runtime/action.yml b/.github/actions/setup-bun-compile-runtime/action.yml new file mode 100644 index 0000000000..0628278d7d --- /dev/null +++ b/.github/actions/setup-bun-compile-runtime/action.yml @@ -0,0 +1,51 @@ +name: 'Setup Bun Compile Runtime' +description: 'Download and cache a Bun runtime used by bun build --compile-executable-path' + +inputs: + target: + description: 'Bun compile target, for example bun-windows-x64-baseline' + required: true + +runs: + using: 'composite' + steps: + - name: Get Bun version + id: bun-version + shell: bash + run: echo "version=$(bun --version)" >> "$GITHUB_OUTPUT" + + - name: Cache Bun compile runtime + uses: actions/cache@v5 + with: + path: ${{ runner.temp }}/bun-compile-runtimes/${{ inputs.target }}-v${{ steps.bun-version.outputs.version }} + key: ${{ runner.os }}-bun-compile-runtime-${{ inputs.target }}-v${{ steps.bun-version.outputs.version }} + + - name: Prepare Bun compile runtime + shell: pwsh + env: + BUN_COMPILE_TARGET: ${{ inputs.target }} + BUN_VERSION: ${{ steps.bun-version.outputs.version }} + RUNTIME_DIR: ${{ runner.temp }}/bun-compile-runtimes/${{ inputs.target }}-v${{ steps.bun-version.outputs.version }} + run: | + $ErrorActionPreference = 'Stop' + + $runtimePath = Join-Path $env:RUNTIME_DIR 'bun.exe' + if (!(Test-Path -LiteralPath $runtimePath)) { + New-Item -ItemType Directory -Force -Path $env:RUNTIME_DIR | Out-Null + + $zipPath = Join-Path $env:RUNTIME_DIR "$($env:BUN_COMPILE_TARGET).zip" + $downloadUrl = "https://github.com/oven-sh/bun/releases/download/bun-v$($env:BUN_VERSION)/$($env:BUN_COMPILE_TARGET).zip" + + Write-Host "Downloading $($env:BUN_COMPILE_TARGET): $downloadUrl" + Invoke-WebRequest -Uri $downloadUrl -OutFile $zipPath + Expand-Archive -LiteralPath $zipPath -DestinationPath $env:RUNTIME_DIR -Force + + $extractedRuntimePath = Join-Path $env:RUNTIME_DIR "$($env:BUN_COMPILE_TARGET)/bun.exe" + if (!(Test-Path -LiteralPath $extractedRuntimePath)) { + throw "Downloaded $($env:BUN_COMPILE_TARGET), but bun.exe was not found at $extractedRuntimePath" + } + + Copy-Item -LiteralPath $extractedRuntimePath -Destination $runtimePath -Force + } + + "BUN_COMPILE_EXECUTABLE_PATH=$runtimePath" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8 diff --git a/.github/workflows/cli-release-build.yml b/.github/workflows/cli-release-build.yml index 8b23b94af8..741b32bbd7 100644 --- a/.github/workflows/cli-release-build.yml +++ b/.github/workflows/cli-release-build.yml @@ -315,6 +315,11 @@ jobs: echo "$ENV_OVERRIDES" | jq -r 'to_entries | .[] | .key + "=" + .value' >> $GITHUB_ENV fi + - name: Prepare Windows baseline Bun compile runtime + uses: ./.github/actions/setup-bun-compile-runtime + with: + target: bun-windows-x64-baseline + - name: Build binary run: bun run scripts/build-binary.ts ${{ inputs.binary-name }} ${{ inputs.new-version }} working-directory: cli diff --git a/.github/workflows/freebuff-e2e.yml b/.github/workflows/freebuff-e2e.yml index a090ade3ab..f1fc8afbba 100644 --- a/.github/workflows/freebuff-e2e.yml +++ b/.github/workflows/freebuff-e2e.yml @@ -162,6 +162,11 @@ jobs: echo "NEXT_PUBLIC_CB_ENVIRONMENT=prod" >> $GITHUB_ENV echo "CODEBUFF_GITHUB_ACTIONS=true" >> $GITHUB_ENV + - name: Prepare Windows baseline Bun compile runtime + uses: ./.github/actions/setup-bun-compile-runtime + with: + target: bun-windows-x64-baseline + - name: Build Freebuff binary run: bun freebuff/cli/build.ts 0.0.0-e2e shell: bash diff --git a/cli/scripts/build-binary.ts b/cli/scripts/build-binary.ts index 367ac8f95f..3401e85288 100644 --- a/cli/scripts/build-binary.ts +++ b/cli/scripts/build-binary.ts @@ -12,7 +12,7 @@ import { rmSync, writeFileSync, } from 'fs' -import { homedir, tmpdir } from 'os' +import { tmpdir } from 'os' import { dirname, join } from 'path' import { fileURLToPath } from 'url' @@ -113,82 +113,6 @@ function getTargetInfo(): TargetInfo { return target } -async function getCompileExecutablePath( - targetInfo: TargetInfo, -): Promise { - if (OVERRIDE_COMPILE_EXECUTABLE_PATH) { - return OVERRIDE_COMPILE_EXECUTABLE_PATH - } - - // Bun 1.3.11 can fail to extract this target from inside `bun build` on - // Windows runners. Pre-fetch the official release zip and point Bun at the - // extracted runtime so release and Freebuff CI builds still produce the - // baseline binary. - if ( - process.platform !== 'win32' || - targetInfo.bunTarget !== 'bun-windows-x64-baseline' - ) { - return undefined - } - - const cacheRoot = join( - process.env.BUN_INSTALL ?? join(homedir(), '.bun'), - 'install', - 'cache', - ) - const runtimeDir = join( - cacheRoot, - `${targetInfo.bunTarget}-v${Bun.version}-runtime`, - ) - const runtimePath = join(runtimeDir, 'bun.exe') - - if (existsSync(runtimePath)) { - return runtimePath - } - - rmSync(runtimeDir, { recursive: true, force: true }) - mkdirSync(runtimeDir, { recursive: true }) - - const zipPath = join(runtimeDir, `${targetInfo.bunTarget}.zip`) - const downloadUrl = `https://github.com/oven-sh/bun/releases/download/bun-v${Bun.version}/${targetInfo.bunTarget}.zip` - - logAlways(`Downloading ${targetInfo.bunTarget}: ${downloadUrl}`) - const response = await fetch(downloadUrl) - if (!response.ok) { - throw new Error( - `Failed to download ${targetInfo.bunTarget}: ${response.status} ${response.statusText}`, - ) - } - writeFileSync(zipPath, new Uint8Array(await response.arrayBuffer())) - - runCommand( - 'powershell.exe', - [ - '-NoProfile', - '-NonInteractive', - '-ExecutionPolicy', - 'Bypass', - '-Command', - `Expand-Archive -LiteralPath '${zipPath.replaceAll("'", "''")}' -DestinationPath '${runtimeDir.replaceAll("'", "''")}' -Force`, - ], - { env: process.env }, - ) - - const extractedRuntimePath = join( - runtimeDir, - 'bun-windows-x64-baseline', - 'bun.exe', - ) - if (!existsSync(extractedRuntimePath)) { - throw new Error( - `Downloaded ${targetInfo.bunTarget}, but bun.exe was not found at ${extractedRuntimePath}`, - ) - } - - writeFileSync(runtimePath, readFileSync(extractedRuntimePath)) - return runtimePath -} - async function main() { const [, , binaryNameArg, version] = process.argv const binaryName = binaryNameArg ?? 'codecane' @@ -200,7 +124,6 @@ async function main() { log(`Building ${binaryName} @ ${version}`) const targetInfo = getTargetInfo() - const compileExecutablePath = await getCompileExecutablePath(targetInfo) const binDir = join(cliRoot, 'bin') if (!existsSync(binDir)) { @@ -251,8 +174,8 @@ async function main() { '--compile', '--production', // Required so compiled binaries use the production JSX runtime (avoids jsxDEV crashes). `--target=${targetInfo.bunTarget}`, - ...(compileExecutablePath - ? [`--compile-executable-path=${compileExecutablePath}`] + ...(OVERRIDE_COMPILE_EXECUTABLE_PATH + ? [`--compile-executable-path=${OVERRIDE_COMPILE_EXECUTABLE_PATH}`] : []), `--outfile=${outputFile}`, '--sourcemap=none',