From 6986a9964ae76b46d2007453df8677891d2b5f5b Mon Sep 17 00:00:00 2001 From: donpetry-bot <281750570+donpetry-bot@users.noreply.github.com> Date: Fri, 26 Jun 2026 23:29:01 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20implement=20issue=20#538=20=E2=80=94=20?= =?UTF-8?q?Title:=20pr-review-mention:=20kill=20the=20self-trigger=20?= =?UTF-8?q?=E2=80=94=20bot's=20own=20mention-ack=20re-fires=20the=20listen?= =?UTF-8?q?er?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../workflows/pr-review-mention-reusable.yml | 16 ++++- .github/workflows/pr-review-mention-tests.yml | 55 +++++++++++++++ .../pr-review-mention/helpers/setup.bash | 29 ++++++++ .../pr-review-mention/self-trigger.bats | 68 +++++++++++++++++++ 4 files changed, 166 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/pr-review-mention-tests.yml create mode 100644 test/workflows/pr-review-mention/helpers/setup.bash create mode 100644 test/workflows/pr-review-mention/self-trigger.bats diff --git a/.github/workflows/pr-review-mention-reusable.yml b/.github/workflows/pr-review-mention-reusable.yml index 2aa7bfa2..8fc46174 100644 --- a/.github/workflows/pr-review-mention-reusable.yml +++ b/.github/workflows/pr-review-mention-reusable.yml @@ -37,12 +37,20 @@ jobs: # team review requests; comment fields don't exist on pull_request events. # For comment events we also gate at the job level on author_association so # that the job (and its secrets) never start for external/untrusted actors. + # + # Self-trigger guard (#538): a comment authored by donpetry-bot, or one + # carrying a `` marker, is the agent talking + # about its own work and must never re-fire the listener. Without this the + # mention-ack (@-mention authored by the bot, marker-tagged) re-triggers the + # cascade — the #860 runaway (1,481 identical acks at ~9s for ~4.5h). if: | (github.event_name == 'pull_request' && github.event.requested_reviewer != null && github.event.requested_reviewer.login == 'donpetry-bot' && github.event.pull_request.head.repo.full_name == github.repository) || (github.event_name != 'pull_request' && + github.event.comment.user.login != 'donpetry-bot' && + !contains(github.event.comment.body, '\n%s' "${MSG}")" diff --git a/.github/workflows/pr-review-mention-tests.yml b/.github/workflows/pr-review-mention-tests.yml new file mode 100644 index 00000000..548e1de0 --- /dev/null +++ b/.github/workflows/pr-review-mention-tests.yml @@ -0,0 +1,55 @@ +# Quality gate for the reusable pr-review-mention workflow. +# +# Triggered on any PR that touches: +# - .github/workflows/pr-review-mention*.yml +# - test/workflows/pr-review-mention/** +# - standards/workflows/pr-review-mention.yml +# +# Gate (must pass before merge): +# 1. bats — contract tests for the self-trigger guard and ack text (#538) +# +# Standard: this workflow pins the anti-self-trigger contract from issue #538 +# (the org-repo half of the #860 mention-ack runaway). + +name: PR Review Mention Tests + +on: + pull_request: + paths: + - '.github/workflows/pr-review-mention*.yml' + - 'test/workflows/pr-review-mention/**' + - 'standards/workflows/pr-review-mention.yml' + push: + branches: + - main + paths: + - '.github/workflows/pr-review-mention*.yml' + - 'test/workflows/pr-review-mention/**' + - 'standards/workflows/pr-review-mention.yml' + +permissions: + contents: read + +concurrency: + group: pr-review-mention-tests-${{ github.ref }} + cancel-in-progress: true + +jobs: + test: + name: bats + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Checkout repository + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + fetch-depth: 1 + + - name: Install bats + run: | + set -euo pipefail + sudo apt-get update -qq + sudo apt-get install -y --no-install-recommends bats + + - name: Run bats suite + run: bats --print-output-on-failure test/workflows/pr-review-mention/ diff --git a/test/workflows/pr-review-mention/helpers/setup.bash b/test/workflows/pr-review-mention/helpers/setup.bash new file mode 100644 index 00000000..9b8ed0ac --- /dev/null +++ b/test/workflows/pr-review-mention/helpers/setup.bash @@ -0,0 +1,29 @@ +# Common test helpers for the pr-review-mention bats suite. + +# Repo root, regardless of where bats is invoked from. +TT_REPO_ROOT="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../../../.." && pwd)" +export TT_REPO_ROOT + +# The reusable workflow that owns all mention-dispatch logic (the file under test). +TT_REUSABLE="${TT_REPO_ROOT}/.github/workflows/pr-review-mention-reusable.yml" +export TT_REUSABLE + +# Print the job-level `if:` guard block: everything from the `if: |` line up to +# (but not including) the `steps:` key of the handle-mention job. +prm_if_guard() { + awk ' + /^[[:space:]]*if:[[:space:]]*\|/ { grab=1; next } + grab && /^[[:space:]]*steps:[[:space:]]*$/ { exit } + grab { print } + ' "$TT_REUSABLE" +} + +# Print the body of the "Post acknowledgement comment" step: everything from that +# step's `- name:` line up to (but not including) the next `- name:` line. +prm_ack_step() { + awk ' + /^[[:space:]]*-[[:space:]]*name:[[:space:]]*Post acknowledgement comment/ { grab=1; print; next } + grab && /^[[:space:]]*-[[:space:]]*name:/ { exit } + grab { print } + ' "$TT_REUSABLE" +} diff --git a/test/workflows/pr-review-mention/self-trigger.bats b/test/workflows/pr-review-mention/self-trigger.bats new file mode 100644 index 00000000..ac707b43 --- /dev/null +++ b/test/workflows/pr-review-mention/self-trigger.bats @@ -0,0 +1,68 @@ +#!/usr/bin/env bats +# Contract tests for .github/workflows/pr-review-mention-reusable.yml +# +# Pins issue #538: the mention listener must never trigger on its own work. +# The pr-review ack comment is authored by donpetry-bot and carries a +# `` marker; either property must keep the job's +# `if:` gate (and its secrets) from ever starting. The ack must also fire no +# mention webhook (#860 runaway: 1,481 byte-identical acks at ~9s cadence). + +load 'helpers/setup' + +@test "reusable workflow file exists" { + [ -f "$TT_REUSABLE" ] +} + +# ── Acceptance 1: bot-authored / marker comments never re-trigger ───────────── + +@test "if-guard excludes comments authored by the bot itself" { + run prm_if_guard + [ "$status" -eq 0 ] + [[ "$output" == *"github.event.comment.user.login != 'donpetry-bot'"* ]] +} + +@test "if-guard excludes comments carrying a pr-review-agent marker" { + run prm_if_guard + [ "$status" -eq 0 ] + [[ "$output" == *"!contains(github.event.comment.body, '"* ]] +} + +@test "ack step still names the actor in the acknowledgement (plain text)" { + run prm_ack_step + [ "$status" -eq 0 ] + [[ "$output" == *'${ACTOR}'* ]] +}