Relase: Clean up and condensing (#3321)
This commit is contained in:
parent
4be32d1f73
commit
a7256f630c
|
@ -1,31 +0,0 @@
|
|||
steps:
|
||||
- name: 'us-west1-docker.pkg.dev/gemini-code-dev/gemini-code-containers/gemini-code-builder'
|
||||
entrypoint: 'npm'
|
||||
args: ['install']
|
||||
|
||||
- name: 'us-west1-docker.pkg.dev/gemini-code-dev/gemini-code-containers/gemini-code-builder'
|
||||
entrypoint: 'npm'
|
||||
args: ['run', 'auth']
|
||||
|
||||
- name: 'us-west1-docker.pkg.dev/gemini-code-dev/gemini-code-containers/gemini-code-builder'
|
||||
entrypoint: 'npm'
|
||||
args:
|
||||
[
|
||||
'run',
|
||||
'prerelease:version',
|
||||
'--workspaces',
|
||||
'--',
|
||||
'--suffix="$SHORT_SHA.$_REVISION"',
|
||||
]
|
||||
|
||||
- name: 'us-west1-docker.pkg.dev/gemini-code-dev/gemini-code-containers/gemini-code-builder'
|
||||
entrypoint: 'npm'
|
||||
args: ['run', 'prerelease:deps', '--workspaces']
|
||||
|
||||
- name: 'us-west1-docker.pkg.dev/gemini-code-dev/gemini-code-containers/gemini-code-builder'
|
||||
entrypoint: 'npm'
|
||||
args:
|
||||
['publish', '--tag=head', '--dry-run', '--workspace=@google/gemini-cli']
|
||||
|
||||
options:
|
||||
defaultLogsBucketBehavior: REGIONAL_USER_OWNED_BUCKET
|
|
@ -1,150 +0,0 @@
|
|||
steps:
|
||||
# Step 1: Install root dependencies (includes workspaces)
|
||||
- name: 'us-west1-docker.pkg.dev/gemini-code-dev/gemini-code-containers/gemini-code-builder'
|
||||
id: 'Install Dependencies'
|
||||
entrypoint: 'npm'
|
||||
args: ['install']
|
||||
|
||||
# Step 2: Update version in root package.json
|
||||
- name: 'us-west1-docker.pkg.dev/gemini-code-dev/gemini-code-containers/gemini-code-builder'
|
||||
id: 'Set version in workspace root'
|
||||
entrypoint: 'bash'
|
||||
args:
|
||||
- -c # Use bash -c to allow for command substitution and string manipulation
|
||||
- |
|
||||
current_version=$(npm pkg get version | sed 's/"//g')
|
||||
if [ "$_OFFICIAL_RELEASE" = "true" ]; then
|
||||
new_version="$current_version"
|
||||
else
|
||||
new_version="${current_version}-rc.$_REVISION"
|
||||
fi
|
||||
npm pkg set "version=${new_version}"
|
||||
echo "Set root package.json version to: ${new_version}"
|
||||
|
||||
# Step 3: Binds the package versions to the version in the repo root's package.json
|
||||
- name: 'us-west1-docker.pkg.dev/gemini-code-dev/gemini-code-containers/gemini-code-builder'
|
||||
id: 'Bind package versions to workspace root'
|
||||
entrypoint: 'npm'
|
||||
args: ['run', 'prerelease:dev'] # This will run prerelease:version and prerelease:deps
|
||||
|
||||
# Step 4: Authenticate for Docker (so we can push images to the artifact registry)
|
||||
- name: 'us-west1-docker.pkg.dev/gemini-code-dev/gemini-code-containers/gemini-code-builder'
|
||||
id: 'Authenticate docker'
|
||||
entrypoint: 'npm'
|
||||
args: ['run', 'auth']
|
||||
|
||||
# Step 5: Build workspace packages
|
||||
- name: 'us-west1-docker.pkg.dev/gemini-code-dev/gemini-code-containers/gemini-code-builder'
|
||||
id: 'Build packages'
|
||||
entrypoint: 'npm'
|
||||
args: ['run', 'build:packages']
|
||||
|
||||
# Step 6: Prepare CLI package.json for publishing
|
||||
- name: 'us-west1-docker.pkg.dev/gemini-code-dev/gemini-code-containers/gemini-code-builder'
|
||||
id: 'Prepare @google/gemini-cli and @google/gemini-cli-core packages'
|
||||
entrypoint: 'npm'
|
||||
args: ['run', 'prepare:packages']
|
||||
env:
|
||||
- 'GEMINI_SANDBOX=$_CONTAINER_TOOL'
|
||||
- 'SANDBOX_IMAGE_REGISTRY=$_SANDBOX_IMAGE_REGISTRY'
|
||||
- 'SANDBOX_IMAGE_NAME=$_SANDBOX_IMAGE_NAME'
|
||||
|
||||
# Step 7: Build sandbox container image
|
||||
- name: 'us-west1-docker.pkg.dev/gemini-code-dev/gemini-code-containers/gemini-code-builder'
|
||||
id: 'Build sandbox Docker image'
|
||||
entrypoint: 'npm'
|
||||
args: ['run', 'build:sandbox:fast']
|
||||
env:
|
||||
- 'GEMINI_SANDBOX=$_CONTAINER_TOOL'
|
||||
- 'SANDBOX_IMAGE_REGISTRY=$_SANDBOX_IMAGE_REGISTRY'
|
||||
- 'SANDBOX_IMAGE_NAME=$_SANDBOX_IMAGE_NAME'
|
||||
|
||||
# Step 8: Publish sandbox container image
|
||||
- name: 'us-west1-docker.pkg.dev/gemini-code-dev/gemini-code-containers/gemini-code-builder'
|
||||
id: 'Publish sandbox Docker image'
|
||||
entrypoint: 'npm'
|
||||
args: ['run', 'publish:sandbox']
|
||||
env:
|
||||
- 'GEMINI_SANDBOX=$_CONTAINER_TOOL'
|
||||
- 'SANDBOX_IMAGE_REGISTRY=$_SANDBOX_IMAGE_REGISTRY'
|
||||
- 'SANDBOX_IMAGE_NAME=$_SANDBOX_IMAGE_NAME'
|
||||
|
||||
# Pre-Step 9: authenticate to our intermediate npm registry
|
||||
# NOTE: when running locally, run this instead (from the `packages/core` directory):
|
||||
# - `npm login --registry https://wombat-dressing-room.appspot.com`
|
||||
# - use a 24hr token
|
||||
- name: 'us-west1-docker.pkg.dev/gemini-code-dev/gemini-code-containers/gemini-code-builder'
|
||||
id: 'Setup @google/gemini-cli-core auth token for publishing'
|
||||
entrypoint: 'bash'
|
||||
args:
|
||||
- -c
|
||||
- |
|
||||
echo "//wombat-dressing-room.appspot.com/:_authToken=$$CORE_PACKAGE_PUBLISH_TOKEN" > $$HOME/.npmrc
|
||||
secretEnv: ['CORE_PACKAGE_PUBLISH_TOKEN']
|
||||
|
||||
# Step 9: Publish @google/gemini-cli-core to NPM
|
||||
- name: 'us-west1-docker.pkg.dev/gemini-code-dev/gemini-code-containers/gemini-code-builder'
|
||||
id: 'Publish @google/gemini-cli-core package'
|
||||
entrypoint: 'bash'
|
||||
args:
|
||||
- -c
|
||||
- |
|
||||
if [ "$_OFFICIAL_RELEASE" = "true" ]; then
|
||||
npm publish --workspace=@google/gemini-cli-core --tag=latest
|
||||
else
|
||||
npm publish --workspace=@google/gemini-cli-core --tag=rc
|
||||
fi
|
||||
env:
|
||||
- 'GEMINI_SANDBOX=$_CONTAINER_TOOL'
|
||||
- 'SANDBOX_IMAGE_REGISTRY=$_SANDBOX_IMAGE_REGISTRY'
|
||||
- 'SANDBOX_IMAGE_NAME=$_SANDBOX_IMAGE_NAME'
|
||||
|
||||
# Pre-Step 10: authenticate to our intermediate npm registry
|
||||
# NOTE: when running locally, run this instead (from the `packages/cli` directory)
|
||||
# - `npm login --registry https://wombat-dressing-room.appspot.com`
|
||||
# - use a 24hr token
|
||||
- name: 'us-west1-docker.pkg.dev/gemini-code-dev/gemini-code-containers/gemini-code-builder'
|
||||
id: 'Setup @google/gemini-cli auth token for publishing'
|
||||
entrypoint: 'bash'
|
||||
args:
|
||||
- -c
|
||||
- |
|
||||
echo "//wombat-dressing-room.appspot.com/:_authToken=$$CLI_PACKAGE_PUBLISH_TOKEN" > $$HOME/.npmrc
|
||||
secretEnv: ['CLI_PACKAGE_PUBLISH_TOKEN']
|
||||
|
||||
# Step 10: Publish @google/gemini-cli to NPM
|
||||
- name: 'us-west1-docker.pkg.dev/gemini-code-dev/gemini-code-containers/gemini-code-builder'
|
||||
id: 'Publish @google/gemini-cli package'
|
||||
entrypoint: 'bash'
|
||||
args:
|
||||
- -c
|
||||
- |
|
||||
if [ "$_OFFICIAL_RELEASE" = "true" ]; then
|
||||
npm publish --workspace=@google/gemini-cli --tag=latest
|
||||
else
|
||||
npm publish --workspace=@google/gemini-cli --tag=rc
|
||||
fi
|
||||
env:
|
||||
- 'GEMINI_SANDBOX=$_CONTAINER_TOOL'
|
||||
- 'SANDBOX_IMAGE_REGISTRY=$_SANDBOX_IMAGE_REGISTRY'
|
||||
- 'SANDBOX_IMAGE_NAME=$_SANDBOX_IMAGE_NAME'
|
||||
|
||||
options:
|
||||
defaultLogsBucketBehavior: REGIONAL_USER_OWNED_BUCKET
|
||||
dynamicSubstitutions: true
|
||||
|
||||
availableSecrets:
|
||||
secretManager:
|
||||
- versionName: ${_CLI_PACKAGE_WOMBAT_TOKEN_RESOURCE_NAME}
|
||||
env: 'CLI_PACKAGE_PUBLISH_TOKEN'
|
||||
- versionName: ${_CORE_PACKAGE_WOMBAT_TOKEN_RESOURCE_NAME}
|
||||
env: 'CORE_PACKAGE_PUBLISH_TOKEN'
|
||||
|
||||
substitutions:
|
||||
_REVISION: '0'
|
||||
_OFFICIAL_RELEASE: 'false'
|
||||
_CONTAINER_TOOL: 'docker'
|
||||
_SANDBOX_IMAGE_REGISTRY: ''
|
||||
_SANDBOX_IMAGE_NAME: ''
|
||||
_CLI_PACKAGE_WOMBAT_TOKEN_RESOURCE_NAME: ''
|
||||
_CORE_PACKAGE_WOMBAT_TOKEN_RESOURCE_NAME: ''
|
|
@ -1,14 +1,14 @@
|
|||
name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*.*.*'
|
||||
schedule:
|
||||
# Runs every day at midnight UTC for the nightly release.
|
||||
- cron: '0 0 * * *'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'The version to release (e.g., v0.1.11). Required for manual patch releases.'
|
||||
required: true
|
||||
required: false # Not required for scheduled runs
|
||||
type: string
|
||||
ref:
|
||||
description: 'The branch or ref to release from.'
|
||||
|
@ -20,24 +20,54 @@ on:
|
|||
required: true
|
||||
type: boolean
|
||||
default: true
|
||||
create_nightly_release:
|
||||
description: 'Simulate a scheduled nightly release. If true, the version input is ignored.'
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
force_skip_tests:
|
||||
description: 'If true, skip the "Run Tests" step. This is only applicable for nightly releases.'
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
environment:
|
||||
name: production-release
|
||||
url: ${{ github.server_url }}/${{ github.repository }}/releases/tag/${{ steps.version.outputs.RELEASE_TAG }}
|
||||
if: github.repository == 'google-gemini/gemini-cli'
|
||||
permissions:
|
||||
contents: write
|
||||
packages: write
|
||||
id-token: write
|
||||
issues: write # For creating issues on failure
|
||||
outputs:
|
||||
RELEASE_TAG: ${{ steps.version.outputs.RELEASE_TAG }}
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
# For manual runs, checkout the specified ref (e.g., main). For tag pushes, checkout the tag itself.
|
||||
ref: ${{ github.event_name == 'workflow_dispatch' && inputs.ref || github.ref }}
|
||||
ref: ${{ github.sha }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set booleans for simplified logic
|
||||
id: vars
|
||||
run: |
|
||||
is_nightly="false"
|
||||
if [[ "${{ github.event_name }}" == "schedule" || "${{ github.event.inputs.create_nightly_release }}" == "true" ]]; then
|
||||
is_nightly="true"
|
||||
fi
|
||||
echo "is_nightly=${is_nightly}" >> $GITHUB_OUTPUT
|
||||
|
||||
is_dry_run="false"
|
||||
if [[ "${{ github.event.inputs.dry_run }}" == "true" ]]; then
|
||||
is_dry_run="true"
|
||||
fi
|
||||
echo "is_dry_run=${is_dry_run}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
|
@ -50,39 +80,27 @@ jobs:
|
|||
- name: Get the version
|
||||
id: version
|
||||
run: |
|
||||
echo "Workflow triggered by: ${{ github.event_name }}"
|
||||
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
|
||||
echo "Input ref: ${{ inputs.ref }}"
|
||||
echo "Input version: ${{ inputs.version }}"
|
||||
RELEASE_TAG=${{ inputs.version }}
|
||||
else
|
||||
echo "Triggering ref: ${{ github.ref }}"
|
||||
RELEASE_TAG=${GITHUB_REF_NAME}
|
||||
fi
|
||||
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:
|
||||
IS_NIGHTLY: ${{ steps.vars.outputs.is_nightly }}
|
||||
MANUAL_VERSION: ${{ inputs.version }}
|
||||
|
||||
echo "---"
|
||||
echo "Initial RELEASE_TAG: ${RELEASE_TAG}"
|
||||
- name: Run Tests
|
||||
if: github.event.inputs.force_skip_tests != 'true'
|
||||
run: |
|
||||
npm run preflight
|
||||
npm run test:integration:sandbox:none
|
||||
npm run test:integration:sandbox:docker
|
||||
env:
|
||||
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
|
||||
|
||||
# Validate that the tag starts with 'v' and follows semver
|
||||
if [[ ! "$RELEASE_TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?$ ]]; then
|
||||
echo "Error: Version must be in the format vX.Y.Z, vX.Y.Z-prerelease, or vX.Y.Z+buildmeta"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
RELEASE_VERSION="${RELEASE_TAG#v}"
|
||||
if [[ $RELEASE_VERSION == *-* ]]; then
|
||||
NPM_TAG=$(echo $RELEASE_VERSION | cut -d'-' -f2 | cut -d'.' -f1)
|
||||
else
|
||||
NPM_TAG="latest"
|
||||
fi
|
||||
|
||||
echo "Finalized RELEASE_VERSION: ${RELEASE_VERSION}"
|
||||
echo "Finalized NPM_TAG: ${NPM_TAG}"
|
||||
echo "---"
|
||||
|
||||
echo "RELEASE_TAG=${RELEASE_TAG}" >> $GITHUB_OUTPUT
|
||||
echo "RELEASE_VERSION=${RELEASE_VERSION}" >> $GITHUB_OUTPUT
|
||||
echo "NPM_TAG=${NPM_TAG}" >> $GITHUB_OUTPUT
|
||||
- name: Configure Git User
|
||||
run: |
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
|
||||
- name: Create and switch to a release branch
|
||||
id: release_branch
|
||||
|
@ -95,13 +113,16 @@ jobs:
|
|||
run: |
|
||||
npm run release:version ${{ steps.version.outputs.RELEASE_VERSION }}
|
||||
|
||||
- name: Commit package versions
|
||||
- name: Commit and Conditionally Push package versions
|
||||
run: |
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git add package.json package-lock.json packages/*/package.json
|
||||
git commit -m "chore(release): ${{ steps.version.outputs.RELEASE_TAG }}"
|
||||
git push --set-upstream origin ${{ steps.release_branch.outputs.BRANCH_NAME }} --follow-tags
|
||||
if [[ "${{ steps.vars.outputs.is_dry_run }}" == "false" ]]; then
|
||||
echo "Pushing release branch to remote..."
|
||||
git push --set-upstream origin ${{ steps.release_branch.outputs.BRANCH_NAME }} --follow-tags
|
||||
else
|
||||
echo "Dry run enabled. Skipping push."
|
||||
fi
|
||||
|
||||
- name: Build and Prepare Packages
|
||||
run: |
|
||||
|
@ -116,20 +137,21 @@ jobs:
|
|||
scope: '@google'
|
||||
|
||||
- name: Publish @google/gemini-cli-core
|
||||
run: npm publish --workspace=@google/gemini-cli-core --tag=${{ steps.version.outputs.NPM_TAG }} ${{ inputs.dry_run && '--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:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.WOMBAT_TOKEN_CORE }}
|
||||
|
||||
- name: Install latest core package
|
||||
if: steps.vars.outputs.is_dry_run == 'false'
|
||||
run: npm install @google/gemini-cli-core@${{ steps.version.outputs.NPM_TAG }} --workspace=@google/gemini-cli --save-exact
|
||||
|
||||
- name: Publish @google/gemini-cli
|
||||
run: npm publish --workspace=@google/gemini-cli --tag=${{ steps.version.outputs.NPM_TAG }} ${{ inputs.dry_run && '--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:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.WOMBAT_TOKEN_CLI }}
|
||||
|
||||
- name: Create GitHub Release and Tag
|
||||
if: '!inputs.dry_run'
|
||||
if: ${{ steps.vars.outputs.is_dry_run == 'false' }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
RELEASE_BRANCH: ${{ steps.release_branch.outputs.BRANCH_NAME }}
|
||||
|
@ -139,3 +161,13 @@ jobs:
|
|||
--target "$RELEASE_BRANCH" \
|
||||
--title "Release ${{ steps.version.outputs.RELEASE_TAG }}" \
|
||||
--generate-notes
|
||||
|
||||
- name: Create Issue on Failure
|
||||
if: failure()
|
||||
run: |
|
||||
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 "type: bug,release-failure"
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
|
|
@ -1,74 +0,0 @@
|
|||
name: Scheduled Nightly Release
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# Runs every day at midnight UTC.
|
||||
- cron: '0 0 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
nightly-release:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
issues: write
|
||||
|
||||
steps:
|
||||
- name: Checkout main branch
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: 'main'
|
||||
fetch-depth: 0 # Fetch all history for git tags
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: 'npm'
|
||||
|
||||
- name: Install Dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Run Preflight Checks
|
||||
run: npm run preflight
|
||||
|
||||
- name: Run Integration Tests (without Docker)
|
||||
uses: nick-fields/retry@ce71cc2ab81d554ebbe88c79ab5975992d79ba08
|
||||
env:
|
||||
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
|
||||
with:
|
||||
timeout_minutes: 10
|
||||
max_attempts: 3
|
||||
retry_wait_seconds: 30
|
||||
command: npm run test:integration:sandbox:none
|
||||
|
||||
- name: Run Integration Tests (with Docker)
|
||||
uses: nick-fields/retry@ce71cc2ab81d554ebbe88c79ab5975992d79ba08
|
||||
env:
|
||||
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
|
||||
with:
|
||||
timeout_minutes: 10
|
||||
max_attempts: 3
|
||||
retry_wait_seconds: 30
|
||||
command: npm run test:integration:sandbox:docker
|
||||
|
||||
- name: Configure Git User
|
||||
run: |
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
|
||||
- name: Create and Push Nightly Tag
|
||||
if: success()
|
||||
run: npm run tag:release:nightly
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Create Issue on Failure
|
||||
if: failure()
|
||||
run: |
|
||||
gh issue create \
|
||||
--title "Nightly Release Failed on $(date +'%Y-%m-%d')" \
|
||||
--body "The scheduled nightly release workflow failed. See the full run for details: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" \
|
||||
--label "type: bug,nightly-failure"
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
54
docs/npm.md
54
docs/npm.md
|
@ -18,21 +18,18 @@ This package is not bundled. When it is published, it is published as a standard
|
|||
|
||||
This project follows a structured release process to ensure that all packages are versioned and published correctly. The process is designed to be as automated as possible.
|
||||
|
||||
## Current Theory
|
||||
|
||||
For most all changes, simply patching the minor version is acceptable. We can and should release frequently; the more often we release the easier it is to tell what change broke something. Developers are encouraged to push a release as described below after their branch merges. I also think I'm open to doing the release publishing steps as a part of an existing PR, though this could have more churn if others are also releasing and version numbers change frequently.
|
||||
|
||||
## How To Release
|
||||
|
||||
Releasing a new version is as simple as creating and pushing a new Git tag. The tag must follow semantic versioning and be prefixed with `v`, for example `v0.2.0` or `v1.0.0-alpha.1`. From the branch you want to release from, run the following commands:
|
||||
Releases are managed through the [release.yml](https://github.com/google-gemini/gemini-cli/actions/workflows/release.yml) GitHub Actions workflow. To perform a manual release for a patch or hotfix:
|
||||
|
||||
```bash
|
||||
# Create the new tag (e.g., v0.2.0)
|
||||
# Optional use git log to find an older commit sha to tag
|
||||
git tag v0.2.0 <optional sha>
|
||||
# Push the tag to the remote repository to trigger the release
|
||||
git push origin v0.2.0
|
||||
```
|
||||
1. Navigate to the **Actions** tab of the repository.
|
||||
2. Select the **Release** workflow from the list.
|
||||
3. Click the **Run workflow** dropdown button.
|
||||
4. Fill in the required inputs:
|
||||
- **Version**: The exact version to release (e.g., `v0.2.1`).
|
||||
- **Ref**: The branch or commit SHA to release from (defaults to `main`).
|
||||
- **Dry Run**: Leave as `true` to test the workflow without publishing, or set to `false` to perform a live release.
|
||||
5. Click **Run workflow**.
|
||||
|
||||
## Nightly Releases
|
||||
|
||||
|
@ -40,14 +37,14 @@ In addition to manual releases, this project has an automated nightly release pr
|
|||
|
||||
### Process
|
||||
|
||||
Every night at midnight UTC, the [Scheduled Nightly Release workflow](https://github.com/google-gemini/gemini-cli/actions/workflows/scheduled-nightly-release.yml) runs automatically. It performs the following steps:
|
||||
Every night at midnight UTC, the [Release workflow](https://github.com/google-gemini/gemini-cli/actions/workflows/release.yml) runs automatically on a schedule. It performs the following steps:
|
||||
|
||||
1. Checks out the latest code from the `main` branch.
|
||||
2. Installs all dependencies.
|
||||
3. Runs the full suite of `preflight` checks (linting, type-checking, etc.).
|
||||
4. Runs the integration tests, both with and without Docker. The tests are automatically retried up to three times to handle any flakiness.
|
||||
5. If all checks and tests succeed, it runs the `npm run tag:release:nightly` script. This script creates and pushes a new annotated Git tag with the format `v<version>+nightly.<ddmmyy>.<sha>`.
|
||||
6. Pushing this tag triggers the main [release workflow](https://github.com/google-gemini/gemini-cli/actions/workflows/release.yml), which publishes the package to npm with the `nightly` tag.
|
||||
3. Runs the full suite of `preflight` checks and integration tests.
|
||||
4. If all tests succeed, it calculates the next nightly version number (e.g., `v0.2.1-nightly.20230101`).
|
||||
5. It then builds and publishes the packages to npm with the `nightly` dist-tag.
|
||||
6. Finally, it creates a GitHub Release for the nightly version.
|
||||
|
||||
### Failure Handling
|
||||
|
||||
|
@ -61,32 +58,11 @@ To install the latest nightly build, use the `@nightly` tag:
|
|||
npm install -g @google/gemini-cli@nightly
|
||||
```
|
||||
|
||||
The high-level process is:
|
||||
|
||||
1. Ensure your local branch `main` or `release-xxx` if hotfixing a previous release is up-to-date with the remote repository.
|
||||
1. Decide on the new version number based on the changes since the last release.
|
||||
1. _Optionally_ `git log` to find the sha of the commit you want to push if not latest
|
||||
1. _Optionally_ run [integration tests](integration-tests.md) locally to increase confidence in the release.
|
||||
1. Create a new Git tag with the desired version number.
|
||||
1. Push the tag to the `google-gemini/gemini-cli` repository.
|
||||
1. The push will trigger the release workflow, which automates the rest of the process.
|
||||
1. Once the workflow is complete, it will have created a `release/vX.Y.Z` branch with the version bumps. Create a pull request from this branch to merge the version changes back into `main`.
|
||||
|
||||
Pushing a new tag will trigger the [release workflow](https://github.com/google-gemini/gemini-cli/actions/workflows/release.yml), which will automatically:
|
||||
|
||||
- Build and publish the packages to the npm registry.
|
||||
- Create a new GitHub release with generated release notes.
|
||||
- Create a new branch `release/vX.Y.Z` containing the version bump in the `package.json` files.
|
||||
|
||||
We also run a Google cloud build called [release-docker.yml](../.gcp/release-docker.yaml). Which publishes the sandbox docker to match your release. This will also be moved to GH and combined with the main release file once service account permissions are sorted out.
|
||||
|
||||
### 2. Monitor the Release Workflow
|
||||
|
||||
You can monitor the progress of the release workflow in the [GitHub Actions tab](https://github.com/google-gemini/gemini-cli/actions/workflows/release.yml). If the workflow fails, you will need to investigate the cause of the failure, fix the issue, and then create a new tag to trigger a new release.
|
||||
|
||||
### After the Release
|
||||
|
||||
After the workflow has successfully completed, you should:
|
||||
After the workflow has successfully completed, you can monitor its progress in the [GitHub Actions tab](https://github.com/google-gemini/gemini-cli/actions/workflows/release.yml). Once complete, you should:
|
||||
|
||||
1. Go to the [pull requests page](https://github.com/google-gemini/gemini-cli/pulls) of the repository.
|
||||
2. Create a new pull request from the `release/vX.Y.Z` branch to `main`.
|
||||
|
|
|
@ -18,6 +18,8 @@ test('should be able to list a directory', async (t) => {
|
|||
const prompt = `Can you list the files in the current directory`;
|
||||
const result = await rig.run(prompt);
|
||||
|
||||
assert.ok(result.includes('file1.txt'));
|
||||
assert.ok(result.includes('subdir'));
|
||||
const lines = result.split('\n').filter((line) => line.trim() !== '');
|
||||
assert.equal(lines.length, 2);
|
||||
assert.ok(lines.includes('file1.txt'));
|
||||
assert.ok(lines.includes('subdir'));
|
||||
});
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
"prettier": "^3.5.3",
|
||||
"react-devtools-core": "^4.28.5",
|
||||
"typescript-eslint": "^8.30.1",
|
||||
"vitest": "^3.2.4",
|
||||
"yargs": "^17.7.2"
|
||||
},
|
||||
"engines": {
|
||||
|
@ -1879,8 +1880,7 @@
|
|||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"peer": true
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm64": {
|
||||
"version": "4.44.0",
|
||||
|
@ -1894,8 +1894,7 @@
|
|||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"peer": true
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-arm64": {
|
||||
"version": "4.44.0",
|
||||
|
@ -1909,8 +1908,7 @@
|
|||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"peer": true
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-x64": {
|
||||
"version": "4.44.0",
|
||||
|
@ -1924,8 +1922,7 @@
|
|||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"peer": true
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-freebsd-arm64": {
|
||||
"version": "4.44.0",
|
||||
|
@ -1939,8 +1936,7 @@
|
|||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"peer": true
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-freebsd-x64": {
|
||||
"version": "4.44.0",
|
||||
|
@ -1954,8 +1950,7 @@
|
|||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"peer": true
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
||||
"version": "4.44.0",
|
||||
|
@ -1969,8 +1964,7 @@
|
|||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
||||
"version": "4.44.0",
|
||||
|
@ -1984,8 +1978,7 @@
|
|||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
||||
"version": "4.44.0",
|
||||
|
@ -1999,8 +1992,7 @@
|
|||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
||||
"version": "4.44.0",
|
||||
|
@ -2014,8 +2006,7 @@
|
|||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-loongarch64-gnu": {
|
||||
"version": "4.44.0",
|
||||
|
@ -2029,8 +2020,7 @@
|
|||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
|
||||
"version": "4.44.0",
|
||||
|
@ -2044,8 +2034,7 @@
|
|||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
||||
"version": "4.44.0",
|
||||
|
@ -2059,8 +2048,7 @@
|
|||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-riscv64-musl": {
|
||||
"version": "4.44.0",
|
||||
|
@ -2074,8 +2062,7 @@
|
|||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
||||
"version": "4.44.0",
|
||||
|
@ -2089,8 +2076,7 @@
|
|||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
||||
"version": "4.44.0",
|
||||
|
@ -2104,8 +2090,7 @@
|
|||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-musl": {
|
||||
"version": "4.44.0",
|
||||
|
@ -2119,8 +2104,7 @@
|
|||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
||||
"version": "4.44.0",
|
||||
|
@ -2134,8 +2118,7 @@
|
|||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"peer": true
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
||||
"version": "4.44.0",
|
||||
|
@ -2149,8 +2132,7 @@
|
|||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"peer": true
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
||||
"version": "4.44.0",
|
||||
|
@ -2164,8 +2146,7 @@
|
|||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"peer": true
|
||||
]
|
||||
},
|
||||
"node_modules/@rtsao/scc": {
|
||||
"version": "1.1.0",
|
||||
|
@ -5398,7 +5379,6 @@
|
|||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
|
|
|
@ -24,12 +24,13 @@
|
|||
"clean": "node scripts/clean.js",
|
||||
"prepare": "npm run bundle",
|
||||
"test": "npm run test --workspaces",
|
||||
"test:ci": "npm run test:ci --workspaces --if-present",
|
||||
"test:ci": "npm run test:ci --workspaces --if-present && npm run test:scripts",
|
||||
"test:e2e": "npm run test:integration:sandbox:none -- --verbose --keep-output",
|
||||
"test:integration:all": "npm run test:integration:sandbox:none && npm run test:integration:sandbox:docker && npm run test:integration:sandbox:podman",
|
||||
"test:integration:sandbox:none": "GEMINI_SANDBOX=false node integration-tests/run-tests.js",
|
||||
"test:integration:sandbox:docker": "GEMINI_SANDBOX=docker node integration-tests/run-tests.js",
|
||||
"test:integration:sandbox:podman": "GEMINI_SANDBOX=podman node integration-tests/run-tests.js",
|
||||
"test:scripts": "vitest --config ./scripts/tests/vitest.config.ts",
|
||||
"start": "node scripts/start.js",
|
||||
"debug": "cross-env DEBUG=1 node --inspect-brk scripts/start.js",
|
||||
"lint:fix": "eslint . --fix && eslint integration-tests --fix",
|
||||
|
@ -85,7 +86,7 @@
|
|||
"prettier": "^3.5.3",
|
||||
"react-devtools-core": "^4.28.5",
|
||||
"typescript-eslint": "^8.30.1",
|
||||
"vitest": "^3.2.4",
|
||||
"yargs": "^17.7.2"
|
||||
},
|
||||
"dependencies": {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { execSync } from 'child_process';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
function getPackageVersion() {
|
||||
const packageJsonPath = path.resolve(process.cwd(), 'package.json');
|
||||
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
||||
return packageJson.version;
|
||||
}
|
||||
|
||||
function getShortSha() {
|
||||
return execSync('git rev-parse --short HEAD').toString().trim();
|
||||
}
|
||||
|
||||
export function getNightlyTagName() {
|
||||
const version = getPackageVersion();
|
||||
const now = new Date();
|
||||
const year = now.getUTCFullYear().toString().slice(-2);
|
||||
const month = (now.getUTCMonth() + 1).toString().padStart(2, '0');
|
||||
const day = now.getUTCDate().toString().padStart(2, '0');
|
||||
const date = `${year}${month}${day}`;
|
||||
|
||||
const sha = getShortSha();
|
||||
return `v${version}-nightly.${date}.${sha}`;
|
||||
}
|
||||
|
||||
export function getReleaseVersion() {
|
||||
const isNightly = process.env.IS_NIGHTLY === 'true';
|
||||
const manualVersion = process.env.MANUAL_VERSION;
|
||||
|
||||
let releaseTag;
|
||||
|
||||
if (isNightly) {
|
||||
console.error('Calculating next nightly version...');
|
||||
releaseTag = getNightlyTagName();
|
||||
} else if (manualVersion) {
|
||||
console.error(`Using manual version: ${manualVersion}`);
|
||||
releaseTag = manualVersion;
|
||||
} else {
|
||||
throw new Error(
|
||||
'Error: No version specified and this is not a nightly release.',
|
||||
);
|
||||
}
|
||||
|
||||
if (!releaseTag) {
|
||||
throw new Error('Error: Version could not be determined.');
|
||||
}
|
||||
|
||||
if (!releaseTag.startsWith('v')) {
|
||||
console.error("Version is missing 'v' prefix. Prepending it.");
|
||||
releaseTag = `v${releaseTag}`;
|
||||
}
|
||||
|
||||
if (releaseTag.includes('+')) {
|
||||
throw new Error(
|
||||
'Error: Versions with build metadata (+) are not supported for releases. Please use a pre-release version (e.g., v1.2.3-alpha.4) instead.',
|
||||
);
|
||||
}
|
||||
|
||||
if (!releaseTag.match(/^v[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?$/)) {
|
||||
throw new Error(
|
||||
'Error: Version must be in the format vX.Y.Z or vX.Y.Z-prerelease',
|
||||
);
|
||||
}
|
||||
|
||||
const releaseVersion = releaseTag.substring(1);
|
||||
let npmTag = 'latest';
|
||||
if (releaseVersion.includes('-')) {
|
||||
npmTag = releaseVersion.split('-')[1].split('.')[0];
|
||||
}
|
||||
|
||||
return { releaseTag, releaseVersion, npmTag };
|
||||
}
|
||||
|
||||
if (process.argv[1] === new URL(import.meta.url).pathname) {
|
||||
try {
|
||||
const versions = getReleaseVersion();
|
||||
console.log(JSON.stringify(versions));
|
||||
} catch (error) {
|
||||
console.error(error.message);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { execSync } from 'child_process';
|
||||
import { readFileSync } from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
function getVersion() {
|
||||
const packageJsonPath = path.resolve(process.cwd(), 'package.json');
|
||||
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
|
||||
return packageJson.version;
|
||||
}
|
||||
|
||||
function getShortSha() {
|
||||
return execSync('git rev-parse --short HEAD').toString().trim();
|
||||
}
|
||||
|
||||
function getNightlyTagName() {
|
||||
const version = getVersion();
|
||||
const now = new Date();
|
||||
const year = now.getUTCFullYear().toString().slice(-2);
|
||||
const month = (now.getUTCMonth() + 1).toString().padStart(2, '0');
|
||||
const day = now.getUTCDate().toString().padStart(2, '0');
|
||||
const date = `${year}${month}${day}`;
|
||||
|
||||
const sha = getShortSha();
|
||||
return `v${version}-nightly.${date}.${sha}`;
|
||||
}
|
||||
|
||||
function createAndPushTag(tagName, isSigned) {
|
||||
const command = isSigned
|
||||
? `git tag -s -a ${tagName} -m ''`
|
||||
: `git tag ${tagName}`;
|
||||
|
||||
try {
|
||||
console.log(`Executing: ${command}`);
|
||||
execSync(command, { stdio: 'inherit' });
|
||||
console.log(`Successfully created tag: ${tagName}`);
|
||||
|
||||
console.log(`Pushing tag to origin...`);
|
||||
execSync(`git push origin ${tagName}`, { stdio: 'inherit' });
|
||||
console.log(`Successfully pushed tag: ${tagName}`);
|
||||
} catch (error) {
|
||||
console.error(`Failed to create or push tag: ${tagName}`);
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
const tagName = getNightlyTagName();
|
||||
// In GitHub Actions, the CI variable is set to true.
|
||||
// We will create a signed commit if not in a CI environment.
|
||||
const shouldSign = !process.env.CI;
|
||||
|
||||
createAndPushTag(tagName, shouldSign);
|
|
@ -0,0 +1,108 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||
import { getReleaseVersion } from '../get-release-version';
|
||||
import { execSync } from 'child_process';
|
||||
import * as fs from 'fs';
|
||||
|
||||
vi.mock('child_process', () => ({
|
||||
execSync: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('fs', async (importOriginal) => {
|
||||
const mod = await importOriginal();
|
||||
return {
|
||||
...mod,
|
||||
readFileSync: vi.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
describe('getReleaseVersion', () => {
|
||||
const originalEnv = { ...process.env };
|
||||
|
||||
beforeEach(() => {
|
||||
vi.resetModules();
|
||||
process.env = { ...originalEnv };
|
||||
vi.useFakeTimers();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
process.env = originalEnv;
|
||||
vi.clearAllMocks();
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it('should calculate nightly version when IS_NIGHTLY is true', () => {
|
||||
process.env.IS_NIGHTLY = 'true';
|
||||
const knownDate = new Date('2025-07-20T10:00:00.000Z');
|
||||
vi.setSystemTime(knownDate);
|
||||
vi.mocked(fs.readFileSync).mockReturnValue(
|
||||
JSON.stringify({ version: '0.1.0' }),
|
||||
);
|
||||
vi.mocked(execSync).mockReturnValue('abcdef');
|
||||
const { releaseTag, releaseVersion, npmTag } = getReleaseVersion();
|
||||
expect(releaseTag).toBe('v0.1.9-nightly.250720.abcdef');
|
||||
expect(releaseVersion).toBe('0.1.9-nightly.250720.abcdef');
|
||||
expect(npmTag).toBe('nightly');
|
||||
});
|
||||
|
||||
it('should use manual version when provided', () => {
|
||||
process.env.MANUAL_VERSION = '1.2.3';
|
||||
const { releaseTag, releaseVersion, npmTag } = getReleaseVersion();
|
||||
expect(releaseTag).toBe('v1.2.3');
|
||||
expect(releaseVersion).toBe('1.2.3');
|
||||
expect(npmTag).toBe('latest');
|
||||
});
|
||||
|
||||
it('should prepend v to manual version if missing', () => {
|
||||
process.env.MANUAL_VERSION = '1.2.3';
|
||||
const { releaseTag } = getReleaseVersion();
|
||||
expect(releaseTag).toBe('v1.2.3');
|
||||
});
|
||||
|
||||
it('should handle pre-release versions correctly', () => {
|
||||
process.env.MANUAL_VERSION = 'v1.2.3-beta.1';
|
||||
const { releaseTag, releaseVersion, npmTag } = getReleaseVersion();
|
||||
expect(releaseTag).toBe('v1.2.3-beta.1');
|
||||
expect(releaseVersion).toBe('1.2.3-beta.1');
|
||||
expect(npmTag).toBe('beta');
|
||||
});
|
||||
|
||||
it('should throw an error for invalid version format', () => {
|
||||
process.env.MANUAL_VERSION = '1.2';
|
||||
expect(() => getReleaseVersion()).toThrow(
|
||||
'Error: Version must be in the format vX.Y.Z or vX.Y.Z-prerelease',
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw an error if no version is provided for non-nightly release', () => {
|
||||
expect(() => getReleaseVersion()).toThrow(
|
||||
'Error: No version specified and this is not a nightly release.',
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw an error for versions with build metadata', () => {
|
||||
process.env.MANUAL_VERSION = 'v1.2.3+build456';
|
||||
expect(() => getReleaseVersion()).toThrow(
|
||||
'Error: Versions with build metadata (+) are not supported for releases.',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('get-release-version script', () => {
|
||||
it('should print version JSON to stdout when executed directly', () => {
|
||||
const expectedJson = {
|
||||
releaseTag: 'v0.1.0-nightly.20250705',
|
||||
releaseVersion: '0.1.0-nightly.20250705',
|
||||
npmTag: 'nightly',
|
||||
};
|
||||
execSync.mockReturnValue(JSON.stringify(expectedJson));
|
||||
|
||||
const result = execSync('node scripts/get-release-version.js').toString();
|
||||
expect(JSON.parse(result)).toEqual(expectedJson);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,12 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { vi } from 'vitest';
|
||||
|
||||
vi.mock('fs', () => ({
|
||||
...vi.importActual('fs'),
|
||||
appendFileSync: vi.fn(),
|
||||
}));
|
|
@ -0,0 +1,20 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { defineConfig } from 'vitest/config';
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
globals: true,
|
||||
environment: 'node',
|
||||
include: ['scripts/tests/**/*.test.js'],
|
||||
setupFiles: ['scripts/tests/test-setup.ts'],
|
||||
coverage: {
|
||||
provider: 'v8',
|
||||
reporter: ['text', 'lcov'],
|
||||
},
|
||||
},
|
||||
});
|
Loading…
Reference in New Issue