Pull request dashboard #13099
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: Pull request dashboard | |
| on: | |
| schedule: | |
| - cron: "0 * * * *" # hourly | |
| workflow_dispatch: | |
| inputs: | |
| repository: | |
| description: Target repository. Empty means all configured repositories. | |
| required: false | |
| type: string | |
| pr_number: | |
| description: Pull request number to refresh. Empty means full rebuild. | |
| required: false | |
| type: string | |
| trigger_event: | |
| description: Event that requested the refresh. | |
| required: false | |
| type: string | |
| trigger_action: | |
| description: Event action that requested the refresh. | |
| required: false | |
| type: string | |
| trigger_review_id: | |
| description: Pull request review id that requested the refresh. | |
| required: false | |
| type: string | |
| permissions: | |
| contents: read | |
| env: | |
| DASHBOARD_CONFIG: .github/scripts/pull-request-dashboard/repositories.json | |
| jobs: | |
| resolve-targets: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| environment: protected | |
| outputs: | |
| matrix: ${{ steps.targets.outputs.matrix }} | |
| pr_number: ${{ steps.trigger.outputs.pr_number }} | |
| trigger_event: ${{ steps.trigger.outputs.event }} | |
| trigger_action: ${{ steps.trigger.outputs.action }} | |
| trigger_review_id: ${{ steps.trigger.outputs.review_id }} | |
| dashboard_precondition_met: ${{ steps.dashboard-precondition.outputs.met }} | |
| steps: | |
| - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| with: | |
| persist-credentials: false | |
| - name: Resolve trigger inputs | |
| id: trigger | |
| env: | |
| EVENT_NAME: ${{ github.event_name }} | |
| TARGET_REPOSITORY_FROM_INPUT: ${{ inputs.repository }} | |
| PR_FROM_INPUT: ${{ inputs.pr_number }} | |
| TRIGGER_EVENT_FROM_INPUT: ${{ inputs.trigger_event }} | |
| TRIGGER_ACTION_FROM_INPUT: ${{ inputs.trigger_action }} | |
| TRIGGER_REVIEW_ID_FROM_INPUT: ${{ inputs.trigger_review_id }} | |
| run: | | |
| set -euo pipefail | |
| pr_number="" | |
| trigger_event="" | |
| trigger_action="" | |
| trigger_review_id="" | |
| case "$EVENT_NAME" in | |
| schedule) | |
| trigger_event="$EVENT_NAME" | |
| ;; | |
| workflow_dispatch) | |
| pr_number="$PR_FROM_INPUT" | |
| trigger_event="${TRIGGER_EVENT_FROM_INPUT:-workflow_dispatch}" | |
| trigger_action="$TRIGGER_ACTION_FROM_INPUT" | |
| trigger_review_id="$TRIGGER_REVIEW_ID_FROM_INPUT" | |
| ;; | |
| esac | |
| if [[ -n "$pr_number" ]]; then | |
| [[ "$pr_number" =~ ^[1-9][0-9]{0,6}$ ]] || { echo "bad PR number: $pr_number"; exit 1; } | |
| [[ -n "$TARGET_REPOSITORY_FROM_INPUT" ]] || { echo "repository is required when pr_number is set"; exit 1; } | |
| fi | |
| if [[ -n "$trigger_event" ]]; then | |
| [[ "$trigger_event" =~ ^(schedule|workflow_dispatch|check_suite|pull_request|issue_comment|pull_request_review|pull_request_review_comment|pull_request_review_thread)$ ]] \ | |
| || { echo "bad trigger event: $trigger_event"; exit 1; } | |
| fi | |
| if [[ -n "$trigger_action" ]]; then | |
| [[ "$trigger_action" =~ ^[a-z_]{1,32}$ ]] || { echo "bad trigger action: $trigger_action"; exit 1; } | |
| fi | |
| if [[ -n "$trigger_review_id" ]]; then | |
| [[ "$trigger_review_id" =~ ^[1-9][0-9]{0,19}$ ]] || { echo "bad review id: $trigger_review_id"; exit 1; } | |
| fi | |
| { | |
| echo "pr_number=$pr_number" | |
| echo "event=$trigger_event" | |
| echo "action=$trigger_action" | |
| echo "review_id=$trigger_review_id" | |
| } >> "$GITHUB_OUTPUT" | |
| - name: Resolve target repositories | |
| id: targets | |
| env: | |
| TARGET_REPOSITORY: ${{ inputs.repository }} | |
| run: | | |
| set -euo pipefail | |
| matrix=$(jq -c --arg repo "$TARGET_REPOSITORY" ' | |
| if $repo == "" then | |
| . | |
| else | |
| [ .[] | select(.name == $repo or ("open-telemetry/" + .name) == $repo) ] | |
| end | |
| ' "$DASHBOARD_CONFIG") | |
| if [[ "$matrix" == "[]" ]]; then | |
| echo "no configured repository matched: ${TARGET_REPOSITORY:-<all>}" >&2 | |
| exit 1 | |
| fi | |
| echo "matrix={\"include\":${matrix}}" >> "$GITHUB_OUTPUT" | |
| echo "repository=$(jq -r '.[0].name' <<< "$matrix")" >> "$GITHUB_OUTPUT" | |
| echo "$matrix" | jq . | |
| # Webhook-driven runs (trigger_event is a real GitHub event like | |
| # pull_request_review_comment, not schedule/workflow_dispatch) skip | |
| # large_repo targets. Webhook volume on large repos exhausts the App's | |
| # hourly API quota; scheduled full rebuilds and manual dispatches still | |
| # process them. | |
| - name: Check webhook large-repo skip | |
| id: webhook-large-repo-skip | |
| if: steps.trigger.outputs.event != 'schedule' && steps.trigger.outputs.event != 'workflow_dispatch' | |
| env: | |
| TARGET_REPOSITORY: ${{ steps.targets.outputs.repository }} | |
| TRIGGER_EVENT: ${{ steps.trigger.outputs.event }} | |
| run: | | |
| set -euo pipefail | |
| is_large=$(jq -r --arg repo "$TARGET_REPOSITORY" ' | |
| [ .[] | select(.name == $repo) | .large_repo // false ][0] // false | |
| ' "$DASHBOARD_CONFIG") | |
| if [[ "$is_large" == "true" ]]; then | |
| echo "Skipping webhook-driven run for large_repo target ($TARGET_REPOSITORY, event=$TRIGGER_EVENT)." | |
| echo "skip=true" >> "$GITHUB_OUTPUT" | |
| fi | |
| - uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 | |
| if: >- | |
| steps.trigger.outputs.pr_number != '' && | |
| steps.webhook-large-repo-skip.outputs.skip != 'true' | |
| id: dashboard-token | |
| with: | |
| client-id: ${{ vars.PR_DASHBOARD_CLIENT_ID }} | |
| private-key: ${{ secrets.PR_DASHBOARD_PRIVATE_KEY }} | |
| owner: open-telemetry | |
| repositories: ${{ steps.targets.outputs.repository }} | |
| permission-issues: read | |
| - name: Check dashboard precondition | |
| id: dashboard-precondition | |
| env: | |
| GH_TOKEN: ${{ steps.dashboard-token.outputs.token }} | |
| TRIGGER_PR_NUMBER: ${{ steps.trigger.outputs.pr_number }} | |
| WEBHOOK_LARGE_REPO_SKIP: ${{ steps.webhook-large-repo-skip.outputs.skip }} | |
| REPO: open-telemetry/${{ steps.targets.outputs.repository }} | |
| run: | | |
| set -euo pipefail | |
| if [[ "${WEBHOOK_LARGE_REPO_SKIP:-}" == "true" ]]; then | |
| echo "met=false" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| if [[ -z "$TRIGGER_PR_NUMBER" ]]; then | |
| echo "met=true" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| dashboard_exists=$(python3 .github/scripts/pull-request-dashboard/publish_dashboard.py --repo "$REPO" --check-dashboard-exists) | |
| if [[ "$dashboard_exists" == "true" ]]; then | |
| echo "met=true" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| if [[ "$dashboard_exists" == "false" ]]; then | |
| echo "Dashboard issue does not exist yet; skipping targeted refresh." | |
| echo "met=false" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| echo "unexpected dashboard existence result: $dashboard_exists" >&2 | |
| exit 1 | |
| run-repo-dashboard: | |
| needs: resolve-targets | |
| if: needs.resolve-targets.outputs.dashboard_precondition_met == 'true' | |
| permissions: | |
| contents: write | |
| strategy: | |
| fail-fast: false | |
| max-parallel: 2 | |
| matrix: ${{ fromJSON(needs.resolve-targets.outputs.matrix) }} | |
| uses: ./.github/workflows/pull-request-dashboard-repo.yml | |
| with: | |
| repository: ${{ matrix.name }} | |
| pr_number: ${{ needs.resolve-targets.outputs.pr_number }} | |
| trigger_event: ${{ needs.resolve-targets.outputs.trigger_event }} | |
| trigger_action: ${{ needs.resolve-targets.outputs.trigger_action }} | |
| trigger_review_id: ${{ needs.resolve-targets.outputs.trigger_review_id }} | |
| required_approvals: ${{ matrix.required_approvals || 1 }} | |
| approver_teams_json: ${{ toJSON(matrix.approver_teams || fromJSON('[]')) }} | |
| slack_channel: ${{ matrix.slack_channel }} | |
| slack_user_mapping_json: ${{ toJSON(matrix.slack_user_mapping || fromJSON('{}')) }} | |
| large_repo: ${{ matrix.large_repo || false }} | |
| secrets: | |
| PR_DASHBOARD_PRIVATE_KEY: ${{ secrets.PR_DASHBOARD_PRIVATE_KEY }} | |
| COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} | |
| SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} |