feat(ci): trigger pr-review on pull_request events instead of hourly cron #362

Merged
k.renner merged 7 commits from claude-bot/pr-review-on-pr-events into master 2026-07-23 10:45:30 +00:00
Member

Switches the PR review bot from a homelab-hosted hourly cron that polls four repos over the Gitea API to a per-repo, event-driven pipeline that reviews only the PR that triggered it.

What changes (this diff — homelab only)

  • when: → event: pull_request (Woodpecker raises this on PR open AND push to an open PR) + event: manual for on-demand re-review (--var PR=<n>).
  • Reviews only the triggering PR, from CI_REPO + CI_COMMIT_PULL_REQUEST. Drops the cross-repo discovery loop and MAX_PRS cap.
  • Keeps the pr-review:sha= dedup marker (now guards pipeline retries), Haiku triage → model pick → review → Gitea post, never-approve posture.
  • Resolves the agent prompts from the local checkout or a shallow homelab clone, so the identical file drops into every reviewed repo.

Rollout — out of band, NOT in this diff (order matters)

  • Grant the pull_request event to claude_code_oauth_token + review_bot_token on every reviewed repo — else Woodpecker withholds them and every PR pipeline fails auth. Do this before merging.
  • homelab --require-approval forks (public repo — fork PRs can't reach the tokens without a maintainer approval click).
  • Deploy this same file into shelly-manager, authentik-samba-bridge (folder-converted), homeassistant; register homeassistant in Woodpecker.
  • Delete the pr-review cron (id 6) last, once all four PR triggers are verified live.
Switches the PR review bot from a homelab-hosted **hourly cron** that polls four repos over the Gitea API to a **per-repo, event-driven** pipeline that reviews only the PR that triggered it. ## What changes (this diff — homelab only) - `when:` → `event: pull_request` (Woodpecker raises this on **PR open AND push to an open PR**) + `event: manual` for on-demand re-review (`--var PR=<n>`). - Reviews only the triggering PR, from `CI_REPO` + `CI_COMMIT_PULL_REQUEST`. Drops the cross-repo discovery loop and `MAX_PRS` cap. - Keeps the `pr-review:sha=` dedup marker (now guards pipeline retries), Haiku triage → model pick → review → Gitea post, never-approve posture. - Resolves the agent prompts from the local checkout or a shallow homelab clone, so the **identical file** drops into every reviewed repo. ## Rollout — out of band, NOT in this diff (order matters) - [ ] Grant the **`pull_request`** event to `claude_code_oauth_token` + `review_bot_token` on every reviewed repo — else Woodpecker withholds them and every PR pipeline fails auth. **Do this before merging.** - [x] `homelab --require-approval forks` (public repo — fork PRs can't reach the tokens without a maintainer approval click). - [ ] Deploy this same file into `shelly-manager`, `authentik-samba-bridge` (folder-converted), `homeassistant`; register `homeassistant` in Woodpecker. - [ ] Delete the `pr-review` cron (id 6) **last**, once all four PR triggers are verified live.
claude-bot added 1 commit 2026-07-23 07:15:27 +00:00
Switch the PR review bot from a homelab-hosted hourly cron that polls four
repos over the Gitea API to a per-repo, event-driven pipeline that reviews
only the PR that triggered it (CI_REPO + CI_COMMIT_PULL_REQUEST). Fires on
pull_request, which Woodpecker raises on both PR-open and push-to-open-PR.

Drops the cross-repo discovery loop and MAX_PRS cap; keeps the sha-dedup
marker (now guarding pipeline retries), Haiku triage, model selection, and
never-approve posture. Resolves the agent prompts from the local checkout or
a shallow homelab clone so the identical file drops into every reviewed repo.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
review-bot left a comment
Member

🤖 Automated review — model claude-sonnet-5 (complexity: normal), commit f9c5ab6b1b0236b32e3c0d9e9d363fbc51a61287.

Advisory review by the review-bot pipeline. It never approves; a human merges. Push new commits to get a fresh review.


Summary

This PR rewrites .woodpecker/pr-review.yml from an hourly cross-repo-discovery cron into an event-driven, single-PR pipeline triggered by Woodpecker's pull_request event (plus a manual --var PR=<n> path), and moves the two agent prompts to a shared location with a shallow-clone fallback for other repos. The control flow (fetch PR → skip draft/WIP/closed → dedup by head-sha marker → clone, triage, review, post) is a faithful, correctly-simplified port of the old per-PR body; I did not find a concrete defect in the shell logic itself. There is one design assumption worth confirming before the "Rollout" checklist proceeds (see Suggestions), and the rollout itself (granting the pull_request event to the two tokens on every repo) is correctly called out in the PR body as a prerequisite, not part of this diff.

🟡 Suggestions

  • .woodpecker/pr-review.yml:72 — SLUG="$$CI_REPO" replaces the old hardcoded canonical-slug REPOS list. The comment block this diff deletes (old lines 17-20) explicitly documented a real gotcha: "gitea_admin/homelab 307-redirects to [k.renner/homelab] and a naked POST would not follow the redirect." The new code trusts CI_REPO to already be the canonical k.renner/* slug for every downstream curl call (PR fetch, reviews list, and — in the second step — the review POST). If CI_REPO is ever populated with a non-canonical owner alias for any of the four target repos, the dedup GET and/or the review POST would silently fail or redirect-fail, defeating the whole point of this PR. Worth confirming with a real pull_request trigger (env | grep CI_REPO in a debug step, or woodpecker repo info <repo>) before deleting the cron, since there's no test coverage that would catch this otherwise. If it's already known to be canonical, restoring a one-line note to that effect would preserve the institutional knowledge the deleted comment carried.
  • .woodpecker/pr-review.yml:39-42 — the new comment documents that homelab (public) requires approval for forked-PR pipelines so a fork can't rewrite this file to exfiltrate tokens. The rollout checklist in the PR body defers granting the pull_request event (and, implicitly, equivalent fork-approval settings) to the other three repos to a follow-up, out-of-band step. Worth double-checking each of those repos gets the same fork-approval treatment before this file is copied there — the comment currently only justifies the posture for homelab.
  • .woodpecker/pr-review.yml:104 (REVIEWED=$$(curl -sf ... | jq ... || echo 0)) — carried over unchanged from the old file, but worth noting: in a pipe without pipefail, || echo 0 only catches a nonzero exit from jq, not from curl. If the reviews-list GET fails (e.g. transient 5xx), jq still gets empty stdin and errors too, so the fallback happens to work today — but it's fragile and pre-existing, not introduced here.

🟢 Nits

  • .woodpecker/pr-review.yml:120 (PROMPTS="$$WS/ci/agents/pr-review") — if a non-homelab reviewed repo ever grows its own ci/agents/pr-review/prompt.md (e.g. copy-pasted by mistake), it would silently take precedence over the shared-prompt fallback, quietly forking "one source of truth for the review rubric." Not a bug, just a sharp edge worth a one-line comment if it ever bites.

Tests

No tests exist for this file or its siblings (issue-fix.yml, alert-monitor.yml) today — consistent with the rest of .woodpecker/, which has no CI-config test harness. Given the pipeline can't be unit-tested short of actually firing a pull_request event, the practical verification is the manual --var PR=<n> trigger path this PR keeps, which is the right fallback for a dry run.

Recommendation

Looks reasonable — no blocking issues; the CI_REPO canonicalization point above is worth a quick confirmation before deleting the cron (step 4 of the rollout checklist), but it's a pre-merge-worth-checking assumption, not a defect I can point at in this diff.

🤖 **Automated review** — model `claude-sonnet-5` (complexity: normal), commit `f9c5ab6b1b0236b32e3c0d9e9d363fbc51a61287`. > Advisory review by the `review-bot` pipeline. It never approves; a human merges. Push new commits to get a fresh review. --- ## Summary This PR rewrites `.woodpecker/pr-review.yml` from an hourly cross-repo-discovery cron into an event-driven, single-PR pipeline triggered by Woodpecker's `pull_request` event (plus a manual `--var PR=<n>` path), and moves the two agent prompts to a shared location with a shallow-clone fallback for other repos. The control flow (fetch PR → skip draft/WIP/closed → dedup by head-sha marker → clone, triage, review, post) is a faithful, correctly-simplified port of the old per-PR body; I did not find a concrete defect in the shell logic itself. There is one design assumption worth confirming before the "Rollout" checklist proceeds (see Suggestions), and the rollout itself (granting the `pull_request` event to the two tokens on every repo) is correctly called out in the PR body as a prerequisite, not part of this diff. ## 🟡 Suggestions - **`.woodpecker/pr-review.yml:72`** — `SLUG="$$CI_REPO"` replaces the old hardcoded canonical-slug `REPOS` list. The comment block this diff deletes (old lines 17-20) explicitly documented a real gotcha: *"gitea_admin/homelab 307-redirects to [k.renner/homelab] and a naked POST would not follow the redirect."* The new code trusts `CI_REPO` to already be the canonical `k.renner/*` slug for every downstream `curl` call (PR fetch, reviews list, and — in the second step — the review POST). If `CI_REPO` is ever populated with a non-canonical owner alias for any of the four target repos, the dedup GET and/or the review POST would silently fail or redirect-fail, defeating the whole point of this PR. Worth confirming with a real `pull_request` trigger (`env | grep CI_REPO` in a debug step, or `woodpecker repo info <repo>`) before deleting the cron, since there's no test coverage that would catch this otherwise. If it's already known to be canonical, restoring a one-line note to that effect would preserve the institutional knowledge the deleted comment carried. - **`.woodpecker/pr-review.yml:39-42`** — the new comment documents that homelab (public) requires approval for forked-PR pipelines so a fork can't rewrite this file to exfiltrate tokens. The rollout checklist in the PR body defers granting the `pull_request` event (and, implicitly, equivalent fork-approval settings) to the other three repos to a follow-up, out-of-band step. Worth double-checking each of those repos gets the same fork-approval treatment before this file is copied there — the comment currently only justifies the posture for homelab. - **`.woodpecker/pr-review.yml:104`** (`REVIEWED=$$(curl -sf ... | jq ... || echo 0)`) — carried over unchanged from the old file, but worth noting: in a pipe without `pipefail`, `|| echo 0` only catches a nonzero exit from `jq`, not from `curl`. If the reviews-list GET fails (e.g. transient 5xx), `jq` still gets empty stdin and errors too, so the fallback happens to work today — but it's fragile and pre-existing, not introduced here. ## 🟢 Nits - `.woodpecker/pr-review.yml:120` (`PROMPTS="$$WS/ci/agents/pr-review"`) — if a non-homelab reviewed repo ever grows its own `ci/agents/pr-review/prompt.md` (e.g. copy-pasted by mistake), it would silently take precedence over the shared-prompt fallback, quietly forking "one source of truth for the review rubric." Not a bug, just a sharp edge worth a one-line comment if it ever bites. ## Tests No tests exist for this file or its siblings (`issue-fix.yml`, `alert-monitor.yml`) today — consistent with the rest of `.woodpecker/`, which has no CI-config test harness. Given the pipeline can't be unit-tested short of actually firing a `pull_request` event, the practical verification is the manual `--var PR=<n>` trigger path this PR keeps, which is the right fallback for a dry run. ## Recommendation Looks reasonable — no blocking issues; the CI_REPO canonicalization point above is worth a quick confirmation before deleting the cron (step 4 of the rollout checklist), but it's a pre-merge-worth-checking assumption, not a defect I can point at in this diff. <!-- pr-review:sha=f9c5ab6b1b0236b32e3c0d9e9d363fbc51a61287 -->
claude-bot added 1 commit 2026-07-23 08:56:27 +00:00
Woodpecker exposes --var values to YAML ${...} substitution and when-filters
only, not as a step's shell environment, so the documented
'--var PIPELINE=pr-review --var PR=<n>' re-review read an unset shell var and
silently no-opped with a green build (caught by the review bot on PR #49).

Trigger on pull_request only; re-review by pushing a commit (new head sha
clears the dedup marker). Resolve the PR from CI_COMMIT_PULL_REQUEST alone.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
review-bot left a comment
Member

🤖 Automated review — model claude-sonnet-5 (complexity: normal), commit e96056dabdda32b3f8ff4aa521b28e12635425bf.

Advisory review by the review-bot pipeline. It never approves; a human merges. Push new commits to get a fresh review.


Summary

This PR rewrites .woodpecker/pr-review.yml from an hourly cron that polls four
repos for open PRs into an event-driven pipeline that reacts to a single
pull_request event and reviews only the triggering PR (CI_REPO +
CI_COMMIT_PULL_REQUEST). It also relocates the agent-prompt lookup so the file
can be deployed unchanged to repos that don't carry ci/agents/pr-review/
locally (falling back to a shallow clone of k.renner/homelab@master). The
shell logic is internally consistent — set -eu is respected, the
CI_COMMIT_PULL_REQUEST:- default avoids an unbound-variable failure, the
draft/WIP/state/dedup checks are preserved from the old code, and the referenced
ci/agents/pr-review/{prompt,triage-prompt}.md files exist in this checkout. I
found no concrete logic bugs in the diff itself. The residual risk is entirely
in things that live outside this repo (Woodpecker's event semantics, and the
per-repo token/approval grants called out in the PR's own rollout checklist),
which is worth flagging explicitly since this is a security-sensitive change
(secrets now execute in response to externally-triggered pull_request events
on a public repo).

🟡 Suggestions

  • PR description doesn't match the shipped diff. The PR body says the
    when: block becomes event: pull_request + event: manual for
    on-demand re-review (--var PR=<n>)
    . The actual diff removes the manual
    trigger entirely (.woodpecker/pr-review.yml:50-51 in the checkout) and adds
    a comment explicitly justifying why a manual --var trigger wouldn't work
    (.woodpecker/pr-review.yml:44-49: Woodpecker only exposes --var to YAML
    ${...}/when evaluation, not to the step's shell environment). The code
    and its own comment are internally consistent and this reasoning looks
    correct — the PR body is just stale/wrong on this point. Worth fixing the
    description so the PR history doesn't claim a capability that isn't there,
    since a future reader (or the next agent) may look for a manual re-review
    path that doesn't exist.

  • Secret exposure now depends entirely on fork-approval being correctly
    configured on every reviewed repo, not just homelab.
    This pipeline runs
    with CLAUDE_CODE_OAUTH_TOKEN and REVIEW_TOKEN and is now triggered by an
    externally-influenceable pull_request event instead of an admin-controlled
    cron. The comment at .woodpecker/pr-review.yml:39-42 notes homelab is set
    to --require-approval forks, and the PR checklist marks that item done for
    homelab specifically. The other three rollout targets
    (shelly-manager, authentik-samba-bridge, homeassistant) aren't
    mentioned — if any of those are or become public without the same
    fork-approval setting, a PR from an untrusted fork could edit this same file
    (it's deployed "unchanged" per the file's own header) to exfiltrate both
    tokens. Worth either confirming those repos are private, or adding the same
    --require-approval forks gate to each before wiring the pull_request
    event grant, per the rollout checklist item that's still unchecked.

  • Losing the poll may drop PRs that never fire a fresh pull_request
    event.
    The old hourly cron would eventually pick up any open PR regardless
    of why it became reviewable. The new design only reviews on the event
    Woodpecker classifies as pull_request (per the file's own comment: PR open

    • push-to-open-PR). A PR that's opened as a draft and later marked "ready for
      review" without a new commit may or may not fire that event depending on
      how Woodpecker maps Gitea's pull_request webhook actions — worth
      double-checking against a real draft→ready transition once this is live,
      since a silent gap here is hard to notice (no error, just no review posted).

🟢 Nits

  • .woodpecker/pr-review.yml:126-129 — the fallback git clone of
    k.renner/homelab for prompts isn't checked for success before use. If it
    fails, the next cat "$$PROMPTS/triage-prompt.md" will die under set -eu
    with a generic "no such file" error rather than a clear "could not fetch
    agent prompts" message. Not a functional bug (it still fails loud, not
    silent), just a slightly less helpful failure mode than the rest of the
    script's explicit || { echo ...; } error messages.

Tests

No tests exist for this file, consistent with the repo's other Woodpecker
pipeline definitions (alert-monitor.yml, issue-fix.yml, security-scan.yml
etc. are all similarly untested shell-in-YAML). Given this is a security- and
cost-sensitive pipeline (spends Claude tokens and posts to Gitea automatically
on external triggers), the realistic verification is a live smoke test after
merge: open a throwaway PR, confirm the pipeline fires once, posts one review,
and that pushing a follow-up commit produces exactly one fresh review rather
than a duplicate. The PR's own rollout checklist already calls for this kind of
staged verification, which is the right approach here.

Recommendation

Looks reasonable — no blocking issues; comments above (especially confirming
fork-approval on every rollout target before granting the pull_request event
to the tokens) are worth addressing before flipping this on for the other
repos.

🤖 **Automated review** — model `claude-sonnet-5` (complexity: normal), commit `e96056dabdda32b3f8ff4aa521b28e12635425bf`. > Advisory review by the `review-bot` pipeline. It never approves; a human merges. Push new commits to get a fresh review. --- ## Summary This PR rewrites `.woodpecker/pr-review.yml` from an hourly cron that polls four repos for open PRs into an event-driven pipeline that reacts to a single `pull_request` event and reviews only the triggering PR (`CI_REPO` + `CI_COMMIT_PULL_REQUEST`). It also relocates the agent-prompt lookup so the file can be deployed unchanged to repos that don't carry `ci/agents/pr-review/` locally (falling back to a shallow clone of `k.renner/homelab@master`). The shell logic is internally consistent — `set -eu` is respected, the `CI_COMMIT_PULL_REQUEST:-` default avoids an unbound-variable failure, the draft/WIP/state/dedup checks are preserved from the old code, and the referenced `ci/agents/pr-review/{prompt,triage-prompt}.md` files exist in this checkout. I found no concrete logic bugs in the diff itself. The residual risk is entirely in things that live outside this repo (Woodpecker's event semantics, and the per-repo token/approval grants called out in the PR's own rollout checklist), which is worth flagging explicitly since this is a security-sensitive change (secrets now execute in response to externally-triggered `pull_request` events on a public repo). ## 🟡 Suggestions - **PR description doesn't match the shipped diff.** The PR body says the `when:` block becomes `event: pull_request` **+ `event: manual` for on-demand re-review (`--var PR=<n>`)**. The actual diff removes the manual trigger entirely (`.woodpecker/pr-review.yml:50-51` in the checkout) and adds a comment explicitly justifying why a manual `--var` trigger *wouldn't work* (`.woodpecker/pr-review.yml:44-49`: Woodpecker only exposes `--var` to YAML `${...}`/`when` evaluation, not to the step's shell environment). The code and its own comment are internally consistent and this reasoning looks correct — the PR body is just stale/wrong on this point. Worth fixing the description so the PR history doesn't claim a capability that isn't there, since a future reader (or the next agent) may look for a manual re-review path that doesn't exist. - **Secret exposure now depends entirely on fork-approval being correctly configured on every reviewed repo, not just homelab.** This pipeline runs with `CLAUDE_CODE_OAUTH_TOKEN` and `REVIEW_TOKEN` and is now triggered by an externally-influenceable `pull_request` event instead of an admin-controlled cron. The comment at `.woodpecker/pr-review.yml:39-42` notes homelab is set to `--require-approval forks`, and the PR checklist marks that item done for homelab specifically. The other three rollout targets (`shelly-manager`, `authentik-samba-bridge`, `homeassistant`) aren't mentioned — if any of those are or become public without the same fork-approval setting, a PR from an untrusted fork could edit this same file (it's deployed "unchanged" per the file's own header) to exfiltrate both tokens. Worth either confirming those repos are private, or adding the same `--require-approval forks` gate to each before wiring the `pull_request` event grant, per the rollout checklist item that's still unchecked. - **Losing the poll may drop PRs that never fire a fresh `pull_request` event.** The old hourly cron would eventually pick up any open PR regardless of *why* it became reviewable. The new design only reviews on the event Woodpecker classifies as `pull_request` (per the file's own comment: PR open + push-to-open-PR). A PR that's opened as a draft and later marked "ready for review" *without* a new commit may or may not fire that event depending on how Woodpecker maps Gitea's `pull_request` webhook actions — worth double-checking against a real draft→ready transition once this is live, since a silent gap here is hard to notice (no error, just no review posted). ## 🟢 Nits - `.woodpecker/pr-review.yml:126-129` — the fallback `git clone` of `k.renner/homelab` for prompts isn't checked for success before use. If it fails, the next `cat "$$PROMPTS/triage-prompt.md"` will die under `set -eu` with a generic "no such file" error rather than a clear "could not fetch agent prompts" message. Not a functional bug (it still fails loud, not silent), just a slightly less helpful failure mode than the rest of the script's explicit `|| { echo ...; }` error messages. ## Tests No tests exist for this file, consistent with the repo's other Woodpecker pipeline definitions (`alert-monitor.yml`, `issue-fix.yml`, `security-scan.yml` etc. are all similarly untested shell-in-YAML). Given this is a security- and cost-sensitive pipeline (spends Claude tokens and posts to Gitea automatically on external triggers), the realistic verification is a live smoke test after merge: open a throwaway PR, confirm the pipeline fires once, posts one review, and that pushing a follow-up commit produces exactly one fresh review rather than a duplicate. The PR's own rollout checklist already calls for this kind of staged verification, which is the right approach here. ## Recommendation Looks reasonable — no blocking issues; comments above (especially confirming fork-approval on every rollout target before granting the `pull_request` event to the tokens) are worth addressing before flipping this on for the other repos. <!-- pr-review:sha=e96056dabdda32b3f8ff4aa521b28e12635425bf -->
claude-bot added 1 commit 2026-07-23 09:49:43 +00:00
The clone embedded REVIEW_TOKEN in the URL, which git persists in
work/target/.git/config. The review agent runs next cd'd into the workspace
with Read/Glob/Grep and its report is posted verbatim as a public PR comment,
so the write:repository PAT was readable and could be exfiltrated via prompt
injection from the reviewed PR (caught by the review bot on PR #180). The
agent needs only the working tree, so drop .git entirely after checkout.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
claude-bot added 1 commit 2026-07-23 09:54:52 +00:00
Replace the credential-in-URL clone (which git wrote into
work/target/.git/config, readable by the review agent whose output is posted
publicly) with a tokenless clone that supplies auth via an HTTP header through
GIT_CONFIG_* env. The persisted remote is tokenless, so no repo file holds the
PAT — fixing the leak at the source instead of deleting .git after the fact.

Supersedes the previous rm -rf .git stopgap.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
claude-bot added 1 commit 2026-07-23 10:00:02 +00:00
The clone existed only to hand review_bot_token to git, which persisted it in
work/target/.git/config — a file the review agent (Read/Glob/Grep, output
posted publicly) could read. Nothing needed the checkout that the API-fetched
diff + metadata don't already provide, so drop the clone entirely: no token on
disk, no work/target, no cleanup. The PR + diff are still read over the API
with ephemeral curl Authorization headers (never written to disk).

Prompts updated to match: the agents review the diff alone (no ./work/target),
and are told to flag, not guess, when a judgement needs code they can't see.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
claude-bot added 1 commit 2026-07-23 10:03:37 +00:00
On a pull_request event Woodpecker's built-in clone already checks the repo out
at the PR head in the workspace, so the agents read that directly for context.
Restores full-context review (the prior commit over-corrected to diff-only)
while keeping the manual, token-bearing clone gone. Prompts point at the
working-directory checkout instead of ./work/target.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
review-bot left a comment
Member

🤖 Automated review — model claude-opus-4-8 (complexity: complex), commit 2a6506667293e98f804564b9949971864643b13c.

Advisory review by the review-bot pipeline. It never approves; a human merges. Push new commits to get a fresh review.


Summary

Converts the PR-review pipeline from an hourly cross-repo cron to a per-repo,
event-driven pull_request trigger, and updates the two agent prompts to match
(the reviewer now reads the Woodpecker checkout in its CWD instead of a
hand-cloned work/target). The change is coherent and internally consistent —
the prompt edits, the removed manual clone, and the removed work/ cleanup all
line up — and it genuinely removes the write-scoped token that the old model
persisted in work/target/.git/config. No blocking issues; the notes below are
verification points and optional hardening.

🟡 Suggestions

  • Correctness depends on CI_REPO being the canonical k.renner/… slug
    (.woodpecker/pr-review.yml:76, SLUG="$$CI_REPO"). The old code hardcoded
    k.renner/* specifically because it noted a 307 redirect from the
    gitea_admin/homelab slug that a naked request won't follow (old comment,
    now deleted). Every curl here uses -sf without -L, so if Woodpecker
    ever surfaces a non-canonical owner slug in CI_REPO, the first GET at
    .woodpecker/pr-review.yml:88 returns the redirect body instead of JSON and
    the subsequent jq -r '.state' aborts under set -eu. It will fail loudly
    rather than post a wrong review, so this is low-risk — but worth confirming
    once that CI_REPO for these four repos is exactly the canonical owner the
    Gitea API accepts without redirect.

  • Homelab self-reviews load the rubric from the PR-head checkout
    (.woodpecker/pr-review.yml:129-130): PROMPTS="$$WS/ci/agents/pr-review"
    is used whenever prompt.md exists in the workspace, which for a homelab PR
    is the PR's own (possibly modified) copy — exactly what PR #362 itself
    edits. So a PR can be reviewed under a rubric it changed. This adds no
    incremental risk beyond Woodpecker's existing trust model (an approved fork
    pipeline already runs fork-controlled .woodpecker config wholesale, and the
    PR body confirms --require-approval forks), and the private repos correctly
    fall back to homelab@master prompts. Flagging only so the dependency on the
    fork-approval gate is explicit.

  • Fallback prompt clone has no error handling (.woodpecker/pr-review.yml:132).
    Under set -eu, a transient failure of git clone … homelab aborts the
    whole step with no review posted (only affects the non-homelab repos, which
    always take this path). Consider || { echo "prompt clone failed — skipping"; exit 0; }
    so an unrelated network blip doesn't hard-fail the pipeline.

🟢 Nits

  • Woodpecker's built-in clone for a pull_request event may check out a
    merge-ref rather than the bare head sha, while diff.patch, the review
    commit_id, and the dedup marker are all anchored to .head.sha
    (.woodpecker/pr-review.yml:228,231). The on-disk context the agent reads
    could therefore differ slightly from the head diff. Almost always immaterial
    (the diff is authoritative and the agent only uses the checkout for context),
    but it's the one place the "checkout == head" claim in the new prompt could be
    imprecise.

Tests

This is CI/infra YAML with no unit-test harness in the repo, so the absence of
tests is consistent with the surrounding conventions. The real safety net is the
staged rollout, which the PR body documents well and in the right order (grant
the pull_request event to both secrets before merge, deploy the file to
the other repos, delete cron id 6 last). The one item worth double-checking
live is the CI_REPO slug point above — the cheapest verification is a single
manual PR-event run on one private repo before deleting the cron.

Recommendation

Looks reasonable — no blocking issues; comments above are optional.

🤖 **Automated review** — model `claude-opus-4-8` (complexity: complex), commit `2a6506667293e98f804564b9949971864643b13c`. > Advisory review by the `review-bot` pipeline. It never approves; a human merges. Push new commits to get a fresh review. --- ## Summary Converts the PR-review pipeline from an hourly cross-repo cron to a per-repo, event-driven `pull_request` trigger, and updates the two agent prompts to match (the reviewer now reads the Woodpecker checkout in its CWD instead of a hand-cloned `work/target`). The change is coherent and internally consistent — the prompt edits, the removed manual clone, and the removed `work/` cleanup all line up — and it genuinely removes the write-scoped token that the old model persisted in `work/target/.git/config`. No blocking issues; the notes below are verification points and optional hardening. ## 🟡 Suggestions - **Correctness depends on `CI_REPO` being the canonical `k.renner/…` slug** (`.woodpecker/pr-review.yml:76`, `SLUG="$$CI_REPO"`). The old code hardcoded `k.renner/*` specifically because it noted a 307 redirect from the `gitea_admin/homelab` slug that a naked request won't follow (old comment, now deleted). Every `curl` here uses `-sf` **without `-L`**, so if Woodpecker ever surfaces a non-canonical owner slug in `CI_REPO`, the first GET at `.woodpecker/pr-review.yml:88` returns the redirect body instead of JSON and the subsequent `jq -r '.state'` aborts under `set -eu`. It will fail loudly rather than post a wrong review, so this is low-risk — but worth confirming once that `CI_REPO` for these four repos is exactly the canonical owner the Gitea API accepts without redirect. - **Homelab self-reviews load the rubric from the PR-head checkout** (`.woodpecker/pr-review.yml:129-130`): `PROMPTS="$$WS/ci/agents/pr-review"` is used whenever `prompt.md` exists in the workspace, which for a homelab PR is the PR's *own* (possibly modified) copy — exactly what PR #362 itself edits. So a PR can be reviewed under a rubric it changed. This adds no incremental risk beyond Woodpecker's existing trust model (an approved fork pipeline already runs fork-controlled `.woodpecker` config wholesale, and the PR body confirms `--require-approval forks`), and the private repos correctly fall back to `homelab@master` prompts. Flagging only so the dependency on the fork-approval gate is explicit. - **Fallback prompt clone has no error handling** (`.woodpecker/pr-review.yml:132`). Under `set -eu`, a transient failure of `git clone … homelab` aborts the whole step with no review posted (only affects the non-homelab repos, which always take this path). Consider `|| { echo "prompt clone failed — skipping"; exit 0; }` so an unrelated network blip doesn't hard-fail the pipeline. ## 🟢 Nits - Woodpecker's built-in clone for a `pull_request` event may check out a merge-ref rather than the bare head sha, while `diff.patch`, the review `commit_id`, and the dedup marker are all anchored to `.head.sha` (`.woodpecker/pr-review.yml:228,231`). The on-disk context the agent reads could therefore differ slightly from the head diff. Almost always immaterial (the diff is authoritative and the agent only uses the checkout for context), but it's the one place the "checkout == head" claim in the new prompt could be imprecise. ## Tests This is CI/infra YAML with no unit-test harness in the repo, so the absence of tests is consistent with the surrounding conventions. The real safety net is the staged rollout, which the PR body documents well and in the right order (grant the `pull_request` event to both secrets **before** merge, deploy the file to the other repos, delete cron id 6 **last**). The one item worth double-checking live is the `CI_REPO` slug point above — the cheapest verification is a single manual PR-event run on one private repo before deleting the cron. ## Recommendation Looks reasonable — no blocking issues; comments above are optional. <!-- pr-review:sha=2a6506667293e98f804564b9949971864643b13c -->
claude-bot added 1 commit 2026-07-23 10:33:06 +00:00
The triage + review agents run with REVIEW_TOKEN and CLAUDE_CODE_OAUTH_TOKEN in
their environment, process attacker-controlled PR title/body/diff, and their
output is posted verbatim as a public PR comment. Without a Read sandbox a
prompt-injected diff could read /proc/self/environ (the tokens) or .git/config
(Woodpecker's clone credential) and get it published. Every other agent
pipeline already denies this via --settings; pr-review had none.

Add ci/agents/pr-review/settings.json (mirrors issue-fix/update-audit, gitea
#197) denying Read on /proc,/root,/home,/etc,.git, and pass --settings
$PROMPTS/settings.json to both claude invocations (resolves alongside the
prompts, so it works for the repos that fetch prompts from the homelab clone).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
k.renner merged commit 8b9468caa3 into master 2026-07-23 10:45:30 +00:00
Sign in to join this conversation.