From a49b9d1cd7c8db5619e68b2c25a3043f733f0cb6 Mon Sep 17 00:00:00 2001 From: addisonbeck Date: Tue, 28 Oct 2025 10:28:28 -0400 Subject: [PATCH 1/7] build: implement SDK breaking change detection - Add dedicated detect-breaking-changes.yml workflow with matrix strategy - Implement cross-repository coordination using repository_dispatch - Add GitHub App authentication with Azure Key Vault integration - Create synchronous monitoring with gh run watch --exit-status - Add comprehensive PR comment system with status tracking - Include automatic breaking-change label management - Support workflow_call integration with build-wasm-internal.yml Provides immediate feedback on TypeScript breaking changes when SDK PRs are created, catching issues before client integration attempts. Resolves: PM-22218 --- .github/PULL_REQUEST_TEMPLATE.md | 14 + .github/workflows/build-wasm-internal.yml | 42 +- .github/workflows/detect-breaking-changes.yml | 392 ++++++++++++++++++ 3 files changed, 442 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/detect-breaking-changes.yml diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index e43ad29df..d2f0d706e 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -6,6 +6,20 @@ +## 🚨 Breaking Changes + + + ## ⏰ Reminders before review - Contributor guidelines followed diff --git a/.github/workflows/build-wasm-internal.yml b/.github/workflows/build-wasm-internal.yml index 1e3af5330..03828f340 100644 --- a/.github/workflows/build-wasm-internal.yml +++ b/.github/workflows/build-wasm-internal.yml @@ -35,13 +35,15 @@ jobs: steps: - name: Checkout repo uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + persist-credentials: false - name: Set version (PR) if: ${{ github.event_name == 'pull_request' }} env: PR_HEAD_REF: "${{ github.event.pull_request.head.ref }}" run: | - echo REF_NAME="$PR_HEAD_REF" >> $GITHUB_ENV + echo REF_NAME="${PR_HEAD_REF}" >> $GITHUB_ENV echo SHA="${{ github.event.pull_request.head.sha }}" >> $GITHUB_ENV - name: Set env variables (Branch/Tag) @@ -117,12 +119,19 @@ jobs: tenant_id: ${{ secrets.AZURE_TENANT_ID }} client_id: ${{ secrets.AZURE_CLIENT_ID }} - - name: Retrieve github PAT secrets - id: retrieve-secret-pat + - name: Get Azure Key Vault secrets + id: get-kv-secrets uses: bitwarden/gh-actions/get-keyvault-secrets@main with: - keyvault: "bitwarden-ci" - secrets: "github-pat-bitwarden-devops-bot-repo-scope" + keyvault: gh-org-bitwarden + secrets: "BW-GHAPP-ID,BW-GHAPP-KEY" + + - name: Generate GH App token + uses: actions/create-github-app-token@30bf6253fa41bdc8d1501d202ad15287582246b4 # v2.0.3 + id: app-token + with: + app-id: ${{ steps.get-kv-secrets.outputs.BW-GHAPP-ID }} + private-key: ${{ steps.get-kv-secrets.outputs.BW-GHAPP-KEY }} - name: Log out from Azure uses: bitwarden/gh-actions/azure-logout@main @@ -130,7 +139,7 @@ jobs: - name: Trigger WASM publish uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 with: - github-token: ${{ steps.retrieve-secret-pat.outputs.github-pat-bitwarden-devops-bot-repo-scope }} + github-token: ${{ steps.app-token.outputs.token }} script: | await github.rest.actions.createWorkflowDispatch({ owner: 'bitwarden', @@ -138,3 +147,24 @@ jobs: workflow_id: 'publish-wasm-internal.yml', ref: 'main', }) + + trigger-breaking-change-check: + name: Trigger client breaking change checks + if: github.event_name == 'pull_request' + needs: build + permissions: + contents: write + actions: write + pull-requests: write + id-token: write + uses: ./.github/workflows/detect-breaking-changes.yml + secrets: inherit + with: + pr_number: ${{ github.event.number }} + pr_head_sha: ${{ github.event.pull_request.head.sha }} + pr_head_ref: ${{ github.event.pull_request.head.ref }} + build_run_id: ${{ github.run_id }} + client_repo: "bitwarden/clients" + client_event_type: "sdk-breaking-change-check" + client_label: "typescript" + client_workflow: "sdk-breaking-change-check.yml" diff --git a/.github/workflows/detect-breaking-changes.yml b/.github/workflows/detect-breaking-changes.yml new file mode 100644 index 000000000..977825c2a --- /dev/null +++ b/.github/workflows/detect-breaking-changes.yml @@ -0,0 +1,392 @@ +name: SDK Breaking Change Detection + +on: + workflow_call: + inputs: + pr_number: + description: "PR number" + required: true + type: string + pr_head_sha: + description: "PR head SHA" + required: true + type: string + pr_head_ref: + description: "PR head ref" + required: true + type: string + build_run_id: + description: "Build workflow run ID for artifacts" + required: true + type: string + client_repo: + description: "Target client repository (e.g., 'bitwarden/clients')" + required: true + type: string + client_event_type: + description: "Repository dispatch event type" + required: true + type: string + client_label: + description: "Client label for display purposes" + required: true + type: string + client_workflow: + description: "Target workflow filename in client repo" + required: true + type: string + +permissions: + contents: read + actions: write + pull-requests: write + id-token: write + +jobs: + detect-breaking-changes: + name: Detect client breaking changes + runs-on: ubuntu-24.04 + defaults: + run: + shell: bash + working-directory: . + env: + _CLIENT_REPO: ${{ inputs.client_repo }} + _EVENT_TYPE: ${{ inputs.client_event_type }} + _CLIENT_LABEL: ${{ inputs.client_label }} + _WORKFLOW_NAME: ${{ inputs.client_workflow }} + _MAX_RETRIES: 3 + + steps: + - name: Set context variables + id: context + env: + PR_NUMBER: ${{ inputs.pr_number }} + PR_HEAD_SHA: ${{ inputs.pr_head_sha }} + PR_HEAD_REF: ${{ inputs.pr_head_ref }} + SOURCE_RUN_ID: ${{ inputs.build_run_id }} + run: | + SHORT_SHA="${PR_HEAD_SHA:0:7}" + SDK_VERSION="${PR_HEAD_REF} (${SHORT_SHA})" + + echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT + echo "pr_head_sha=$PR_HEAD_SHA" >> $GITHUB_OUTPUT + echo "pr_head_ref=$PR_HEAD_REF" >> $GITHUB_OUTPUT + echo "sdk_version=$SDK_VERSION" >> $GITHUB_OUTPUT + echo "source_run_id=$SOURCE_RUN_ID" >> $GITHUB_OUTPUT + + echo "📊 Context:" + echo " PR Number: $PR_NUMBER" + echo " SDK Version: $SDK_VERSION" + echo " Source Run ID: $SOURCE_RUN_ID" + + - name: Prepare dispatch payload + id: payload + env: + PR_NUMBER: ${{ steps.context.outputs.pr_number }} + SDK_VERSION: ${{ steps.context.outputs.sdk_version }} + PR_HEAD_SHA: ${{ steps.context.outputs.pr_head_sha }} + SOURCE_RUN_ID: ${{ steps.context.outputs.source_run_id }} + GITHUB_REPOSITORY: ${{ github.repository }} + run: | + # Create payload JSON + PAYLOAD=$(jq -n \ + --arg pr_number "$PR_NUMBER" \ + --arg sdk_version "$SDK_VERSION" \ + --arg source_repo "$GITHUB_REPOSITORY" \ + --arg workflow_context "$_WORKFLOW_NAME" \ + --arg client_label "$_CLIENT_LABEL" \ + --arg pr_head_sha "$PR_HEAD_SHA" \ + --arg pr_base_ref "main" \ + --arg comment_id "" \ + --arg run_id "$SOURCE_RUN_ID" \ + --arg artifact_name "sdk-internal" \ + '{ + pr_number: $pr_number, + sdk_version: $sdk_version, + source_repo: $source_repo, + workflow_context: $workflow_context, + client_label: $client_label, + pr_head_sha: $pr_head_sha, + pr_base_ref: $pr_base_ref, + comment_id: $comment_id, + artifacts_info: { + run_id: $run_id, + artifact_name: $artifact_name + } + }') + + echo "payload<> $GITHUB_OUTPUT + echo "$PAYLOAD" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + - name: Log in to Azure + uses: bitwarden/gh-actions/azure-login@main + with: + subscription_id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + tenant_id: ${{ secrets.AZURE_TENANT_ID }} + client_id: ${{ secrets.AZURE_CLIENT_ID }} + + - name: Get Azure Key Vault secrets + id: get-kv-secrets + uses: bitwarden/gh-actions/get-keyvault-secrets@main + with: + keyvault: gh-org-bitwarden + secrets: "BW-GHAPP-ID,BW-GHAPP-KEY" + + - name: Generate GH App token + uses: actions/create-github-app-token@30bf6253fa41bdc8d1501d202ad15287582246b4 # v2.0.3 + id: app-token + with: + app-id: ${{ steps.get-kv-secrets.outputs.BW-GHAPP-ID }} + private-key: ${{ steps.get-kv-secrets.outputs.BW-GHAPP-KEY }} + owner: ${{ github.repository_owner }} + permission-contents: write + repositories: | + clients + sdk-internal + + - name: Create initial status comment + id: initial-comment + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + SDK_VERSION: ${{ steps.context.outputs.sdk_version }} + PR_NUMBER: ${{ steps.context.outputs.pr_number }} + GITHUB_REPOSITORY: ${{ github.repository }} + GITHUB_RUN_ID: ${{ github.run_id }} + run: | + echo "💬 Creating initial breaking change detection status comment..." + + COMMENT_HEADER="" + TIMESTAMP=$(date -u '+%Y-%m-%d %H:%M:%S UTC') + + INITIAL_COMMENT=$(cat << EOF + $COMMENT_HEADER + ## 🔍 SDK Breaking Change Detection Status + + **SDK Version:** \`$SDK_VERSION\` + **Started:** $TIMESTAMP + **Progress:** 🚀 Triggering client workflows and waiting for completion... + + --- + *Results will be updated when workflow completes. [View SDK workflow](https://github.com/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID)* + EOF + ) + + # Check for existing comment from previous runs + EXISTING_COMMENT=$(gh api repos/$GITHUB_REPOSITORY/issues/$PR_NUMBER/comments | \ + jq -r --arg header "$COMMENT_HEADER" '.[] | select(.body | contains($header)) | .id' | head -1) + + if [ -n "$EXISTING_COMMENT" ]; then + echo "Updating existing comment ID: $EXISTING_COMMENT" + gh api --method PATCH repos/$GITHUB_REPOSITORY/issues/comments/$EXISTING_COMMENT \ + --field body="$INITIAL_COMMENT" + echo "comment_id=$EXISTING_COMMENT" >> $GITHUB_OUTPUT + else + echo "Creating new comment" + COMMENT_ID=$(gh api --method POST repos/$GITHUB_REPOSITORY/issues/$PR_NUMBER/comments \ + --field body="$INITIAL_COMMENT" --jq '.id') + echo "comment_id=$COMMENT_ID" >> $GITHUB_OUTPUT + fi + + echo "✅ Initial comment created/updated" + + - name: Trigger client repository dispatch and watch + id: trigger-dispatch-and-watch + timeout-minutes: 15 + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + PAYLOAD: ${{ steps.payload.outputs.payload }} + SDK_VERSION: ${{ steps.context.outputs.sdk_version }} + run: | + echo "🚀 Triggering $_WORKFLOW_NAME in $_CLIENT_REPO..." + + # Step 1: Dispatch the workflow + RETRY_COUNT=0 + DISPATCH_SUCCESS=false + + while [ $RETRY_COUNT -lt $_MAX_RETRIES ]; do + RETRY_COUNT=$((RETRY_COUNT + 1)) + echo "🔄 Attempt $RETRY_COUNT of $_MAX_RETRIES for $_CLIENT_REPO..." + + # Create the complete dispatch payload + DISPATCH_PAYLOAD=$(jq -n \ + --arg event_type "$_EVENT_TYPE" \ + --argjson client_payload "$PAYLOAD" \ + '{ + event_type: $event_type, + client_payload: $client_payload + }') + + if echo "$DISPATCH_PAYLOAD" | gh api repos/$_CLIENT_REPO/dispatches \ + --method POST \ + --input -; then + + echo "✅ Successfully triggered $_CLIENT_REPO ($_CLIENT_LABEL)" + echo "✅ **$_CLIENT_REPO**: $_WORKFLOW_NAME triggered - [Monitor Progress](https://github.com/$_CLIENT_REPO/actions)" >> $GITHUB_STEP_SUMMARY + DISPATCH_SUCCESS=true + break + else + echo "⚠️ $_CLIENT_REPO dispatch attempt $RETRY_COUNT failed" + [ $RETRY_COUNT -lt $_MAX_RETRIES ] && sleep 5 + fi + done + + if [ "$DISPATCH_SUCCESS" = "false" ]; then + echo "::error::Failed to trigger $_CLIENT_REPO after $_MAX_RETRIES attempts" + echo "::warning::$_CLIENT_LABEL breaking change detection will be skipped" + echo "❌ **$_CLIENT_REPO**: Failed to trigger - [Manual Check Required](https://github.com/$_CLIENT_REPO)" >> $GITHUB_STEP_SUMMARY + echo "client_error_code=1" >> $GITHUB_OUTPUT + echo "status=dispatch-failed" >> $GITHUB_OUTPUT + exit 0 + fi + + # Step 2: Wait for workflow to appear and monitor + echo "🔍 Looking for triggered workflow run..." + RETRY_COUNT=0 + MAX_RUN_LIST_RETRIES=10 + WORKFLOW_RUN_ID="" + + JQ_FILTER='.[] | select(.status as $s | (["requested", "queued", "in_progress", "waiting"] | contains([$s])) and (.displayTitle | contains("'"$SDK_VERSION"'")) and (.name | startswith("SDK breaking change check"))) | .databaseId' + + while + sleep 5 + RETRY_COUNT=$((RETRY_COUNT + 1)) + echo "🔄 Attempt $RETRY_COUNT of $MAX_RUN_LIST_RETRIES to find workflow run..." + WORKFLOW_RUN_ID=$(gh run list --repo $_CLIENT_REPO --workflow=$_WORKFLOW_NAME --limit=10 \ + --json databaseId,status,displayTitle,name --jq "$JQ_FILTER" | head -1) + [ -z "$WORKFLOW_RUN_ID" ] && [ $RETRY_COUNT -lt $MAX_RUN_LIST_RETRIES ] + do true; done + + if [ -z "$WORKFLOW_RUN_ID" ]; then + echo "::error::No workflow found after $MAX_RUN_LIST_RETRIES attempts." + echo "::warning::$_CLIENT_LABEL breaking change detection will be skipped" + echo "❌ **$_CLIENT_REPO**: Workflow not found - [Manual Check Required](https://github.com/$_CLIENT_REPO)" >> $GITHUB_STEP_SUMMARY + echo "client_error_code=1" >> $GITHUB_OUTPUT + echo "status=workflow-not-found" >> $GITHUB_OUTPUT + exit 0 + fi + + echo "🔍 Workflow run ID: $WORKFLOW_RUN_ID" + WORKFLOW_URL="https://github.com/$_CLIENT_REPO/actions/runs/$WORKFLOW_RUN_ID" + echo "## 🔗 $_CLIENT_LABEL SDK Test Run: [$WORKFLOW_RUN_ID]($WORKFLOW_URL)" >> $GITHUB_STEP_SUMMARY + + # Step 3: Monitor workflow execution + echo "⏳ Watching workflow execution with gh run watch..." + ERROR_CODE=0 + WATCH_START_TIME=$(date +%s) + + if ! gh run watch $WORKFLOW_RUN_ID --repo $_CLIENT_REPO --compact --exit-status --interval 30; then + echo "❌ $_CLIENT_LABEL SDK Test failed." + echo "❌ **$_CLIENT_LABEL Status:** Failed - [View Details]($WORKFLOW_URL)" >> $GITHUB_STEP_SUMMARY + echo "status=breaking-changes-detected" >> $GITHUB_OUTPUT + ERROR_CODE=1 + else + echo "✅ $_CLIENT_LABEL SDK Test passed." + echo "✅ **$_CLIENT_LABEL Status:** Passed - [View Details]($WORKFLOW_URL)" >> $GITHUB_STEP_SUMMARY + echo "status=no-breaking-changes" >> $GITHUB_OUTPUT + fi + + WATCH_END_TIME=$(date +%s) + TOTAL_WAIT_TIME=$((WATCH_END_TIME - WATCH_START_TIME)) + + echo "client_error_code=$ERROR_CODE" >> $GITHUB_OUTPUT + echo "workflow_run_id=$WORKFLOW_RUN_ID" >> $GITHUB_OUTPUT + echo "total_wait_time=$TOTAL_WAIT_TIME" >> $GITHUB_OUTPUT + + echo "⏱️ **Synchronization Complete**: Waited ${TOTAL_WAIT_TIME}s for client workflow" >> $GITHUB_STEP_SUMMARY + + - name: Manage breaking change labels + if: always() + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + CLIENT_STATUS: ${{ steps.trigger-dispatch-and-watch.outputs.status }} + PR_NUMBER: ${{ steps.context.outputs.pr_number }} + run: | + echo "🏷️ Managing breaking change labels..." + + case "$CLIENT_STATUS" in + "breaking-changes-detected") + echo "Adding breaking-change label to PR #$PR_NUMBER" + gh issue edit $PR_NUMBER --add-label "breaking-change" --repo ${{ github.repository }} || { + echo "⚠️ Failed to add label, but continuing" + } + ;; + "no-breaking-changes") + echo "Removing breaking-change label from PR #$PR_NUMBER" + gh issue edit $PR_NUMBER --remove-label "breaking-change" --repo ${{ github.repository }} || { + echo "⚠️ Label may not exist or failed to remove, but continuing" + } + ;; + *) + echo "⚠️ Unknown status '$CLIENT_STATUS', skipping label management" + ;; + esac + + echo "✅ Label management completed" + + - name: Update final status comment + if: always() + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + COMMENT_ID: ${{ steps.initial-comment.outputs.comment_id }} + SDK_VERSION: ${{ steps.context.outputs.sdk_version }} + CLIENT_STATUS: ${{ steps.trigger-dispatch-and-watch.outputs.status }} + WORKFLOW_ID: ${{ steps.trigger-dispatch-and-watch.outputs.workflow_run_id }} + TOTAL_TIME: ${{ steps.trigger-dispatch-and-watch.outputs.total_wait_time }} + run: | + echo "💬 Updating final breaking change detection status comment..." + + if [ -z "$COMMENT_ID" ]; then + echo "⚠️ No comment ID found, skipping comment update" + exit 0 + fi + + COMMENT_HEADER="" + TIMESTAMP=$(date -u '+%Y-%m-%d %H:%M:%S UTC') + + # Determine status message + case "$CLIENT_STATUS" in + "breaking-changes-detected") + STATUS_EMOJI="❌" + STATUS_MESSAGE="Breaking changes detected" + STATUS_DETAILS="TypeScript compilation failed with new SDK version - [View Details](https://github.com/$_CLIENT_REPO/actions/runs/$WORKFLOW_ID)" + ;; + "no-breaking-changes") + STATUS_EMOJI="✅" + STATUS_MESSAGE="No breaking changes detected" + STATUS_DETAILS="TypeScript compilation passed with new SDK version - [View Details](https://github.com/$_CLIENT_REPO/actions/runs/$WORKFLOW_ID)" + ;; + *) + STATUS_EMOJI="⚠️" + STATUS_MESSAGE="Workflow execution issues" + STATUS_DETAILS="Check workflow logs for details" + ;; + esac + + FINAL_COMMENT=$(cat << EOF + $COMMENT_HEADER + ## 🔍 SDK Breaking Change Detection Results + + **SDK Version:** \`$SDK_VERSION\` + **Completed:** $TIMESTAMP + **Total Time:** ${TOTAL_TIME:-"Unknown"}s + + | Client | Status | Details | + |--------|---------|------------| + |$_CLIENT_LABEL|$STATUS_EMOJI $STATUS_MESSAGE|$STATUS_DETAILS| + + --- + *Breaking change detection completed. [View SDK workflow](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})* + EOF + ) + + gh api --method PATCH repos/${{ github.repository }}/issues/comments/$COMMENT_ID \ + --field body="$FINAL_COMMENT" || { + echo "⚠️ Failed to update comment, but continuing" + } + + echo "✅ Final comment updated" + + - name: Log out from Azure + uses: bitwarden/gh-actions/azure-logout@main From 7551f37a96ca75e8dbac3f3f9313cd15271842d9 Mon Sep 17 00:00:00 2001 From: addisonbeck Date: Wed, 5 Nov 2025 15:01:50 -0500 Subject: [PATCH 2/7] review: add explicit perms to the build bots token --- .github/workflows/build-wasm-internal.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/build-wasm-internal.yml b/.github/workflows/build-wasm-internal.yml index 03828f340..eecd2bee7 100644 --- a/.github/workflows/build-wasm-internal.yml +++ b/.github/workflows/build-wasm-internal.yml @@ -132,6 +132,9 @@ jobs: with: app-id: ${{ steps.get-kv-secrets.outputs.BW-GHAPP-ID }} private-key: ${{ steps.get-kv-secrets.outputs.BW-GHAPP-KEY }} + owner: bitwarden + repositories: sdk-internal + permission-actions: write - name: Log out from Azure uses: bitwarden/gh-actions/azure-logout@main From d98d4d8bf30fa9e45f2f1ca3a91ee9f0c777a8bf Mon Sep 17 00:00:00 2001 From: addisonbeck Date: Wed, 5 Nov 2025 15:02:20 -0500 Subject: [PATCH 3/7] review: swap to using warnings for some notifications --- .github/workflows/detect-breaking-changes.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/detect-breaking-changes.yml b/.github/workflows/detect-breaking-changes.yml index 977825c2a..c8f7d5dad 100644 --- a/.github/workflows/detect-breaking-changes.yml +++ b/.github/workflows/detect-breaking-changes.yml @@ -309,17 +309,17 @@ jobs: "breaking-changes-detected") echo "Adding breaking-change label to PR #$PR_NUMBER" gh issue edit $PR_NUMBER --add-label "breaking-change" --repo ${{ github.repository }} || { - echo "⚠️ Failed to add label, but continuing" + echo "::warning::Failed to add label, but continuing" } ;; "no-breaking-changes") echo "Removing breaking-change label from PR #$PR_NUMBER" gh issue edit $PR_NUMBER --remove-label "breaking-change" --repo ${{ github.repository }} || { - echo "⚠️ Label may not exist or failed to remove, but continuing" + echo "::warning::Label may not exist or failed to remove, but continuing" } ;; *) - echo "⚠️ Unknown status '$CLIENT_STATUS', skipping label management" + echo "::warning::Unknown status '$CLIENT_STATUS', skipping label management" ;; esac @@ -338,7 +338,7 @@ jobs: echo "💬 Updating final breaking change detection status comment..." if [ -z "$COMMENT_ID" ]; then - echo "⚠️ No comment ID found, skipping comment update" + echo "::warning::No comment ID found, skipping comment update" exit 0 fi @@ -383,7 +383,7 @@ jobs: gh api --method PATCH repos/${{ github.repository }}/issues/comments/$COMMENT_ID \ --field body="$FINAL_COMMENT" || { - echo "⚠️ Failed to update comment, but continuing" + echo "::warning::Failed to update comment, but continuing" } echo "✅ Final comment updated" From 22338f6ef74407cde8c277007368eaf09e6b73a3 Mon Sep 17 00:00:00 2001 From: addisonbeck Date: Mon, 10 Nov 2025 16:12:18 -0500 Subject: [PATCH 4/7] review: switch to workflow_dispatch for the trigger --- .github/workflows/build-wasm-internal.yml | 1 - .github/workflows/detect-breaking-changes.yml | 84 ++++++------------- 2 files changed, 27 insertions(+), 58 deletions(-) diff --git a/.github/workflows/build-wasm-internal.yml b/.github/workflows/build-wasm-internal.yml index 6acc7e893..b19a86a7d 100644 --- a/.github/workflows/build-wasm-internal.yml +++ b/.github/workflows/build-wasm-internal.yml @@ -168,6 +168,5 @@ jobs: pr_head_ref: ${{ github.event.pull_request.head.ref }} build_run_id: ${{ github.run_id }} client_repo: "bitwarden/clients" - client_event_type: "sdk-breaking-change-check" client_label: "typescript" client_workflow: "sdk-breaking-change-check.yml" diff --git a/.github/workflows/detect-breaking-changes.yml b/.github/workflows/detect-breaking-changes.yml index c8f7d5dad..4891d8962 100644 --- a/.github/workflows/detect-breaking-changes.yml +++ b/.github/workflows/detect-breaking-changes.yml @@ -23,10 +23,6 @@ on: description: "Target client repository (e.g., 'bitwarden/clients')" required: true type: string - client_event_type: - description: "Repository dispatch event type" - required: true - type: string client_label: description: "Client label for display purposes" required: true @@ -35,6 +31,10 @@ on: description: "Target workflow filename in client repo" required: true type: string + client_branch: + description: "The branch to target on the client repo" + type: string + default: "sdk-warn-on-breaking-changes" permissions: contents: read @@ -52,9 +52,9 @@ jobs: working-directory: . env: _CLIENT_REPO: ${{ inputs.client_repo }} - _EVENT_TYPE: ${{ inputs.client_event_type }} _CLIENT_LABEL: ${{ inputs.client_label }} _WORKFLOW_NAME: ${{ inputs.client_workflow }} + _BRANCH_NAME: ${{ inputs.client_branch }} _MAX_RETRIES: 3 steps: @@ -80,45 +80,19 @@ jobs: echo " SDK Version: $SDK_VERSION" echo " Source Run ID: $SOURCE_RUN_ID" - - name: Prepare dispatch payload - id: payload + - name: Prepare workflow inputs + id: inputs env: PR_NUMBER: ${{ steps.context.outputs.pr_number }} SDK_VERSION: ${{ steps.context.outputs.sdk_version }} - PR_HEAD_SHA: ${{ steps.context.outputs.pr_head_sha }} SOURCE_RUN_ID: ${{ steps.context.outputs.source_run_id }} GITHUB_REPOSITORY: ${{ github.repository }} run: | - # Create payload JSON - PAYLOAD=$(jq -n \ - --arg pr_number "$PR_NUMBER" \ - --arg sdk_version "$SDK_VERSION" \ - --arg source_repo "$GITHUB_REPOSITORY" \ - --arg workflow_context "$_WORKFLOW_NAME" \ - --arg client_label "$_CLIENT_LABEL" \ - --arg pr_head_sha "$PR_HEAD_SHA" \ - --arg pr_base_ref "main" \ - --arg comment_id "" \ - --arg run_id "$SOURCE_RUN_ID" \ - --arg artifact_name "sdk-internal" \ - '{ - pr_number: $pr_number, - sdk_version: $sdk_version, - source_repo: $source_repo, - workflow_context: $workflow_context, - client_label: $client_label, - pr_head_sha: $pr_head_sha, - pr_base_ref: $pr_base_ref, - comment_id: $comment_id, - artifacts_info: { - run_id: $run_id, - artifact_name: $artifact_name - } - }') - - echo "payload<> $GITHUB_OUTPUT - echo "$PAYLOAD" >> $GITHUB_OUTPUT - echo "EOF" >> $GITHUB_OUTPUT + echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT + echo "sdk_version=$SDK_VERSION" >> $GITHUB_OUTPUT + echo "source_repo=$GITHUB_REPOSITORY" >> $GITHUB_OUTPUT + echo "artifacts_run_id=$SOURCE_RUN_ID" >> $GITHUB_OUTPUT + echo "artifact_name=sdk-internal" >> $GITHUB_OUTPUT - name: Log in to Azure uses: bitwarden/gh-actions/azure-login@main @@ -141,7 +115,7 @@ jobs: app-id: ${{ steps.get-kv-secrets.outputs.BW-GHAPP-ID }} private-key: ${{ steps.get-kv-secrets.outputs.BW-GHAPP-KEY }} owner: ${{ github.repository_owner }} - permission-contents: write + permissions-actions: write repositories: | clients sdk-internal @@ -191,17 +165,20 @@ jobs: echo "✅ Initial comment created/updated" - - name: Trigger client repository dispatch and watch + - name: Trigger client workflow dispatch and watch id: trigger-dispatch-and-watch timeout-minutes: 15 env: GH_TOKEN: ${{ steps.app-token.outputs.token }} - PAYLOAD: ${{ steps.payload.outputs.payload }} - SDK_VERSION: ${{ steps.context.outputs.sdk_version }} + PR_NUMBER: ${{ steps.inputs.outputs.pr_number }} + SDK_VERSION: ${{ steps.inputs.outputs.sdk_version }} + SOURCE_REPO: ${{ steps.inputs.outputs.source_repo }} + ARTIFACTS_RUN_ID: ${{ steps.inputs.outputs.artifacts_run_id }} + ARTIFACT_NAME: ${{ steps.inputs.outputs.artifact_name }} run: | - echo "🚀 Triggering $_WORKFLOW_NAME in $_CLIENT_REPO..." + echo "🚀 Triggering ${_WORKFLOW_NAME} in ${_CLIENT_REPO} via workflow_dispatch..." - # Step 1: Dispatch the workflow + # Step 1: Trigger workflow via workflow_dispatch RETRY_COUNT=0 DISPATCH_SUCCESS=false @@ -209,18 +186,11 @@ jobs: RETRY_COUNT=$((RETRY_COUNT + 1)) echo "🔄 Attempt $RETRY_COUNT of $_MAX_RETRIES for $_CLIENT_REPO..." - # Create the complete dispatch payload - DISPATCH_PAYLOAD=$(jq -n \ - --arg event_type "$_EVENT_TYPE" \ - --argjson client_payload "$PAYLOAD" \ - '{ - event_type: $event_type, - client_payload: $client_payload - }') - - if echo "$DISPATCH_PAYLOAD" | gh api repos/$_CLIENT_REPO/dispatches \ - --method POST \ - --input -; then + if gh workflow run $_WORKFLOW_NAME --repo $_CLIENT_REPO --ref "$_BRANCH_NAME" \ + -f sdk_version="$SDK_VERSION" \ + -f source_repo="$SOURCE_REPO" \ + -f artifacts_run_id="$ARTIFACTS_RUN_ID" \ + -f artifact_name="$ARTIFACT_NAME"; then echo "✅ Successfully triggered $_CLIENT_REPO ($_CLIENT_LABEL)" echo "✅ **$_CLIENT_REPO**: $_WORKFLOW_NAME triggered - [Monitor Progress](https://github.com/$_CLIENT_REPO/actions)" >> $GITHUB_STEP_SUMMARY @@ -247,7 +217,7 @@ jobs: MAX_RUN_LIST_RETRIES=10 WORKFLOW_RUN_ID="" - JQ_FILTER='.[] | select(.status as $s | (["requested", "queued", "in_progress", "waiting"] | contains([$s])) and (.displayTitle | contains("'"$SDK_VERSION"'")) and (.name | startswith("SDK breaking change check"))) | .databaseId' + JQ_FILTER='.[] | select(.status as $s | (["requested", "queued", "in_progress", "waiting"] | contains([$s])) and (.displayTitle | contains("'"$SDK_VERSION"'")) and (.name | contains("SDK breaking change check"))) | .databaseId' while sleep 5 From dbbcc37b803565a295814c95f5f4b517b0cdf6a7 Mon Sep 17 00:00:00 2001 From: addisonbeck Date: Mon, 10 Nov 2025 17:19:35 -0500 Subject: [PATCH 5/7] review: log out of azure after getting the gh app token --- .github/workflows/detect-breaking-changes.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/detect-breaking-changes.yml b/.github/workflows/detect-breaking-changes.yml index 4891d8962..09009e350 100644 --- a/.github/workflows/detect-breaking-changes.yml +++ b/.github/workflows/detect-breaking-changes.yml @@ -108,6 +108,9 @@ jobs: keyvault: gh-org-bitwarden secrets: "BW-GHAPP-ID,BW-GHAPP-KEY" + - name: Log out from Azure + uses: bitwarden/gh-actions/azure-logout@main + - name: Generate GH App token uses: actions/create-github-app-token@30bf6253fa41bdc8d1501d202ad15287582246b4 # v2.0.3 id: app-token @@ -357,6 +360,3 @@ jobs: } echo "✅ Final comment updated" - - - name: Log out from Azure - uses: bitwarden/gh-actions/azure-logout@main From a24a2cb743ad155358d928f93bdb0e8062ac636b Mon Sep 17 00:00:00 2001 From: addisonbeck Date: Mon, 10 Nov 2025 17:30:33 -0500 Subject: [PATCH 6/7] review: correct gh token permissions and usage --- .github/workflows/build-wasm-internal.yml | 3 +-- .github/workflows/detect-breaking-changes.yml | 8 +++----- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build-wasm-internal.yml b/.github/workflows/build-wasm-internal.yml index b19a86a7d..74bf596c4 100644 --- a/.github/workflows/build-wasm-internal.yml +++ b/.github/workflows/build-wasm-internal.yml @@ -156,8 +156,7 @@ jobs: if: github.event_name == 'pull_request' needs: build permissions: - contents: write - actions: write + contents: read pull-requests: write id-token: write uses: ./.github/workflows/detect-breaking-changes.yml diff --git a/.github/workflows/detect-breaking-changes.yml b/.github/workflows/detect-breaking-changes.yml index 09009e350..b7f499b8a 100644 --- a/.github/workflows/detect-breaking-changes.yml +++ b/.github/workflows/detect-breaking-changes.yml @@ -38,7 +38,6 @@ on: permissions: contents: read - actions: write pull-requests: write id-token: write @@ -121,12 +120,11 @@ jobs: permissions-actions: write repositories: | clients - sdk-internal - name: Create initial status comment id: initial-comment env: - GH_TOKEN: ${{ steps.app-token.outputs.token }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} SDK_VERSION: ${{ steps.context.outputs.sdk_version }} PR_NUMBER: ${{ steps.context.outputs.pr_number }} GITHUB_REPOSITORY: ${{ github.repository }} @@ -272,7 +270,7 @@ jobs: - name: Manage breaking change labels if: always() env: - GH_TOKEN: ${{ steps.app-token.outputs.token }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} CLIENT_STATUS: ${{ steps.trigger-dispatch-and-watch.outputs.status }} PR_NUMBER: ${{ steps.context.outputs.pr_number }} run: | @@ -301,7 +299,7 @@ jobs: - name: Update final status comment if: always() env: - GH_TOKEN: ${{ steps.app-token.outputs.token }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} COMMENT_ID: ${{ steps.initial-comment.outputs.comment_id }} SDK_VERSION: ${{ steps.context.outputs.sdk_version }} CLIENT_STATUS: ${{ steps.trigger-dispatch-and-watch.outputs.status }} From 8700adad1bd514185e7fe78124454173fb24e650 Mon Sep 17 00:00:00 2001 From: addisonbeck Date: Mon, 10 Nov 2025 17:49:25 -0500 Subject: [PATCH 7/7] chore: set default branch back to main --- .github/workflows/detect-breaking-changes.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/detect-breaking-changes.yml b/.github/workflows/detect-breaking-changes.yml index b7f499b8a..a284146f0 100644 --- a/.github/workflows/detect-breaking-changes.yml +++ b/.github/workflows/detect-breaking-changes.yml @@ -34,7 +34,7 @@ on: client_branch: description: "The branch to target on the client repo" type: string - default: "sdk-warn-on-breaking-changes" + default: "main" permissions: contents: read