mirror of
https://github.com/community-scripts/ProxmoxVE.git
synced 2026-11-05 17:12:37 +01:00
bffc871aa9
close-invalid-pr-template.yml now comments and labels (missing pr template, stale) instead of closing immediately, clears itself once fixed. stale_pr_close.yml gains the engagement-gated/keep-open/maintainer-reset behavior already used in DevScripts, so a template-stale PR only closes via its own re-check. request-script.yml discussion template gets an optional Justification field; a new workflow verifies stars/age against GitHub for the submitted repo and either does nothing (eligible), labels investigation (ineligible + justified), or closes the discussion (ineligible, no justification). Moved the AI Assistance section to the end of pull_request_template.md.
134 lines
6.3 KiB
YAML
Generated
134 lines
6.3 KiB
YAML
Generated
name: Stale PR Management
|
|
on:
|
|
schedule:
|
|
- cron: "0 0 * * *"
|
|
workflow_dispatch:
|
|
pull_request_target:
|
|
types:
|
|
- labeled
|
|
|
|
jobs:
|
|
stale-prs:
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
pull-requests: write
|
|
issues: write
|
|
contents: read
|
|
steps:
|
|
- name: Handle stale PRs
|
|
uses: actions/github-script@v9
|
|
with:
|
|
script: |
|
|
const now = new Date();
|
|
const owner = context.repo.owner;
|
|
const repo = context.repo.repo;
|
|
const TEMPLATE_LABEL = "missing pr template";
|
|
const MAINTAINERS = ["OWNER", "MEMBER", "COLLABORATOR"];
|
|
const days = d => (now - d) / (1000 * 60 * 60 * 24);
|
|
const staleNotice = author =>
|
|
`@${author} This PR has been marked as stale. It will be closed if no new commits are added in 7 days.`;
|
|
|
|
// Only fires for a maintainer's label: labels set with GITHUB_TOKEN raise no event.
|
|
if (context.eventName === "pull_request_target" && context.payload.action === "labeled") {
|
|
const pr = context.payload.pull_request;
|
|
const labels = (pr.labels || []).map(l => l.name);
|
|
if (context.payload.label?.name === "stale" && !labels.includes(TEMPLATE_LABEL)) {
|
|
await github.rest.issues.createComment({
|
|
owner, repo, issue_number: pr.number, body: staleNotice(pr.user.login)
|
|
});
|
|
}
|
|
return;
|
|
}
|
|
|
|
const prs = await github.paginate(github.rest.pulls.list, { owner, repo, state: "open", per_page: 100 });
|
|
|
|
for (const pr of prs) {
|
|
const labels = pr.labels.map(l => l.name);
|
|
const author = pr.user.login;
|
|
const hasStale = labels.includes("stale");
|
|
|
|
if (labels.includes("keep-open")) {
|
|
if (hasStale) {
|
|
await github.rest.issues.removeLabel({ owner, repo, issue_number: pr.number, name: "stale" });
|
|
}
|
|
continue;
|
|
}
|
|
|
|
const comments = await github.paginate(github.rest.issues.listComments, {
|
|
owner, repo, issue_number: pr.number, per_page: 100
|
|
});
|
|
const commits = await github.paginate(github.rest.pulls.listCommits, {
|
|
owner, repo, pull_number: pr.number, per_page: 100
|
|
});
|
|
const lastCommitDate = commits.length > 0
|
|
? new Date(commits[commits.length - 1].commit.author.date)
|
|
: new Date(pr.created_at);
|
|
const humanComments = comments.filter(c => c.user?.type !== "Bot");
|
|
|
|
// -------------------------------------------------------
|
|
// Auto-label PRs with no activity in the last 14 days,
|
|
// but ONLY if someone other than the author has engaged.
|
|
// -------------------------------------------------------
|
|
if (!hasStale) {
|
|
const reviews = await github.paginate(github.rest.pulls.listReviews, {
|
|
owner, repo, pull_number: pr.number, per_page: 100
|
|
});
|
|
const engaged =
|
|
reviews.some(r => r.user?.type !== "Bot" && r.user?.login !== author) ||
|
|
humanComments.some(c => c.user?.login !== author);
|
|
if (!engaged) continue;
|
|
|
|
const lastCommentDate = humanComments.length > 0
|
|
? new Date(humanComments[humanComments.length - 1].created_at)
|
|
: null;
|
|
const lastActivityDate = lastCommentDate && lastCommentDate > lastCommitDate
|
|
? lastCommentDate
|
|
: lastCommitDate;
|
|
|
|
if (days(lastActivityDate) > 14) {
|
|
await github.rest.issues.addLabels({ owner, repo, issue_number: pr.number, labels: ["stale"] });
|
|
await github.rest.issues.createComment({ owner, repo, issue_number: pr.number, body: staleNotice(author) });
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// -------------------------------------------------------
|
|
// Manage already-stale PRs
|
|
// -------------------------------------------------------
|
|
const events = await github.paginate(github.rest.issues.listEvents, {
|
|
owner, repo, issue_number: pr.number, per_page: 100
|
|
});
|
|
const staleDates = events
|
|
.filter(e => e.event === "labeled" && e.label?.name === "stale")
|
|
.map(e => new Date(e.created_at));
|
|
if (staleDates.length === 0) continue;
|
|
const staleLabelDate = new Date(Math.max(...staleDates));
|
|
const isTemplateIssue = labels.includes(TEMPLATE_LABEL);
|
|
|
|
// A push ends an inactivity mark; a template mark only ends via close-invalid-pr-template.yml's re-check.
|
|
if (!isTemplateIssue && lastCommitDate > staleLabelDate) {
|
|
await github.rest.issues.removeLabel({ owner, repo, issue_number: pr.number, name: "stale" });
|
|
await github.rest.issues.createComment({
|
|
owner, repo, issue_number: pr.number,
|
|
body: `@${author} Recent activity detected. Removing stale label.`
|
|
});
|
|
continue;
|
|
}
|
|
|
|
// A maintainer comment after the mark restarts the 7 days.
|
|
const maintainerDates = humanComments
|
|
.filter(c => MAINTAINERS.includes(c.author_association) && c.user?.login !== author)
|
|
.map(c => new Date(c.created_at))
|
|
.filter(d => d > staleLabelDate);
|
|
const clockStart = maintainerDates.length > 0 ? new Date(Math.max(...maintainerDates)) : staleLabelDate;
|
|
if (days(clockStart) <= 7) continue;
|
|
|
|
await github.rest.issues.createComment({
|
|
owner, repo, issue_number: pr.number,
|
|
body: isTemplateIssue
|
|
? `@${author} Closing: the PR template requirements listed above were still missing 7 days later. Once they are in place, please open a new pull request.`
|
|
: `@${author} Closing stale PR due to inactivity (no commits for 7 days after stale label).`
|
|
});
|
|
await github.rest.pulls.update({ owner, repo, pull_number: pr.number, state: "closed" });
|
|
}
|