mirror of
https://github.com/Lissy93/awesome-privacy.git
synced 2026-03-11 08:55:33 +00:00
Update existing comment once user fixes issues
This commit is contained in:
parent
3baabcafe5
commit
259314537a
3 changed files with 207 additions and 22 deletions
84
.github/workflows/pr-comment.yml
vendored
84
.github/workflows/pr-comment.yml
vendored
|
|
@ -7,6 +7,7 @@ on:
|
|||
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
|
|
@ -16,6 +17,8 @@ jobs:
|
|||
if: github.event.workflow_run.event == 'pull_request'
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Download PR metadata
|
||||
id: download
|
||||
continue-on-error: true
|
||||
|
|
@ -26,20 +29,13 @@ jobs:
|
|||
run-id: ${{ github.event.workflow_run.id }}
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Post comment
|
||||
- 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');
|
||||
const marker = '<!-- pr-check-bot -->';
|
||||
|
||||
// Check if there are findings to post
|
||||
const commentFile = 'pr-meta/comment.md';
|
||||
if (!fs.existsSync(commentFile)) {
|
||||
console.log('No findings to post — skipping.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine the PR number
|
||||
let prNumber;
|
||||
|
|
@ -48,8 +44,6 @@ jobs:
|
|||
prNumber = parseInt(fs.readFileSync(numberFile, 'utf8').trim());
|
||||
}
|
||||
if (!prNumber) {
|
||||
// workflow_run.pull_requests is empty for fork PRs, so
|
||||
// fall back to searching by head SHA if needed
|
||||
const prs = context.payload.workflow_run.pull_requests;
|
||||
if (prs && prs.length > 0) {
|
||||
prNumber = prs[0].number;
|
||||
|
|
@ -72,23 +66,69 @@ jobs:
|
|||
}
|
||||
}
|
||||
|
||||
// Skip if we already commented on this PR
|
||||
// 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,
|
||||
});
|
||||
if (comments.some(c => c.body.includes(marker))) {
|
||||
console.log('Bot comment already exists — skipping.');
|
||||
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;
|
||||
}
|
||||
|
||||
// Post the comment
|
||||
const body = fs.readFileSync(commentFile, 'utf8').trim();
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
body,
|
||||
});
|
||||
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.');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -108,6 +108,8 @@ def main():
|
|||
f.write(run_id)
|
||||
|
||||
findings = collect_findings()
|
||||
with open(os.path.join(OUTPUT_DIR, "findings-count.txt"), "w") as f:
|
||||
f.write(str(len(findings)))
|
||||
changes_summary = load_diff_summary()
|
||||
write_step_summary(findings)
|
||||
|
||||
|
|
|
|||
143
lib/checks/prepare-comment.py
Normal file
143
lib/checks/prepare-comment.py
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
"""Decides whether to create, update, or skip the PR bot comment.
|
||||
|
||||
Reads:
|
||||
pr-meta/comment.md — new comment from format-comment.py
|
||||
pr-meta/findings-count.txt — number of findings (from format-comment.py)
|
||||
pr-meta/existing-comment.md — current bot comment on the PR (from workflow)
|
||||
|
||||
Writes:
|
||||
pr-meta/action.txt — "create", "update", or "skip"
|
||||
pr-meta/final-comment.md — the body to post or update with
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
|
||||
WORK_DIR = "pr-meta"
|
||||
|
||||
|
||||
def read_file(path):
|
||||
"""Read a file and return its stripped content, or None if missing/empty."""
|
||||
try:
|
||||
with open(path) as f:
|
||||
content = f.read().strip()
|
||||
return content if content else None
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def read_findings_count(new_body):
|
||||
"""Return the findings count from findings-count.txt, or by counting bullets."""
|
||||
raw = read_file(os.path.join(WORK_DIR, "findings-count.txt"))
|
||||
if raw is not None:
|
||||
try:
|
||||
return int(raw)
|
||||
except ValueError:
|
||||
pass
|
||||
# Fallback: count bullet lines before <details> (diff summary has its own bullets)
|
||||
body_before_details = new_body.split("<details>")[0]
|
||||
return len(re.findall(r"^- .+$", body_before_details, re.MULTILINE))
|
||||
|
||||
|
||||
def _was_already_passing(existing_body):
|
||||
"""Check if the most recent state in the comment is already all-clear."""
|
||||
# If there's a previous "all passing" edit, the last state was passing
|
||||
if re.search(r"^Edit(?: \d+)?: All checks are (now passing|passing now)", existing_body, re.MULTILINE):
|
||||
return True
|
||||
# If there are no edits at all, check the original comment body
|
||||
if not re.search(r"^Edit(?: \d+)?:", existing_body, re.MULTILINE):
|
||||
return "All our automated checks have passed" in existing_body
|
||||
return False
|
||||
|
||||
|
||||
def _previous_failing_count(existing_body):
|
||||
"""Extract the findings count from the most recent state in the comment."""
|
||||
# Check edit lines first (most recent state)
|
||||
matches = re.findall(r"^Edit(?: \d+)?: (\d+) checks? (?:is|are) still failing", existing_body, re.MULTILINE)
|
||||
if matches:
|
||||
return int(matches[-1])
|
||||
# No edits — count bullets in the original comment (before <details>)
|
||||
if not re.search(r"^Edit(?: \d+)?:", existing_body, re.MULTILINE):
|
||||
body_before_details = existing_body.split("<details>")[0]
|
||||
bullets = re.findall(r"^- .+$", body_before_details, re.MULTILINE)
|
||||
return len(bullets) if bullets else None
|
||||
return None
|
||||
|
||||
|
||||
def build_edit_line(existing_body, findings_count, check_run_id, repo):
|
||||
"""Build the edit line to append, or None if nothing to do."""
|
||||
run_tag = f"<!-- run:{check_run_id} -->"
|
||||
|
||||
# Idempotency: this run was already processed
|
||||
if run_tag in existing_body:
|
||||
return None
|
||||
|
||||
# Skip if the state hasn't changed
|
||||
if findings_count == 0 and _was_already_passing(existing_body):
|
||||
return None
|
||||
if findings_count > 0 and _previous_failing_count(existing_body) == findings_count:
|
||||
return None
|
||||
|
||||
# Count previous edits to determine the next number
|
||||
edits = re.findall(r"^Edit(?: \d+)?:", existing_body, re.MULTILINE)
|
||||
edit_count = len(edits)
|
||||
next_edit = edit_count + 1
|
||||
|
||||
run_url = f"https://github.com/{repo}/actions/runs/{check_run_id}"
|
||||
|
||||
if findings_count == 0:
|
||||
if edit_count == 0:
|
||||
return f"Edit: All checks are now passing \U0001f389 {run_tag}"
|
||||
return f"Edit {next_edit}: All checks are passing now \u2705 {run_tag}"
|
||||
|
||||
verb = "check is" if findings_count == 1 else "checks are"
|
||||
return (
|
||||
f"Edit {next_edit}: {findings_count} {verb} still failing, "
|
||||
f"see [here]({run_url}) for details {run_tag}"
|
||||
)
|
||||
|
||||
|
||||
def write_output(action, body=""):
|
||||
"""Write action.txt and (optionally) final-comment.md."""
|
||||
os.makedirs(WORK_DIR, exist_ok=True)
|
||||
with open(os.path.join(WORK_DIR, "action.txt"), "w") as f:
|
||||
f.write(action)
|
||||
if body:
|
||||
with open(os.path.join(WORK_DIR, "final-comment.md"), "w") as f:
|
||||
f.write(body)
|
||||
|
||||
|
||||
def main():
|
||||
check_run_id = os.environ.get("CHECK_RUN_ID", "")
|
||||
repo = os.environ.get("GITHUB_REPOSITORY", "")
|
||||
|
||||
new_body = read_file(os.path.join(WORK_DIR, "comment.md"))
|
||||
if not new_body:
|
||||
write_output("skip")
|
||||
return
|
||||
|
||||
existing_body = read_file(os.path.join(WORK_DIR, "existing-comment.md"))
|
||||
|
||||
# No existing comment — create a new one
|
||||
if not existing_body:
|
||||
write_output("create", new_body)
|
||||
return
|
||||
|
||||
# Existing comment — build an edit line to append
|
||||
if not check_run_id:
|
||||
write_output("skip")
|
||||
return
|
||||
|
||||
findings_count = read_findings_count(new_body)
|
||||
edit_line = build_edit_line(existing_body, findings_count, check_run_id, repo)
|
||||
|
||||
if not edit_line:
|
||||
write_output("skip")
|
||||
return
|
||||
|
||||
updated = existing_body.rstrip() + "\n\n" + edit_line
|
||||
write_output("update", updated)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Reference in a new issue