Add review checklists workflow - #899
Conversation
|
|
||
| from __future__ import annotations | ||
|
|
||
| import os |
| import pytest | ||
| import yaml | ||
|
|
||
| import helpers |
| import pytest | ||
| import yaml | ||
|
|
||
| import helpers |
| candidate = r.Rlocation(f"_main/{config_relpath}") | ||
| if candidate and os.path.isfile(candidate): | ||
| return candidate | ||
| except (ImportError, Exception): |
| monkeypatch.setenv("GITHUB_REPOSITORY", "org/repo") | ||
| monkeypatch.setenv("PR_NUMBER", "42") | ||
| gh = MagicMock() | ||
| repo, pr = get_repo_and_pr(gh) |
| monkeypatch.setenv("GITHUB_REPOSITORY", "org/repo") | ||
| monkeypatch.setenv("PR_NUMBER", "42") | ||
| gh = MagicMock() | ||
| repo, pr = get_repo_and_pr(gh) |
e6fb9d8 to
6f2b4c9
Compare
castler
left a comment
There was a problem hiding this comment.
We need to extend the CI design also to explain why and for what we have these workflows.
Due to the size, let's discuss f2f which of the findings we tackle, tackle now or in a follow up.
| name = "copyright", | ||
| srcs = [ | ||
| ".github", | ||
| "actions", |
There was a problem hiding this comment.
Fixed, code was moved and this was not updated.
|
|
||
| concurrency: | ||
| group: >- | ||
| review-checklists-apply-${{ github.event.workflow_run.head_sha || github.event.workflow_run.id }} |
There was a problem hiding this comment.
I think we are missing here a "cancel-in-progress" option? Or do we say this is so short running, that it does not make sense?
There was a problem hiding this comment.
Adding the cancel-in-progress does not hurt. So I added it. But in general this is quite fast (given GitHub schedules the runs fast).
| GITHUB_TOKEN: ${{ inputs.github-token }} | ||
| GITHUB_REPOSITORY: ${{ github.repository }} | ||
| PR_NUMBER: ${{ inputs.pr-number }} | ||
| run: python3 "$GITHUB_ACTION_PATH/scripts/post_checklists.py" --config-path "${{ inputs.config-path }}" |
There was a problem hiding this comment.
We should make a comment here that we we do not on purpose start this with Bazel, and what was our reasoning for it.
| @@ -0,0 +1,109 @@ | |||
| # ******************************************************************************* | |||
There was a problem hiding this comment.
Wondering if we do not want to place it also under actions/00_infrastructure or similar?
There was a problem hiding this comment.
Not quite sure about that. Separating the code over the repo is not perfect, since this file contains logic that is closely related. GitHub actually has no convention where actions must be placed. So keeping it here is perfectly fine.
| ruleset to require the commit status context **`review-checklists`** | ||
| (the one this action sets via `set_commit_status`). Requiring the | ||
| workflow job itself will not gate merges correctly. | ||
| 5. **Require at least one approving review** in branch protection. The |
There was a problem hiding this comment.
Are we sure this is possible with Otterdog? As per https://otterdog.readthedocs.io/en/latest/reference/organization/repository/status-check/ I do not believe so?
There was a problem hiding this comment.
Commit status checks are viable with any: prefix. I extended the documentation. Of course this is subject to trials.
| return is_in_queue | ||
|
|
||
| print("GraphQL merge-queue lookup returned no boolean state") | ||
| return False |
There was a problem hiding this comment.
Should we not throw here and rather catch that. Are we sure that IF we would be in the merge queue but the lookup fails for what ever reason (e.g. API error on the graphQL call) that FALSE would not bring us problems?
There was a problem hiding this comment.
This branch is only used to provide the warning comment if a PR is modified after entering the merge queue. The worst happening here would be that the comment is wrongly emitted.
Still this omission would not modify the evidence.
|
|
||
| repo, pr = get_repo_and_pr(gh) | ||
|
|
||
| if is_pr_in_merge_queue(pr): |
There was a problem hiding this comment.
This I do not get, why do we not execute this in the if statement above?
if event_name == "merge_group":
then the PR needs to be in the merge_queue, no?
There was a problem hiding this comment.
We want to add this warning comment when the PR is modified after it enters the merge queue. This means we react to events like pull_request_comment, pull_request_review.
Hence, we do not enter the first if condition at all but must still verify whether the PR is in the merge queue or not.
| from typing import Any | ||
|
|
||
| from helpers import ( | ||
| _collect_acknowledgement_details, |
There was a problem hiding this comment.
I personally think its weird to "import" private functions.
| return "success", "No checklists applicable" | ||
|
|
||
| existing = find_existing_checklist_comments(pr) | ||
| relevant_ids = [cl["id"] for cl in relevant if cl["id"] in existing] |
There was a problem hiding this comment.
As per my current understanding, this would filter out only checklists where a comment has been made. Thus, if for a checklist no comment has been made (yet), we would skip it here.
Thus we would set success later, if for another checklist is succesful. Do we not rather have to "pend" on that all comments have been posted?
| for comment in pr.get_review_comments(): | ||
| reply_to = getattr(comment, "in_reply_to_id", None) | ||
| if reply_to is None or reply_to not in cl_comment_ids: | ||
| continue | ||
|
|
||
| cid = cl_comment_ids[reply_to] # type: ignore[index] | ||
| body = (comment.body or "").strip() | ||
| user = comment.user.login | ||
|
|
||
| if body.upper() == OK_KEYWORD: | ||
| acks[cid].add(user) | ||
|
|
||
| return acks |
There was a problem hiding this comment.
This functionality is duplicated.
See for example: def _find_ok_comments_for_checklist
I think we can extract it into an helper. (I am aware that we store different information, but I think we can make a super set of information returned?
The composite action was later moved from actions/review_checklists to tools/review-checklists, but the root BUILD file's copyright_checker srcs list still referenced the now-nonexistent "actions" directory (a no-op glob) and never gained a "tools" entry, so the new tools/review-checklists sources were not covered by the copyright check at all. Addresses review comment: #899 (comment) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Multiple non-merge_group events (e.g. several review comments posted in quick succession) can each trigger a completed 'Review Checklists (Trigger)' run for the same head SHA, spawning overlapping apply-stage runs in the same concurrency group. Let a newer run cancel an older, now-superseded one instead of leaving it queued/running: the job only recomputes idempotent PR state (comments/statuses) from scratch each time, so cancelling a stale run is safe and matches this repo's existing convention of setting cancel-in-progress explicitly. Addresses review comment: #899 (comment) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Addresses review comment: #899 (comment) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Otterdog's status-check reference distinguishes plain commit statuses (which need the 'any:' prefix) from GitHub Actions workflow-job or GitHub App statuses. The 'review-checklists' status this action sets via create_status is a plain commit status, so an Otterdog-managed branch protection rule/ruleset must reference it as 'any:review-checklists' — the bare name would not be recognized. Addresses review comment: #899 (comment) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
helpers.find_ok_replies (unused in production), helpers.collect_ acknowledgement_details, check_acknowledgements._collect_ok_ acknowledgements, and dismiss_and_invalidate._find_ok_comments_for_ checklist each independently re-implemented the same scan for OK-reply comments to a checklist finding. Extract a single shared helpers.find_ok_replies_for_checklists(pr, existing_comments, checklist_ids) that returns the 'superset' (full comment objects, in one pass over pr.get_review_comments()) and have each caller project it down to the narrower shape it needs. This also removes an O(n*k) re-scan in dismiss_and_invalidate.handle_synchronize (one scan per affected checklist) in favor of a single O(n) pass. Also renamed the now-shared collect_acknowledgement_details parameter 'relevant_ids' to 'checklist_ids' for clarity, since it isn't obvious from the name alone what the ids are relevant to. Addresses review comments: #899 (comment) #899 (comment) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
'_main' is the current Bzlmod-internal runfiles-directory name for the root module. Rlocation() accepts the module name directly and resolves it through the runfiles repo mapping to whichever directory the root module is actually materialized under. Use 'score_communication' (this repo's declared module name in MODULE.bazel) instead — this is the portable, correct API usage, and keeps working even if Bazel's internal main-repo directory naming convention changes, instead of depending on that implementation detail directly. Addresses review comment: #899 (comment) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
get_reviews() returns every review a user has ever submitted on the PR (in submission order), not just their latest state, so a user can approve and later request changes (or have an approval dismissed) in a subsequent review. The approvers.discard() call is what removes such a since-superseded approval recorded earlier in the same loop — it is not a no-op, even though 'approvers' starts as an empty set. Addresses review comment: #899 (comment) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Addresses review comment: #899 (comment) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fail-closed to False on any lookup error is intentional here: the only callers use the result to decide whether to (re-)post an advisory merge-queue notice on the PR, so a false negative merely delays that notice — it never affects merge gating. The actual gating decision for merge_group events is made independently in check_acknowledgements.py, which resolves and validates the underlying PR itself and fails the commit status (rather than defaulting to success) if that resolution or validation fails. Addresses review comment: #899 (comment) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
_validate_checklist_evidence (used for merge_group evidence validation) and main()'s live-PR flow both derived 'relevant_ids' as only the relevant checklists that already have a posted finding comment, and then treated an empty relevant_ids as the only 'not all posted yet' case. If some but not all relevant checklists had been posted (e.g. a partial post_checklists run, or a checklist added to the config after others were already posted), the unposted ones were silently dropped from consideration entirely, and the status could go to 'success' once the posted subset was fully acknowledged — without a checklist that was never even posted ever being acknowledged. Fixed to require every relevant checklist to have a posted comment before proceeding past 'pending'. Addresses review comment: #899 (comment) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… handling Explain that this is_pr_in_merge_queue() check in main()'s regular (non-merge_group) flow is intentionally distinct from the merge_group handling above it: it covers events fired for a PR that is still enqueued but is not itself a merge_group event (e.g. a review comment posted while the PR is queued), so the advisory merge-queue notice can be (re-)posted for it. Addresses review comment: #899 (comment) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Rename 'cl' -> 'checklist' and 'cid' -> 'checklist_id' (and 'cl_copy' -> 'checklist_copy', 'comment_id_to_cid' -> 'comment_id_to_checklist_id') throughout helpers.py, check_acknowledgements.py, dismiss_and_invalidate.py, post_checklists.py, and helpers_test.py. These abbreviations made call sites and comprehensions harder to scan at a glance, especially where 'cl' (checklist) could be misread as a different kind of list/class abbreviation. Wrapped the handful of lines that exceeded 88 columns after the rename. Addresses review comments (previously deferred by the reviewer to a future PR): #899 (comment) #899 (comment) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Following up on discussion_r3772590463 ('relevant_ids is a bad naming
... what is it relevant for?'): the same ambiguity existed one level up
in the variables holding the matched checklist dicts themselves.
'relevant' and 'affected' read as bare adjectives with no object, which
invited exactly this kind of question at every read site. Rename to
'relevant_checklists' (helpers.match_checklists/build_evidence_block,
check_acknowledgements.py, post_checklists.py) and
'affected_checklists' (dismiss_and_invalidate.py) to make explicit what
they are relevant/affected checklists of. Wrapped two comprehensions
that exceeded 88 columns after the rename.
No behavior change; verified with bazel test //tools/review-checklists/...
Addresses review comment: #899 (comment)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The composite action was later moved from actions/review_checklists to tools/review-checklists, but the root BUILD file's copyright_checker srcs list still referenced the now-nonexistent "actions" directory (a no-op glob) and never gained a "tools" entry, so the new tools/review-checklists sources were not covered by the copyright check at all. Addresses review comment: #899 (comment) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Multiple non-merge_group events (e.g. several review comments posted in quick succession) can each trigger a completed 'Review Checklists (Trigger)' run for the same head SHA, spawning overlapping apply-stage runs in the same concurrency group. Let a newer run cancel an older, now-superseded one instead of leaving it queued/running: the job only recomputes idempotent PR state (comments/statuses) from scratch each time, so cancelling a stale run is safe and matches this repo's existing convention of setting cancel-in-progress explicitly. Addresses review comment: #899 (comment) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Addresses review comment: #899 (comment) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Otterdog's status-check reference distinguishes plain commit statuses (which need the 'any:' prefix) from GitHub Actions workflow-job or GitHub App statuses. The 'review-checklists' status this action sets via create_status is a plain commit status, so an Otterdog-managed branch protection rule/ruleset must reference it as 'any:review-checklists' — the bare name would not be recognized. Addresses review comment: #899 (comment) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
helpers.find_ok_replies (unused in production), helpers.collect_ acknowledgement_details, check_acknowledgements._collect_ok_ acknowledgements, and dismiss_and_invalidate._find_ok_comments_for_ checklist each independently re-implemented the same scan for OK-reply comments to a checklist finding. Extract a single shared helpers.find_ok_replies_for_checklists(pr, existing_comments, checklist_ids) that returns the 'superset' (full comment objects, in one pass over pr.get_review_comments()) and have each caller project it down to the narrower shape it needs. This also removes an O(n*k) re-scan in dismiss_and_invalidate.handle_synchronize (one scan per affected checklist) in favor of a single O(n) pass. Also renamed the now-shared collect_acknowledgement_details parameter 'relevant_ids' to 'checklist_ids' for clarity, since it isn't obvious from the name alone what the ids are relevant to. Addresses review comments: #899 (comment) #899 (comment) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
'_main' is the current Bzlmod-internal runfiles-directory name for the root module. Rlocation() accepts the module name directly and resolves it through the runfiles repo mapping to whichever directory the root module is actually materialized under. Use 'score_communication' (this repo's declared module name in MODULE.bazel) instead — this is the portable, correct API usage, and keeps working even if Bazel's internal main-repo directory naming convention changes, instead of depending on that implementation detail directly. Addresses review comment: #899 (comment) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Rename 'cl' -> 'checklist' and 'cid' -> 'checklist_id' (and 'cl_copy' -> 'checklist_copy', 'comment_id_to_cid' -> 'comment_id_to_checklist_id') throughout helpers.py, check_acknowledgements.py, dismiss_and_invalidate.py, post_checklists.py, and helpers_test.py. These abbreviations made call sites and comprehensions harder to scan at a glance, especially where 'cl' (checklist) could be misread as a different kind of list/class abbreviation. Wrapped the handful of lines that exceeded 88 columns after the rename. Addresses review comments (previously deferred by the reviewer to a future PR): #899 (comment) #899 (comment) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Following up on discussion_r3772590463 ('relevant_ids is a bad naming
... what is it relevant for?'): the same ambiguity existed one level up
in the variables holding the matched checklist dicts themselves.
'relevant' and 'affected' read as bare adjectives with no object, which
invited exactly this kind of question at every read site. Rename to
'relevant_checklists' (helpers.match_checklists/build_evidence_block,
check_acknowledgements.py, post_checklists.py) and
'affected_checklists' (dismiss_and_invalidate.py) to make explicit what
they are relevant/affected checklists of. Wrapped two comprehensions
that exceeded 88 columns after the rename.
No behavior change; verified with bazel test //tools/review-checklists/...
Addresses review comment: #899 (comment)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…tion race The 'Resolve PR number' step previously matched open PRs by head.sha == workflow_run.head_sha. If the PR receives another push while the apply run is queued or executing, no open PR will still have that exact SHA as its head by the time this step runs, so pr_number resolves empty and the whole apply run silently no-ops for that (now-superseded) trigger. Match on the head branch reference instead (workflow_run.head_repository.owner.login + head_branch via 'pulls?head=owner:branch'), which stays valid across such races as long as the PR is still open — the exact commit no longer matters for identifying which PR this run belongs to. Downstream steps already re-fetch the PR object and use its live pr.head.sha for everything except the (SHA-independent) merge_group status-setting path, so no script changes were needed. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Disable the example checklist to not spam users when the infrastructure is merged. Actual production checklists to be merged later.
Relocates the composite action and its scripts from actions/review_checklists/ to tools/review-checklists/, updating all path references: MODULE.bazel's pip.parse requirements_lock, the Bazel BUILD files' visibility/label, requirements.txt(.in)'s embedded paths, and the apply workflow's three 'uses: ./...' steps. Adds tools/review-checklists/README.md documenting: - What the action/workflows do and the acknowledgement model (threaded 'OK' replies to file-level review comment findings). - Why the logic is split into an unprivileged trigger workflow and a privileged workflow_run-triggered apply workflow (pwn-request prevention), and that the apply workflow always reads .github/review_checklists.yml from the base branch, never the PR branch. - A step-by-step list of what a consuming repository must additionally configure: vendoring/referencing the action, creating the checklist config, adjusting the base-branch filter, requiring the 'review-checklists' commit status (not the workflow job) plus an approving-review rule in branch protection, the merge-queue notice's assumption that the queue's merge method is a real merge commit, the 'reply OK in-thread' acknowledgement UX, the diff-position-1 anchor constraint on checklist include patterns, runner/network requirements, and keeping (or updating) the trigger workflow's filename since it's looked up by name for run-history diffing. - The checklist YAML schema and the composite action's inputs. All 5 existing Bazel tests (including the requirements lock check) pass unchanged at the new location. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1332063 to
997ef3e
Compare
Switch from create_review(comments=[{path, position: 1, body}]) to the
single-comment create_review_comment(..., subject_type="file") API.
File-level review comments are anchored to a file as a whole rather
than a specific diff position, so checklist include patterns can now
match binary files (images, archives, etc.) without failing to post.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This makes glob builds more reliable on different platforms.
Previously, the merge_group handler in check_acknowledgements.py just
set the commit status to success unconditionally ("checklists assumed
OK"), trusting that the required status check had already validated
the evidence before the PR entered the queue. This left a window where
stale evidence (e.g. dismissed reviews, invalidated acks after the
initial check went green) would not be caught at merge-queue time.
Now the merge_group handler resolves the originating PR (parsed from
the merge-queue's synthetic gh-readonly-queue/<base>/pr-<n>-<sha> head
ref) and re-runs the same acknowledgement validation against its
current live state (changed files, checklist findings, OK replies,
approving reviewers), setting success/pending/failure on the merge
commit SHA accordingly. It performs no PR writes (no evidence/notice
updates), only a read-only re-validation.
Refactored the shared decision logic (approvers vs. acks -> state)
into _acknowledgement_status() so both the live-PR flow and the
merge_group validation path use identical logic.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace the apply stage's own PR-number resolution (matching the workflow_run head branch, or a fragile commit-SHA search for pull_request_review*/fork events) with a value read directly from the trusted event payload in the trigger stage and forwarded as a build artifact. This fixes a real bug: for pull_request_review and pull_request_review_comment events, github.event.workflow_run's head_repository/head_branch always reflect the base repository, never the fork the review was actually submitted on, so the previous owner:branch lookup could never resolve a PR number for fork PRs on those events. The PR number is not sensitive/attacker-steerable data (worst case a wrong number just points at the wrong already-public, same-repo PR), so carrying it across the trust boundary this way does not reintroduce the pwn-request risk the two-stage split exists to avoid. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
GITHUB_EVENT_NAME is a GitHub Actions reserved variable name: setting it via a step's env: block is silently ignored for the subprocess, which always sees the runner's real ambient value instead (here, "workflow_run", since that's what actually triggers review_checklists_apply.yml). This meant check_acknowledgements.py's `if event_name == "merge_group"` branch could never be reached when invoked from the apply workflow — confirmed live: for merge_group runs the script instead executed the normal PR-head-sha flow, posting the merge-queue notice and setting the commit status on pr.head.sha instead of the merge queue's synthetic HEAD_SHA, and never actually re-running _validate_checklist_evidence(). Fix: use a non-reserved variable name (CHECKLISTS_EVENT_NAME) to pass the original triggering event name into the script, so a step's own env: can actually override it. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…yle)
Python's fnmatch treats '*' as matching across '/' with no distinction
between recursive and single-segment wildcards, so patterns that look
like standard glob syntax behave unintuitively:
- "**/*.md" requires a literal '/' in the matched path, so it silently
fails to match root-level files (discovered while live-testing a
multi-checklist merge-queue scenario).
- There was no way to anchor a pattern to the repo root at all.
Switch include/exclude matching to the pathspec library's gitwildmatch
style, i.e. the same pattern semantics as .gitignore:
- Unanchored patterns ("*.md") match at any depth.
- A leading '/' anchors a pattern to the repo root ("/*.md").
- "**" explicitly matches zero or more path segments, and correctly
matches at the root too ("**/BUILD" now matches a root-level BUILD
file, unlike the previous fnmatch-based implementation).
Added pathspec as a new dependency (requirements.txt.in/BUILD), and
added tests covering root-anchored and unanchored matching. Documented
the new gitignore-style syntax in the README. All 5/5 existing tests
continue to pass unchanged.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Public visibility is actually not required for these tools. Recude it to necessary levels.
The composite action was later moved from actions/review_checklists to tools/review-checklists, but the root BUILD file's copyright_checker srcs list still referenced the now-nonexistent "actions" directory (a no-op glob) and never gained a "tools" entry, so the new tools/review-checklists sources were not covered by the copyright check at all. Addresses review comment: #899 (comment) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Multiple non-merge_group events (e.g. several review comments posted in quick succession) can each trigger a completed 'Review Checklists (Trigger)' run for the same head SHA, spawning overlapping apply-stage runs in the same concurrency group. Let a newer run cancel an older, now-superseded one instead of leaving it queued/running: the job only recomputes idempotent PR state (comments/statuses) from scratch each time, so cancelling a stale run is safe and matches this repo's existing convention of setting cancel-in-progress explicitly. Addresses review comment: #899 (comment) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Addresses review comment: #899 (comment) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Otterdog's status-check reference distinguishes plain commit statuses (which need the 'any:' prefix) from GitHub Actions workflow-job or GitHub App statuses. The 'review-checklists' status this action sets via create_status is a plain commit status, so an Otterdog-managed branch protection rule/ruleset must reference it as 'any:review-checklists' — the bare name would not be recognized. Addresses review comment: #899 (comment) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
helpers.find_ok_replies (unused in production), helpers.collect_ acknowledgement_details, check_acknowledgements._collect_ok_ acknowledgements, and dismiss_and_invalidate._find_ok_comments_for_ checklist each independently re-implemented the same scan for OK-reply comments to a checklist finding. Extract a single shared helpers.find_ok_replies_for_checklists(pr, existing_comments, checklist_ids) that returns the 'superset' (full comment objects, in one pass over pr.get_review_comments()) and have each caller project it down to the narrower shape it needs. This also removes an O(n*k) re-scan in dismiss_and_invalidate.handle_synchronize (one scan per affected checklist) in favor of a single O(n) pass. Also renamed the now-shared collect_acknowledgement_details parameter 'relevant_ids' to 'checklist_ids' for clarity, since it isn't obvious from the name alone what the ids are relevant to. Addresses review comments: #899 (comment) #899 (comment) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
'_main' is the current Bzlmod-internal runfiles-directory name for the root module. Rlocation() accepts the module name directly and resolves it through the runfiles repo mapping to whichever directory the root module is actually materialized under. Use 'score_communication' (this repo's declared module name in MODULE.bazel) instead — this is the portable, correct API usage, and keeps working even if Bazel's internal main-repo directory naming convention changes, instead of depending on that implementation detail directly. Addresses review comment: #899 (comment) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
get_reviews() returns every review a user has ever submitted on the PR (in submission order), not just their latest state, so a user can approve and later request changes (or have an approval dismissed) in a subsequent review. The approvers.discard() call is what removes such a since-superseded approval recorded earlier in the same loop — it is not a no-op, even though 'approvers' starts as an empty set. Addresses review comment: #899 (comment) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Addresses review comment: #899 (comment) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fail-closed to False on any lookup error is intentional here: the only callers use the result to decide whether to (re-)post an advisory merge-queue notice on the PR, so a false negative merely delays that notice — it never affects merge gating. The actual gating decision for merge_group events is made independently in check_acknowledgements.py, which resolves and validates the underlying PR itself and fails the commit status (rather than defaulting to success) if that resolution or validation fails. Addresses review comment: #899 (comment) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
_validate_checklist_evidence (used for merge_group evidence validation) and main()'s live-PR flow both derived 'relevant_ids' as only the relevant checklists that already have a posted finding comment, and then treated an empty relevant_ids as the only 'not all posted yet' case. If some but not all relevant checklists had been posted (e.g. a partial post_checklists run, or a checklist added to the config after others were already posted), the unposted ones were silently dropped from consideration entirely, and the status could go to 'success' once the posted subset was fully acknowledged — without a checklist that was never even posted ever being acknowledged. Fixed to require every relevant checklist to have a posted comment before proceeding past 'pending'. Addresses review comment: #899 (comment) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… handling Explain that this is_pr_in_merge_queue() check in main()'s regular (non-merge_group) flow is intentionally distinct from the merge_group handling above it: it covers events fired for a PR that is still enqueued but is not itself a merge_group event (e.g. a review comment posted while the PR is queued), so the advisory merge-queue notice can be (re-)posted for it. Addresses review comment: #899 (comment) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Rename 'cl' -> 'checklist' and 'cid' -> 'checklist_id' (and 'cl_copy' -> 'checklist_copy', 'comment_id_to_cid' -> 'comment_id_to_checklist_id') throughout helpers.py, check_acknowledgements.py, dismiss_and_invalidate.py, post_checklists.py, and helpers_test.py. These abbreviations made call sites and comprehensions harder to scan at a glance, especially where 'cl' (checklist) could be misread as a different kind of list/class abbreviation. Wrapped the handful of lines that exceeded 88 columns after the rename. Addresses review comments (previously deferred by the reviewer to a future PR): #899 (comment) #899 (comment) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Rename a few names left unclear by a broader review pass: - helpers.py: rename '_find_checklists_config's local 'r' -> 'runfiles' (matches the imported Runfiles type it holds an instance of). - dismiss_and_invalidate.py: rename '_get_files_in_latest_push's list comprehension variable 'r' -> 'run' (workflow run objects, not something needing single-letter treatment); rename 'handle_synchronize's 'review' -> 'finding_comment' — it actually holds the bot-posted checklist finding comment looked up from 'existing', not a PR review object, so the old name was actively misleading, not just terse. - check_acknowledgements.py: rename '_acknowledgement_status's single-letter comprehension variable 'u' -> 'username' for clarity. No behavior change; verified with bazel test //tools/review-checklists/... Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ed_ids These names looked like they held every relevant/affected checklist id, but they actually only held the subset that already has a posted finding comment (filtered by 'if checklist["id"] in existing'). That ambiguity was directly responsible for a prior bug in this same code (commit 631b5ff): a checklist missing from this filtered list was silently treated as 'no relevant checklists remain to check' instead of 'a relevant checklist has not been posted yet'. Renaming to make the 'posted' filtering explicit at every read site, not just at the point of construction. No behavior change; verified with bazel test //tools/review-checklists/... Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Following up on discussion_r3772590463 ('relevant_ids is a bad naming
... what is it relevant for?'): the same ambiguity existed one level up
in the variables holding the matched checklist dicts themselves.
'relevant' and 'affected' read as bare adjectives with no object, which
invited exactly this kind of question at every read site. Rename to
'relevant_checklists' (helpers.match_checklists/build_evidence_block,
check_acknowledgements.py, post_checklists.py) and
'affected_checklists' (dismiss_and_invalidate.py) to make explicit what
they are relevant/affected checklists of. Wrapped two comprehensions
that exceeded 88 columns after the rename.
No behavior change; verified with bazel test //tools/review-checklists/...
Addresses review comment: #899 (comment)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
997ef3e to
71a0974
Compare
|
I'll update the CI design tomorrow. |
Adds a workflow to require acknowledgement of checklists by reviewers.
Currently no checklist is enforced.