Pull Request Labeler #32784
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 Labeler" | |
| # Multi-purpose PR labeling flow. | |
| # This workflow consumes workflow_run events from dedicated PR producer workflows | |
| # and applies labels in a metadata-only privileged context. | |
| on: | |
| workflow_run: | |
| workflows: ["PR event trigger", "Check if translation is required"] | |
| types: [completed] | |
| jobs: | |
| # Collects PR metadata once for PR event trigger runs. | |
| # Outputs are reused by specialized labeling jobs below. | |
| pr-metadata: | |
| # Security invariant: keep this workflow_run job metadata-only. | |
| # Do not checkout or execute content from PR branches in this privileged context. | |
| if: >- | |
| ${{ github.event.workflow_run.conclusion == 'success' && | |
| github.event.workflow_run.event == 'pull_request' && | |
| github.event.workflow_run.name == 'PR event trigger' }} | |
| outputs: | |
| pr-number: ${{ steps.pr.outputs.number }} | |
| is-external: ${{ steps.association.outputs.is_external }} | |
| permissions: | |
| actions: read | |
| pull-requests: read | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Download PR metadata artifact | |
| uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 #v4 | |
| with: | |
| name: pr-metadata | |
| run-id: ${{ github.event.workflow_run.id }} | |
| github-token: ${{ github.token }} | |
| path: . | |
| - name: Read PR number from artifact | |
| id: pr | |
| run: | | |
| pr_number="$(cat pr-number)" | |
| echo "number=${pr_number}" >> "$GITHUB_OUTPUT" | |
| - name: Read PR author | |
| id: pr_author | |
| uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd #v8.0.0 | |
| with: | |
| github-token: ${{ github.token }} | |
| script: | | |
| const prNumber = Number('${{ steps.pr.outputs.number }}'); | |
| const { data: pullRequest } = await github.rest.pulls.get({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| pull_number: prNumber, | |
| }); | |
| const username = pullRequest.user?.login; | |
| if (!username) { | |
| core.setFailed('PR payload has no user.login'); | |
| return; | |
| } | |
| core.setOutput('username', username); | |
| - id: generate-token | |
| name: Generate an access token | |
| uses: actions/create-github-app-token@fee1f7d63c2ff003460e3d139729b119787bc349 #v2 | |
| with: | |
| app-id: ${{ vars.EXTERNAL_PR_LABELER_APP_ID }} | |
| private-key: ${{ secrets.EXTERNAL_PR_LABELER_PK }} | |
| - name: Check if author is an org member | |
| id: association | |
| uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd #v8.0.0 | |
| with: | |
| github-token: ${{ steps.generate-token.outputs.token }} | |
| script: | | |
| const username = '${{ steps.pr_author.outputs.username }}'; | |
| let isExternal = false; | |
| try { | |
| await github.request('GET /orgs/{org}/members/{username}', { | |
| org: context.repo.owner, | |
| username, | |
| }); | |
| // 204: user is a member | |
| isExternal = false; | |
| } catch (error) { | |
| if (error.status === 404) { | |
| // 404: user is not a member | |
| isExternal = true; | |
| } else { | |
| throw error; | |
| } | |
| } | |
| core.setOutput('is_external', isExternal ? 'true' : 'false'); | |
| - name: Debug external classification | |
| if: ${{ runner.debug == '1' }} | |
| run: | | |
| echo "PR author: ${{ steps.pr_author.outputs.username }}" | |
| echo "is_external: ${{ steps.association.outputs.is_external }}" | |
| # Applies path-based labels from .github/labeler.yml. | |
| path-labeler: | |
| # Security invariant: keep this workflow_run job metadata-only. | |
| # Do not checkout or execute content from PR branches in this privileged context. | |
| needs: pr-metadata | |
| if: needs.pr-metadata.result == 'success' | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/labeler@634933edcd8ababfe52f92936142cc22ac488b1b #v6.0.1 | |
| with: | |
| pr-number: "${{ needs.pr-metadata.outputs.pr-number }}" | |
| configuration-path: .github/labeler.yml | |
| # Applies the community label to external contributors. | |
| external-pr-labeler: | |
| # Security invariant: keep this workflow_run job metadata-only. | |
| # Do not checkout or execute content from PR branches in this privileged context. | |
| needs: pr-metadata | |
| if: needs.pr-metadata.result == 'success' && needs.pr-metadata.outputs.is-external == 'true' | |
| permissions: | |
| pull-requests: write | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Label external PRs | |
| uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd #v8.0.0 | |
| with: | |
| script: | | |
| await github.rest.issues.addLabels({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: Number('${{ needs.pr-metadata.outputs.pr-number }}'), | |
| labels: ['community'], | |
| }); | |
| # Applies or removes needs-translation based on translation producer | |
| # (check_translations.yml) result artifact. | |
| translation-labeler: | |
| # Security invariant: keep this workflow_run job metadata-only. | |
| # Do not checkout or execute content from PR branches in this privileged context. | |
| if: >- | |
| ${{ github.event.workflow_run.conclusion == 'success' && | |
| github.event.workflow_run.event == 'pull_request' && | |
| github.event.workflow_run.name == 'Check if translation is required' }} | |
| runs-on: ubuntu-latest | |
| permissions: | |
| actions: read | |
| pull-requests: write | |
| steps: | |
| - name: Download translation result artifact | |
| uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 #v4 | |
| with: | |
| name: translation-check-result | |
| run-id: ${{ github.event.workflow_run.id }} | |
| github-token: ${{ github.token }} | |
| path: . | |
| - name: Read translation result | |
| id: translation | |
| run: | | |
| pr_number="$(grep -E '^pr_number=' translation-result | cut -d= -f2)" | |
| needs_translation="$(grep -E '^needs_translation=' translation-result | cut -d= -f2)" | |
| echo "pr_number=${pr_number}" >> "$GITHUB_OUTPUT" | |
| echo "needs_translation=${needs_translation}" >> "$GITHUB_OUTPUT" | |
| - name: Add needs-translation label | |
| if: steps.translation.outputs.needs_translation == 'true' | |
| uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd #v8.0.0 | |
| with: | |
| script: | | |
| await github.rest.issues.addLabels({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: Number('${{ steps.translation.outputs.pr_number }}'), | |
| labels: ['needs-translation'], | |
| }); | |
| - name: Remove needs-translation label | |
| if: steps.translation.outputs.needs_translation != 'true' | |
| uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd #v8.0.0 | |
| with: | |
| script: | | |
| try { | |
| await github.rest.issues.removeLabel({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: Number('${{ steps.translation.outputs.pr_number }}'), | |
| name: 'needs-translation', | |
| }); | |
| } catch (error) { | |
| if (error.status !== 404) { | |
| throw error; | |
| } | |
| } |