Apply PR Labels #2211
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: Apply PR Labels | |
| # workflow_run is used intentionally here: the elevated-permission step runs | |
| # in the base-repo context but never checks out PR code, so it is safe. | |
| on: | |
| workflow_run: # zizmor: ignore[dangerous-triggers] | |
| workflows: ["Labels Trigger"] | |
| types: | |
| - completed | |
| permissions: {} | |
| concurrency: | |
| group: labels-apply-${{ github.event.workflow_run.id }} | |
| cancel-in-progress: true | |
| jobs: | |
| labels-by-language: | |
| name: labels-by-language | |
| runs-on: ubuntu-slim | |
| if: github.event.workflow_run.conclusion == 'success' | |
| permissions: | |
| pull-requests: write # So we can add labels to the pull request | |
| steps: | |
| - uses: actions/github-script@v9 | |
| with: | |
| script: | | |
| // For same-repo PRs the pull_requests array is populated directly. | |
| // For fork PRs it is empty, so fall back to a commit-SHA lookup. | |
| let prNumber; | |
| const prs = context.payload.workflow_run.pull_requests; | |
| if (prs && prs.length > 0) { | |
| prNumber = prs[0].number; | |
| } else { | |
| // For fork PRs, commits don't exist in the base repo history so | |
| // listPullRequestsAssociatedWithCommit returns empty. Use the | |
| // head owner + branch from the workflow_run payload instead. | |
| const headOwner = context.payload.workflow_run.head_repository.owner.login; | |
| const headBranch = context.payload.workflow_run.head_branch; | |
| const result = await github.rest.pulls.list({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| head: `${headOwner}:${headBranch}`, | |
| state: 'open', | |
| }); | |
| if (result.data.length === 0) { | |
| core.warning(`No open PR found for ${headOwner}:${headBranch}`); | |
| return; | |
| } | |
| prNumber = result.data[0].number; | |
| } | |
| const filenames = (await github.paginate(github.rest.pulls.listFiles, { | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| pull_number: prNumber, | |
| per_page: 100, | |
| })).map(file => file.filename); | |
| console.log(filenames); | |
| const vehicle_labels = { | |
| AntennaTracker: 'AntennaTracker', | |
| ArduCopter: 'Copter', | |
| ArduPlane: 'Plane', | |
| ArduSub: 'Sub', | |
| Blimp: 'Blimp', | |
| Rover: 'Rover', | |
| }; | |
| const vehicle_sim_labels = { | |
| Helicopter: 'TradHeli', | |
| QuadPlane: 'VTOL-Plane', | |
| Sailboat: 'Sailboat', | |
| Blimp: 'Blimp', | |
| Plane: 'Plane', | |
| Rover: 'Rover', | |
| Submarine: 'Sub', | |
| }; | |
| const generic_suffix_labels = { | |
| '.lua': 'Scripting', | |
| '.py': 'Python', | |
| }; | |
| const generic_prefix_labels = { | |
| 'libraries/AP_ExternalAHRS': 'ExternalAHRS', | |
| 'libraries/AP_Compass': 'Compass', | |
| 'libraries/AP_NavEKF': 'EKF', | |
| 'libraries/AP_HAL_Linux': 'Linux', | |
| 'libraries/GCS_MAVLink': 'MAVLink', | |
| 'libraries/SITL': 'SITL', | |
| 'Tools/AP_Periph': 'AP_Periph', | |
| 'Tools/autotest': 'CI', | |
| '.github/workflows': 'github_actions', | |
| }; | |
| const specific_vehicle_checks = { | |
| 'VTOL-Plane': ['ArduPlane/quadplane'], | |
| 'TradHeli': [ | |
| 'ArduCopter/heli.cpp', | |
| 'Tools/autotest/default_params/copter-heli', | |
| 'libraries/AP_Motors/AP_MotorsHeli', | |
| ], | |
| 'Sailboat': ['Rover/sailboat'], | |
| 'Sub': ['libraries/AP_Motors/AP_Motors6DOF'], | |
| }; | |
| const labels = new Set(); | |
| for (const filename of filenames) { | |
| // Generic checks | |
| for (const [suffix, label] of Object.entries(generic_suffix_labels)) { | |
| if (filename.endsWith(suffix)) { | |
| labels.add(label); | |
| break; | |
| } | |
| } | |
| for (const [prefix, label] of Object.entries(generic_prefix_labels)) { | |
| if (filename.startsWith(prefix)) { | |
| labels.add(label); | |
| break; | |
| } | |
| } | |
| // Specific/niche vehicle checks | |
| var specific = false; // There can only be one specific label per file. | |
| for (const [vehicle, prefixes] of Object.entries(specific_vehicle_checks)) { | |
| for (const prefix of prefixes) { | |
| if (filename.startsWith(prefix)) { | |
| labels.add(vehicle); | |
| specific = true; | |
| break; | |
| } | |
| } | |
| if (specific) break; | |
| } | |
| if (specific) continue; | |
| for (const [vehicle, label] of Object.entries(vehicle_sim_labels)) { | |
| if ( | |
| filename === `Tools/autotest/${vehicle.toLowerCase()}.py` || | |
| filename.startsWith(`Tools/autotest/default_params/${vehicle.toLowerCase()}`) || | |
| filename.startsWith(`libraries/SITL/SIM_${vehicle}`) | |
| ) { | |
| labels.add(label); | |
| specific = true; | |
| break; | |
| } | |
| } | |
| if (specific) continue; | |
| // General vehicle checks | |
| for (const [vehicle, label] of Object.entries(vehicle_labels)) { | |
| if ( | |
| filename.startsWith(vehicle) || | |
| filename.startsWith(`Tools/autotest/${vehicle}_Test`) || | |
| filename === `Tools/autotest/${vehicle.toLowerCase()}.py` | |
| ) { | |
| labels.add(label); | |
| break; | |
| } | |
| } | |
| } | |
| console.log(labels); | |
| if (labels.size > 0) { | |
| github.rest.issues.addLabels({ | |
| issue_number: prNumber, | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| labels: Array.from(labels), | |
| }); | |
| } |