From 07316598bdf8c9a5c04ff67ae8b3f2518a6e6706 Mon Sep 17 00:00:00 2001 From: Daniel Young Lee Date: Sun, 10 Aug 2025 12:44:05 -0700 Subject: [PATCH] feat: add native binary compilation to releases - Add Bun-based native binary compilation for macOS, Linux, and Windows - Create build_binaries.js script to compile from bundle/gemini.js - Update release workflow to build and include binaries in GitHub releases - Add build:binaries npm script Related to #3804 --- .github/workflows/release.yml | 9 ++++ package.json | 1 + scripts/build_binaries.js | 78 +++++++++++++++++++++++++++++++++++ 3 files changed, 88 insertions(+) create mode 100644 scripts/build_binaries.js diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a011f776b4..bbbcdbe97b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -74,6 +74,11 @@ jobs: node-version: '20' cache: 'npm' + - name: Setup Bun + uses: oven-sh/setup-bun@4bc047ad259df6fc24a6c9b0f9a0cb08cf17fbe5 # v2 + with: + bun-version: '1.2.20' + - name: Install Dependencies run: npm ci @@ -129,6 +134,9 @@ jobs: npm run build:packages npm run prepare:package + - name: Build Native Binaries + run: npm run build:binaries + - name: Configure npm for publishing uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: @@ -158,6 +166,7 @@ jobs: run: | gh release create ${{ steps.version.outputs.RELEASE_TAG }} \ bundle/gemini.js \ + bundle/binaries/* \ --target "$RELEASE_BRANCH" \ --title "Release ${{ steps.version.outputs.RELEASE_TAG }}" \ --generate-notes diff --git a/package.json b/package.json index 281f59fec9..1a7eb852bc 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "build:all": "npm run build && npm run build:sandbox && npm run build:vscode", "build:packages": "npm run build --workspaces", "build:sandbox": "node scripts/build_sandbox.js --skip-npm-install-build", + "build:binaries": "node scripts/build_binaries.js", "bundle": "npm run generate && node esbuild.config.js && node scripts/copy_bundle_assets.js", "test": "npm run test --workspaces --if-present", "test:ci": "npm run test:ci --workspaces --if-present && npm run test:scripts", diff --git a/scripts/build_binaries.js b/scripts/build_binaries.js new file mode 100644 index 0000000000..b54cb01c27 --- /dev/null +++ b/scripts/build_binaries.js @@ -0,0 +1,78 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import { execSync } from 'child_process'; +import fs from 'fs'; +import path from 'path'; +import { fileURLToPath } from 'url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const rootDir = path.resolve(__dirname, '..'); + +const bundleJs = path.join(rootDir, 'bundle', 'gemini.js'); +const outputDir = path.join(rootDir, 'bundle', 'binaries'); + +// Binary targets configuration +const targets = [ + { name: 'gemini-cli-darwin-x64', target: 'bun-darwin-x64' }, + { name: 'gemini-cli-darwin-arm64', target: 'bun-darwin-arm64' }, + { name: 'gemini-cli-linux-x64', target: 'bun-linux-x64' }, + { name: 'gemini-cli-linux-arm64', target: 'bun-linux-arm64' }, + { name: 'gemini-cli-windows-x64.exe', target: 'bun-windows-x64' }, +]; + +// Check if bundle/gemini.js exists +if (!fs.existsSync(bundleJs)) { + console.error('Error: bundle/gemini.js not found. Please run "npm run bundle" first.'); + process.exit(1); +} + +// Create output directory +if (!fs.existsSync(outputDir)) { + fs.mkdirSync(outputDir, { recursive: true }); + console.log(`Created directory: ${outputDir}`); +} + +console.log('Building native binaries from bundle/gemini.js...\n'); + +let successCount = 0; +let failedTargets = []; + +for (const { name, target } of targets) { + const outputPath = path.join(outputDir, name); + console.log(`Building ${name}...`); + + try { + const command = `bun build --compile --target=${target} ${bundleJs} --outfile ${outputPath}`; + execSync(command, { stdio: 'pipe' }); + + // Check if file was created + if (fs.existsSync(outputPath)) { + const stats = fs.statSync(outputPath); + const sizeMB = (stats.size / (1024 * 1024)).toFixed(1); + console.log(` ✓ Built ${name} (${sizeMB} MB)`); + successCount++; + } else { + throw new Error('Binary file was not created'); + } + } catch (error) { + console.error(` ✗ Failed to build ${name}`); + console.error(` ${error.message}`); + failedTargets.push(name); + } +} + +console.log('\n' + '='.repeat(50)); +console.log(`Build complete: ${successCount}/${targets.length} binaries built successfully`); + +if (failedTargets.length > 0) { + console.log(`Failed targets: ${failedTargets.join(', ')}`); + console.log('\nNote: Cross-compilation may require Bun 1.1.0+ and might not work for all targets from all host platforms.'); + console.log('In CI, all targets should build successfully on the appropriate runner.'); +} + +console.log(`\nBinaries saved to: ${outputDir}`); \ No newline at end of file