Skip to content
Draft
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
731d00c
Add independent peer review validation workflow
AndrewGable May 13, 2026
aba7b19
Use GitHub App client ID for peer review check
AndrewGable May 13, 2026
2d2a6c9
Use GraphQL for peer review validation
AndrewGable May 15, 2026
89bebf1
Simplify peer review validation helpers
AndrewGable May 15, 2026
e72fba8
Add newline
AndrewGable May 15, 2026
f473b3b
Hard-code OS Botify app ID
AndrewGable May 15, 2026
23b2a2c
use client-id
AndrewGable May 15, 2026
61d554c
Fix OS Botify token permissions
AndrewGable May 15, 2026
0b48d74
Rename peer review workflow
AndrewGable May 15, 2026
e57f967
Rename peer review verifier
AndrewGable May 15, 2026
b6aea85
Clarify peer review failures
AndrewGable May 15, 2026
2d8ce9c
Test informational peer review checks
AndrewGable May 15, 2026
4bee49d
Undo draft
AndrewGable May 15, 2026
1d3a559
Show informational peer review failures
AndrewGable May 15, 2026
74415cd
Restore hard peer review failure
AndrewGable May 15, 2026
74efe1d
Pass peer review check before approval
AndrewGable May 18, 2026
72a99fd
Use Melvin app for peer review check
AndrewGable May 18, 2026
fec8f31
Pin Node 24 and upgrade setup-node to v6 with npm cache
roryabraham Jun 10, 2026
76b600f
Add GithubUtils wrapper with rate limiting
roryabraham Jun 10, 2026
9eafdd8
Extract peer review logic into testable modules
roryabraham Jun 10, 2026
da27d6e
Normalize co-author email parsing to match Web-Expensify chore
roryabraham Jun 10, 2026
104bf8e
Extract evaluatePeerReview pass/skip/fail decision function
roryabraham Jun 10, 2026
c690b0c
Fail peer review check when branch protection cannot be read
roryabraham Jun 10, 2026
a886588
Add unit tests for peer review verification
roryabraham Jun 10, 2026
0a8b0f8
Use Peer Review Checker bot and fix workflow triggers
roryabraham Jun 10, 2026
11c368d
Add test and typecheck CI workflows and local dev docs
roryabraham Jun 10, 2026
82015d8
Remove OSBotify guard from test and typecheck jobs
roryabraham Jun 10, 2026
0ba1286
Rename GithubUtils to GitHubAPI and standardize module exports
roryabraham Jun 10, 2026
c61abf0
Rename GitHubAPI class to GitHubAPIClient
roryabraham Jun 10, 2026
a20f5bd
Switch peer review verification to CLI-based arguments
roryabraham Jun 11, 2026
6a0d11f
Use published expensify-common 2.0.184 for CLI parsing
roryabraham Jun 18, 2026
7a72ee9
Add ESLint with eslint-config-expensify and lint CI workflow
roryabraham Jun 18, 2026
f817ca1
Upgrade actions/create-github-app-token to 3.2.0
roryabraham Jun 25, 2026
3e08588
app-id -> client-id
roryabraham Jun 25, 2026
f4bdf04
Alphabetize scripts
roryabraham Jun 25, 2026
cf5b09d
refactor: move libs to scripts/libs
roryabraham Jun 25, 2026
4236743
Add Prettier with CI check
roryabraham Jun 25, 2026
fa4e331
refactor(peer-review): drop eventContext eslint overrides
roryabraham Jun 25, 2026
d082b36
refactor(tests): drop test eslint overrides
roryabraham Jun 26, 2026
41b95cb
refactor: extract CLI wrapper to scripts/libs/CLI.ts
roryabraham Jun 26, 2026
334a6d6
chore: upgrade expensify-common to 2.0.188 for CLI subpath
roryabraham Jun 26, 2026
1537ef7
style: fix prettier formatting in eventContext.ts
roryabraham Jun 26, 2026
b3d954a
Remove unhelpful docs
roryabraham Jun 26, 2026
c3b65ea
ci: broaden workflow path filters for JS/TS extensions
roryabraham Jun 26, 2026
3b1ce0d
ci: remove redundant CI=true from workflow env
roryabraham Jun 26, 2026
af679b5
refactor: flatten peer review libs into reusable modules
roryabraham Jun 26, 2026
e243313
refactor: rename coAuthorEmails to parseCoAuthorEmails
roryabraham Jun 26, 2026
2296821
Run prettier
roryabraham Jun 26, 2026
87af15a
refactor: extract uniqueSorted into CollectionUtils
roryabraham Jun 26, 2026
38a4caf
refactor: split GitHubUtils into focused submodules
roryabraham Jun 26, 2026
4a6aa55
feat: detect GitHub App bots via isBotUser helper
roryabraham Jun 26, 2026
3630fe4
refactor: rename CLI arg to --pull-request-number
roryabraham Jun 26, 2026
e38935d
refactor: inline CLI parsing in verifyPeerReview main
roryabraham Jun 26, 2026
40831be
refactor: inline peer review failure handler in main catch
roryabraham Jun 26, 2026
a944774
refactor: extract prSlug in evaluatePeerReview
roryabraham Jun 26, 2026
ec927c2
feat: resolve co-authors from display name fallback
roryabraham Jun 26, 2026
62b825a
Fix prettier
roryabraham Jun 26, 2026
817d866
fix: throw when canonical commit author cannot be resolved
roryabraham Jun 29, 2026
7c496a8
Fix prettier
roryabraham Jun 29, 2026
3a3b3e8
fix: attribute authors from referenced PR on merge commits
roryabraham Jun 29, 2026
de60ef3
Revert "fix: attribute authors from referenced PR on merge commits"
roryabraham Jun 29, 2026
f496ec3
Merge origin/main into andrew-validate-independent-peer-review
roryabraham Jul 2, 2026
18facec
Merge origin/main into andrew-validate-independent-peer-review
roryabraham Jul 9, 2026
d7c2da9
Add OSBotify and CLABotify to known bot users
roryabraham Jul 9, 2026
6f10b59
Match employee approvers case-insensitively
roryabraham Jul 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions .github/workflows/verifyPeerReview.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Note: This workflow is configured to run on all pull requests throughout the Expensify org, not just this repo.
# That has a few consequences:
# - We need to checkout the repo it's running on, and not just the GitHub-Actions repo
# - branch and path matching does not work in the workflow layer. From the docs: https://docs.github.com/en/enterprise-cloud@latest/repositories/configuring-branches-and-merges-in-your-repository/managing-rulesets/available-rules-for-rulesets#supported-event-triggers
# > Any filters you specify for the supported events are ignored - for example, branches, branches-ignore, paths, types and so on. The workflow is only triggered, and is always triggered, by the default activity types of the supported events
name: Verify peer review
run-name: Verify peer review for ${{ github.repository }}#${{ github.event.pull_request.number }}

on: pull_request

permissions:
contents: read
pull-requests: read

jobs:
verifyPeerReview:
name: Check independent approval
runs-on: blacksmith-2vcpu-ubuntu-2404
steps:
# v3.1.1
- name: Generate a GitHub App token
id: generateAppToken
uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3
with:
client-id: Iv1.26553ed1d375dd3c
private-key: ${{ secrets.OS_BOTIFY_PRIVATE_KEY }}
owner: ${{ github.repository_owner }}
repositories: |
${{ github.event.repository.name }}
GitHub-Actions
permission-contents: read
permission-members: read
permission-metadata: read
permission-pull-requests: read
Comment thread
roryabraham marked this conversation as resolved.

- name: Checkout repos
id: repo
uses: Expensify/GitHub-Actions/checkoutRepoAndGitHubActions@main

# v4.3.0
- name: Setup Node
uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e

- name: Install npm packages
run: npm ci
working-directory: GitHub-Actions

- name: Verify peer review
run: npm run verify-peer-review
working-directory: GitHub-Actions
env:
GITHUB_TOKEN: ${{ steps.generateAppToken.outputs.token }}
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ jobs:
secrets: inherit
```

### `verifyPeerReview.yml`

Used as an org-level ruleset workflow to block pull requests that do not have enough independent Expensify employee approvals. The check only reads GitHub pull request metadata; it does not checkout or execute code from the pull request branch.

This workflow requires a GitHub App token with read access for repository metadata, pull requests, and organization members. It uses the OS Botify client ID `Iv1.26553ed1d375dd3c` and `OS_BOTIFY_PRIVATE_KEY` to generate that token. If GitHub does not return a branch-protection review count, the workflow defaults to requiring one independent approval, so the ruleset should target only the intended protected branches.

## Rulesets
GitHub [org-level rulesets](https://docs.github.com/en/enterprise-cloud@latest/repositories/configuring-branches-and-merges-in-your-repository/managing-rulesets/available-rules-for-rulesets#require-workflows-to-pass-before-merging) can be configured to run a workflow check against pull requests in all repos in the org. This is a very powerful feature, but there are some caveats and best practices to be aware of when enabling a ruleset.

Expand All @@ -47,3 +53,4 @@ GitHub [org-level rulesets](https://docs.github.com/en/enterprise-cloud@latest/r
- If you need to target or exclude specific paths, that must be implemented manually in the workflow itself.
- Due to a GitHub :bug:, PRs that are open when the rule is enabled will get stuck with a pending check that will never get picked up. The easiest way to fix that is to close and reopen the PR. Consider writing a script to close and reopen all open PRs across the org after the check is enabled.
- It is less disruptive to [configure the ruleset to `Evaluate` first](https://docs.github.com/en/enterprise-cloud@latest/repositories/configuring-branches-and-merges-in-your-repository/managing-rulesets/available-rules-for-rulesets#using-evaluate-mode-for-ruleset-workflows), then `Active` once the kinks are worked out.
- For `verifyPeerReview.yml`, start with a ruleset targeting only a test branch, then test the workflow from a GitHub-Actions branch, then from `main`, and only then enable it for the intended repositories and branches.
Loading
Loading