chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 11:58:09 +08:00
commit c48e26cdd0
2909 changed files with 1591131 additions and 0 deletions
+27
View File
@@ -0,0 +1,27 @@
name: 'Calculate vars'
description: 'Calculate commonly used var in our release process'
inputs:
dry_run:
description: 'Whether or not this is a dry run'
type: 'boolean'
outputs:
is_dry_run:
description: 'Boolean flag indicating if the current run is a dry-run or a production release.'
value: '${{ steps.set_vars.outputs.is_dry_run }}'
runs:
using: 'composite'
steps:
- name: 'Set vars for simplified logic'
id: 'set_vars'
shell: 'bash'
env:
DRY_RUN_INPUT: '${{ inputs.dry_run }}'
run: |-
is_dry_run="true"
if [[ "${DRY_RUN_INPUT}" == "" || "${DRY_RUN_INPUT}" == "false" ]]; then
is_dry_run="false"
fi
echo "is_dry_run=${is_dry_run}" >> "${GITHUB_OUTPUT}"
@@ -0,0 +1,55 @@
name: 'Create Pull Request'
description: 'Creates a pull request.'
inputs:
branch-name:
description: 'The name of the branch to create the PR from.'
required: true
pr-title:
description: 'The title of the pull request.'
required: true
pr-body:
description: 'The body of the pull request.'
required: true
base-branch:
description: 'The branch to merge into.'
required: true
default: 'main'
github-token:
description: 'The GitHub token to use for creating the pull request.'
required: true
dry-run:
description: 'Whether to run in dry-run mode.'
required: false
default: 'false'
working-directory:
description: 'The working directory to run the commands in.'
required: false
default: '.'
runs:
using: 'composite'
steps:
- name: 'Creates a Pull Request'
if: "inputs.dry-run != 'true'"
env:
GH_TOKEN: '${{ inputs.github-token }}'
INPUTS_BRANCH_NAME: '${{ inputs.branch-name }}'
INPUTS_PR_TITLE: '${{ inputs.pr-title }}'
INPUTS_PR_BODY: '${{ inputs.pr-body }}'
INPUTS_BASE_BRANCH: '${{ inputs.base-branch }}'
shell: 'bash'
working-directory: '${{ inputs.working-directory }}'
run: |
set -e
if ! git ls-remote --exit-code --heads origin "${INPUTS_BRANCH_NAME}"; then
echo "::error::Branch '${INPUTS_BRANCH_NAME}' does not exist on the remote repository."
exit 1
fi
PR_URL=$(gh pr create \
--title "${INPUTS_PR_TITLE}" \
--body "${INPUTS_PR_BODY}" \
--base "${INPUTS_BASE_BRANCH}" \
--head "${INPUTS_BRANCH_NAME}" \
--fill)
gh pr merge "$PR_URL" --auto
@@ -0,0 +1,23 @@
name: 'Download Mac Binaries'
description: 'Downloads the unsigned macOS binaries (x64 and arm64)'
inputs:
path:
description: 'The base path to download the binaries to'
required: true
default: 'dist'
runs:
using: 'composite'
steps:
- name: 'Download macOS arm64 binary'
uses: 'actions/download-artifact@cc203385981b70ca67e1cc392babf9cc229d5806' # ratchet:actions/download-artifact@v4
continue-on-error: true
with:
name: 'gemini-darwin-arm64-unsigned'
path: '${{ inputs.path }}/darwin-arm64'
- name: 'Download macOS x64 binary'
uses: 'actions/download-artifact@cc203385981b70ca67e1cc392babf9cc229d5806' # ratchet:actions/download-artifact@v4
continue-on-error: true
with:
name: 'gemini-darwin-x64-unsigned'
path: '${{ inputs.path }}/darwin-x64'
+51
View File
@@ -0,0 +1,51 @@
name: 'NPM Auth Token'
description: 'Generates an NPM auth token for publishing a specific package'
inputs:
package-name:
description: 'The name of the package to publish'
required: true
github-token:
description: 'the github token'
required: true
wombat-token-core:
description: 'The npm token for the cli-core package.'
required: true
wombat-token-cli:
description: 'The npm token for the cli package.'
required: true
wombat-token-a2a-server:
description: 'The npm token for the a2a package.'
required: true
outputs:
auth-token:
description: 'The generated NPM auth token'
value: '${{ steps.npm_auth_token.outputs.auth-token }}'
runs:
using: 'composite'
steps:
- name: 'Generate NPM Auth Token'
id: 'npm_auth_token'
shell: 'bash'
run: |
AUTH_TOKEN="${INPUTS_GITHUB_TOKEN}"
PACKAGE_NAME="${INPUTS_PACKAGE_NAME}"
PRIVATE_REPO="@google-gemini/"
if [[ "$PACKAGE_NAME" == "$PRIVATE_REPO"* ]]; then
AUTH_TOKEN="${INPUTS_GITHUB_TOKEN}"
elif [[ "$PACKAGE_NAME" == "@google/gemini-cli" ]]; then
AUTH_TOKEN="${INPUTS_WOMBAT_TOKEN_CLI}"
elif [[ "$PACKAGE_NAME" == "@google/gemini-cli-core" ]]; then
AUTH_TOKEN="${INPUTS_WOMBAT_TOKEN_CORE}"
elif [[ "$PACKAGE_NAME" == "@google/gemini-cli-a2a-server" ]]; then
AUTH_TOKEN="${INPUTS_WOMBAT_TOKEN_A2A_SERVER}"
fi
echo "auth-token=$AUTH_TOKEN" >> $GITHUB_OUTPUT
env:
INPUTS_GITHUB_TOKEN: '${{ inputs.github-token }}'
INPUTS_PACKAGE_NAME: '${{ inputs.package-name }}'
INPUTS_WOMBAT_TOKEN_CLI: '${{ inputs.wombat-token-cli }}'
INPUTS_WOMBAT_TOKEN_CORE: '${{ inputs.wombat-token-core }}'
INPUTS_WOMBAT_TOKEN_A2A_SERVER: '${{ inputs.wombat-token-a2a-server }}'
@@ -0,0 +1,114 @@
name: 'Post Coverage Comment Action'
description: 'Prepares and posts a code coverage comment to a PR.'
inputs:
cli_json_file:
description: 'Path to CLI coverage-summary.json'
required: true
core_json_file:
description: 'Path to Core coverage-summary.json'
required: true
cli_full_text_summary_file:
description: 'Path to CLI full-text-summary.txt'
required: true
core_full_text_summary_file:
description: 'Path to Core full-text-summary.txt'
required: true
node_version:
description: 'Node.js version for context in messages'
required: true
os:
description: 'The os for context in messages'
required: true
github_token:
description: 'GitHub token for posting comments'
required: true
runs:
using: 'composite'
steps:
- name: 'Prepare Coverage Comment'
id: 'prep_coverage_comment'
shell: 'bash'
env:
CLI_JSON_FILE: '${{ inputs.cli_json_file }}'
CORE_JSON_FILE: '${{ inputs.core_json_file }}'
CLI_FULL_TEXT_SUMMARY_FILE: '${{ inputs.cli_full_text_summary_file }}'
CORE_FULL_TEXT_SUMMARY_FILE: '${{ inputs.core_full_text_summary_file }}'
COMMENT_FILE: 'coverage-comment.md'
NODE_VERSION: '${{ inputs.node_version }}'
OS: '${{ inputs.os }}'
run: |-
# Extract percentages using jq for the main table
if [ -f "${CLI_JSON_FILE}" ]; then
cli_lines_pct="$(jq -r '.total.lines.pct' "${CLI_JSON_FILE}")"
cli_statements_pct="$(jq -r '.total.statements.pct' "${CLI_JSON_FILE}")"
cli_functions_pct="$(jq -r '.total.functions.pct' "${CLI_JSON_FILE}")"
cli_branches_pct="$(jq -r '.total.branches.pct' "${CLI_JSON_FILE}")"
else
cli_lines_pct="N/A"
cli_statements_pct="N/A"
cli_functions_pct="N/A"
cli_branches_pct="N/A"
echo "CLI coverage-summary.json not found at: ${CLI_JSON_FILE}" >&2 # Error to stderr
fi
if [ -f "${CORE_JSON_FILE}" ]; then
core_lines_pct="$(jq -r '.total.lines.pct' "${CORE_JSON_FILE}")"
core_statements_pct="$(jq -r '.total.statements.pct' "${CORE_JSON_FILE}")"
core_functions_pct="$(jq -r '.total.functions.pct' "${CORE_JSON_FILE}")"
core_branches_pct="$(jq -r '.total.branches.pct' "${CORE_JSON_FILE}")"
else
core_lines_pct="N/A"
core_statements_pct="N/A"
core_functions_pct="N/A"
core_branches_pct="N/A"
echo "Core coverage-summary.json not found at: ${CORE_JSON_FILE}" >&2 # Error to stderr
fi
echo "## Code Coverage Summary" > "${COMMENT_FILE}"
echo "" >> "${COMMENT_FILE}"
echo "| Package | Lines | Statements | Functions | Branches |" >> "${COMMENT_FILE}"
echo "|---|---|---|---|---|" >> "${COMMENT_FILE}"
echo "| CLI | ${cli_lines_pct}% | ${cli_statements_pct}% | ${cli_functions_pct}% | ${cli_branches_pct}% |" >> "${COMMENT_FILE}"
echo "| Core | ${core_lines_pct}% | ${core_statements_pct}% | ${core_functions_pct}% | ${core_branches_pct}% |" >> "${COMMENT_FILE}"
echo "" >> "${COMMENT_FILE}"
# CLI Package - Collapsible Section (with full text summary from file)
echo "<details>" >> "${COMMENT_FILE}"
echo "<summary>CLI Package - Full Text Report</summary>" >> "${COMMENT_FILE}"
echo "" >> "${COMMENT_FILE}"
echo '```text' >> "${COMMENT_FILE}"
if [ -f "${CLI_FULL_TEXT_SUMMARY_FILE}" ]; then
cat "${CLI_FULL_TEXT_SUMMARY_FILE}" >> "${COMMENT_FILE}"
else
echo "CLI full-text-summary.txt not found at: ${CLI_FULL_TEXT_SUMMARY_FILE}" >> "${COMMENT_FILE}"
fi
echo '```' >> "${COMMENT_FILE}"
echo "</details>" >> "${COMMENT_FILE}"
echo "" >> "${COMMENT_FILE}"
# Core Package - Collapsible Section (with full text summary from file)
echo "<details>" >> "${COMMENT_FILE}"
echo "<summary>Core Package - Full Text Report</summary>" >> "${COMMENT_FILE}"
echo "" >> "${COMMENT_FILE}"
echo '```text' >> "${COMMENT_FILE}"
if [ -f "${CORE_FULL_TEXT_SUMMARY_FILE}" ]; then
cat "${CORE_FULL_TEXT_SUMMARY_FILE}" >> "${COMMENT_FILE}"
else
echo "Core full-text-summary.txt not found at: ${CORE_FULL_TEXT_SUMMARY_FILE}" >> "${COMMENT_FILE}"
fi
echo '```' >> "${COMMENT_FILE}"
echo "</details>" >> "${COMMENT_FILE}"
echo "" >> "${COMMENT_FILE}"
echo "_For detailed HTML reports, please see the 'coverage-reports-${NODE_VERSION}-${OS}' artifact from the main CI run._" >> "${COMMENT_FILE}"
- name: 'Post Coverage Comment'
uses: 'thollander/actions-comment-pull-request@65f9e5c9a1f2cd378bd74b2e057c9736982a8e74' # ratchet:thollander/actions-comment-pull-request@v3
if: |-
${{ always() }}
with:
file-path: 'coverage-comment.md' # Use the generated file directly
comment-tag: 'code-coverage-summary'
github-token: '${{ inputs.github_token }}'
+359
View File
@@ -0,0 +1,359 @@
name: 'Publish Release'
description: 'Builds, prepares, and publishes the gemini-cli packages to npm and creates a GitHub release.'
inputs:
release-version:
description: 'The version to release (e.g., 0.1.11).'
required: true
npm-tag:
description: 'The npm tag to publish with (e.g., latest, preview, nightly).'
required: true
wombat-token-core:
description: 'The npm token for the cli-core package.'
required: true
wombat-token-cli:
description: 'The npm token for the cli package.'
required: true
wombat-token-a2a-server:
description: 'The npm token for the a2a package.'
required: true
github-token:
description: 'The GitHub token for creating the release.'
required: true
github-release-token:
description: 'The GitHub token used specifically for creating the GitHub release (to trigger other workflows).'
required: false
dry-run:
description: 'Whether to run in dry-run mode.'
type: 'string'
required: true
release-tag:
description: 'The release tag for the release (e.g., v0.1.11).'
required: true
previous-tag:
description: 'The previous tag to use for generating release notes.'
required: true
skip-github-release:
description: 'Whether to skip creating a GitHub release.'
type: 'boolean'
required: false
default: false
working-directory:
description: 'The working directory to run the steps in.'
required: false
default: '.'
force-skip-tests:
description: 'Skip tests and validation'
required: false
default: false
skip-branch-cleanup:
description: 'Whether to skip cleaning up the release branch.'
type: 'boolean'
required: false
default: false
gemini_api_key:
description: 'The API key for running integration tests.'
required: true
npm-registry-publish-url:
description: 'npm registry publish url'
required: true
npm-registry-url:
description: 'npm registry url'
required: true
npm-registry-scope:
description: 'npm registry scope'
required: true
cli-package-name:
description: 'The name of the cli package.'
required: true
core-package-name:
description: 'The name of the core package.'
required: true
a2a-package-name:
description: 'The name of the a2a package.'
required: true
runs:
using: 'composite'
steps:
- name: '👤 Configure Git User'
working-directory: '${{ inputs.working-directory }}'
shell: 'bash'
run: |
git config user.name "gemini-cli-robot"
git config user.email "gemini-cli-robot@google.com"
- name: '🌿 Create and switch to a release branch'
working-directory: '${{ inputs.working-directory }}'
id: 'release_branch'
shell: 'bash'
run: |
BRANCH_NAME="release/${INPUTS_RELEASE_TAG}"
git switch -c "${BRANCH_NAME}"
echo "BRANCH_NAME=${BRANCH_NAME}" >> "${GITHUB_OUTPUT}"
env:
INPUTS_RELEASE_TAG: '${{ inputs.release-tag }}'
- name: '⬆️ Update package versions'
working-directory: '${{ inputs.working-directory }}'
shell: 'bash'
run: |
npm run release:version "${INPUTS_RELEASE_VERSION}"
env:
INPUTS_RELEASE_VERSION: '${{ inputs.release-version }}'
- name: '💾 Commit and Conditionally Push package versions'
working-directory: '${{ inputs.working-directory }}'
shell: 'bash'
env:
BRANCH_NAME: '${{ steps.release_branch.outputs.BRANCH_NAME }}'
DRY_RUN: '${{ inputs.dry-run }}'
RELEASE_TAG: '${{ inputs.release-tag }}'
GIT_PUSH_TOKEN: '${{ inputs.github-release-token || inputs.github-token }}'
run: |-
set -e
git add package.json package-lock.json packages/*/package.json
git commit -m "chore(release): ${RELEASE_TAG}"
if [[ "${DRY_RUN}" == "false" ]]; then
echo "Pushing release branch to remote..."
git push "https://x-access-token:${GIT_PUSH_TOKEN}@github.com/${{ github.repository }}.git" "HEAD:${BRANCH_NAME}" --follow-tags
else
echo "Dry run enabled. Skipping push."
fi
- name: '🛠️ Build and Prepare Packages'
working-directory: '${{ inputs.working-directory }}'
shell: 'bash'
run: |
npm run build:packages
npm run prepare:package
- name: '🎁 Bundle'
working-directory: '${{ inputs.working-directory }}'
shell: 'bash'
run: |
npm run bundle
# TODO: Refactor this github specific publishing script to be generalized based upon inputs.
- name: '📦 Prepare for GitHub release'
if: "inputs.npm-registry-url == 'https://npm.pkg.github.com/'"
working-directory: '${{ inputs.working-directory }}'
shell: 'bash'
run: |
node ${{ github.workspace }}/scripts/prepare-github-release.js
- name: 'Configure npm for publishing to npm'
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020'
with:
node-version-file: '${{ inputs.working-directory }}/.nvmrc'
registry-url: '${{inputs.npm-registry-publish-url}}'
scope: '${{inputs.npm-registry-scope}}'
- name: 'Get core Token'
uses: './.github/actions/npm-auth-token'
id: 'core-token'
with:
package-name: '${{ inputs.core-package-name }}'
github-token: '${{ inputs.github-token }}'
wombat-token-core: '${{ inputs.wombat-token-core }}'
wombat-token-cli: '${{ inputs.wombat-token-cli }}'
wombat-token-a2a-server: '${{ inputs.wombat-token-a2a-server }}'
- name: '📦 Publish CORE to NPM'
working-directory: '${{ inputs.working-directory }}'
env:
NODE_AUTH_TOKEN: '${{ steps.core-token.outputs.auth-token }}'
INPUTS_DRY_RUN: '${{ inputs.dry-run }}'
INPUTS_CORE_PACKAGE_NAME: '${{ inputs.core-package-name }}'
shell: 'bash'
run: |
npm publish \
--ignore-scripts \
--dry-run="${INPUTS_DRY_RUN}" \
--workspace="${INPUTS_CORE_PACKAGE_NAME}" \
--tag staging-tmp
if [[ "${INPUTS_DRY_RUN}" == "false" ]]; then
npm dist-tag rm ${INPUTS_CORE_PACKAGE_NAME} staging-tmp
fi
- name: '🔗 Install latest core package'
working-directory: '${{ inputs.working-directory }}'
if: "${{ inputs.dry-run != 'true' }}"
shell: 'bash'
run: |
npm install "${INPUTS_CORE_PACKAGE_NAME}@${INPUTS_RELEASE_VERSION}" \
--workspace="${INPUTS_CLI_PACKAGE_NAME}" \
--workspace="${INPUTS_A2A_PACKAGE_NAME}" \
--save-exact
env:
INPUTS_CORE_PACKAGE_NAME: '${{ inputs.core-package-name }}'
INPUTS_RELEASE_VERSION: '${{ inputs.release-version }}'
INPUTS_CLI_PACKAGE_NAME: '${{ inputs.cli-package-name }}'
INPUTS_A2A_PACKAGE_NAME: '${{ inputs.a2a-package-name }}'
- name: '📦 Prepare bundled CLI for npm release'
if: "inputs.npm-registry-url != 'https://npm.pkg.github.com/'"
working-directory: '${{ inputs.working-directory }}'
shell: 'bash'
run: |
node ${{ github.workspace }}/scripts/prepare-npm-release.js
- name: '📦 Pack CLI for verification'
if: "inputs.dry-run != 'true' && inputs.force-skip-tests != 'true'"
working-directory: '${{ inputs.working-directory }}'
shell: 'bash'
run: |
npm pack --workspace="${INPUTS_CLI_PACKAGE_NAME}"
# We restore the package.json so that `npm ci` in verify-release doesn't fail due to deleted dependencies
git checkout packages/cli/package.json
env:
INPUTS_CLI_PACKAGE_NAME: '${{ inputs.cli-package-name }}'
- name: '🔬 Verify NPM release by version'
uses: './.github/actions/verify-release'
if: "${{ inputs.dry-run != 'true' && inputs.force-skip-tests != 'true' }}"
with:
npm-package: './google-gemini-cli-${{ inputs.release-version }}.tgz'
expected-version: '${{ inputs.release-version }}'
working-directory: '${{ inputs.working-directory }}'
gemini_api_key: '${{ inputs.gemini_api_key }}'
github-token: '${{ inputs.github-token }}'
npm-registry-url: '${{ inputs.npm-registry-url }}'
npm-registry-scope: '${{ inputs.npm-registry-scope }}'
- name: 'Get CLI Token'
uses: './.github/actions/npm-auth-token'
id: 'cli-token'
with:
package-name: '${{ inputs.cli-package-name }}'
github-token: '${{ inputs.github-token }}'
wombat-token-core: '${{ inputs.wombat-token-core }}'
wombat-token-cli: '${{ inputs.wombat-token-cli }}'
wombat-token-a2a-server: '${{ inputs.wombat-token-a2a-server }}'
- name: '📦 Publish CLI'
working-directory: '${{ inputs.working-directory }}'
env:
NODE_AUTH_TOKEN: '${{ steps.cli-token.outputs.auth-token }}'
INPUTS_DRY_RUN: '${{ inputs.dry-run }}'
INPUTS_CLI_PACKAGE_NAME: '${{ inputs.cli-package-name }}'
INPUTS_RELEASE_VERSION: '${{ inputs.release-version }}'
shell: 'bash'
run: |
if [ -f "google-gemini-cli-${INPUTS_RELEASE_VERSION}.tgz" ]; then
PUBLISH_TARGET="google-gemini-cli-${INPUTS_RELEASE_VERSION}.tgz"
else
PUBLISH_TARGET="--workspace=${INPUTS_CLI_PACKAGE_NAME}"
fi
npm publish \
--ignore-scripts \
--dry-run="${INPUTS_DRY_RUN}" \
${PUBLISH_TARGET} \
--tag staging-tmp
if [[ "${INPUTS_DRY_RUN}" == "false" ]]; then
npm dist-tag rm ${INPUTS_CLI_PACKAGE_NAME} staging-tmp
fi
- name: 'Get a2a-server Token'
uses: './.github/actions/npm-auth-token'
id: 'a2a-token'
with:
package-name: '${{ inputs.a2a-package-name }}'
github-token: '${{ inputs.github-token }}'
wombat-token-core: '${{ inputs.wombat-token-core }}'
wombat-token-cli: '${{ inputs.wombat-token-cli }}'
wombat-token-a2a-server: '${{ inputs.wombat-token-a2a-server }}'
- name: '📦 Publish a2a'
working-directory: '${{ inputs.working-directory }}'
env:
NODE_AUTH_TOKEN: '${{ steps.a2a-token.outputs.auth-token }}'
INPUTS_DRY_RUN: '${{ inputs.dry-run }}'
INPUTS_A2A_PACKAGE_NAME: '${{ inputs.a2a-package-name }}'
shell: 'bash'
# Tag staging for initial release
run: |
npm publish \
--ignore-scripts \
--dry-run="${INPUTS_DRY_RUN}" \
--workspace="${INPUTS_A2A_PACKAGE_NAME}" \
--tag staging-tmp
if [[ "${INPUTS_DRY_RUN}" == "false" ]]; then
npm dist-tag rm ${INPUTS_A2A_PACKAGE_NAME} staging-tmp
fi
- name: '🏷️ Tag release'
uses: './.github/actions/tag-npm-release'
with:
channel: '${{ inputs.npm-tag }}'
version: '${{ inputs.release-version }}'
dry-run: '${{ inputs.dry-run }}'
github-token: '${{ inputs.github-token }}'
wombat-token-core: '${{ inputs.wombat-token-core }}'
wombat-token-cli: '${{ inputs.wombat-token-cli }}'
wombat-token-a2a-server: '${{ inputs.wombat-token-a2a-server }}'
cli-package-name: '${{ inputs.cli-package-name }}'
core-package-name: '${{ inputs.core-package-name }}'
a2a-package-name: '${{ inputs.a2a-package-name }}'
working-directory: '${{ inputs.working-directory }}'
- name: '🎉 Create GitHub Release'
working-directory: '${{ inputs.working-directory }}'
if: "${{ inputs.dry-run != 'true' && inputs.skip-github-release != 'true' && inputs.npm-tag != 'dev' && inputs.npm-registry-url != 'https://npm.pkg.github.com/' }}"
env:
GITHUB_TOKEN: '${{ inputs.github-release-token || inputs.github-token }}'
INPUTS_RELEASE_TAG: '${{ inputs.release-tag }}'
STEPS_RELEASE_BRANCH_OUTPUTS_BRANCH_NAME: '${{ steps.release_branch.outputs.BRANCH_NAME }}'
INPUTS_PREVIOUS_TAG: '${{ inputs.previous-tag }}'
shell: 'bash'
run: |
rm -f gemini-cli-bundle.zip
(cd bundle && chmod +x gemini.js && zip -r ../gemini-cli-bundle.zip .)
echo "Testing the generated bundle archive..."
rm -rf test-bundle
mkdir -p test-bundle
unzip -q gemini-cli-bundle.zip -d test-bundle
# Verify it runs and outputs a version
BUNDLE_VERSION=$(node test-bundle/gemini.js --version | xargs)
echo "Bundle version output: ${BUNDLE_VERSION}"
if [[ -z "${BUNDLE_VERSION}" ]]; then
echo "Error: Bundle failed to execute or return version."
exit 1
fi
rm -rf test-bundle
RELEASE_ASSETS=("gemini-cli-bundle.zip")
# Check for and prepare macOS binaries if they exist
for arch in arm64 x64; do
BINARY_PATH="dist/darwin-${arch}/gemini"
if [[ -f "$BINARY_PATH" ]]; then
chmod +x "$BINARY_PATH"
ZIP_NAME="gemini-darwin-${arch}-unsigned.zip"
zip -j "$ZIP_NAME" "$BINARY_PATH"
RELEASE_ASSETS+=("$ZIP_NAME")
fi
done
gh release create "${INPUTS_RELEASE_TAG}" \
"${RELEASE_ASSETS[@]}" \
--target "${STEPS_RELEASE_BRANCH_OUTPUTS_BRANCH_NAME}" \
--title "Release ${INPUTS_RELEASE_TAG}" \
--notes-start-tag "${INPUTS_PREVIOUS_TAG}" \
--generate-notes \
${{ inputs.npm-tag != 'latest' && '--prerelease' || '' }}
- name: '🧹 Clean up release branch'
working-directory: '${{ inputs.working-directory }}'
if: "${{ inputs.dry-run != 'true' && inputs.skip-branch-cleanup != 'true' }}"
continue-on-error: true
shell: 'bash'
run: |
echo "Cleaning up release branch ${STEPS_RELEASE_BRANCH_OUTPUTS_BRANCH_NAME}..."
git push "https://x-access-token:${GIT_PUSH_TOKEN}@github.com/${{ github.repository }}.git" --delete "${STEPS_RELEASE_BRANCH_OUTPUTS_BRANCH_NAME}"
env:
GIT_PUSH_TOKEN: '${{ inputs.github-release-token || inputs.github-token }}'
STEPS_RELEASE_BRANCH_OUTPUTS_BRANCH_NAME: '${{ steps.release_branch.outputs.BRANCH_NAME }}'
+75
View File
@@ -0,0 +1,75 @@
name: 'Push to docker'
description: 'Builds packages and pushes a docker image to GHCR'
inputs:
github-actor:
description: 'Github actor'
required: true
github-secret:
description: 'Github secret'
required: true
ref-name:
description: 'Github ref name'
required: true
github-sha:
description: 'Github Commit SHA Hash'
required: true
runs:
using: 'composite'
steps:
- name: 'Checkout'
uses: 'actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955' # ratchet:actions/checkout@v4
with:
ref: '${{ inputs.github-sha }}'
fetch-depth: 0
- name: 'Install Dependencies'
shell: 'bash'
run: 'npm install'
- name: 'Set up Docker Buildx'
uses: 'docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435' # ratchet:docker/setup-buildx-action@v3
- name: 'build'
shell: 'bash'
run: 'npm run build'
- name: 'pack @google/gemini-cli'
shell: 'bash'
run: 'npm pack -w @google/gemini-cli --pack-destination ./packages/cli/dist'
- name: 'pack @google/gemini-cli-core'
shell: 'bash'
run: 'npm pack -w @google/gemini-cli-core --pack-destination ./packages/core/dist'
- name: 'Log in to GitHub Container Registry'
uses: 'docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1' # ratchet:docker/login-action@v3
with:
registry: 'ghcr.io'
username: '${{ inputs.github-actor }}'
password: '${{ inputs.github-secret }}'
- name: 'Get branch name'
id: 'branch_name'
shell: 'bash'
run: |
REF_NAME="${INPUTS_REF_NAME}"
echo "name=${REF_NAME%/merge}" >> $GITHUB_OUTPUT
env:
INPUTS_REF_NAME: '${{ inputs.ref-name }}'
- name: 'Build and Push the Docker Image'
uses: 'docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83' # ratchet:docker/build-push-action@v6
with:
context: '.'
file: './Dockerfile'
push: true
provenance: false # avoid pushing 3 images to Aritfact Registry
tags: |
ghcr.io/${{ github.repository }}/cli:${{ steps.branch_name.outputs.name }}
ghcr.io/${{ github.repository }}/cli:${{ inputs.github-sha }}
- name: 'Create issue on failure'
if: |-
${{ failure() }}
shell: 'bash'
env:
GITHUB_TOKEN: '${{ inputs.github-secret }}'
DETAILS_URL: '${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}'
run: |-
gh issue create \
--title "Docker build failed" \
--body "The docker build failed. See the full run for details: ${DETAILS_URL}" \
--label "release-failure"
+114
View File
@@ -0,0 +1,114 @@
name: 'Build and push sandbox docker'
description: 'Pushes sandbox docker image to container registry'
inputs:
github-actor:
description: 'Github actor'
required: true
github-secret:
description: 'Github secret'
required: true
dockerhub-username:
description: 'Dockerhub username'
required: true
dockerhub-token:
description: 'Dockerhub PAT w/ R+W'
required: true
github-sha:
description: 'Github Commit SHA Hash'
required: true
github-ref-name:
description: 'Github ref name'
required: true
dry-run:
description: 'Whether this is a dry run.'
required: true
type: 'boolean'
runs:
using: 'composite'
steps:
- name: 'Checkout'
uses: 'actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5' # ratchet:actions/checkout@v4
with:
ref: '${{ inputs.github-sha }}'
fetch-depth: 0
- name: 'Install Dependencies'
shell: 'bash'
run: 'npm install'
- name: 'npm build'
shell: 'bash'
run: 'npm run build'
- name: 'Set up QEMU'
uses: 'docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130' # ratchet:docker/setup-qemu-action@v3
- name: 'Set up Docker Buildx'
uses: 'docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f' # ratchet:docker/setup-buildx-action@v3
- name: 'Log in to GitHub Container Registry'
uses: 'docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9' # ratchet:docker/login-action@v3
with:
registry: 'docker.io'
username: '${{ inputs.dockerhub-username }}'
password: '${{ inputs.dockerhub-token }}'
- name: 'determine image tag'
id: 'image_tag'
shell: 'bash'
run: |-
SHELL_TAG_NAME="${INPUTS_GITHUB_REF_NAME}"
FINAL_TAG="${INPUTS_GITHUB_SHA}"
if [[ "$SHELL_TAG_NAME" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?$ ]]; then
echo "Release detected."
FINAL_TAG="${SHELL_TAG_NAME#v}"
else
echo "Development release detected. Using commit SHA as tag."
fi
echo "Determined image tag: $FINAL_TAG"
echo "FINAL_TAG=$FINAL_TAG" >> $GITHUB_OUTPUT
env:
INPUTS_GITHUB_REF_NAME: '${{ inputs.github-ref-name }}'
INPUTS_GITHUB_SHA: '${{ inputs.github-sha }}'
# We build amd64 just so we can verify it.
# We build and push both amd64 and arm64 in the publish step.
- name: 'build'
id: 'docker_build'
shell: 'bash'
env:
GEMINI_SANDBOX_IMAGE_TAG: '${{ steps.image_tag.outputs.FINAL_TAG }}'
GEMINI_SANDBOX: 'docker'
BUILD_SANDBOX_FLAGS: '--platform linux/amd64 --load'
STEPS_IMAGE_TAG_OUTPUTS_FINAL_TAG: '${{ steps.image_tag.outputs.FINAL_TAG }}'
run: |-
npm run build:sandbox -- \
--image "google/gemini-cli-sandbox:${STEPS_IMAGE_TAG_OUTPUTS_FINAL_TAG}" \
--output-file final_image_uri.txt
echo "uri=$(cat final_image_uri.txt)" >> $GITHUB_OUTPUT
- name: 'verify'
shell: 'bash'
run: |-
docker run --rm --entrypoint sh "${{ steps.docker_build.outputs.uri }}" -lc '
set -e
node -e "const fs=require(\"node:fs\"); JSON.parse(fs.readFileSync(\"/usr/local/share/npm-global/lib/node_modules/@google/gemini-cli/package.json\",\"utf8\")); JSON.parse(fs.readFileSync(\"/usr/local/share/npm-global/lib/node_modules/@google/gemini-cli-core/package.json\",\"utf8\"));"
/usr/local/share/npm-global/bin/gemini --version >/dev/null
'
- name: 'publish'
shell: 'bash'
if: "${{ inputs.dry-run != 'true' }}"
env:
GEMINI_SANDBOX_IMAGE_TAG: '${{ steps.image_tag.outputs.FINAL_TAG }}'
GEMINI_SANDBOX: 'docker'
BUILD_SANDBOX_FLAGS: '--platform linux/amd64,linux/arm64 --push'
STEPS_IMAGE_TAG_OUTPUTS_FINAL_TAG: '${{ steps.image_tag.outputs.FINAL_TAG }}'
run: |-
npm run build:sandbox -- \
--image "google/gemini-cli-sandbox:${STEPS_IMAGE_TAG_OUTPUTS_FINAL_TAG}"
- name: 'Create issue on failure'
if: |-
${{ failure() }}
shell: 'bash'
env:
GITHUB_TOKEN: '${{ inputs.github-secret }}'
DETAILS_URL: '${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}'
run: |-
gh issue create \
--title "Docker build failed" \
--body "The docker build failed. See the full run for details: ${DETAILS_URL}" \
--label "release-failure"
+41
View File
@@ -0,0 +1,41 @@
name: 'Run Tests'
description: 'Runs the preflight checks and integration tests.'
inputs:
gemini_api_key:
description: 'The API key for running integration tests.'
required: true
working-directory:
description: 'The working directory to run the tests in.'
required: false
default: '.'
runs:
using: 'composite'
steps:
- name: 'Install system dependencies'
if: "runner.os == 'Linux'"
run: |
sudo apt-get update -qq && sudo DEBIAN_FRONTEND=noninteractive apt-get install -y -qq bubblewrap
# Ubuntu 24.04+ requires this to allow bwrap to function in CI
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 || true
shell: 'bash'
- name: 'Run Tests'
env:
GEMINI_API_KEY: '${{ inputs.gemini_api_key }}'
GEMINI_CLI_TRUST_WORKSPACE: true
working-directory: '${{ inputs.working-directory }}'
run: |-
echo "::group::Build"
npm run build
echo "::endgroup::"
echo "::group::Unit Tests"
npm run test:ci
echo "::endgroup::"
echo "::group::Integration Tests (no sandbox)"
npm run test:integration:sandbox:none
echo "::endgroup::"
echo "::group::Integration Tests (docker sandbox)"
npm run test:integration:sandbox:docker
echo "::endgroup::"
shell: 'bash'
+24
View File
@@ -0,0 +1,24 @@
name: 'Setup NPMRC'
description: 'Sets up NPMRC with all the correct repos for readonly access.'
inputs:
github-token:
description: 'the github token'
required: true
outputs:
auth-token:
description: 'The generated NPM auth token'
value: '${{ steps.npm_auth_token.outputs.auth-token }}'
runs:
using: 'composite'
steps:
- name: 'Configure .npmrc'
shell: 'bash'
run: |-
echo ""@google-gemini:registry=https://npm.pkg.github.com"" > ~/.npmrc
echo ""//npm.pkg.github.com/:_authToken=${INPUTS_GITHUB_TOKEN}"" >> ~/.npmrc
echo ""@google:registry=https://wombat-dressing-room.appspot.com"" >> ~/.npmrc
env:
INPUTS_GITHUB_TOKEN: '${{ inputs.github-token }}'
+139
View File
@@ -0,0 +1,139 @@
name: 'Tag an NPM release'
description: 'Tags a specific npm version to a specific channel.'
inputs:
channel:
description: 'NPM Channel tag'
required: true
version:
description: 'version'
required: true
dry-run:
description: 'Whether to run in dry-run mode.'
required: true
github-token:
description: 'The GitHub token for creating the release.'
required: true
wombat-token-core:
description: 'The npm token for the wombat @google/gemini-cli-core'
required: true
wombat-token-cli:
description: 'The npm token for wombat @google/gemini-cli'
required: true
wombat-token-a2a-server:
description: 'The npm token for the @google/gemini-cli-a2a-server package.'
required: true
cli-package-name:
description: 'The name of the cli package.'
required: true
core-package-name:
description: 'The name of the core package.'
required: true
a2a-package-name:
description: 'The name of the a2a package.'
required: true
working-directory:
description: 'The working directory to run the commands in.'
required: false
default: '.'
runs:
using: 'composite'
steps:
- name: 'Setup Node.js'
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020'
with:
node-version-file: '${{ inputs.working-directory }}/.nvmrc'
- name: 'configure .npmrc'
uses: './.github/actions/setup-npmrc'
with:
github-token: '${{ inputs.github-token }}'
- name: 'Get core Token'
uses: './.github/actions/npm-auth-token'
id: 'core-token'
with:
package-name: '${{ inputs.core-package-name }}'
github-token: '${{ inputs.github-token }}'
wombat-token-core: '${{ inputs.wombat-token-core }}'
wombat-token-cli: '${{ inputs.wombat-token-cli }}'
wombat-token-a2a-server: '${{ inputs.wombat-token-a2a-server }}'
- name: 'Change tag for CORE'
if: |-
${{ inputs.dry-run != 'true' }}
env:
NODE_AUTH_TOKEN: '${{ steps.core-token.outputs.auth-token }}'
INPUTS_CORE_PACKAGE_NAME: '${{ inputs.core-package-name }}'
INPUTS_VERSION: '${{ inputs.version }}'
INPUTS_CHANNEL: '${{ inputs.channel }}'
shell: 'bash'
working-directory: '${{ inputs.working-directory }}'
run: |
npm dist-tag add ${INPUTS_CORE_PACKAGE_NAME}@${INPUTS_VERSION} ${INPUTS_CHANNEL}
- name: 'Get cli Token'
uses: './.github/actions/npm-auth-token'
id: 'cli-token'
with:
package-name: '${{ inputs.cli-package-name }}'
github-token: '${{ inputs.github-token }}'
wombat-token-core: '${{ inputs.wombat-token-core }}'
wombat-token-cli: '${{ inputs.wombat-token-cli }}'
wombat-token-a2a-server: '${{ inputs.wombat-token-a2a-server }}'
- name: 'Change tag for CLI'
if: |-
${{ inputs.dry-run != 'true' }}
env:
NODE_AUTH_TOKEN: '${{ steps.cli-token.outputs.auth-token }}'
INPUTS_CLI_PACKAGE_NAME: '${{ inputs.cli-package-name }}'
INPUTS_VERSION: '${{ inputs.version }}'
INPUTS_CHANNEL: '${{ inputs.channel }}'
shell: 'bash'
working-directory: '${{ inputs.working-directory }}'
run: |
npm dist-tag add ${INPUTS_CLI_PACKAGE_NAME}@${INPUTS_VERSION} ${INPUTS_CHANNEL}
- name: 'Get a2a Token'
uses: './.github/actions/npm-auth-token'
id: 'a2a-token'
with:
package-name: '${{ inputs.a2a-package-name }}'
github-token: '${{ inputs.github-token }}'
wombat-token-core: '${{ inputs.wombat-token-core }}'
wombat-token-cli: '${{ inputs.wombat-token-cli }}'
wombat-token-a2a-server: '${{ inputs.wombat-token-a2a-server }}'
- name: 'Change tag for a2a'
if: |-
${{ inputs.dry-run == 'false' }}
env:
NODE_AUTH_TOKEN: '${{ steps.a2a-token.outputs.auth-token }}'
INPUTS_A2A_PACKAGE_NAME: '${{ inputs.a2a-package-name }}'
INPUTS_VERSION: '${{ inputs.version }}'
INPUTS_CHANNEL: '${{ inputs.channel }}'
shell: 'bash'
working-directory: '${{ inputs.working-directory }}'
run: |
npm dist-tag add ${INPUTS_A2A_PACKAGE_NAME}@${INPUTS_VERSION} ${INPUTS_CHANNEL}
- name: 'Log dry run'
if: |-
${{ inputs.dry-run == 'true' }}
shell: 'bash'
working-directory: '${{ inputs.working-directory }}'
run: |
echo "Dry run: Would have added tag '${INPUTS_CHANNEL}' to version '${INPUTS_VERSION}' for ${INPUTS_CLI_PACKAGE_NAME}, ${INPUTS_CORE_PACKAGE_NAME}, and ${INPUTS_A2A_PACKAGE_NAME}."
env:
INPUTS_CHANNEL: '${{ inputs.channel }}'
INPUTS_VERSION: '${{ inputs.version }}'
INPUTS_CLI_PACKAGE_NAME: '${{ inputs.cli-package-name }}'
INPUTS_CORE_PACKAGE_NAME: '${{ inputs.core-package-name }}'
INPUTS_A2A_PACKAGE_NAME: '${{ inputs.a2a-package-name }}'
+103
View File
@@ -0,0 +1,103 @@
name: 'Verify an NPM release'
description: 'Fetches a package from NPM and does some basic smoke tests'
inputs:
npm-package:
description: 'NPM Package'
required: true
default: '@google/gemini-cli@latest'
npm-registry-url:
description: 'NPM Registry URL'
required: true
npm-registry-scope:
description: 'NPM Registry Scope'
required: true
expected-version:
description: 'Expected version'
required: true
gemini_api_key:
description: 'The API key for running integration tests.'
required: true
github-token:
description: 'The GitHub token for running integration tests.'
required: true
working-directory:
description: 'The working directory to run the tests in.'
required: false
default: '.'
runs:
using: 'composite'
steps:
- name: 'setup node'
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions/setup-node@v4
with:
node-version: '20'
- name: 'configure .npmrc'
uses: './.github/actions/setup-npmrc'
with:
github-token: '${{ inputs.github-token }}'
- name: 'Clear npm cache'
shell: 'bash'
run: 'npm cache clean --force'
- name: 'Install from NPM'
uses: 'nick-fields/retry@ce71cc2ab81d554ebbe88c79ab5975992d79ba08' # ratchet:nick-fields/retry@v3
with:
timeout_seconds: 900
retry_wait_seconds: 30
max_attempts: 10
command: |-
cd ${{ inputs.working-directory }}
npm install --prefer-online --no-cache -g "${{ inputs.npm-package }}"
- name: 'Smoke test - NPM Install'
shell: 'bash'
working-directory: '${{ inputs.working-directory }}'
run: |-
gemini_version=$(gemini --version)
if [ "$gemini_version" != "${INPUTS_EXPECTED_VERSION}" ]; then
echo "❌ NPM Version mismatch: Got $gemini_version from ${INPUTS_NPM_PACKAGE}, expected ${INPUTS_EXPECTED_VERSION}"
exit 1
fi
env:
INPUTS_EXPECTED_VERSION: '${{ inputs.expected-version }}'
INPUTS_NPM_PACKAGE: '${{ inputs.npm-package }}'
- name: 'Clear npm cache'
shell: 'bash'
run: 'npm cache clean --force'
- name: 'Smoke test - NPX Run'
shell: 'bash'
working-directory: '${{ inputs.working-directory }}'
run: |-
gemini_version=$(npx --yes --prefer-online "${INPUTS_NPM_PACKAGE}" --version)
if [ "$gemini_version" != "${INPUTS_EXPECTED_VERSION}" ]; then
echo "❌ NPX Run Version mismatch: Got $gemini_version from ${INPUTS_NPM_PACKAGE}, expected ${INPUTS_EXPECTED_VERSION}"
exit 1
fi
env:
INPUTS_NPM_PACKAGE: '${{ inputs.npm-package }}'
INPUTS_EXPECTED_VERSION: '${{ inputs.expected-version }}'
- name: 'Install dependencies for integration tests'
shell: 'bash'
working-directory: '${{ inputs.working-directory }}'
run: 'npm ci --ignore-scripts'
- name: '🔬 Run integration tests against NPM release'
working-directory: '${{ inputs.working-directory }}'
env:
GEMINI_API_KEY: '${{ inputs.gemini_api_key }}'
GEMINI_CLI_TRUST_WORKSPACE: true
INTEGRATION_TEST_USE_INSTALLED_GEMINI: 'true'
# We must diable CI mode here because it interferes with interactive tests.
# See https://github.com/google-gemini/gemini-cli/issues/10517
CI: 'false'
shell: 'bash'
run: |
export INTEGRATION_TEST_GEMINI_BINARY_PATH=$(which gemini)
npm run test:integration:sandbox:none