Ramon Roche 8c4b703103 ci(pr-comment-poster): add generic PR comment poster and migrate producers
Adds a stand-alone workflow that posts or updates sticky PR comments on
behalf of any analysis workflow, including those triggered by fork PRs.
The poster runs on `workflow_run` in the base repo context, which is the
standard GitHub-sanctioned way to get a write token on events that
originate from untrusted forks without ever checking out fork code.

All validation, GitHub API interaction, and upsert logic lives in
Tools/ci/pr-comment-poster.py (Python 3 stdlib only, two subcommands:
`validate` and `post`). The workflow file itself is a thin orchestrator:
sparse-checkout the script, download the pr-comment artifact via
github-script, unzip, then invoke the script twice. No inline jq, no
inline bash validation, no shell-interpolated marker strings. The
sparse-checkout ensures only Tools/ci/pr-comment-poster.py lands in the
workspace, never the rest of the repo.

Artifact contract: a producer uploads an artifact named exactly
`pr-comment` containing `manifest.json` (with `pr_number`, `marker`, and
optional `mode`) and `body.md`. The script validates the manifest
(positive integer pr_number, printable-ASCII marker bounded 1..200
chars, UTF-8 body under 60000 bytes, mode in an allowlist), finds any
existing comment containing the marker via the comments REST API, and
either edits it in place or creates a new one.

The workflow file header documents six security invariants that any
future change MUST preserve, most importantly: NEVER check out PR code,
NEVER execute anything from the artifact, and treat all artifact
contents as opaque data.

Why a generic poster and not `pull_request_target`: `pull_request_target`
is the tool people reach for first and the one that most often turns
into a supply-chain vulnerability, because it hands a write token to a
workflow that is then tempted to check out the PR head. `workflow_run`
gives the same write token without any check-out temptation, because
the only input is a pre-produced artifact treated as opaque data.

Producer migrations
===================

flash_analysis.yml:
- Drop the fork gate on the `post_pr_comment` job.
- Drop the obsolete TODO pointing at issue #24408 (the fork-comment
  workflow does not error anymore; it just no-ops).
- Keep the existing "comment only if threshold crossed or previous
  comment exists" behaviour verbatim. peter-evans/find-comment@v3
  stays as a read-only probe (forks can read issue comments just fine);
  its body-includes is updated to search for the new marker
  `<!-- pr-comment-poster:flash-analysis -->` instead of the old
  "FLASH Analysis" heading substring.
- Replace the peter-evans/create-or-update-comment@v4 step with two
  new steps that write pr-comment/manifest.json and pr-comment/body.md
  and then upload them as artifact pr-comment. The body markdown is
  byte-for-byte identical to the previous heredoc, with the marker
  prepended as the first line so subsequent runs can find it.
- The threshold-or-existing-comment gate is preserved on both new
  steps. When the gate does not fire no artifact is uploaded and the
  poster no-ops.

docs-orchestrator.yml (link-check job):
- Drop the fork gate on the sticky-comment step.
- Replace marocchino/sticky-pull-request-comment@v2 with two new steps
  that copy logs/filtered-link-check-results.md into pr-comment/body.md,
  write a pr-comment/manifest.json with the marker
  `<!-- pr-comment-poster:docs-link-check -->`, and upload the directory
  as artifact pr-comment.
- The prepare step checks `test -s` on the results file and emits a
  prepared step output; the upload step is gated on that output. In
  practice the existing link-check step always writes a placeholder
  ("No broken links found in changed files.") into the file when empty,
  so the guard is defensive but not load-bearing today.
- Tighten the link-check job's permissions from `pull-requests: write`
  down to `contents: read`; writing PR comments now happens in the
  poster workflow.

The poster's workflows allowlist is seeded with the two active
producers: "FLASH usage analysis" and "Docs - Orchestrator".
clang-tidy (workflow name "Static Analysis") is not in the list because
platisd/clang-tidy-pr-comments posts line-level review comments, a
different REST API from issue comments that the poster script does not
handle. Extending the poster to cover review comments is a follow-up.

Signed-off-by: Ramon Roche <mrpollo@gmail.com>
2026-04-08 23:49:56 -06:00
..
2025-07-02 14:43:55 -07:00