name: Close Unauthorized New Script PRs on: pull_request_target: branches: ["main"] types: [opened, labeled, reopened, synchronize] jobs: check-new-script: if: github.repository == 'community-scripts/ProxmoxVE' runs-on: self-hosted permissions: pull-requests: write contents: read steps: - name: Close PR if unauthorized new script submission uses: actions/github-script@v7 with: script: | const pr = context.payload.pull_request; const prNumber = pr.number; const author = pr.user.login; const authorType = pr.user.type; // "User" or "Bot" const owner = context.repo.owner; const repo = context.repo.repo; // --- Allow our bots --- const allowedBots = [ "push-app-to-main[bot]", "push-app-to-main", ]; if (allowedBots.includes(author)) { core.info(`PR #${prNumber} by allowed bot "${author}" — skipping.`); return; } // --- Exempt contributors via author_association --- // OWNER/MEMBER/COLLABORATOR are trusted; CONTRIBUTOR ("has merged before") // and NONE are not — their new-script PRs are still closed. const association = pr.author_association; const exempt = ["OWNER", "MEMBER", "COLLABORATOR"]; if (exempt.includes(association)) { core.info(`PR #${prNumber} by ${association} "${author}" — skipping.`); return; } // --- Detect a new-script PR: "new script" label OR a newly-added // script file under ct/ install/ turnkey/ vm/ (mirrors // autolabeler-config.json). Removes the label-timing dependency. --- const labels = pr.labels.map(l => l.name); const hasNewScriptLabel = labels.includes("new script"); const scriptPrefixes = ["ct/", "install/", "turnkey/", "vm/"]; let hasAddedScriptFile = false; try { const files = await github.paginate(github.rest.pulls.listFiles, { owner, repo, pull_number: prNumber, per_page: 100, }); hasAddedScriptFile = files.some( f => f.status === "added" && scriptPrefixes.some(p => f.filename.startsWith(p)) ); } catch (error) { core.warning(`Could not list files for PR #${prNumber}: ${error.message}`); } if (!hasNewScriptLabel && !hasAddedScriptFile) { core.info(`PR #${prNumber} is not a new-script submission (no label, no added script file) — skipping.`); return; } // --- Unauthorized: close the PR with a comment --- core.info(`Closing PR #${prNumber} by "${author}" — not a contributor or allowed bot.`); const comment = [ `👋 Hi @${author},`, ``, `Thank you for your interest in contributing a new script!`, ``, `However, **new scripts must first be submitted to our development repository** for testing and review before they can be merged here.`, ``, `> 🛑 New scripts must be submitted to [**ProxmoxVED**](https://github.com/community-scripts/ProxmoxVED) for testing.`, `> PRs without prior testing will be closed.`, ``, `Please open your PR at **https://github.com/community-scripts/ProxmoxVED** instead.`, `Once your script has been tested and approved there, it will be pushed to this repository automatically.`, ``, `This PR will now be closed. Thank you for understanding! 🙏`, ].join("\n"); await github.rest.issues.createComment({ owner, repo, issue_number: prNumber, body: comment, }); await github.rest.pulls.update({ owner, repo, pull_number: prNumber, state: "closed", }); // Add a label to indicate why it was closed await github.rest.issues.addLabels({ owner, repo, issue_number: prNumber, labels: ["not a script issue"], });