Files
AdventureLog/.github/workflows/adventurelog-bot.yml

114 lines
3.7 KiB
YAML

name: AdventureLog Bot
on:
pull_request_target:
types: [opened, edited, synchronize]
jobs:
enforce-ready:
permissions:
contents: read
issues: write
pull-requests: write
runs-on: ubuntu-latest
steps:
- name: Validate linked issue
uses: actions/github-script@v7
with:
script: |
const pr = context.payload.pull_request;
const repoFullName = `${context.repo.owner}/${context.repo.repo}`;
async function safeCreateComment(body) {
try {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
body
});
return true;
} catch (error) {
if (error.status === 403) {
core.warning(`Unable to comment on PR #${pr.number}: ${error.message}`);
return false;
}
throw error;
}
}
async function safeClosePr() {
try {
await github.rest.pulls.update({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: pr.number,
state: "closed"
});
return true;
} catch (error) {
if (error.status === 403) {
core.warning(`Unable to close PR #${pr.number}: ${error.message}`);
return false;
}
throw error;
}
}
async function safeCommentAndClose(message) {
await safeCreateComment(message);
await safeClosePr();
}
// Ignore specific user
if (context.actor === "seanmorley15") {
console.log("Skipping maintainer PR");
return;
}
// Ignore PRs created before enforcement date
const cutoff = new Date("2026-03-16T00:00:00Z");
const created = new Date(pr.created_at);
if (created < cutoff) {
console.log("Skipping PR created before enforcement date");
return;
}
const body = pr.body || "";
const match = body.match(/\b(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?)\s+#(\d+)\b/i);
if (!match) {
await safeCommentAndClose("🤖 **AdventureLog Bot**\n\n🚫 This PR was automatically closed because it does not reference an issue.\n\nPlease link an issue using `Closes #issue-number`.");
return;
}
const issueNumber = Number(match[1]);
let issue;
try {
({ data: issue } = await github.rest.issues.get({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issueNumber
}));
} catch (error) {
if (error.status === 404) {
await safeCommentAndClose(`🤖 **AdventureLog Bot**\n\n🚫 This PR was automatically closed because the referenced issue #${issueNumber} was not found.\n\nPlease link a valid issue in this repository using \`Closes #issue-number\`.`);
return;
}
throw error;
}
const labels = issue.labels.map(l => l.name);
if (!labels.includes("ready") && !labels.includes("in progress")) {
await safeCommentAndClose("🤖 **AdventureLog Bot**\n\n🚫 This PR was automatically closed.\n\nPull requests may only be opened for issues labeled **ready**.");
}