From 3fa8a7e9fdfbfb6d3a7923a02c29bd1e94666f8e Mon Sep 17 00:00:00 2001 From: mkorwel Date: Tue, 28 Oct 2025 12:43:10 -0700 Subject: [PATCH] feat(ci): generalize publish-bundle action and script --- .github/actions/publish-bundle/action.yml | 51 ++++ .../publish-release-bundled/action.yml | 171 +++++++++++ .../publish-release-deprecated/action.yml | 274 ++++++++++++++++++ .github/actions/publish-release/action.yml | 120 +++----- .github/workflows/build-and-publish.yml | 37 +-- .github/workflows/release-manual-bundled.yml | 5 +- .github/workflows/release-manual.yml | 2 +- scripts/prepare-github-release.js | 41 --- scripts/prepare-package.js | 72 ++--- 9 files changed, 593 insertions(+), 180 deletions(-) create mode 100644 .github/actions/publish-bundle/action.yml create mode 100644 .github/actions/publish-release-bundled/action.yml create mode 100644 .github/actions/publish-release-deprecated/action.yml delete mode 100644 scripts/prepare-github-release.js diff --git a/.github/actions/publish-bundle/action.yml b/.github/actions/publish-bundle/action.yml new file mode 100644 index 0000000000..17270a3d80 --- /dev/null +++ b/.github/actions/publish-bundle/action.yml @@ -0,0 +1,51 @@ +# .github/actions/publish-bundle/action.yml +name: 'Publish Bundle' +description: 'Builds, versions, and publishes the bundled CLI package.' +inputs: + version: + description: 'The version to publish the package with.' + required: true + tag: + description: 'The npm dist-tag to publish the package with.' + required: true + github-token: + description: 'GitHub token for authentication.' + required: true + package-scope: + description: 'The npm scope to publish the package to.' + required: false +runs: + using: 'composite' + steps: + - name: 'Install dependencies' + shell: 'bash' + run: 'npm ci' + + - name: 'Setup NPMRC' + uses: './.github/actions/setup-npmrc' + with: + github-token: '${{ inputs.github-token }}' + + - name: 'Bundle' + shell: 'bash' + run: 'npm run bundle' + + - name: 'Prepare Package for Publishing' + if: "${{ inputs.package-scope != '' }}" + shell: 'bash' + env: + PACKAGE_SCOPE: '${{ inputs.package-scope }}' + run: 'node scripts/prepare-package.js --scope=${{ env.PACKAGE_SCOPE }}' + + - name: 'Set Version' + shell: 'bash' + env: + PACKAGE_VERSION: '${{ inputs.version }}' + run: 'npm version --no-git-tag-version "${{ env.PACKAGE_VERSION }}"' + + - name: 'Publish to GitHub Packages' + env: + NODE_AUTH_TOKEN: '${{ inputs.github-token }}' + NPM_TAG: '${{ inputs.tag }}' + shell: 'bash' + run: 'npm publish --tag="${{ env.NPM_TAG }}"' diff --git a/.github/actions/publish-release-bundled/action.yml b/.github/actions/publish-release-bundled/action.yml new file mode 100644 index 0000000000..7d3b410e06 --- /dev/null +++ b/.github/actions/publish-release-bundled/action.yml @@ -0,0 +1,171 @@ +name: 'Publish Release Bundled' +description: 'Builds, prepares, and publishes a bundled gemini-cli package 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-cli: + description: 'The npm token for the cli package.' + required: true + github-token: + description: 'The GitHub token for creating the release.' + required: true + 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 +runs: + using: 'composite' + steps: + - name: '๐Ÿ“ Print Inputs' + shell: 'bash' + env: + JSON_INPUTS: '${{ toJSON(inputs) }}' + run: 'echo "$JSON_INPUTS"' + + - 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' + env: + RELEASE_TAG: '${{ inputs.release-tag }}' + run: | + BRANCH_NAME="release/${RELEASE_TAG}" + git switch -c "${BRANCH_NAME}" + echo "BRANCH_NAME=${BRANCH_NAME}" >> "${GITHUB_OUTPUT}" + + - name: 'โฌ†๏ธ Update package versions' + working-directory: '${{ inputs.working-directory }}' + shell: 'bash' + env: + RELEASE_VERSION: '${{ inputs.release-version }}' + run: | + npm run release:version "${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 }}' + 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 --set-upstream origin "${BRANCH_NAME}" --follow-tags + else + echo "Dry run enabled. Skipping push." + fi + + - name: '๐ŸŽ Bundle' + working-directory: '${{ inputs.working-directory }}' + shell: 'bash' + run: | + npm run bundle + + - name: 'Configure npm for publishing' + 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}}' + cache-dependency-path: '${{ inputs.working-directory }}/package-lock.json' + cache: 'npm' + + - name: '๐Ÿ“ฆ Publish to NPM' + working-directory: '${{ inputs.working-directory }}' + env: + NODE_AUTH_TOKEN: '${{ inputs.wombat-token-cli }}' + run: | + npm publish --dry-run="${{ inputs.dry-run }}" + + - name: '๐Ÿ”ฌ Verify Bundled NPM release by version' + if: "${{ inputs.dry-run == 'false' && inputs.force-skip-tests == 'false' }}" + working-directory: '${{ inputs.working-directory }}' + shell: 'bash' + env: + CLI_PACKAGE_NAME: '${{ inputs.cli-package-name }}' + RELEASE_VERSION: '${{ inputs.release-version }}' + run: | + npx -y ${CLI_PACKAGE_NAME}@${RELEASE_VERSION} --version + + - name: '๐ŸŽ‰ Create GitHub Release' + working-directory: '${{ inputs.working-directory }}' + if: "${{ inputs.dry-run == 'false' && inputs.skip-github-release == 'false' && inputs.npm-tag != 'dev' && inputs.npm-registry-url != 'https://npm.pkg.github.com/' }}" + env: + GITHUB_TOKEN: '${{ inputs.github-token }}' + RELEASE_TAG: '${{ inputs.release-tag }}' + BRANCH_NAME: '${{ steps.release_branch.outputs.BRANCH_NAME }}' + PREVIOUS_TAG: '${{ inputs.previous-tag }}' + shell: 'bash' + run: | + gh release create "${RELEASE_TAG}" \ + bundle/gemini.js \ + --target "${BRANCH_NAME}" \ + --title "Release ${RELEASE_TAG}" \ + --notes-start-tag "${PREVIOUS_TAG}" \ + --generate-notes + + - name: '๐Ÿงน Clean up release branch' + working-directory: '${{ inputs.working-directory }}' + if: "${{ inputs.dry-run == 'false' && inputs.skip-branch-cleanup == 'false' }}" + continue-on-error: true + shell: 'bash' + env: + BRANCH_NAME: '${{ steps.release_branch.outputs.BRANCH_NAME }}' + run: | + echo "Cleaning up release branch ${BRANCH_NAME}..." + git push origin --delete "${BRANCH_NAME}" \ No newline at end of file diff --git a/.github/actions/publish-release-deprecated/action.yml b/.github/actions/publish-release-deprecated/action.yml new file mode 100644 index 0000000000..a639bb3b8f --- /dev/null +++ b/.github/actions/publish-release-deprecated/action.yml @@ -0,0 +1,274 @@ +name: 'Publish Release (DEPRECATED)' +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 + 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: '๐Ÿ“ Print Inputs' + shell: 'bash' + env: + JSON_INPUTS: '${{ toJSON(inputs) }}' + run: 'echo "$JSON_INPUTS"' + + - 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}" + + - name: 'โฌ†๏ธ Update package versions' + working-directory: '${{ inputs.working-directory }}' + shell: 'bash' + run: | + npm run 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 }}' + 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 --set-upstream origin "${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 }}' + shell: 'bash' + run: | + npm publish \ + --dry-run="${{ inputs.dry-run }}" \ + --workspace="${{ inputs.core-package-name }}" \ + --no-tag + npm dist-tag rm ${{ inputs.core-package-name }} false --silent + + - 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 + + - 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 }}' + shell: 'bash' + run: | + npm publish \ + --dry-run="${{ inputs.dry-run }}" \ + --workspace="${{ inputs.cli-package-name }}" \ + --no-tag + npm dist-tag rm ${{ inputs.cli-package-name }} false --silent + + - 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 }}' + shell: 'bash' + # Tag staging for initial release + run: | + npm publish \ + --dry-run="${{ inputs.dry-run }}" \ + --workspace="${{ inputs.a2a-package-name }}" \ + --no-tag + npm dist-tag rm ${{ inputs.a2a-package-name }} false --silent + + - 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: + 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-token }}' + shell: 'bash' + run: | + gh release create "${{ inputs.release-tag }}" \ + bundle/gemini.js \ + --target "${{ steps.release_branch.outputs.BRANCH_NAME }}" \ + --title "Release ${{ inputs.release-tag }}" \ + --notes-start-tag "${{ inputs.previous-tag }}" \ + --generate-notes + + - 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 origin --delete "${{ steps.release_branch.outputs.BRANCH_NAME }}" diff --git a/.github/actions/publish-release/action.yml b/.github/actions/publish-release/action.yml index 6e66260594..62411fb272 100644 --- a/.github/actions/publish-release/action.yml +++ b/.github/actions/publish-release/action.yml @@ -69,11 +69,6 @@ inputs: a2a-package-name: description: 'The name of the a2a package.' required: true - use-bundle-release: - description: 'Whether to use the new bundle-based release process.' - type: 'string' - required: false - default: 'false' runs: using: 'composite' steps: @@ -94,20 +89,16 @@ runs: working-directory: '${{ inputs.working-directory }}' id: 'release_branch' shell: 'bash' - env: - RELEASE_TAG: '${{ inputs.release-tag }}' run: | - BRANCH_NAME="release/${RELEASE_TAG}" + BRANCH_NAME="release/${{ inputs.release-tag }}" git switch -c "${BRANCH_NAME}" echo "BRANCH_NAME=${BRANCH_NAME}" >> "${GITHUB_OUTPUT}" - name: 'โฌ†๏ธ Update package versions' working-directory: '${{ inputs.working-directory }}' shell: 'bash' - env: - RELEASE_VERSION: '${{ inputs.release-version }}' run: | - npm run release:version "${RELEASE_VERSION}" + npm run release:version "${{ inputs.release-version }}" - name: '๐Ÿ’พ Commit and Conditionally Push package versions' working-directory: '${{ inputs.working-directory }}' @@ -127,25 +118,34 @@ runs: echo "Dry run enabled. Skipping push." fi - - name: 'Configure npm for publishing to npm (non-bundle release)' - if: "inputs.use-bundle-release == 'false'" - 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}}' - cache-dependency-path: '${{ inputs.working-directory }}/package-lock.json' - cache: 'npm' - cache-key: 'npm-cache-non-bundle-${{ github.sha }}' - - name: '๐Ÿ› ๏ธ Build and Prepare Packages' - if: "${{ inputs.use-bundle-release == 'false' }}" 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' @@ -157,37 +157,28 @@ runs: wombat-token-a2a-server: '${{ inputs.wombat-token-a2a-server }}' - name: '๐Ÿ“ฆ Publish CORE to NPM' - if: "${{ inputs.use-bundle-release == 'false' }}" working-directory: '${{ inputs.working-directory }}' env: NODE_AUTH_TOKEN: '${{ steps.core-token.outputs.auth-token }}' - DRY_RUN: '${{ inputs.dry-run }}' - CORE_PACKAGE_NAME: '${{ inputs.core-package-name }}' shell: 'bash' run: | npm publish \ - --dry-run="${DRY_RUN}" \ - --workspace="${CORE_PACKAGE_NAME}" \ + --dry-run="${{ inputs.dry-run }}" \ + --workspace="${{ inputs.core-package-name }}" \ --no-tag - npm dist-tag rm ${CORE_PACKAGE_NAME} false --silent + npm dist-tag rm ${{ inputs.core-package-name }} false --silent - name: '๐Ÿ”— Install latest core package' - if: "${{ inputs.dry-run == 'false' && inputs.use-bundle-release == 'false' }}" working-directory: '${{ inputs.working-directory }}' + if: "${{ inputs.dry-run != 'true' }}" shell: 'bash' - env: - CORE_PACKAGE_NAME: '${{ inputs.core-package-name }}' - RELEASE_VERSION: '${{ inputs.release-version }}' - CLI_PACKAGE_NAME: '${{ inputs.cli-package-name }}' - A2A_PACKAGE_NAME: '${{ inputs.a2a-package-name }}' run: | - npm install "${CORE_PACKAGE_NAME}@${RELEASE_VERSION}" \ - --workspace="${CLI_PACKAGE_NAME}" \ - --workspace="${A2A_PACKAGE_NAME}" \ + npm install "${{ inputs.core-package-name }}@${{ inputs.release-version }}" \ + --workspace="${{ inputs.cli-package-name }}" \ + --workspace="${{ inputs.a2a-package-name }}" \ --save-exact - name: 'Get CLI Token' - if: "${{ inputs.use-bundle-release == 'false' }}" uses: './.github/actions/npm-auth-token' id: 'cli-token' with: @@ -198,21 +189,18 @@ runs: wombat-token-a2a-server: '${{ inputs.wombat-token-a2a-server }}' - name: '๐Ÿ“ฆ Publish CLI' - if: "${{ inputs.use-bundle-release == 'false' }}" working-directory: '${{ inputs.working-directory }}' env: NODE_AUTH_TOKEN: '${{ steps.cli-token.outputs.auth-token }}' - DRY_RUN: '${{ inputs.dry-run }}' - CLI_PACKAGE_NAME: '${{ inputs.cli-package-name }}' shell: 'bash' run: | npm publish \ - --dry-run="${DRY_RUN}" \ + --dry-run="${{ inputs.dry-run }}" \ + --workspace="${{ inputs.cli-package-name }}" \ --no-tag - npm dist-tag rm ${CLI_PACKAGE_NAME} false --silent + npm dist-tag rm ${{ inputs.cli-package-name }} false --silent - name: 'Get a2a-server Token' - if: "${{ inputs.use-bundle-release == 'false' }}" uses: './.github/actions/npm-auth-token' id: 'a2a-token' with: @@ -223,24 +211,21 @@ runs: wombat-token-a2a-server: '${{ inputs.wombat-token-a2a-server }}' - name: '๐Ÿ“ฆ Publish a2a' - if: "${{ inputs.use-bundle-release == 'false' }}" working-directory: '${{ inputs.working-directory }}' env: NODE_AUTH_TOKEN: '${{ steps.a2a-token.outputs.auth-token }}' - DRY_RUN: '${{ inputs.dry-run }}' - A2A_PACKAGE_NAME: '${{ inputs.a2a-package-name }}' shell: 'bash' # Tag staging for initial release run: | npm publish \ - --dry-run="${DRY_RUN}" \ - --workspace="${A2A_PACKAGE_NAME}" \ + --dry-run="${{ inputs.dry-run }}" \ + --workspace="${{ inputs.a2a-package-name }}" \ --no-tag - npm dist-tag rm ${A2A_PACKAGE_NAME} false --silent + npm dist-tag rm ${{ inputs.a2a-package-name }} false --silent - name: '๐Ÿ”ฌ Verify NPM release by version' - if: "${{ inputs.dry-run == 'false' && inputs.force-skip-tests == 'false' && inputs.use-bundle-release == 'false' }}" 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 }}' @@ -265,40 +250,25 @@ runs: a2a-package-name: '${{ inputs.a2a-package-name }}' working-directory: '${{ inputs.working-directory }}' - - name: '๐Ÿ”ฌ Verify Bundled NPM release by version' - if: "${{ inputs.dry-run == 'false' && inputs.force-skip-tests == 'false' && inputs.use-bundle-release == 'true' }}" - working-directory: '${{ inputs.working-directory }}' - shell: 'bash' - env: - CLI_PACKAGE_NAME: '${{ inputs.cli-package-name }}' - RELEASE_VERSION: '${{ inputs.release-version }}' - run: | - npx -y ${CLI_PACKAGE_NAME}@${RELEASE_VERSION} --version - - name: '๐ŸŽ‰ Create GitHub Release' working-directory: '${{ inputs.working-directory }}' - if: "${{ inputs.dry-run == 'false' && inputs.skip-github-release == 'false' && inputs.npm-tag != 'dev' && inputs.npm-registry-url != 'https://npm.pkg.github.com/' }}" + 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-token }}' - RELEASE_TAG: '${{ inputs.release-tag }}' - BRANCH_NAME: '${{ steps.release_branch.outputs.BRANCH_NAME }}' - PREVIOUS_TAG: '${{ inputs.previous-tag }}' shell: 'bash' run: | - gh release create "${RELEASE_TAG}" \ + gh release create "${{ inputs.release-tag }}" \ bundle/gemini.js \ - --target "${BRANCH_NAME}" \ - --title "Release ${RELEASE_TAG}" \ - --notes-start-tag "${PREVIOUS_TAG}" \ + --target "${{ steps.release_branch.outputs.BRANCH_NAME }}" \ + --title "Release ${{ inputs.release-tag }}" \ + --notes-start-tag "${{ inputs.previous-tag }}" \ --generate-notes - name: '๐Ÿงน Clean up release branch' working-directory: '${{ inputs.working-directory }}' - if: "${{ inputs.dry-run == 'false' && inputs.skip-branch-cleanup == 'false' }}" + if: "${{ inputs.dry-run != 'true' && inputs.skip-branch-cleanup != 'true' }}" continue-on-error: true shell: 'bash' - env: - BRANCH_NAME: '${{ steps.release_branch.outputs.BRANCH_NAME }}' run: | - echo "Cleaning up release branch ${BRANCH_NAME}..." - git push origin --delete "${BRANCH_NAME}" + echo "Cleaning up release branch ${{ steps.release_branch.outputs.BRANCH_NAME }}..." + git push origin --delete "${{ steps.release_branch.outputs.BRANCH_NAME }}" diff --git a/.github/workflows/build-and-publish.yml b/.github/workflows/build-and-publish.yml index 33222cc95d..711c9e7f28 100644 --- a/.github/workflows/build-and-publish.yml +++ b/.github/workflows/build-and-publish.yml @@ -29,7 +29,7 @@ jobs: name: 'Publish Bundle' runs-on: 'ubuntu-latest' outputs: - version: '${{ steps.version.outputs.version }}' + version: '${{ steps.vars.outputs.version }}' permissions: contents: 'read' packages: 'write' @@ -46,37 +46,24 @@ jobs: node-version-file: '.nvmrc' cache: 'npm' - - name: 'Install dependencies' - run: 'npm ci' - - - name: 'Setup NPMRC' - uses: './.github/actions/setup-npmrc' - with: - github-token: '${{ secrets.GITHUB_TOKEN }}' - - - name: 'Bundle' - run: 'npm run bundle' - - - name: 'Prepare for GitHub release' - run: 'node scripts/prepare-github-release.js' - - - name: 'Set CI Version' - id: 'version' + - name: 'Calculate Version and Tag' + id: 'vars' run: | CURRENT_VERSION=$(node -p "require('./package.json').version") NEW_VERSION="${CURRENT_VERSION}-ci.${{ github.run_number }}.${{ github.sha }}" echo "version=${NEW_VERSION}" >> "$GITHUB_OUTPUT" - npm version --no-git-tag-version "${NEW_VERSION}" - - - name: 'Publish to GitHub Packages' - env: - NODE_AUTH_TOKEN: '${{ secrets.GITHUB_TOKEN }}' - run: | if [[ "${{ github.event_name }}" == "pull_request" ]]; then - npm publish --tag="pr-${{ github.event.pull_request.number }}" + echo "tag=pr-${{ github.event.pull_request.number }}" >> "$GITHUB_OUTPUT" else - npm publish --tag="ci" + echo "tag=ci" >> "$GITHUB_OUTPUT" fi + - name: 'Publish Bundle' + uses: './.github/actions/publish-bundle' + with: + version: '${{ steps.vars.outputs.version }}' + tag: '${{ steps.vars.outputs.tag }}' + github-token: '${{ secrets.GITHUB_TOKEN }}' + package-scope: '${{ inputs.npm-registry-scope }}' build-and-publish-sandbox: name: 'Build and Publish Sandbox' diff --git a/.github/workflows/release-manual-bundled.yml b/.github/workflows/release-manual-bundled.yml index 97e2edf925..c7194e53ad 100644 --- a/.github/workflows/release-manual-bundled.yml +++ b/.github/workflows/release-manual-bundled.yml @@ -100,15 +100,13 @@ jobs: working-directory: './release' - name: 'Publish Release' - uses: './.github/actions/publish-release' + uses: './.github/actions/publish-release-bundled' with: force-skip-tests: '${{ github.event.inputs.force_skip_tests }}' release-version: '${{ steps.release_info.outputs.RELEASE_VERSION }}' release-tag: '${{ github.event.inputs.version }}' npm-tag: '${{ github.event.inputs.npm_channel }}' - wombat-token-core: '${{ secrets.WOMBAT_TOKEN_CORE }}' wombat-token-cli: '${{ secrets.WOMBAT_TOKEN_CLI }}' - wombat-token-a2a-server: '${{ secrets.WOMBAT_TOKEN_A2A_SERVER }}' github-token: '${{ secrets.GITHUB_TOKEN }}' dry-run: '${{ github.event.inputs.dry_run }}' previous-tag: '${{ steps.release_info.outputs.PREVIOUS_TAG }}' @@ -121,7 +119,6 @@ jobs: cli-package-name: '${{ vars.CLI_PACKAGE_NAME }}' core-package-name: '${{ vars.CORE_PACKAGE_NAME }}' a2a-package-name: '${{ vars.A2A_PACKAGE_NAME }}' - use-bundle-release: 'true' - name: 'Create Issue on Failure' if: '${{ failure() && github.event.inputs.dry_run == false }}' diff --git a/.github/workflows/release-manual.yml b/.github/workflows/release-manual.yml index ffe326441c..836586e6b5 100644 --- a/.github/workflows/release-manual.yml +++ b/.github/workflows/release-manual.yml @@ -1,4 +1,4 @@ -name: 'Release: Manual' +name: 'Release: Manual (Depreciated)' on: workflow_dispatch: diff --git a/scripts/prepare-github-release.js b/scripts/prepare-github-release.js deleted file mode 100644 index 681452a6c9..0000000000 --- a/scripts/prepare-github-release.js +++ /dev/null @@ -1,41 +0,0 @@ -/** - * @license - * Copyright 2025 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -import fs from 'node:fs'; -import path from 'node:path'; - -const rootDir = process.cwd(); - -function updatePackageJson(packagePath, updateFn) { - const packageJsonPath = path.resolve(rootDir, packagePath); - if (!fs.existsSync(packageJsonPath)) { - console.error(`Error: package.json not found at ${packageJsonPath}`); - process.exit(1); - } - const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8')); - updateFn(packageJson); - fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2)); - console.log(`Updated ${packagePath}`); -} - -console.log('Preparing packages for GitHub release...'); - -// Update root package.json -updatePackageJson('package.json', (pkg) => { - pkg.name = '@google-gemini/gemini-cli'; -}); - -// Update @google/gemini-cli-a2a-server -updatePackageJson('packages/a2a-server/package.json', (pkg) => { - pkg.name = '@google-gemini/gemini-cli-a2a-server'; -}); - -// Update @google/gemini-cli-core -updatePackageJson('packages/core/package.json', (pkg) => { - pkg.name = '@google-gemini/gemini-cli-core'; -}); - -console.log('Successfully prepared packages for GitHub release.'); diff --git a/scripts/prepare-package.js b/scripts/prepare-package.js index ff1dc137ff..02d124cffa 100644 --- a/scripts/prepare-package.js +++ b/scripts/prepare-package.js @@ -6,46 +6,50 @@ import fs from 'node:fs'; import path from 'node:path'; -import { fileURLToPath } from 'node:url'; -// ES module equivalent of __dirname -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); +const rootDir = process.cwd(); -const rootDir = path.resolve(__dirname, '..'); - -function copyFiles(packageName, filesToCopy) { - const packageDir = path.resolve(rootDir, 'packages', packageName); - if (!fs.existsSync(packageDir)) { - console.error(`Error: Package directory not found at ${packageDir}`); - process.exit(1); - } - - console.log(`Preparing package: ${packageName}`); - for (const [source, dest] of Object.entries(filesToCopy)) { - const sourcePath = path.resolve(rootDir, source); - const destPath = path.resolve(packageDir, dest); - try { - fs.copyFileSync(sourcePath, destPath); - console.log(`Copied ${source} to packages/${packageName}/`); - } catch (err) { - console.error(`Error copying ${source}:`, err); - process.exit(1); - } +function getArg(name) { + const arg = process.argv.find((arg) => arg.startsWith(name)); + if (!arg) { + return null; } + return arg.split('=')[1]; } -// Prepare 'core' package -copyFiles('core', { - 'README.md': 'README.md', - LICENSE: 'LICENSE', - '.npmrc': '.npmrc', +function updatePackageJson(packagePath, updateFn) { + const packageJsonPath = path.resolve(rootDir, packagePath); + if (!fs.existsSync(packageJsonPath)) { + console.error(`Error: package.json not found at ${packageJsonPath}`); + process.exit(1); + } + const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8')); + updateFn(packageJson); + fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2)); + console.log(`Updated ${packagePath}`); +} + +const scope = getArg('--scope'); +if (!scope) { + console.error('Error: --scope argument is required.'); + process.exit(1); +} + +console.log(`Preparing packages with scope: ${scope}...`); + +// Update root package.json +updatePackageJson('package.json', (pkg) => { + pkg.name = `${scope}/gemini-cli`; }); -// Prepare 'cli' package -copyFiles('cli', { - 'README.md': 'README.md', - LICENSE: 'LICENSE', +// Update @google/gemini-cli-a2a-server +updatePackageJson('packages/a2a-server/package.json', (pkg) => { + pkg.name = `${scope}/gemini-cli-a2a-server`; }); -console.log('Successfully prepared all packages.'); +// Update @google/gemini-cli-core +updatePackageJson('packages/core/package.json', (pkg) => { + pkg.name = `${scope}/gemini-cli-core`; +}); + +console.log('Successfully prepared packages.');