mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-19 13:36:48 +00:00
1aa1aea57e
Dependabot cannot be pointed at dev. \ arget-branch\ in dependabot.yml applies to version updates only, and security updates always open against the default branch. Security updates are all this repository gets, since there is no dependabot.yml at all, so every one of them arrives on main. The branch check then labelled them wrong-base-branch and close-stale-wrong-branch shut them 24 hours later. Thirteen of the seventeen Dependabot pull requests died that way; the four that survived were retargeted by hand. The comment the check leaves asks the author to click Edit, which a bot cannot do. So retarget them instead. If that fails, because the branch was cut from main and dev has moved on, it falls through to the existing path so a human sees it rather than the check quietly passing. Note this only takes effect once it is on main: pull_request_target loads the workflow from the pull request's base branch, and these arrive on main.
171 lines
6.6 KiB
YAML
171 lines
6.6 KiB
YAML
name: Enforce PR Target Branch
|
|
|
|
on:
|
|
pull_request_target:
|
|
types: [opened, reopened, edited, synchronize]
|
|
|
|
jobs:
|
|
check-target:
|
|
if: github.repository == 'liketrek/TREK'
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
pull-requests: write
|
|
issues: write
|
|
contents: read
|
|
|
|
steps:
|
|
- name: Flag or clear wrong base branch
|
|
uses: actions/github-script@v7
|
|
with:
|
|
script: |
|
|
const base = context.payload.pull_request.base.ref;
|
|
const labels = context.payload.pull_request.labels.map(l => l.name);
|
|
const prNumber = context.payload.pull_request.number;
|
|
|
|
// bypass-branch-check label skips all enforcement
|
|
if (labels.includes('bypass-branch-check')) {
|
|
console.log('bypass-branch-check label present, skipping enforcement.');
|
|
return;
|
|
}
|
|
|
|
// Wiki-only PRs are exempt from branch enforcement
|
|
const files = [];
|
|
for (let page = 1; ; page++) {
|
|
const { data } = await github.rest.pulls.listFiles({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
pull_number: prNumber,
|
|
per_page: 100,
|
|
page,
|
|
});
|
|
files.push(...data);
|
|
if (data.length < 100) break;
|
|
}
|
|
const allWiki = files.length > 0 && files.every(f => f.filename.startsWith('wiki/'));
|
|
if (allWiki) {
|
|
console.log('All changed files are under wiki/ — skipping enforcement.');
|
|
if (labels.includes('wrong-base-branch')) {
|
|
await github.rest.issues.removeLabel({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
issue_number: prNumber,
|
|
name: 'wrong-base-branch',
|
|
});
|
|
}
|
|
return;
|
|
}
|
|
|
|
// If the base was fixed, remove the label and let it through
|
|
if (base !== 'main') {
|
|
if (labels.includes('wrong-base-branch')) {
|
|
await github.rest.issues.removeLabel({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
issue_number: prNumber,
|
|
name: 'wrong-base-branch',
|
|
});
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Dependabot cannot be pointed at dev. `target-branch` in dependabot.yml
|
|
// applies to version updates only; security updates always open against
|
|
// the default branch, and security updates are all this repository gets
|
|
// (there is no dependabot.yml). Labelling them meant close-stale-wrong-branch
|
|
// shut them 24 hours later, which is how 13 of them died. Retarget instead:
|
|
// a bot cannot click "Edit" the way the comment below asks a human to.
|
|
if (context.payload.pull_request.user.login === 'dependabot[bot]') {
|
|
try {
|
|
await github.rest.pulls.update({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
pull_number: prNumber,
|
|
base: 'dev',
|
|
});
|
|
if (labels.includes('wrong-base-branch')) {
|
|
await github.rest.issues.removeLabel({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
issue_number: prNumber,
|
|
name: 'wrong-base-branch',
|
|
});
|
|
}
|
|
console.log('Retargeted the Dependabot PR onto dev.');
|
|
return;
|
|
} catch (err) {
|
|
// Retargeting can fail when the branch was cut from main and dev has
|
|
// moved on. Fall through to the normal path so a human sees it rather
|
|
// than the check passing on a PR that is still aimed at main.
|
|
console.log(`Could not retarget the Dependabot PR: ${err.message}`);
|
|
}
|
|
}
|
|
|
|
// Base is main — check if this user is a maintainer
|
|
let permission = 'none';
|
|
try {
|
|
const { data } = await github.rest.repos.getCollaboratorPermissionLevel({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
username: context.payload.pull_request.user.login,
|
|
});
|
|
permission = data.permission;
|
|
} catch (_) {
|
|
// User is not a collaborator — treat as 'none'
|
|
}
|
|
|
|
if (['admin', 'write'].includes(permission)) {
|
|
console.log(`User has '${permission}' permission, skipping.`);
|
|
return;
|
|
}
|
|
|
|
// Already labeled — avoid spamming on every push
|
|
if (labels.includes('wrong-base-branch')) {
|
|
core.setFailed("PR must target `dev`, not `main`.");
|
|
return;
|
|
}
|
|
|
|
// Ensure the label exists
|
|
try {
|
|
await github.rest.issues.getLabel({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
name: 'wrong-base-branch',
|
|
});
|
|
} catch (err) {
|
|
if (err.status === 404) {
|
|
await github.rest.issues.createLabel({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
name: 'wrong-base-branch',
|
|
color: 'd73a4a',
|
|
description: 'PR is targeting the wrong base branch',
|
|
});
|
|
}
|
|
}
|
|
|
|
await github.rest.issues.addLabels({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
issue_number: prNumber,
|
|
labels: ['wrong-base-branch'],
|
|
});
|
|
|
|
await github.rest.issues.createComment({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
issue_number: prNumber,
|
|
body: [
|
|
'## Wrong target branch',
|
|
'',
|
|
'This PR targets `main`, but contributions must go through `dev` first.',
|
|
'',
|
|
'To fix this, click **Edit** next to the PR title and change the base branch to `dev`.',
|
|
'',
|
|
'**This PR will be automatically closed in 24 hours if the base branch has not been updated.**',
|
|
'',
|
|
'> _If you need to merge directly to `main`, contact a maintainer._',
|
|
].join('\n'),
|
|
});
|
|
|
|
core.setFailed("PR must target `dev`, not `main`.");
|