mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 10:32:41 +00:00
b56e9438d1
## Description: ## Summary Adds a GitHub Action that auto-closes PRs which don't follow the contribution workflow, so maintainer review time goes to legitimate contributions instead of off-roadmap or AI-generated submissions. Triggered on `pull_request_target: [opened, reopened]` and **defaults to dry-run** so it's safe to merge before flipping live. ## Gate logic (first match wins) 1. **Maintainer bypass** — PR carries `bypass-pr-check` label → pass. 2. **Org/repo member** — `author_association` is `OWNER` / `MEMBER` / `COLLABORATOR` → pass. 3. **Approved work** — PR body links an issue (`Closes/Fixes/Resolves #N`) that carries the `approved` label and the PR author is in the issue's assignees → pass. 4. **Small fix** — `additions + deletions ≤ 50` → pass + apply `small-fix` label. 5. **Otherwise** — apply `auto-closed-needs-issue` label, post rejection comment, close. ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: evan
98 lines
2.7 KiB
TypeScript
98 lines
2.7 KiB
TypeScript
import { CLOSE_COMMENT, LABELS } from "./config";
|
|
import {
|
|
addLabel,
|
|
closePR,
|
|
ensureLabel,
|
|
getIssue,
|
|
getPR,
|
|
getPRFiles,
|
|
makeOctokit,
|
|
postComment,
|
|
} from "./github";
|
|
import { evaluate } from "./rules";
|
|
|
|
function parseArgs(argv: string[]): {
|
|
prNumber: number | null;
|
|
dryRunOverride: boolean | null;
|
|
} {
|
|
let prNumber: number | null = null;
|
|
let dryRunOverride: boolean | null = null;
|
|
for (let i = 0; i < argv.length; i++) {
|
|
const arg = argv[i];
|
|
if (arg === "--pr") {
|
|
const next = argv[i + 1];
|
|
if (next) prNumber = parseInt(next, 10);
|
|
i++;
|
|
} else if (arg === "--dry-run") {
|
|
dryRunOverride = true;
|
|
} else if (arg === "--no-dry-run") {
|
|
dryRunOverride = false;
|
|
}
|
|
}
|
|
return { prNumber, dryRunOverride };
|
|
}
|
|
|
|
async function main(): Promise<void> {
|
|
const args = parseArgs(process.argv.slice(2));
|
|
const cliMode = args.prNumber !== null;
|
|
|
|
const prNumber = args.prNumber ?? parseInt(process.env.PR_NUMBER ?? "", 10);
|
|
if (!prNumber || Number.isNaN(prNumber)) {
|
|
throw new Error(
|
|
"PR number missing — set PR_NUMBER env or pass --pr <number>",
|
|
);
|
|
}
|
|
|
|
const token = process.env.GITHUB_TOKEN;
|
|
if (!token) throw new Error("GITHUB_TOKEN env var is required");
|
|
|
|
// CLI mode: always dry-run unless --no-dry-run is explicitly passed.
|
|
// Workflow mode: read DRY_RUN env, default true.
|
|
const dryRun = cliMode
|
|
? args.dryRunOverride !== false
|
|
: (process.env.DRY_RUN ?? "true").toLowerCase() !== "false";
|
|
|
|
const octokit = makeOctokit(token);
|
|
|
|
const pr = await getPR(octokit, prNumber);
|
|
const files = await getPRFiles(octokit, prNumber);
|
|
|
|
const decision = await evaluate(pr, files, (n) => getIssue(octokit, n));
|
|
|
|
const prefix = `[pr-gate] PR #${prNumber}`;
|
|
if (decision.action === "pass") {
|
|
const labelNote = decision.labelToAdd
|
|
? ` — label: ${decision.labelToAdd}`
|
|
: "";
|
|
console.log(
|
|
`${prefix} → decision: pass — reason: ${decision.reason}${labelNote}`,
|
|
);
|
|
} else {
|
|
console.log(`${prefix} → decision: close — reason: ${decision.reason}`);
|
|
}
|
|
|
|
if (dryRun) {
|
|
console.log(`${prefix} → DRY_RUN=true, no action taken`);
|
|
return;
|
|
}
|
|
|
|
if (decision.action === "pass") {
|
|
if (decision.labelToAdd) {
|
|
await ensureLabel(octokit, decision.labelToAdd);
|
|
await addLabel(octokit, prNumber, decision.labelToAdd);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Comment must land on an open PR — post before closing.
|
|
await ensureLabel(octokit, LABELS.AUTO_CLOSED);
|
|
await addLabel(octokit, prNumber, LABELS.AUTO_CLOSED);
|
|
await postComment(octokit, prNumber, CLOSE_COMMENT(pr.user.login));
|
|
await closePR(octokit, prNumber);
|
|
}
|
|
|
|
main().catch((err) => {
|
|
console.error("[pr-gate] Unexpected error:", err);
|
|
process.exit(1);
|
|
});
|