mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-08-02 21:21:09 -07:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e98f41fe86 | |||
| 49abf1fe16 | |||
| 55747791ba | |||
| 1487867e5e | |||
| f9c3e0d71a | |||
| 46480326cf | |||
| 07316598bd |
@@ -31,10 +31,10 @@ runs:
|
|||||||
id: prep_coverage_comment
|
id: prep_coverage_comment
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
cli_json_file="${{ inputs.cli_json_file }}"
|
cli_json_file="${INPUTS_CLI_JSON_FILE}"
|
||||||
core_json_file="${{ inputs.core_json_file }}"
|
core_json_file="${INPUTS_CORE_JSON_FILE}"
|
||||||
cli_full_text_summary_file="${{ inputs.cli_full_text_summary_file }}"
|
cli_full_text_summary_file="${INPUTS_CLI_FULL_TEXT_SUMMARY_FILE}"
|
||||||
core_full_text_summary_file="${{ inputs.core_full_text_summary_file }}"
|
core_full_text_summary_file="${INPUTS_CORE_FULL_TEXT_SUMMARY_FILE}"
|
||||||
comment_file="coverage-comment.md"
|
comment_file="coverage-comment.md"
|
||||||
|
|
||||||
# Extract percentages using jq for the main table
|
# Extract percentages using jq for the main table
|
||||||
@@ -94,7 +94,14 @@ runs:
|
|||||||
echo "</details>" >> "$comment_file"
|
echo "</details>" >> "$comment_file"
|
||||||
echo "" >> "$comment_file"
|
echo "" >> "$comment_file"
|
||||||
|
|
||||||
echo "_For detailed HTML reports, please see the 'coverage-reports-${{ inputs.node_version }}-${{ inputs.os }}' artifact from the main CI run._" >> "$comment_file"
|
echo "_For detailed HTML reports, please see the 'coverage-reports-${INPUTS_NODE_VERSION}-${INPUTS_OS}' artifact from the main CI run._" >> "$comment_file"
|
||||||
|
env:
|
||||||
|
INPUTS_CLI_JSON_FILE: ${{ inputs.cli_json_file }}
|
||||||
|
INPUTS_CORE_JSON_FILE: ${{ inputs.core_json_file }}
|
||||||
|
INPUTS_CLI_FULL_TEXT_SUMMARY_FILE: ${{ inputs.cli_full_text_summary_file }}
|
||||||
|
INPUTS_CORE_FULL_TEXT_SUMMARY_FILE: ${{ inputs.core_full_text_summary_file }}
|
||||||
|
INPUTS_NODE_VERSION: ${{ inputs.node_version }}
|
||||||
|
INPUTS_OS: ${{ inputs.os }}
|
||||||
|
|
||||||
- name: Post Coverage Comment
|
- name: Post Coverage Comment
|
||||||
uses: thollander/actions-comment-pull-request@v3
|
uses: thollander/actions-comment-pull-request@v3
|
||||||
|
|||||||
@@ -71,3 +71,24 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
|
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
|
||||||
run: npm run test:e2e
|
run: npm run test:e2e
|
||||||
|
|
||||||
|
e2e-test-native-binary:
|
||||||
|
name: E2E Native Binary Smoke Test
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
|
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||||
|
with:
|
||||||
|
node-version: 20.x
|
||||||
|
cache: 'npm'
|
||||||
|
- uses: oven-sh/setup-bun@4bc047ad259df6fc24a6c9b0f9a0cb08cf17fbe5 # v2
|
||||||
|
with:
|
||||||
|
bun-version: '1.2.20'
|
||||||
|
- run: npm ci
|
||||||
|
- run: npm run bundle
|
||||||
|
- run: npm run build:binaries
|
||||||
|
- name: Smoke test native binary
|
||||||
|
env:
|
||||||
|
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
|
||||||
|
GEMINI_BINARY: ${{ github.workspace }}/bundle/binaries/gemini-cli-linux-x64
|
||||||
|
run: npm run test:integration:sandbox:none -- list_directory
|
||||||
|
|||||||
@@ -57,16 +57,19 @@ jobs:
|
|||||||
id: vars
|
id: vars
|
||||||
run: |
|
run: |
|
||||||
is_nightly="false"
|
is_nightly="false"
|
||||||
if [[ "${{ github.event_name }}" == "schedule" || "${{ github.event.inputs.create_nightly_release }}" == "true" ]]; then
|
if [[ "${{ github.event_name }}" == "schedule" || "${GITHUB_EVENT_INPUTS_CREATE_NIGHTLY_RELEASE}" == "true" ]]; then
|
||||||
is_nightly="true"
|
is_nightly="true"
|
||||||
fi
|
fi
|
||||||
echo "is_nightly=${is_nightly}" >> $GITHUB_OUTPUT
|
echo "is_nightly=${is_nightly}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
is_dry_run="false"
|
is_dry_run="false"
|
||||||
if [[ "${{ github.event.inputs.dry_run }}" == "true" ]]; then
|
if [[ "${GITHUB_EVENT_INPUTS_DRY_RUN}" == "true" ]]; then
|
||||||
is_dry_run="true"
|
is_dry_run="true"
|
||||||
fi
|
fi
|
||||||
echo "is_dry_run=${is_dry_run}" >> $GITHUB_OUTPUT
|
echo "is_dry_run=${is_dry_run}" >> $GITHUB_OUTPUT
|
||||||
|
env:
|
||||||
|
GITHUB_EVENT_INPUTS_CREATE_NIGHTLY_RELEASE: ${{ github.event.inputs.create_nightly_release }}
|
||||||
|
GITHUB_EVENT_INPUTS_DRY_RUN: ${{ github.event.inputs.dry_run }}
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||||
@@ -74,6 +77,11 @@ jobs:
|
|||||||
node-version: '20'
|
node-version: '20'
|
||||||
cache: 'npm'
|
cache: 'npm'
|
||||||
|
|
||||||
|
- name: Setup Bun
|
||||||
|
uses: oven-sh/setup-bun@4bc047ad259df6fc24a6c9b0f9a0cb08cf17fbe5 # v2
|
||||||
|
with:
|
||||||
|
bun-version: '1.2.20'
|
||||||
|
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
|
|
||||||
@@ -105,30 +113,41 @@ jobs:
|
|||||||
- name: Create and switch to a release branch
|
- name: Create and switch to a release branch
|
||||||
id: release_branch
|
id: release_branch
|
||||||
run: |
|
run: |
|
||||||
BRANCH_NAME="release/${{ steps.version.outputs.RELEASE_TAG }}"
|
BRANCH_NAME="release/${STEPS_VERSION_OUTPUTS_RELEASE_TAG}"
|
||||||
git switch -c $BRANCH_NAME
|
git switch -c $BRANCH_NAME
|
||||||
echo "BRANCH_NAME=${BRANCH_NAME}" >> $GITHUB_OUTPUT
|
echo "BRANCH_NAME=${BRANCH_NAME}" >> $GITHUB_OUTPUT
|
||||||
|
env:
|
||||||
|
STEPS_VERSION_OUTPUTS_RELEASE_TAG: ${{ steps.version.outputs.RELEASE_TAG }}
|
||||||
|
|
||||||
- name: Update package versions
|
- name: Update package versions
|
||||||
run: |
|
run: |
|
||||||
npm run release:version ${{ steps.version.outputs.RELEASE_VERSION }}
|
npm run release:version ${STEPS_VERSION_OUTPUTS_RELEASE_VERSION}
|
||||||
|
env:
|
||||||
|
STEPS_VERSION_OUTPUTS_RELEASE_VERSION: ${{ steps.version.outputs.RELEASE_VERSION }}
|
||||||
|
|
||||||
- name: Commit and Conditionally Push package versions
|
- name: Commit and Conditionally Push package versions
|
||||||
run: |
|
run: |
|
||||||
git add package.json package-lock.json packages/*/package.json
|
git add package.json package-lock.json packages/*/package.json
|
||||||
git commit -m "chore(release): ${{ steps.version.outputs.RELEASE_TAG }}"
|
git commit -m "chore(release): ${STEPS_VERSION_OUTPUTS_RELEASE_TAG}"
|
||||||
if [[ "${{ steps.vars.outputs.is_dry_run }}" == "false" ]]; then
|
if [[ "${STEPS_VARS_OUTPUTS_IS_DRY_RUN}" == "false" ]]; then
|
||||||
echo "Pushing release branch to remote..."
|
echo "Pushing release branch to remote..."
|
||||||
git push --set-upstream origin ${{ steps.release_branch.outputs.BRANCH_NAME }} --follow-tags
|
git push --set-upstream origin ${STEPS_RELEASE_BRANCH_OUTPUTS_BRANCH_NAME} --follow-tags
|
||||||
else
|
else
|
||||||
echo "Dry run enabled. Skipping push."
|
echo "Dry run enabled. Skipping push."
|
||||||
fi
|
fi
|
||||||
|
env:
|
||||||
|
STEPS_VERSION_OUTPUTS_RELEASE_TAG: ${{ steps.version.outputs.RELEASE_TAG }}
|
||||||
|
STEPS_VARS_OUTPUTS_IS_DRY_RUN: ${{ steps.vars.outputs.is_dry_run }}
|
||||||
|
STEPS_RELEASE_BRANCH_OUTPUTS_BRANCH_NAME: ${{ steps.release_branch.outputs.BRANCH_NAME }}
|
||||||
|
|
||||||
- name: Build and Prepare Packages
|
- name: Build and Prepare Packages
|
||||||
run: |
|
run: |
|
||||||
npm run build:packages
|
npm run build:packages
|
||||||
npm run prepare:package
|
npm run prepare:package
|
||||||
|
|
||||||
|
- name: Build Native Binaries
|
||||||
|
run: npm run build:binaries
|
||||||
|
|
||||||
- name: Configure npm for publishing
|
- name: Configure npm for publishing
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||||
with:
|
with:
|
||||||
@@ -137,29 +156,35 @@ jobs:
|
|||||||
scope: '@google'
|
scope: '@google'
|
||||||
|
|
||||||
- name: Publish @google/gemini-cli-core
|
- name: Publish @google/gemini-cli-core
|
||||||
run: npm publish --workspace=@google/gemini-cli-core --tag=${{ steps.version.outputs.NPM_TAG }} ${{ steps.vars.outputs.is_dry_run == 'true' && '--dry-run' || '' }}
|
run: npm publish --workspace=@google/gemini-cli-core --tag=${STEPS_VERSION_OUTPUTS_NPM_TAG} ${{ steps.vars.outputs.is_dry_run == 'true' && '--dry-run' || '' }}
|
||||||
env:
|
env:
|
||||||
NODE_AUTH_TOKEN: ${{ secrets.WOMBAT_TOKEN_CORE }}
|
NODE_AUTH_TOKEN: ${{ secrets.WOMBAT_TOKEN_CORE }}
|
||||||
|
STEPS_VERSION_OUTPUTS_NPM_TAG: ${{ steps.version.outputs.NPM_TAG }}
|
||||||
|
|
||||||
- name: Install latest core package
|
- name: Install latest core package
|
||||||
if: steps.vars.outputs.is_dry_run == 'false'
|
if: steps.vars.outputs.is_dry_run == 'false'
|
||||||
run: npm install @google/gemini-cli-core@${{ steps.version.outputs.RELEASE_VERSION }} --workspace=@google/gemini-cli --save-exact
|
run: npm install @google/gemini-cli-core@${STEPS_VERSION_OUTPUTS_RELEASE_VERSION} --workspace=@google/gemini-cli --save-exact
|
||||||
|
env:
|
||||||
|
STEPS_VERSION_OUTPUTS_RELEASE_VERSION: ${{ steps.version.outputs.RELEASE_VERSION }}
|
||||||
|
|
||||||
- name: Publish @google/gemini-cli
|
- name: Publish @google/gemini-cli
|
||||||
run: npm publish --workspace=@google/gemini-cli --tag=${{ steps.version.outputs.NPM_TAG }} ${{ steps.vars.outputs.is_dry_run == 'true' && '--dry-run' || '' }}
|
run: npm publish --workspace=@google/gemini-cli --tag=${STEPS_VERSION_OUTPUTS_NPM_TAG} ${{ steps.vars.outputs.is_dry_run == 'true' && '--dry-run' || '' }}
|
||||||
env:
|
env:
|
||||||
NODE_AUTH_TOKEN: ${{ secrets.WOMBAT_TOKEN_CLI }}
|
NODE_AUTH_TOKEN: ${{ secrets.WOMBAT_TOKEN_CLI }}
|
||||||
|
STEPS_VERSION_OUTPUTS_NPM_TAG: ${{ steps.version.outputs.NPM_TAG }}
|
||||||
|
|
||||||
- name: Create GitHub Release and Tag
|
- name: Create GitHub Release and Tag
|
||||||
if: ${{ steps.vars.outputs.is_dry_run == 'false' }}
|
if: ${{ steps.vars.outputs.is_dry_run == 'false' }}
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
RELEASE_BRANCH: ${{ steps.release_branch.outputs.BRANCH_NAME }}
|
RELEASE_BRANCH: ${{ steps.release_branch.outputs.BRANCH_NAME }}
|
||||||
|
STEPS_VERSION_OUTPUTS_RELEASE_TAG: ${{ steps.version.outputs.RELEASE_TAG }}
|
||||||
run: |
|
run: |
|
||||||
gh release create ${{ steps.version.outputs.RELEASE_TAG }} \
|
gh release create ${STEPS_VERSION_OUTPUTS_RELEASE_TAG} \
|
||||||
bundle/gemini.js \
|
bundle/gemini.js \
|
||||||
|
bundle/binaries/* \
|
||||||
--target "$RELEASE_BRANCH" \
|
--target "$RELEASE_BRANCH" \
|
||||||
--title "Release ${{ steps.version.outputs.RELEASE_TAG }}" \
|
--title "Release ${STEPS_VERSION_OUTPUTS_RELEASE_TAG}" \
|
||||||
--generate-notes
|
--generate-notes
|
||||||
|
|
||||||
- name: Create Issue on Failure
|
- name: Create Issue on Failure
|
||||||
|
|||||||
@@ -103,7 +103,14 @@ export function validateModelOutput(
|
|||||||
|
|
||||||
export class TestRig {
|
export class TestRig {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.bundlePath = join(__dirname, '..', 'bundle/gemini.js');
|
// Support testing native binaries via GEMINI_BINARY env variable
|
||||||
|
if (env.GEMINI_BINARY) {
|
||||||
|
this.binaryPath = env.GEMINI_BINARY;
|
||||||
|
this.isNativeBinary = true;
|
||||||
|
} else {
|
||||||
|
this.bundlePath = join(__dirname, '..', 'bundle/gemini.js');
|
||||||
|
this.isNativeBinary = false;
|
||||||
|
}
|
||||||
this.testDir = null;
|
this.testDir = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,7 +169,9 @@ export class TestRig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
run(promptOrOptions, ...args) {
|
run(promptOrOptions, ...args) {
|
||||||
let command = `node ${this.bundlePath} --yolo`;
|
let command = this.isNativeBinary
|
||||||
|
? `${this.binaryPath} --yolo`
|
||||||
|
: `node ${this.bundlePath} --yolo`;
|
||||||
const execOptions = {
|
const execOptions = {
|
||||||
cwd: this.testDir,
|
cwd: this.testDir,
|
||||||
encoding: 'utf-8',
|
encoding: 'utf-8',
|
||||||
@@ -185,9 +194,9 @@ export class TestRig {
|
|||||||
command += ` ${args.join(' ')}`;
|
command += ` ${args.join(' ')}`;
|
||||||
|
|
||||||
const commandArgs = parse(command);
|
const commandArgs = parse(command);
|
||||||
const node = commandArgs.shift();
|
const executable = commandArgs.shift();
|
||||||
|
|
||||||
const child = spawn(node, commandArgs, {
|
const child = spawn(executable, commandArgs, {
|
||||||
cwd: this.testDir,
|
cwd: this.testDir,
|
||||||
stdio: 'pipe',
|
stdio: 'pipe',
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
"build:all": "npm run build && npm run build:sandbox && npm run build:vscode",
|
"build:all": "npm run build && npm run build:sandbox && npm run build:vscode",
|
||||||
"build:packages": "npm run build --workspaces",
|
"build:packages": "npm run build --workspaces",
|
||||||
"build:sandbox": "node scripts/build_sandbox.js --skip-npm-install-build",
|
"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",
|
"bundle": "npm run generate && node esbuild.config.js && node scripts/copy_bundle_assets.js",
|
||||||
"test": "npm run test --workspaces --if-present",
|
"test": "npm run test --workspaces --if-present",
|
||||||
"test:ci": "npm run test:ci --workspaces --if-present && npm run test:scripts",
|
"test:ci": "npm run test:ci --workspaces --if-present && npm run test:scripts",
|
||||||
|
|||||||
@@ -0,0 +1,106 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright 2025 Google LLC
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { execFileSync } 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 {
|
||||||
|
execFileSync(
|
||||||
|
'bun',
|
||||||
|
[
|
||||||
|
'build',
|
||||||
|
'--compile',
|
||||||
|
`--target=${target}`,
|
||||||
|
bundleJs,
|
||||||
|
'--outfile',
|
||||||
|
outputPath,
|
||||||
|
],
|
||||||
|
{ stdio: ['pipe', 'pipe', 'pipe'] },
|
||||||
|
);
|
||||||
|
|
||||||
|
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}`);
|
||||||
|
const errorDetails =
|
||||||
|
error.stderr || error.stdout || error.message || 'Unknown error';
|
||||||
|
const errorString = errorDetails.toString().trim();
|
||||||
|
if (errorString) {
|
||||||
|
console.error(
|
||||||
|
errorString
|
||||||
|
.split('\n')
|
||||||
|
.map((line) => ` ${line}`)
|
||||||
|
.join('\n'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
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.',
|
||||||
|
);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`\nBinaries saved to: ${outputDir}`);
|
||||||
Reference in New Issue
Block a user