merge queue: queuing main (d5ee340) and #556 together #1135
Workflow file for this run
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: PR Status Check | |
| on: | |
| pull_request: | |
| branches: [main] | |
| merge_group: | |
| types: [checks_requested] | |
| # This workflow acts as the single required status check for all PRs. | |
| # It uses a semaphore-based polling approach with improved reliability. | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.event.merge_group.head_sha }} | |
| cancel-in-progress: true | |
| jobs: | |
| detect-changes: | |
| name: Detect Changes | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| outputs: | |
| docs-only: ${{ steps.changes.outputs.docs-only }} | |
| clients-only: ${{ steps.changes.outputs.clients-only }} | |
| code-changes: ${{ steps.changes.outputs.code-changes }} | |
| workflow-only: ${{ steps.changes.outputs.workflow-only }} | |
| has-docs: ${{ steps.changes.outputs.has-docs }} | |
| has-clients: ${{ steps.changes.outputs.has-clients }} | |
| has-workflow: ${{ steps.changes.outputs.has-workflow }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 0 | |
| - name: Detect changed files | |
| id: changes | |
| run: | | |
| # Get the base ref for comparison | |
| if [ "${{ github.event_name }}" = "pull_request" ]; then | |
| BASE_REF="${{ github.event.pull_request.base.sha }}" | |
| else | |
| BASE_REF="origin/main" | |
| fi | |
| # Get list of changed files | |
| CHANGED_FILES=$(git diff --name-only "$BASE_REF" HEAD) | |
| # Define patterns for different change types | |
| # Workflow files take precedence - they may not trigger CI runs due to GitHub security | |
| WORKFLOW_PATTERN='(^\.github/workflows/|^\.mergify\.yml$|^\.github/.*\.md$|^Taskfile\.yml$)' | |
| CLIENTS_PATTERN='(^clients/|^\.github/workflows/client-sdk-ci\.yml)' | |
| DOCS_PATTERN='(^docs-cms/|^docusaurus/(docs|blog|i18n|sidebars|src|static|docusaurus\.config)|^docusaurus/\.[^/]+$|^docusaurus/[^/]+\.(json|ts)$|^tooling/(build_docs|validate_docs)\.py|^\.github/workflows/docs\.yml|^\.claude/|^[A-Z_-]+\.md$|^docs-summary\.md$)' | |
| # Categorize changes (order matters - workflow first, then clients, then docs) | |
| WORKFLOW_FILES=$(echo "$CHANGED_FILES" | grep -E "${WORKFLOW_PATTERN}" || true) | |
| CLIENT_FILES=$(echo "$CHANGED_FILES" | grep -vE "${WORKFLOW_PATTERN}" | grep -E "${CLIENTS_PATTERN}" || true) | |
| DOCS_FILES=$(echo "$CHANGED_FILES" | grep -vE "${WORKFLOW_PATTERN}" | grep -vE "${CLIENTS_PATTERN}" | grep -E "${DOCS_PATTERN}" || true) | |
| OTHER_FILES=$(echo "$CHANGED_FILES" | grep -vE "${WORKFLOW_PATTERN}" | grep -vE "${CLIENTS_PATTERN}" | grep -vE "${DOCS_PATTERN}" || true) | |
| # Set individual flags | |
| HAS_DOCS="false" | |
| HAS_CLIENTS="false" | |
| HAS_WORKFLOW="false" | |
| HAS_CODE="false" | |
| [ -n "$DOCS_FILES" ] && HAS_DOCS="true" | |
| [ -n "$CLIENT_FILES" ] && HAS_CLIENTS="true" | |
| [ -n "$WORKFLOW_FILES" ] && HAS_WORKFLOW="true" | |
| [ -n "$OTHER_FILES" ] && HAS_CODE="true" | |
| # Output individual flags | |
| { | |
| echo "has-docs=${HAS_DOCS}" | |
| echo "has-clients=${HAS_CLIENTS}" | |
| echo "has-workflow=${HAS_WORKFLOW}" | |
| } >> "$GITHUB_OUTPUT" | |
| # Determine exclusive categories | |
| if [[ "$HAS_WORKFLOW" == "true" && "$HAS_DOCS" == "false" && "$HAS_CLIENTS" == "false" && "$HAS_CODE" == "false" ]]; then | |
| { | |
| echo "docs-only=false" | |
| echo "clients-only=false" | |
| echo "code-changes=false" | |
| echo "workflow-only=true" | |
| } >> "$GITHUB_OUTPUT" | |
| echo "⚙️ Detected: Workflow/CI configuration-only changes" | |
| elif [[ "$HAS_DOCS" == "true" && "$HAS_CLIENTS" == "false" && "$HAS_CODE" == "false" && "$HAS_WORKFLOW" == "false" ]]; then | |
| { | |
| echo "docs-only=true" | |
| echo "clients-only=false" | |
| echo "code-changes=false" | |
| echo "workflow-only=false" | |
| } >> "$GITHUB_OUTPUT" | |
| echo "📄 Detected: Documentation-only changes" | |
| elif [[ "$HAS_CLIENTS" == "true" && "$HAS_DOCS" == "false" && "$HAS_CODE" == "false" && "$HAS_WORKFLOW" == "false" ]]; then | |
| { | |
| echo "docs-only=false" | |
| echo "clients-only=true" | |
| echo "code-changes=false" | |
| echo "workflow-only=false" | |
| } >> "$GITHUB_OUTPUT" | |
| echo "📱 Detected: Client SDK-only changes" | |
| elif [[ "$HAS_CODE" == "true" ]]; then | |
| { | |
| echo "docs-only=false" | |
| echo "clients-only=false" | |
| echo "code-changes=true" | |
| echo "workflow-only=false" | |
| } >> "$GITHUB_OUTPUT" | |
| echo "💻 Detected: Code changes (full CI required)" | |
| else | |
| { | |
| echo "docs-only=false" | |
| echo "clients-only=false" | |
| echo "code-changes=true" | |
| echo "workflow-only=false" | |
| } >> "$GITHUB_OUTPUT" | |
| echo "🔀 Detected: Mixed changes (full CI required)" | |
| fi | |
| # Semaphore job that waits for workflow completion signals | |
| pr-status: | |
| name: PR Status Check | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 90 | |
| needs: detect-changes | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v7 | |
| - name: Wait for required workflow completions | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| DOCS_ONLY="${{ needs.detect-changes.outputs.docs-only }}" | |
| CLIENTS_ONLY="${{ needs.detect-changes.outputs.clients-only }}" | |
| CODE_CHANGES="${{ needs.detect-changes.outputs.code-changes }}" | |
| WORKFLOW_ONLY="${{ needs.detect-changes.outputs.workflow-only }}" | |
| HAS_DOCS="${{ needs.detect-changes.outputs.has-docs }}" | |
| HAS_CLIENTS="${{ needs.detect-changes.outputs.has-clients }}" | |
| HAS_WORKFLOW="${{ needs.detect-changes.outputs.has-workflow }}" | |
| SHA="${{ github.event.pull_request.head.sha || github.sha }}" | |
| MAX_WAIT=5400 # 90 minutes in seconds | |
| CHECK_INTERVAL=15 # Check every 15 seconds (faster than before) | |
| ELAPSED=0 | |
| STARTUP_GRACE=180 # 3 minute grace period for workflows to start | |
| { | |
| echo "## PR Status Check (Semaphore)" | |
| echo "" | |
| echo "**SHA**: \`$SHA\`" | |
| echo "**PR**: #${{ github.event.pull_request.number }}" | |
| echo "" | |
| echo "### Change Detection" | |
| echo "- Documentation-only: ${DOCS_ONLY}" | |
| echo "- Client SDK-only: ${CLIENTS_ONLY}" | |
| echo "- Code changes: ${CODE_CHANGES}" | |
| echo "- Workflow-only: ${WORKFLOW_ONLY}" | |
| echo "- Has docs: ${HAS_DOCS}" | |
| echo "- Has clients: ${HAS_CLIENTS}" | |
| echo "- Has workflow: ${HAS_WORKFLOW}" | |
| echo "" | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| # Determine which workflow names we need to wait for | |
| REQUIRED_WORKFLOWS=() | |
| if [[ "$WORKFLOW_ONLY" == "true" ]]; then | |
| # Workflow-only changes don't require waiting for any workflows | |
| # GitHub may not run CI workflows when workflow files themselves are modified (security feature) | |
| { | |
| echo "### ✅ Status: SUCCESS (No workflows required)" | |
| echo "" | |
| echo "This PR only modifies CI/workflow configuration files." | |
| echo "No other workflows need to complete for this change type." | |
| echo "" | |
| echo "**Note**: Workflow changes are validated through:" | |
| echo "1. YAML syntax validation by GitHub" | |
| echo "2. Post-merge testing when the workflows actually run" | |
| echo "3. Manual review of the workflow changes" | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| exit 0 | |
| elif [[ "$DOCS_ONLY" == "true" ]]; then | |
| REQUIRED_WORKFLOWS+=("Docs PR Validation") | |
| elif [[ "$CLIENTS_ONLY" == "true" ]]; then | |
| REQUIRED_WORKFLOWS+=("Client SDK CI") | |
| elif [[ "$CODE_CHANGES" == "true" ]]; then | |
| REQUIRED_WORKFLOWS+=("CI") | |
| [[ "$HAS_DOCS" == "true" ]] && REQUIRED_WORKFLOWS+=("Docs PR Validation") | |
| [[ "$HAS_CLIENTS" == "true" ]] && REQUIRED_WORKFLOWS+=("Client SDK CI") | |
| fi | |
| { | |
| echo "### Required Workflows" | |
| for WF in "${REQUIRED_WORKFLOWS[@]}"; do | |
| echo "- \`$WF\`" | |
| done | |
| echo "" | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| echo "Waiting for: ${REQUIRED_WORKFLOWS[*]}" | |
| # Function to get workflow run status | |
| # Query recent runs first (fast path), then fall back to deeper search if needed. | |
| # This avoids iterating through all workflow runs which can take 80+ seconds in large repos. | |
| get_workflow_status() { | |
| local workflow_name="$1" | |
| local result="" | |
| # Fast path: Check first 100 runs (covers 99% of cases for recent PRs) | |
| result=$(gh api "repos/${{ github.repository }}/actions/runs?per_page=100" \ | |
| --jq ".workflow_runs[] | select(.name == \"$workflow_name\" and .head_sha == \"$SHA\") | {status: .status, conclusion: .conclusion, created_at: .created_at}" \ | |
| 2>/dev/null | jq -s 'sort_by(.created_at) | reverse | .[0] | del(.created_at)') | |
| # If found, return it | |
| if [ -n "$result" ] && [ "$result" != "null" ]; then | |
| echo "$result" | |
| return | |
| fi | |
| # Slow path: Check first 500 runs (handles edge cases) | |
| result=$(gh api "repos/${{ github.repository }}/actions/runs?per_page=100&page=1" \ | |
| --jq ".workflow_runs[] | select(.name == \"$workflow_name\" and .head_sha == \"$SHA\") | {status: .status, conclusion: .conclusion}" \ | |
| 2>/dev/null) | |
| if [ -z "$result" ]; then | |
| result=$(gh api "repos/${{ github.repository }}/actions/runs?per_page=100&page=2" \ | |
| --jq ".workflow_runs[] | select(.name == \"$workflow_name\" and .head_sha == \"$SHA\") | {status: .status, conclusion: .conclusion}" \ | |
| 2>/dev/null) | |
| fi | |
| if [ -z "$result" ]; then | |
| result=$(gh api "repos/${{ github.repository }}/actions/runs?per_page=100&page=3" \ | |
| --jq ".workflow_runs[] | select(.name == \"$workflow_name\" and .head_sha == \"$SHA\") | {status: .status, conclusion: .conclusion}" \ | |
| 2>/dev/null) | |
| fi | |
| if [ -z "$result" ]; then | |
| result=$(gh api "repos/${{ github.repository }}/actions/runs?per_page=100&page=4" \ | |
| --jq ".workflow_runs[] | select(.name == \"$workflow_name\" and .head_sha == \"$SHA\") | {status: .status, conclusion: .conclusion}" \ | |
| 2>/dev/null) | |
| fi | |
| if [ -z "$result" ]; then | |
| result=$(gh api "repos/${{ github.repository }}/actions/runs?per_page=100&page=5" \ | |
| --jq ".workflow_runs[] | select(.name == \"$workflow_name\" and .head_sha == \"$SHA\") | {status: .status, conclusion: .conclusion}" \ | |
| 2>/dev/null) | |
| fi | |
| echo "$result" | |
| } | |
| # Wait for all required workflows to complete | |
| LAST_STATUS="" | |
| while [ $ELAPSED -lt $MAX_WAIT ]; do | |
| ALL_COMPLETE=true | |
| ALL_SUCCESS=true | |
| PENDING_WORKFLOWS=() | |
| FAILED_WORKFLOWS=() | |
| SUCCESS_WORKFLOWS=() | |
| for WORKFLOW in "${REQUIRED_WORKFLOWS[@]}"; do | |
| WORKFLOW_DATA=$(get_workflow_status "$WORKFLOW") | |
| if [ -n "$WORKFLOW_DATA" ]; then | |
| STATUS=$(echo "$WORKFLOW_DATA" | jq -r '.status') | |
| CONCLUSION=$(echo "$WORKFLOW_DATA" | jq -r '.conclusion') | |
| if [[ "$STATUS" == "completed" ]]; then | |
| if [[ "$CONCLUSION" == "success" ]]; then | |
| SUCCESS_WORKFLOWS+=("$WORKFLOW") | |
| else | |
| FAILED_WORKFLOWS+=("$WORKFLOW ($CONCLUSION)") | |
| ALL_SUCCESS=false | |
| fi | |
| else | |
| ALL_COMPLETE=false | |
| PENDING_WORKFLOWS+=("$WORKFLOW ($STATUS)") | |
| fi | |
| else | |
| # Workflow hasn't started yet | |
| ALL_COMPLETE=false | |
| if [ $ELAPSED -lt $STARTUP_GRACE ]; then | |
| PENDING_WORKFLOWS+=("$WORKFLOW (starting...)") | |
| else | |
| # After grace period, this is an error | |
| PENDING_WORKFLOWS+=("$WORKFLOW (NOT STARTED - ERROR)") | |
| fi | |
| fi | |
| done | |
| # Generate status update | |
| CURRENT_STATUS="Success: ${#SUCCESS_WORKFLOWS[@]} | Pending: ${#PENDING_WORKFLOWS[@]} | Failed: ${#FAILED_WORKFLOWS[@]}" | |
| # Only print if status changed | |
| if [ "$CURRENT_STATUS" != "$LAST_STATUS" ]; then | |
| echo "[$ELAPSED s] $CURRENT_STATUS" | |
| LAST_STATUS="$CURRENT_STATUS" | |
| fi | |
| # Check if we're done | |
| if [[ "$ALL_COMPLETE" == "true" ]]; then | |
| if [[ "$ALL_SUCCESS" == "true" ]]; then | |
| { | |
| echo "### ✅ Status: SUCCESS" | |
| echo "" | |
| echo "All required workflows completed successfully:" | |
| for WF in "${SUCCESS_WORKFLOWS[@]}"; do | |
| echo "- ✅ \`$WF\`" | |
| done | |
| echo "" | |
| echo "**Total wait time**: ${ELAPSED}s" | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| exit 0 | |
| else | |
| { | |
| echo "### ❌ Status: FAILURE" | |
| echo "" | |
| echo "Failed workflows:" | |
| for WF in "${FAILED_WORKFLOWS[@]}"; do | |
| echo "- ❌ \`$WF\`" | |
| done | |
| if [ ${#SUCCESS_WORKFLOWS[@]} -gt 0 ]; then | |
| echo "" | |
| echo "Successful workflows:" | |
| for WF in "${SUCCESS_WORKFLOWS[@]}"; do | |
| echo "- ✅ \`$WF\`" | |
| done | |
| fi | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| exit 1 | |
| fi | |
| fi | |
| # Still waiting - check for stalled workflows | |
| if [ $ELAPSED -gt $STARTUP_GRACE ]; then | |
| for WF in "${PENDING_WORKFLOWS[@]}"; do | |
| if [[ "$WF" == *"NOT STARTED"* ]]; then | |
| { | |
| echo "### ❌ Status: ERROR" | |
| echo "" | |
| echo "Workflow \`${WF%% (*)}\` failed to start within ${STARTUP_GRACE}s grace period." | |
| echo "" | |
| echo "This usually indicates:" | |
| echo "1. Path filters prevented the workflow from triggering" | |
| echo "2. Workflow file has syntax errors" | |
| echo "3. GitHub Actions is experiencing issues" | |
| echo "" | |
| echo "Please check the workflow file and GitHub Actions status." | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| exit 1 | |
| fi | |
| done | |
| fi | |
| sleep $CHECK_INTERVAL | |
| ELAPSED=$((ELAPSED + CHECK_INTERVAL)) | |
| done | |
| # Timeout | |
| { | |
| echo "### ❌ Status: TIMEOUT" | |
| echo "" | |
| echo "Timed out after ${MAX_WAIT}s waiting for workflows to complete." | |
| echo "" | |
| if [ ${#PENDING_WORKFLOWS[@]} -gt 0 ]; then | |
| echo "Pending workflows:" | |
| for WF in "${PENDING_WORKFLOWS[@]}"; do | |
| echo "- ⏳ \`$WF\`" | |
| done | |
| fi | |
| if [ ${#FAILED_WORKFLOWS[@]} -gt 0 ]; then | |
| echo "" | |
| echo "Failed workflows:" | |
| for WF in "${FAILED_WORKFLOWS[@]}"; do | |
| echo "- ❌ \`$WF\`" | |
| done | |
| fi | |
| if [ ${#SUCCESS_WORKFLOWS[@]} -gt 0 ]; then | |
| echo "" | |
| echo "Successful workflows:" | |
| for WF in "${SUCCESS_WORKFLOWS[@]}"; do | |
| echo "- ✅ \`$WF\`" | |
| done | |
| fi | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| exit 1 |