vivekv1504 is running Deploy CD #183
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: Deploy CD | |
| run-name: ${{ github.actor }} is running Deploy CD | |
| on: | |
| push: | |
| branches: # Allow list of deployable tags and branches. Note that all allow-listed branches cannot include any `/` characters | |
| - next | |
| env: | |
| rid: ${{ github.run_id }}-${{ github.run_number }} | |
| token: ${{ secrets.NPM_TOKEN }} | |
| GH_TOKEN: ${{ secrets.GH_TOKEN }} | |
| GIT_AUTHOR_NAME: ${{ github.actor }} | |
| GIT_AUTHOR_EMAIL: ${{ github.actor }}@users.noreply.github.com | |
| HUSKY: 0 | |
| jobs: | |
| analyze-changes: | |
| name: Determine Changed Packages | |
| runs-on: ubuntu-latest | |
| outputs: | |
| node-changed: ${{ steps.generate-package-matrix-node-changed.outputs.node-changed }} | |
| node-recursive: ${{ steps.generate-package-matrix-node-recursive.outputs.node-recursive }} | |
| yarn-changed: ${{ steps.generate-package-matrix-yarn-changed.outputs.yarn-changed }} | |
| yarn-recursive: ${{ steps.generate-package-matrix-yarn-recursive.outputs.yarn-recursive }} | |
| steps: | |
| - name: Checkout Project | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 # Fetch all history for package comparison | |
| - name: Setup Node | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version-file: ".nvmrc" | |
| registry-url: "https://registry.npmjs.org" | |
| cache: "yarn" | |
| - name: Install Dependencies | |
| uses: actions/cache@v4 | |
| id: cache-dependencies | |
| with: | |
| path: "**/node_modules" | |
| key: node-modules-${{ hashFiles('./yarn.lock') }} | |
| - name: Install if cache miss | |
| if: steps.cache-dependencies.outputs.cache-hit != 'true' | |
| run: yarn install --immutable | |
| # Generate package matrix for changed packages | |
| - id: generate-package-matrix-node-changed | |
| name: Generate Package Matrix (Node Changed) | |
| run: echo "node-changed=$(yarn package-tools list --mode node --since ${{ github.event.before }})" >> $GITHUB_OUTPUT | |
| - id: generate-package-matrix-node-recursive | |
| name: Generate Package Matrix (Node Recursive) | |
| run: echo "node-recursive=$(yarn package-tools list --mode node --recursive --since ${{ github.event.before }})" >> $GITHUB_OUTPUT | |
| - id: generate-package-matrix-yarn-changed | |
| name: Generate Package Matrix (Yarn Changed) | |
| run: echo "yarn-changed=$(yarn package-tools list --since ${{ github.event.before }})" >> $GITHUB_OUTPUT | |
| - id: generate-package-matrix-yarn-recursive | |
| name: Generate Package Matrix (Yarn Recursive) | |
| run: echo "yarn-recursive=$(yarn package-tools list --recursive --since ${{ github.event.before }})" >> $GITHUB_OUTPUT | |
| - name: Display Package Matrix | |
| run: | | |
| echo "Changed packages (node): ${{ steps.generate-package-matrix-node-changed.outputs.node-changed }}" | |
| echo "Changed packages + dependents (node): ${{ steps.generate-package-matrix-node-recursive.outputs.node-recursive }}" | |
| echo "Changed packages (yarn): ${{ steps.generate-package-matrix-yarn-changed.outputs.yarn-changed }}" | |
| echo "Changed packages + dependents (yarn): ${{ steps.generate-package-matrix-yarn-recursive.outputs.yarn-recursive }}" | |
| publish-npm: | |
| name: Publish to NPM | |
| needs: analyze-changes | |
| runs-on: ubuntu-latest | |
| outputs: | |
| published: ${{ steps.check-changes.outputs.has_changes }} | |
| package_versions: ${{ steps.increment.outputs.package_versions }} | |
| steps: | |
| - name: Checkout Project | |
| uses: actions/checkout@v4 | |
| - name: Setup Node | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version-file: ".nvmrc" | |
| registry-url: "https://registry.npmjs.org" | |
| cache: "yarn" | |
| - name: Restore Dependencies Cache | |
| uses: actions/cache/restore@v4 | |
| with: | |
| path: "**/node_modules" | |
| key: node-modules-${{ hashFiles('./yarn.lock') }} | |
| - name: Check for Changed Packages | |
| id: check-changes | |
| run: | | |
| PACKAGES='${{ needs.analyze-changes.outputs.yarn-recursive }}' | |
| if [ -z "$PACKAGES" ] || [ "$PACKAGES" == "{}" ] || [ "$PACKAGES" == "[]" ]; then | |
| echo "has_changes=false" >> $GITHUB_OUTPUT | |
| echo "⚠️ No packages have changed - skipping deployment" | |
| else | |
| echo "has_changes=true" >> $GITHUB_OUTPUT | |
| echo "✅ Found changed packages: $PACKAGES" | |
| fi | |
| - name: No Changes Detected | |
| if: steps.check-changes.outputs.has_changes == 'false' | |
| run: | | |
| echo "==========================================" | |
| echo "🔍 No packages changed in this push" | |
| echo "==========================================" | |
| echo "Deployment skipped - no changes detected since last push" | |
| echo "Compared against: ${{ github.event.before }}" | |
| echo "==========================================" | |
| - name: Add NPM token to .yarnrc.yml file | |
| if: steps.check-changes.outputs.has_changes == 'true' | |
| run: | | |
| echo "npmAuthToken: ${{ env.token }}" >> ~/.yarnrc.yml | |
| - name: Sync and Increment Package Versions | |
| id: increment | |
| if: steps.check-changes.outputs.has_changes == 'true' | |
| run: | | |
| yarn package-tools sync --tag ${GITHUB_REF##*/} | |
| OUTPUT=$(yarn package-tools increment --packages ${{ needs.analyze-changes.outputs.node-recursive }} --tag ${GITHUB_REF##*/}) | |
| echo "$OUTPUT" | |
| echo "package_versions<<EOF" >> $GITHUB_OUTPUT | |
| echo "$OUTPUT" >> $GITHUB_OUTPUT | |
| echo "EOF" >> $GITHUB_OUTPUT | |
| - name: Build Changed Packages | |
| if: steps.check-changes.outputs.has_changes == 'true' | |
| run: yarn run build:prod | |
| - name: Cache Built Distributables | |
| if: steps.check-changes.outputs.has_changes == 'true' | |
| uses: actions/cache/save@v4 | |
| with: | |
| path: "**/dist" | |
| key: dist-${{ env.rid }} | |
| - name: Deploy Packages | |
| if: steps.check-changes.outputs.has_changes == 'true' | |
| run: yarn workspaces foreach -W --from '${{ needs.analyze-changes.outputs.yarn-recursive }}' --verbose run deploy:npm --access public --tag ${GITHUB_REF##*/} | |
| env: | |
| NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| publish-documentation: | |
| name: Publish - Documentation | |
| needs: [publish-npm,analyze-changes] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Documentation Deploy Steps | |
| run: echo "Documentation Deploy Steps" | |
| - name: Checkout Project | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Setup Node | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version-file: '.nvmrc' | |
| registry-url: 'https://registry.npmjs.org' | |
| cache: 'yarn' | |
| - name: Restore Dependencies Cache | |
| uses: actions/cache/restore@v4 | |
| with: | |
| path: '**/node_modules' | |
| key: node-modules-${{ hashFiles('./yarn.lock') }} | |
| - name: Restore Distributables Cache | |
| uses: actions/cache/restore@v4 | |
| with: | |
| path: '**/dist' | |
| key: dist-${{ env.rid }} | |
| - name: Synchronize Packages | |
| run: yarn | |
| - name: Build tools and set versions | |
| run: | | |
| yarn package-tools sync --tag ${GITHUB_REF##*/} | |
| - name: Build packages | |
| run: yarn run build:prod | |
| - name: Build samples locally | |
| run: yarn samples:build | |
| - name: Set Git Identity | |
| run: | | |
| git config user.email "${GIT_AUTHOR_EMAIL}" | |
| git config user.name "${GIT_AUTHOR_NAME}" | |
| - name: Get existing changelog from documentation Branch | |
| run: | | |
| git fetch origin documentation | |
| git checkout origin/documentation -- docs/changelog/logs | |
| - name: Update changelog | |
| run: yarn package-tools changelog --packages ${{ needs.analyze-changes.outputs.node-recursive }} --tag ${GITHUB_REF##*/} --commit ${{ github.event.before }} | |
| - name: Update Changelog and Publish Docs Folder | |
| run: | | |
| # Check if there's anything to commit before committing | |
| if [ -n "$(git status --porcelain)" ]; then | |
| git add docs/ | |
| git commit -m "chore(docs): update docs" | |
| git push origin HEAD:documentation --force | |
| fi | |
| publish-tag: | |
| name: Publish - Tags | |
| runs-on: ubuntu-latest | |
| needs: [analyze-changes, publish-npm] | |
| steps: | |
| - name: Checkout Project | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Setup Node | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version-file: ".nvmrc" | |
| registry-url: "https://registry.npmjs.org" | |
| cache: "yarn" | |
| - name: Restore Dependencies Cache | |
| uses: actions/cache/restore@v4 | |
| with: | |
| path: "**/node_modules" | |
| key: node-modules-${{ hashFiles('./yarn.lock') }} | |
| - name: Restore Distributables Cache | |
| uses: actions/cache/restore@v4 | |
| with: | |
| path: "**/dist" | |
| key: dist-${{ env.rid }} | |
| - name: Synchronize Packages | |
| run: yarn | |
| - name: Check if packages were published | |
| id: check-publish | |
| run: | | |
| if [ "${{ needs.publish-npm.outputs.published }}" != "true" ]; then | |
| echo "⚠️ NPM publish was skipped - no tags will be created" | |
| echo "should_tag=false" >> $GITHUB_OUTPUT | |
| else | |
| echo "✅ NPM publish succeeded - proceeding with tagging" | |
| echo "should_tag=true" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Set Git Identity | |
| if: steps.check-publish.outputs.should_tag == 'true' | |
| run: | | |
| git config user.email "${GIT_AUTHOR_EMAIL}" | |
| git config user.name "${GIT_AUTHOR_NAME}" | |
| - name: Create and Push Tags for Main Packages | |
| if: steps.check-publish.outputs.should_tag == 'true' | |
| run: | | |
| COMMIT_MESSAGE="$(git log -1 --oneline --no-decorate)" | |
| COMMIT_HASH="$(git rev-parse HEAD)" | |
| # node-recursive includes both changed packages AND their dependents | |
| # e.g., if @webex/cc-store changes, @webex/cc-widgets will be in this list if it depends on store | |
| PACKAGES='${{ needs.analyze-changes.outputs.node-recursive }}' | |
| # Define the packages we want to tag (only main/published packages) | |
| TAGGABLE_PACKAGES=("@webex/widgets" "@webex/cc-widgets") | |
| echo "==========================================" | |
| echo "Creating tags for main packages" | |
| echo "==========================================" | |
| # Check if any packages were changed | |
| if [ -z "$PACKAGES" ] || [ "$PACKAGES" == "{}" ] || [ "$PACKAGES" == "[]" ]; then | |
| echo "⚠️ No packages changed - skipping tagging" | |
| exit 0 | |
| fi | |
| # Extract package names from JSON array | |
| CHANGED_PACKAGES=$(echo "$PACKAGES" | jq -r '.[]' 2>/dev/null || echo "$PACKAGES") | |
| echo "Packages to evaluate (changed + dependents): $CHANGED_PACKAGES" | |
| echo "" | |
| for TAGGABLE_PKG in "${TAGGABLE_PACKAGES[@]}"; do | |
| echo "----------------------------------------" | |
| echo "Checking if $TAGGABLE_PKG needs to be tagged..." | |
| # Check if this package is in the changed packages list (includes dependents) | |
| FOUND=false | |
| for CHANGED_PKG in $CHANGED_PACKAGES; do | |
| if [ "$CHANGED_PKG" == "$TAGGABLE_PKG" ]; then | |
| FOUND=true | |
| break | |
| fi | |
| done | |
| if [ "$FOUND" == "false" ]; then | |
| echo "⏭️ $TAGGABLE_PKG not in publish list - skipping" | |
| continue | |
| fi | |
| echo "✅ $TAGGABLE_PKG needs to be published - creating tag..." | |
| # Get version for this specific package | |
| VERSION=$(yarn package-tools sync --tag ${GITHUB_REF##*/} --packages "$TAGGABLE_PKG" | awk '{print $3}' | tr -d '%' | head -1) | |
| if [ -z "$VERSION" ]; then | |
| echo "⚠️ Could not determine version for $TAGGABLE_PKG, skipping..." | |
| continue | |
| fi | |
| # Create tag name: convert @scope/package to scope-package-vX.X.X | |
| TAG_NAME=$(echo "$TAGGABLE_PKG" | sed 's/@//g' | sed 's/\//-/g')-v${VERSION} | |
| echo "Tag name: $TAG_NAME" | |
| # Check if tag already exists | |
| if git rev-parse "$TAG_NAME" >/dev/null 2>&1; then | |
| echo "⚠️ Tag $TAG_NAME already exists, skipping..." | |
| continue | |
| fi | |
| # Create annotated tag | |
| git tag -a "$TAG_NAME" "$COMMIT_HASH" -m "Release $TAGGABLE_PKG v${VERSION}: $COMMIT_MESSAGE" | |
| # Push tag | |
| if git push origin "$TAG_NAME"; then | |
| echo "✅ Successfully created and pushed tag: $TAG_NAME" | |
| else | |
| echo "❌ Failed to push tag: $TAG_NAME" | |
| fi | |
| echo "----------------------------------------" | |
| done | |
| echo "==========================================" | |
| echo "Tagging complete!" | |
| echo "==========================================" | |
| comment-on-pr: | |
| name: Comment on Released PRs | |
| needs: [publish-npm, publish-tag] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Get PR Number | |
| id: get-pr | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const deploySha = context.sha; | |
| const owner = context.repo.owner; | |
| const repo = context.repo.repo; | |
| try { | |
| const prs = await github.rest.repos.listPullRequestsAssociatedWithCommit({ | |
| owner, repo, commit_sha: deploySha | |
| }); | |
| const mergedPR = prs.data.find(pr => pr.merged_at); | |
| if (mergedPR) { | |
| core.setOutput('pr_number', String(mergedPR.number)); | |
| return; | |
| } | |
| } catch (error) { | |
| console.log(`Failed to discover PR from commit: ${error.message}`); | |
| } | |
| core.setOutput('pr_number', ''); | |
| - name: Post Release Comment on PR | |
| if: steps.get-pr.outputs.pr_number != '' | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const prNumber = parseInt('${{ steps.get-pr.outputs.pr_number }}'); | |
| const raw = `${{ needs.publish-npm.outputs.package_versions }}`; | |
| const packageVersions = {}; | |
| for (const line of raw.split('\n')) { | |
| const match = line.match(/^(.+?)\s+=>\s+(.+)$/); | |
| if (match) packageVersions[match[1].trim()] = match[2].trim(); | |
| } | |
| const packageEntries = Object.entries(packageVersions); | |
| const owner = context.repo.owner; | |
| const repo = context.repo.repo; | |
| const repoUrl = `https://github.com/${owner}/${repo}`; | |
| const hasPackages = packageEntries.length > 0; | |
| const taggablePackages = { | |
| '@webex/widgets': 'webex-widgets', | |
| '@webex/cc-widgets': 'webex-cc-widgets' | |
| }; | |
| const aggregators = ['@webex/cc-widgets', '@webex/widgets']; | |
| let commentBody; | |
| if (hasPackages) { | |
| const primaryPackage = aggregators.find(p => packageVersions[p]) | |
| || packageEntries[0][0]; | |
| const primaryVersion = packageVersions[primaryPackage]; | |
| const stableVersion = primaryVersion | |
| .replace(/-next\..*/, '') | |
| .replace(/-[a-z]*\..*/, ''); | |
| let cname = 'widgets.webex.com'; | |
| try { | |
| const cnameFile = await github.rest.repos.getContent({ owner, repo, path: 'docs/CNAME' }); | |
| const encoded = cnameFile.data.content; | |
| if (typeof encoded === 'string') { | |
| cname = Buffer.from(encoded, 'base64').toString().trim(); | |
| } | |
| } catch (e) { | |
| console.log(`Could not read docs/CNAME, using default changelog host: ${e.message}`); | |
| } | |
| const changelogUrl = new URL(`https://${cname}/changelog/`); | |
| if (stableVersion) { | |
| changelogUrl.searchParams.set('stable_version', stableVersion); | |
| } | |
| changelogUrl.searchParams.set('package', primaryPackage); | |
| changelogUrl.searchParams.set('version', primaryVersion); | |
| const tagLinkParts = Object.entries(taggablePackages) | |
| .filter(([pkg]) => packageVersions[pkg]) | |
| .map(([pkg, prefix]) => { | |
| const tag = `${prefix}-v${packageVersions[pkg]}`; | |
| return `[\`${tag}\`](${repoUrl}/releases/tag/${tag})`; | |
| }); | |
| const releaseLine = tagLinkParts.length | |
| ? `| **Released in:** ${tagLinkParts.join(', ')} |` | |
| : ''; | |
| const rows = packageEntries | |
| .sort(([a], [b]) => { | |
| if (a === '@webex/cc-widgets') return -1; | |
| if (b === '@webex/cc-widgets') return 1; | |
| if (a === '@webex/widgets') return -1; | |
| if (b === '@webex/widgets') return 1; | |
| return a.localeCompare(b); | |
| }) | |
| .map(([pkg, ver]) => `| \`${pkg}\` | \`${ver}\` |`) | |
| .join('\n'); | |
| const packagesTable = [ | |
| '', | |
| '| Packages Updated | Version |', | |
| '|---------|---------|', | |
| rows, | |
| '' | |
| ].join('\n'); | |
| commentBody = [ | |
| '| :tada: Your changes are now available! |', | |
| '|---|', | |
| releaseLine, | |
| `| :book: **[View full changelog →](${changelogUrl})** |`, | |
| packagesTable, | |
| 'Thank you for your contribution!', | |
| '', | |
| `_:robot: This is an automated message. For queries, please contact [support](https://developer.webex.com/support)._` | |
| ].filter(Boolean).join('\n'); | |
| } else { | |
| commentBody = [ | |
| ':white_check_mark: **Your changes have been merged!**', | |
| '', | |
| 'Thank you for your contribution!', | |
| '', | |
| `_:robot: This is an automated message. For queries, please contact [support](https://developer.webex.com/support)._` | |
| ].join('\n'); | |
| } | |
| try { | |
| const pr = await github.rest.pulls.get({ owner, repo, pull_number: prNumber }); | |
| if (!pr.data.merged_at) return; | |
| const comments = await github.paginate(github.rest.issues.listComments, { | |
| owner, repo, | |
| issue_number: prNumber, | |
| per_page: 100 | |
| }); | |
| const detailedComment = comments.find(comment => | |
| comment.user.type === 'Bot' && | |
| comment.body.includes('Your changes are now available') | |
| ); | |
| const mergedComment = comments.find(comment => | |
| comment.user.type === 'Bot' && | |
| comment.body.includes('Your changes have been merged') | |
| ); | |
| if (detailedComment) return; | |
| if (!hasPackages && mergedComment) return; | |
| if (mergedComment && hasPackages) { | |
| await github.rest.issues.updateComment({ | |
| owner, repo, | |
| comment_id: mergedComment.id, | |
| body: commentBody | |
| }); | |
| } else { | |
| await github.rest.issues.createComment({ | |
| owner, repo, | |
| issue_number: prNumber, | |
| body: commentBody | |
| }); | |
| } | |
| } catch (error) { | |
| core.warning(`Failed to comment on PR #${prNumber}: ${error.message}`); | |
| } |