BitGoJS Release #20
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- | |
| name: BitGoJS Release | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| dry-run: | |
| description: | | |
| If true, only runs checks without performing the actual release | |
| type: boolean | |
| required: false | |
| default: false | |
| permissions: | |
| contents: write | |
| id-token: write | |
| pull-requests: read | |
| env: | |
| NX_NO_CLOUD: true | |
| NX_SKIP_NX_CACHE: true | |
| DOCKER_HUB_USERNAME: "bgdeploybot" | |
| jobs: | |
| get-release-context: | |
| name: Get release context | |
| runs-on: ${{ vars.BASE_RUNNER_TYPE || 'ubuntu-latest' }} | |
| timeout-minutes: 10 | |
| outputs: | |
| last-release-tag: ${{ steps.get-release-info.outputs.last-release-tag }} | |
| last-release-sha: ${{ steps.get-release-info.outputs.last-release-sha }} | |
| current-master-sha: ${{ steps.get-release-info.outputs.current-master-sha }} | |
| commits-since-release: ${{ steps.get-release-info.outputs.commits-since-release }} | |
| express-version: ${{ steps.compute-express-git-tag.outputs.version }} | |
| express-git-tag: ${{ steps.compute-express-git-tag.outputs.git-tag }} | |
| express-git-sha: ${{ steps.compute-express-git-sha.outputs.git-sha }} | |
| express-docker-exists: ${{ steps.check-docker-image.outputs.express-docker-exists }} | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v6 | |
| with: | |
| ref: master | |
| fetch-depth: 0 | |
| fetch-tags: true | |
| - name: Get release information | |
| id: get-release-info | |
| run: | | |
| # Get the latest stable release tag | |
| LAST_RELEASE_TAG=$(git tag --sort=-version:refname | grep -E 'bitgo@[0-9]+\.[0-9]+\.[0-9]+$' | head -n 1) | |
| # Get the commit SHA for that release | |
| LAST_RELEASE_SHA=$(git rev-parse "$LAST_RELEASE_TAG^{}") | |
| # Get the current master HEAD commit | |
| CURRENT_MASTER_SHA=$(git rev-parse HEAD) | |
| # Count commits since last release | |
| COMMITS_SINCE_RELEASE=$(git log --oneline "${LAST_RELEASE_TAG}..HEAD" | wc -l) | |
| # Verify we have commits to process | |
| if [ "$COMMITS_SINCE_RELEASE" -eq 0 ]; then | |
| echo "::error::No commits found since last release $LAST_RELEASE_TAG. Nothing to process." | |
| exit 1 | |
| fi | |
| # Output the information | |
| echo "Last release tag: $LAST_RELEASE_TAG" | |
| echo "Last release SHA: $LAST_RELEASE_SHA" | |
| echo "Current master SHA: $CURRENT_MASTER_SHA" | |
| echo "Commits since release: $COMMITS_SINCE_RELEASE" | |
| # Set outputs | |
| { | |
| echo "last-release-tag=$LAST_RELEASE_TAG" | |
| echo "last-release-sha=$LAST_RELEASE_SHA" | |
| echo "current-master-sha=$CURRENT_MASTER_SHA" | |
| echo "commits-since-release=$COMMITS_SINCE_RELEASE" | |
| } >> "$GITHUB_OUTPUT" | |
| echo "Commits to process:" | |
| git log --oneline "${LAST_RELEASE_TAG}..HEAD" | |
| - name: Generate release commit summary | |
| run: | | |
| { | |
| echo "## Commits to be Released" | |
| echo "" | |
| echo "From ${{ steps.get-release-info.outputs.last-release-sha }} to ${{ steps.get-release-info.outputs.current-master-sha }}" | |
| echo "" | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| # Get commits excluding merge commits | |
| git log --oneline --no-merges "${{ steps.get-release-info.outputs.last-release-sha }}..${{ steps.get-release-info.outputs.current-master-sha }}" | while read -r line; do | |
| commit_hash=$(echo "$line" | cut -d' ' -f1) | |
| commit_msg=$(echo "$line" | cut -d' ' -f2-) | |
| # Get full commit message to check for TICKET: pattern | |
| full_commit_msg=$(git log -1 --pretty=format:"%B" "$commit_hash") | |
| # Extract Jira ticket from commit message (handles multiple patterns, case insensitive) | |
| # 1. Direct pattern in subject: VL-1234, CORE-567, etc. | |
| # 2. TICKET: VL-1234 pattern in body | |
| jira_ticket=$(echo "$full_commit_msg" | grep -oiE '(ticket:\s*)?[A-Z]+-[0-9]+' | sed 's/[Tt][Ii][Cc][Kk][Ee][Tt]:\s*//' | head -1) | |
| if [[ -n "$jira_ticket" ]]; then | |
| jira_link="[$jira_ticket](https://bitgoinc.atlassian.net/browse/${jira_ticket})" | |
| echo "- \`$commit_hash\` $commit_msg - $jira_link" >> "$GITHUB_STEP_SUMMARY" | |
| else | |
| echo "- \`$commit_hash\` $commit_msg" >> "$GITHUB_STEP_SUMMARY" | |
| fi | |
| done | |
| echo "" >> "$GITHUB_STEP_SUMMARY" | |
| - name: Checkout rel/latest branch | |
| if: inputs.dry-run == false | |
| uses: actions/checkout@v6 | |
| with: | |
| ref: rel/latest | |
| - name: Compute express target version and tag | |
| if: inputs.dry-run == false | |
| id: compute-express-git-tag | |
| run: | | |
| VERSION=$(jq -r '.version' ./modules/express/package.json) | |
| TAG="@bitgo/express@$VERSION" | |
| echo "Current latest express version: $VERSION" | |
| echo "Expected latest express git tag: $TAG" | |
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | |
| echo "git-tag=$TAG" >> "$GITHUB_OUTPUT" | |
| - name: Checkout express target git tag | |
| if: inputs.dry-run == false | |
| uses: actions/checkout@v6 | |
| with: | |
| ref: ${{ steps.compute-express-git-tag.outputs.git-tag }} | |
| fetch-depth: 2 | |
| - name: Parse express release information | |
| if: inputs.dry-run == false | |
| id: compute-express-git-sha | |
| run: | | |
| GIT_SHA=$(git rev-parse HEAD) | |
| echo "Git SHA: $GIT_SHA" | |
| echo "git-sha=$GIT_SHA" >> "$GITHUB_OUTPUT" | |
| - name: Sanity Check Express Git Tag | |
| if: inputs.dry-run == false | |
| run: | | |
| # Since git tags can be moved, we need to ensure the tag we're releasing | |
| # actually corresponds to a version bump in package.json | |
| CURRENT_VERSION="${{ steps.compute-express-git-tag.outputs.version }}" | |
| PREVIOUS_VERSION=$(git show HEAD~1:./modules/express/package.json | jq -r '.version') | |
| echo "Current version: $CURRENT_VERSION" | |
| echo "Previous version: $PREVIOUS_VERSION" | |
| if [ "$CURRENT_VERSION" == "$PREVIOUS_VERSION" ]; then | |
| echo "::error::Express version bump does not line up with git tag location." | |
| echo "::error::This suggests the git tag may have been moved." | |
| exit 1 | |
| fi | |
| echo "✅ Express version bump lines up with git tag" | |
| - name: Check if Docker image already exists in Docker Hub | |
| if: inputs.dry-run == false | |
| id: check-docker-image | |
| run: | | |
| VERSION="${{ steps.compute-express-git-tag.outputs.version }}" | |
| if curl -s -f "https://hub.docker.com/v2/repositories/bitgo/express/tags/$VERSION" > /dev/null; then | |
| echo "⚠️ Docker image bitgo/express:$VERSION already exists in Docker Hub — Docker publish will be skipped" | |
| echo "express-docker-exists=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "✅ Docker image bitgo/express:$VERSION does not exist in Docker Hub" | |
| echo "express-docker-exists=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Summarise Docker image status | |
| if: inputs.dry-run == false | |
| run: | | |
| if [ "${{ steps.check-docker-image.outputs.express-docker-exists }}" == "true" ]; then | |
| echo "### ⚠️ Docker publish skipped" >> "$GITHUB_STEP_SUMMARY" | |
| echo "Image \`bitgo/express:${{ steps.compute-express-git-tag.outputs.version }}\` already exists in Docker Hub." >> "$GITHUB_STEP_SUMMARY" | |
| echo "The npm release will proceed normally." >> "$GITHUB_STEP_SUMMARY" | |
| fi | |
| - name: Update Express GitHub summary | |
| if: inputs.dry-run == false | |
| run: | | |
| { | |
| echo "## BitGo Express Release Information" | |
| echo "" | |
| echo "Express Version: ${{ steps.compute-express-git-tag.outputs.version }}" | |
| echo "Git Tag: ${{ steps.compute-express-git-tag.outputs.git-tag }}" | |
| echo "Commit SHA: ${{ steps.compute-express-git-sha.outputs.git-sha }}" | |
| echo "" | |
| echo "### Docker Images to be deployed:" | |
| echo "- \`bitgo/express:latest\`" | |
| echo "- \`bitgo/express:${{ steps.compute-express-git-tag.outputs.version }}\`" | |
| echo "" | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| release-bitgojs: | |
| name: Release BitGoJS | |
| needs: | |
| - get-release-context | |
| runs-on: ${{ vars.BASE_RUNNER_TYPE || 'ubuntu-latest' }} | |
| timeout-minutes: 60 | |
| environment: npmjs-release | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v6 | |
| with: | |
| ref: ${{ needs.get-release-context.outputs.current-master-sha }} | |
| token: ${{ secrets.BITGOBOT_PAT_TOKEN || github.token }} | |
| fetch-depth: 0 | |
| - name: Configure GPG | |
| if: inputs.dry-run == false | |
| run: | | |
| echo "${{ secrets.BITGOBOT_GPG_PRIVATE_KEY }}" | gpg --batch --import | |
| git config --global user.signingkey 67A9A0B77F0BD445E45CC8B719828A304678A92F | |
| git config --global commit.gpgsign true | |
| git config --global user.email "bitgobot@bitgo.com" | |
| git config --global user.name "bitgobot" | |
| - name: Configure npmrc | |
| run: | | |
| echo "engine-strict=true" > ~/.npmrc | |
| - name: Setup Node.js with nvm | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version-file: ".nvmrc" | |
| - name: Switch to rel/latest branch | |
| run: | | |
| git checkout rel/latest | |
| git pull origin rel/latest | |
| - name: Merge master into rel/latest | |
| run: | | |
| echo "Merging master commit ${{ needs.get-release-context.outputs.current-master-sha }} into rel/latest" | |
| git merge ${{ needs.get-release-context.outputs.current-master-sha }} --no-edit | |
| - name: Install dependencies | |
| run: | | |
| yarn install --frozen-lockfile | |
| - name: Run yarn audit | |
| run: | | |
| yarn run audit-high | |
| - name: Run dependency check | |
| run: | | |
| yarn check-deps | |
| # Trusted publishing (OIDC) cannot publish a package that doesn't already | |
| # exist on npm. Catch missing packages before the publish step so the | |
| # release doesn't partially fail midway through. | |
| - name: Verify all packages exist on npm | |
| uses: ./.github/actions/verify-npm-packages | |
| - name: Publish new version | |
| if: inputs.dry-run == false | |
| run: | | |
| yarn lerna publish --sign-git-tag --sign-git-commit --include-merged-tags --conventional-commits --conventional-graduate --yes | |
| env: | |
| NPM_CONFIG_PROVENANCE: true | |
| - name: Generate version bump summary | |
| id: version-bump-summary | |
| if: inputs.dry-run == false | |
| continue-on-error: true | |
| uses: ./.github/actions/version-bump-summary | |
| - name: Extract published version | |
| if: inputs.dry-run == false | |
| id: extract-version | |
| run: | | |
| NEW_VERSION=$(jq -r '.version' ./modules/bitgo/package.json) | |
| echo "New version: $NEW_VERSION" | |
| echo "new-version=$NEW_VERSION" >> "$GITHUB_OUTPUT" | |
| - name: Create GitHub release | |
| if: inputs.dry-run == false && steps.version-bump-summary.outcome == 'success' | |
| continue-on-error: true | |
| env: | |
| GH_TOKEN: ${{ secrets.BITGOBOT_PAT_TOKEN || github.token }} | |
| run: | | |
| gh release create "v${{ steps.extract-version.outputs.new-version }}" \ | |
| --latest \ | |
| --title "v${{ steps.extract-version.outputs.new-version }}" \ | |
| --notes-file "${{ steps.version-bump-summary.outputs.text-file }}" | |
| publish-express-to-docker-hub: | |
| name: Publish Express To Docker Hub | |
| if: inputs.dry-run == false && needs.get-release-context.outputs.express-docker-exists != 'true' | |
| needs: | |
| - get-release-context | |
| - release-bitgojs | |
| runs-on: ${{ vars.BASE_RUNNER_TYPE || 'ubuntu-latest' }} | |
| timeout-minutes: 40 | |
| environment: bitgo-express | |
| steps: | |
| - name: Checkout BitGoJS repository | |
| uses: actions/checkout@v6 | |
| with: | |
| ref: ${{ needs.get-release-context.outputs.express-git-sha }} | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 | |
| - name: Log in to Docker Hub | |
| uses: docker/login-action@v4 | |
| with: | |
| username: ${{ env.DOCKER_HUB_USERNAME }} | |
| password: ${{ secrets.DOCKER_HUB_API_KEY }} | |
| - name: Generate build date | |
| id: build-date | |
| run: | | |
| BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ") | |
| echo "build-date=$BUILD_DATE" >> "$GITHUB_OUTPUT" | |
| - name: Build and push Express Docker image | |
| id: docker-build | |
| uses: docker/build-push-action@v7 | |
| with: | |
| context: . | |
| file: ./Dockerfile | |
| push: true | |
| tags: | | |
| bitgo/express:latest | |
| bitgo/express:${{ needs.get-release-context.outputs.express-version }} | |
| build-args: | | |
| VERSION=${{ needs.get-release-context.outputs.express-version }} | |
| BUILD_DATE=${{ steps.build-date.outputs.build-date }} | |
| GIT_HASH=${{ needs.get-release-context.outputs.express-git-sha }} |