awesome-privacy/.github/workflows/pr-comment.yml
Alicia Sykes f95a41c08c
Some checks are pending
📚 Inserts Awesome Privacy into README / build (push) Waiting to run
Add workflow to notify maintainer (me) when pr needs reivew
2026-02-27 23:33:04 +00:00

221 lines
7.8 KiB
YAML

name: PR Comment
on:
workflow_run:
workflows: ["PR Check"]
types: [completed]
pull_request_review:
types: [submitted]
permissions:
actions: read
checks: read
contents: read
pull-requests: write
jobs:
comment:
name: Post PR comment
runs-on: ubuntu-latest
if: github.event.workflow_run.event == 'pull_request'
steps:
- uses: actions/checkout@v4
- name: Download PR metadata
id: download
continue-on-error: true
uses: actions/download-artifact@v4
with:
name: pr-meta
path: pr-meta
run-id: ${{ github.event.workflow_run.id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Resolve PR context
id: context
uses: actions/github-script@v7
with:
github-token: ${{ secrets.BOT_TOKEN || secrets.GITHUB_TOKEN }}
script: |
const fs = require('fs');
// Determine the PR number
let prNumber;
const numberFile = 'pr-meta/number.txt';
if (fs.existsSync(numberFile)) {
prNumber = parseInt(fs.readFileSync(numberFile, 'utf8').trim());
}
if (!prNumber) {
const prs = context.payload.workflow_run.pull_requests;
if (prs && prs.length > 0) {
prNumber = prs[0].number;
} else {
const headSha = context.payload.workflow_run.head_sha;
const { data: prList } = await github.rest.pulls.list({
owner: context.repo.owner,
repo: context.repo.repo,
state: 'open',
sort: 'updated',
direction: 'desc',
per_page: 100,
});
const match = prList.find(pr => pr.head.sha === headSha);
if (!match) {
console.log(`No open PR found for SHA ${headSha} — skipping.`);
return;
}
prNumber = match.number;
}
}
// Fetch existing bot comment (if any) and write to file for Python
const marker = '<!-- pr-check-bot -->';
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
per_page: 100,
});
const existing = comments.find(c => c.body.includes(marker));
if (existing) {
fs.mkdirSync('pr-meta', { recursive: true });
fs.writeFileSync('pr-meta/existing-comment.md', existing.body);
fs.writeFileSync('pr-meta/existing-comment-id.txt', String(existing.id));
}
core.setOutput('pr_number', prNumber);
- name: Prepare comment
if: steps.context.outputs.pr_number
env:
CHECK_RUN_ID: ${{ github.event.workflow_run.id }}
GITHUB_REPOSITORY: ${{ github.repository }}
run: python lib/checks/prepare-comment.py
- name: Post or update comment
if: steps.context.outputs.pr_number
uses: actions/github-script@v7
with:
github-token: ${{ secrets.BOT_TOKEN || secrets.GITHUB_TOKEN }}
script: |
const fs = require('fs');
const actionFile = 'pr-meta/action.txt';
if (!fs.existsSync(actionFile)) return;
const action = fs.readFileSync(actionFile, 'utf8').trim();
if (action === 'skip') {
console.log('Nothing to do — skipping.');
return;
}
const bodyFile = 'pr-meta/final-comment.md';
if (!fs.existsSync(bodyFile)) {
console.log('No comment body found — skipping.');
return;
}
const body = fs.readFileSync(bodyFile, 'utf8').trim();
const prNumber = parseInt('${{ steps.context.outputs.pr_number }}');
if (action === 'create') {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
body,
});
console.log('Created bot comment.');
} else if (action === 'update') {
const commentId = parseInt(fs.readFileSync('pr-meta/existing-comment-id.txt', 'utf8').trim());
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: commentId,
body,
});
console.log('Updated bot comment.');
}
review-notify:
name: Notify maintainer
runs-on: ubuntu-latest
if: github.event_name == 'pull_request_review'
steps:
- uses: actions/checkout@v4
- name: Fetch review and CI context
id: context
uses: actions/github-script@v7
with:
github-token: ${{ secrets.BOT_TOKEN || secrets.GITHUB_TOKEN }}
script: |
const fs = require('fs');
const pr = context.payload.pull_request;
const { owner, repo } = context.repo;
// Fetch reviews — extract user login and state
const { data: reviews } = await github.rest.pulls.listReviews({
owner, repo, pull_number: pr.number, per_page: 100,
});
const reviewData = reviews.map(r => ({
user: r.user.login,
state: r.state,
}));
// Fetch check runs for the PR head SHA
const { data: { check_runs } } = await github.rest.checks.listForRef({
owner, repo, ref: pr.head.sha, per_page: 100,
});
const checkData = check_runs.map(cr => ({
status: cr.status,
conclusion: cr.conclusion,
}));
// Check if we already posted the notification
const marker = '<!-- pr-review-ready -->';
const { data: comments } = await github.rest.issues.listComments({
owner, repo, issue_number: pr.number, per_page: 100,
});
const alreadyNotified = comments.some(c => c.body.includes(marker));
// Write data for Python
fs.mkdirSync('pr-meta', { recursive: true });
fs.writeFileSync('pr-meta/reviews.json', JSON.stringify(reviewData));
fs.writeFileSync('pr-meta/check-runs.json', JSON.stringify(checkData));
fs.writeFileSync('pr-meta/already-notified.txt', String(alreadyNotified));
core.setOutput('pr_number', pr.number);
- name: Check review readiness
run: python lib/checks/check-review-ready.py
- name: Post notification
uses: actions/github-script@v7
with:
github-token: ${{ secrets.BOT_TOKEN || secrets.GITHUB_TOKEN }}
script: |
const fs = require('fs');
const actionFile = 'pr-meta/action.txt';
if (!fs.existsSync(actionFile)) return;
const action = fs.readFileSync(actionFile, 'utf8').trim();
if (action !== 'notify') {
console.log('Not ready for review — skipping.');
return;
}
const body = [
'<!-- pr-review-ready -->',
'This PR is now ready to be merged, pending maintainer review. All checks are passing and it has been peer-reviewed.',
'',
'@Lissy93 - Please evaluate, and either merge or leave feedback.',
].join('\n');
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: parseInt('${{ steps.context.outputs.pr_number }}'),
body,
});
console.log('Posted maintainer notification.');