chore(ci): Ensure release workflow is consistent and not vulnerable to injection attacks (#6059)

This commit is contained in:
Seth Vargo 2025-08-12 15:51:50 -04:00 committed by GitHub
parent 5ee5cf17ba
commit 8524cce7b9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 128 additions and 90 deletions

View File

@ -1,4 +1,4 @@
name: Release name: 'Release'
on: on:
schedule: schedule:
@ -9,165 +9,203 @@ on:
version: version:
description: 'The version to release (e.g., v0.1.11). Required for manual patch releases.' description: 'The version to release (e.g., v0.1.11). Required for manual patch releases.'
required: false # Not required for scheduled runs required: false # Not required for scheduled runs
type: string type: 'string'
ref: ref:
description: 'The branch or ref (full git sha) to release from.' description: 'The branch or ref (full git sha) to release from.'
required: true required: true
type: string type: 'string'
default: 'main' default: 'main'
dry_run: dry_run:
description: 'Run a dry-run of the release process; no branches, npm packages or GitHub releases will be created.' description: 'Run a dry-run of the release process; no branches, npm packages or GitHub releases will be created.'
required: true required: true
type: boolean type: 'boolean'
default: true default: true
create_nightly_release: create_nightly_release:
description: 'Auto apply the nightly release tag, input version is ignored.' description: 'Auto apply the nightly release tag, input version is ignored.'
required: false required: false
type: boolean type: 'boolean'
default: false default: false
force_skip_tests: force_skip_tests:
description: 'Select to skip the "Run Tests" step in testing. Prod releases should run tests' description: 'Select to skip the "Run Tests" step in testing. Prod releases should run tests'
required: false required: false
type: boolean type: 'boolean'
default: false default: false
jobs: jobs:
release: release:
runs-on: ubuntu-latest runs-on: 'ubuntu-latest'
environment: environment:
name: production-release name: 'production-release'
url: ${{ github.server_url }}/${{ github.repository }}/releases/tag/${{ steps.version.outputs.RELEASE_TAG }} url: '${{ github.server_url }}/${{ github.repository }}/releases/tag/${{ steps.version.outputs.RELEASE_TAG }}'
if: github.repository == 'google-gemini/gemini-cli' if: |-
${{ github.repository == 'google-gemini/gemini-cli' }}
permissions: permissions:
contents: write contents: 'write'
packages: write packages: 'write'
id-token: write id-token: 'write'
issues: write # For creating issues on failure issues: 'write' # For creating issues on failure
outputs: outputs:
RELEASE_TAG: ${{ steps.version.outputs.RELEASE_TAG }} RELEASE_TAG: '${{ steps.version.outputs.RELEASE_TAG }}'
steps: steps:
- name: Checkout code - name: 'Checkout'
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
with: with:
ref: ${{ github.sha }} ref: '${{ github.sha }}'
fetch-depth: 0 fetch-depth: 0
- name: Set booleans for simplified logic - name: 'Set booleans for simplified logic'
id: vars env:
run: | CREATE_NIGHTLY_RELEASE: '${{ github.event.inputs.create_nightly_release }}'
EVENT_NAME: '${{ github.event_name }}'
DRY_RUN_INPUT: '${{ github.event.inputs.dry_run }}'
id: 'vars'
run: |-
is_nightly="false" is_nightly="false"
if [[ "${{ github.event_name }}" == "schedule" || "${{ github.event.inputs.create_nightly_release }}" == "true" ]]; then if [[ "${EVENT_NAME}" == "schedule" || "${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 [[ "${DRY_RUN_INPUT}" == "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}"
- name: Setup Node.js - name: 'Setup Node.js'
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions/setup-node@v4
with: with:
node-version: '20' node-version-file: '.nvmrc'
cache: 'npm' cache: 'npm'
- name: Install Dependencies - name: 'Install Dependencies'
run: npm ci run: |-
npm ci
- name: Get the version - name: 'Get the version'
id: version id: 'version'
run: |
VERSION_JSON=$(node scripts/get-release-version.js)
echo "RELEASE_TAG=$(echo $VERSION_JSON | jq -r .releaseTag)" >> $GITHUB_OUTPUT
echo "RELEASE_VERSION=$(echo $VERSION_JSON | jq -r .releaseVersion)" >> $GITHUB_OUTPUT
echo "NPM_TAG=$(echo $VERSION_JSON | jq -r .npmTag)" >> $GITHUB_OUTPUT
env: env:
IS_NIGHTLY: ${{ steps.vars.outputs.is_nightly }} IS_NIGHTLY: '${{ steps.vars.outputs.is_nightly }}'
MANUAL_VERSION: ${{ inputs.version }} MANUAL_VERSION: '${{ inputs.version }}'
run: |-
VERSION_JSON="$(node scripts/get-release-version.js)"
echo "RELEASE_TAG=$(echo "${VERSION_JSON}" | jq -r .releaseTag)" >> "${GITHUB_OUTPUT}"
echo "RELEASE_VERSION=$(echo "${VERSION_JSON}" | jq -r .releaseVersion)" >> "${GITHUB_OUTPUT}"
echo "NPM_TAG=$(echo "${VERSION_JSON}" | jq -r .npmTag)" >> "${GITHUB_OUTPUT}"
- name: Run Tests - name: 'Run Tests'
if: github.event.inputs.force_skip_tests != 'true' if: |-
run: | ${{ github.event.inputs.force_skip_tests != 'true' }}
env:
GEMINI_API_KEY: '${{ secrets.GEMINI_API_KEY }}'
run: |-
npm run preflight npm run preflight
npm run test:integration:sandbox:none npm run test:integration:sandbox:none
npm run test:integration:sandbox:docker npm run test:integration:sandbox:docker
env:
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
- name: Configure Git User - name: 'Configure Git User'
run: | run: |-
git config user.name "gemini-cli-robot" git config user.name "gemini-cli-robot"
git config user.email "gemini-cli-robot@google.com" git config user.email "gemini-cli-robot@google.com"
- name: Create and switch to a release branch - name: 'Create and switch to a release branch'
id: release_branch id: 'release_branch'
run: | env:
BRANCH_NAME="release/${{ steps.version.outputs.RELEASE_TAG }}" RELEASE_TAG: '${{ steps.version.outputs.RELEASE_TAG }}'
git switch -c $BRANCH_NAME run: |-
echo "BRANCH_NAME=${BRANCH_NAME}" >> $GITHUB_OUTPUT BRANCH_NAME="release/${RELEASE_TAG}"
git switch -c "${BRANCH_NAME}"
echo "BRANCH_NAME=${BRANCH_NAME}" >> "${GITHUB_OUTPUT}"
- name: Update package versions - name: 'Update package versions'
run: | env:
npm run release:version ${{ steps.version.outputs.RELEASE_VERSION }} RELEASE_VERSION: '${{ steps.version.outputs.RELEASE_VERSION }}'
run: |-
npm run release:version "${RELEASE_VERSION}"
- name: Commit and Conditionally Push package versions - name: 'Commit and Conditionally Push package versions'
run: | env:
BRANCH_NAME: '${{ steps.release_branch.outputs.BRANCH_NAME }}'
IS_DRY_RUN: '${{ steps.vars.outputs.is_dry_run }}'
RELEASE_TAG: '${{ steps.version.outputs.RELEASE_TAG }}'
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): ${RELEASE_TAG}"
if [[ "${{ steps.vars.outputs.is_dry_run }}" == "false" ]]; then if [[ "${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 "${BRANCH_NAME}" --follow-tags
else else
echo "Dry run enabled. Skipping push." echo "Dry run enabled. Skipping push."
fi fi
- 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: Configure npm for publishing - name: 'Configure npm for publishing'
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions/setup-node@v4
with: with:
node-version: '20' node-version-file: '.nvmrc'
registry-url: 'https://wombat-dressing-room.appspot.com' registry-url: 'https://wombat-dressing-room.appspot.com'
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' || '' }}
env: env:
NODE_AUTH_TOKEN: ${{ secrets.WOMBAT_TOKEN_CORE }} IS_DRY_RUN: '${{ steps.vars.outputs.is_dry_run }}'
NODE_AUTH_TOKEN: '${{ secrets.WOMBAT_TOKEN_CORE }}'
NPM_TAG: '${{ steps.version.outputs.NPM_TAG }}'
run: |-
npm publish \
--dry-run="${IS_DRY_RUN}" \
--workspace="@google/gemini-cli-core" \
--tag="${NPM_TAG}"
- name: Install latest core package - name: 'Install latest core package'
if: steps.vars.outputs.is_dry_run == 'false' if: |-
run: npm install @google/gemini-cli-core@${{ steps.version.outputs.RELEASE_VERSION }} --workspace=@google/gemini-cli --save-exact ${{ steps.vars.outputs.is_dry_run == 'false' }}
- 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' || '' }}
env: env:
NODE_AUTH_TOKEN: ${{ secrets.WOMBAT_TOKEN_CLI }} RELEASE_VERSION: '${{ steps.version.outputs.RELEASE_VERSION }}'
run: |-
npm install "@google/gemini-cli-core@${RELEASE_VERSION}" \
--workspace="@google/gemini-cli" \
--save-exact
- name: Create GitHub Release and Tag - name: 'Publish @google/gemini-cli'
if: ${{ steps.vars.outputs.is_dry_run == 'false' }}
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} IS_DRY_RUN: '${{ steps.vars.outputs.is_dry_run }}'
RELEASE_BRANCH: ${{ steps.release_branch.outputs.BRANCH_NAME }} NODE_AUTH_TOKEN: '${{ secrets.WOMBAT_TOKEN_CLI }}'
run: | NPM_TAG: '${{ steps.version.outputs.NPM_TAG }}'
gh release create ${{ steps.version.outputs.RELEASE_TAG }} \ run: |-
npm publish \
--dry-run="${IS_DRY_RUN}" \
--workspace="@google/gemini-cli" \
--tag="${NPM_TAG}"
- name: 'Create GitHub Release and Tag'
if: |-
${{ steps.vars.outputs.is_dry_run == 'false' }}
env:
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
RELEASE_BRANCH: '${{ steps.release_branch.outputs.BRANCH_NAME }}'
RELEASE_TAG: '${{ steps.version.outputs.RELEASE_TAG }}'
run: |-
gh release create "${RELEASE_TAG}" \
bundle/gemini.js \ bundle/gemini.js \
--target "$RELEASE_BRANCH" \ --target "$RELEASE_BRANCH" \
--title "Release ${{ steps.version.outputs.RELEASE_TAG }}" \ --title "Release ${RELEASE_TAG}" \
--generate-notes --generate-notes
- name: Create Issue on Failure - name: 'Create Issue on Failure'
if: failure() if: |-
run: | ${{ failure() }}
gh issue create \
--title "Release Failed for ${{ steps.version.outputs.RELEASE_TAG || 'N/A' }} on $(date +'%Y-%m-%d')" \
--body "The release workflow failed. See the full run for details: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" \
--label "kind/bug,release-failure"
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
RELEASE_TAG: '${{ steps.version.outputs.RELEASE_TAG }} || "N/A"'
DETAILS_URL: '${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}'
run: |-
gh issue create \
--title "Release Failed for ${RELEASE_TAG} on $(date +'%Y-%m-%d')" \
--body "The release workflow failed. See the full run for details: ${DETAILS_URL}" \
--label "kind/bug,release-failure"