diff --git a/.github/actions/publish-release/action.yml b/.github/actions/publish-release/action.yml index f13867ab61..63fbb22842 100644 --- a/.github/actions/publish-release/action.yml +++ b/.github/actions/publish-release/action.yml @@ -197,6 +197,29 @@ runs: 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' @@ -213,12 +236,19 @@ runs: 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}" \ - --workspace="${INPUTS_CLI_PACKAGE_NAME}" \ + ${PUBLISH_TARGET} \ --tag staging-tmp if [[ "${INPUTS_DRY_RUN}" == "false" ]]; then npm dist-tag rm ${INPUTS_CLI_PACKAGE_NAME} staging-tmp @@ -252,18 +282,6 @@ runs: npm dist-tag rm ${INPUTS_A2A_PACKAGE_NAME} staging-tmp fi - - name: '🔬 Verify NPM release by version' - uses: './.github/actions/verify-release' - if: "${{ inputs.dry-run != 'true' && inputs.force-skip-tests != 'true' }}" - with: - npm-package: '${{ inputs.cli-package-name }}@${{ inputs.release-version }}' - 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: '🏷️ Tag release' uses: './.github/actions/tag-npm-release' with: diff --git a/.github/actions/verify-release/action.yml b/.github/actions/verify-release/action.yml index 122802a9ab..92e7690ae5 100644 --- a/.github/actions/verify-release/action.yml +++ b/.github/actions/verify-release/action.yml @@ -74,7 +74,7 @@ runs: shell: 'bash' working-directory: '${{ inputs.working-directory }}' run: |- - gemini_version=$(npx --prefer-online "${INPUTS_NPM_PACKAGE}" --version) + 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 diff --git a/.github/workflows/release-promote.yml b/.github/workflows/release-promote.yml index 2b703bff7a..d6f0854e33 100644 --- a/.github/workflows/release-promote.yml +++ b/.github/workflows/release-promote.yml @@ -106,7 +106,9 @@ jobs: echo "NIGHTLY_JSON: ${NIGHTLY_JSON}" echo "STABLE_VERSION=${STABLE_VERSION}" >> "${GITHUB_OUTPUT}" # shellcheck disable=SC1083 - echo "STABLE_SHA=$(git rev-parse "$(echo "${PREVIEW_JSON}" | jq -r .previousReleaseTag)"^{commit})" >> "${GITHUB_OUTPUT}" + PREVIOUS_PREVIEW_TAG=$(echo "${PREVIEW_JSON}" | jq -r .previousReleaseTag) + STABLE_SHA=$(git rev-parse "${PREVIOUS_PREVIEW_TAG}^{commit}") + echo "STABLE_SHA=${STABLE_SHA}" >> "${GITHUB_OUTPUT}" echo "PREVIOUS_STABLE_TAG=$(echo "${STABLE_JSON}" | jq -r .previousReleaseTag)" >> "${GITHUB_OUTPUT}" echo "PREVIEW_VERSION=$(echo "${PREVIEW_JSON}" | jq -r .releaseVersion)" >> "${GITHUB_OUTPUT}" # shellcheck disable=SC1083 diff --git a/.github/workflows/release-rollback.yml b/.github/workflows/release-rollback.yml index 1de277d172..56af1d6e66 100644 --- a/.github/workflows/release-rollback.yml +++ b/.github/workflows/release-rollback.yml @@ -82,7 +82,8 @@ jobs: ORIGIN_TAG: '${{ steps.origin_tag.outputs.ORIGIN_TAG }}' shell: 'bash' run: | - echo "ORIGIN_HASH=$(git rev-parse "${ORIGIN_TAG}")" >> "$GITHUB_OUTPUT" + ORIGIN_HASH=$(git rev-parse "${ORIGIN_TAG}") + echo "ORIGIN_HASH=${ORIGIN_HASH}" >> "$GITHUB_OUTPUT" - name: 'Change tag' if: "${{ github.event.inputs.rollback_destination != '' }}" diff --git a/scripts/get-release-version.js b/scripts/get-release-version.js index 019280aadf..e1cbf797e8 100644 --- a/scripts/get-release-version.js +++ b/scripts/get-release-version.js @@ -159,12 +159,37 @@ function detectRollbackAndGetBaseline({ args, npmDistTag } = {}) { // Sort by semver to get a list from highest to lowest matchingVersions.sort((a, b) => semver.rcompare(a, b)); - // Find the highest non-deprecated version + // Find the highest non-deprecated version with a git tag let highestExistingVersion = ''; for (const version of matchingVersions) { if (!isVersionDeprecated({ version, args })) { - highestExistingVersion = version; - break; // Found the one we want + try { + // Only consider versions that have a corresponding git tag. + // This prevents picking up versions that were published to NPM but failed before the github release/tag. + let tagExists = false; + try { + execSync(`git rev-parse v${version}^{commit} 2>/dev/null`); + tagExists = true; + } catch { + const remoteTag = execSync( + `git ls-remote --tags origin refs/tags/v${version} 2>/dev/null`, + ) + .toString() + .trim(); + if (remoteTag) { + tagExists = true; + } + } + if (!tagExists) { + throw new Error(`Tag v${version} not found`); + } + highestExistingVersion = version; + break; // Found the one we want + } catch { + console.error( + `Ignoring version ${version} because it lacks a git tag (likely a failed release).`, + ); + } } else { console.error(`Ignoring deprecated version: ${version}`); }