Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
38 changes: 27 additions & 11 deletions scripts/compliance-audit.sh
Original file line number Diff line number Diff line change
Expand Up @@ -417,28 +417,44 @@ check_codeowners() {
if [ "$found" = false ]; then
add_finding "$repo" "settings" "missing-codeowners" "error" \
"No \`CODEOWNERS\` file found — required for code owner review enforcement (pr-quality ruleset)" \
"standards/github-settings.md#codeowners-standard"
"standards/codeowners-standard.md"
return
fi

# Extract non-comment, non-blank owner lines for accurate matching.
# Each such line has the form: <pattern> <owner1> [<owner2> ...]
# We check that every owner line includes both required bot accounts.
# Standard (codeowners-standard.md): every owner line MUST include
# @petry-projects/org-leads, and legacy bot/individual listings are forbidden.
local owner_lines
owner_lines=$(echo "$codeowners_content" | grep -v '^\s*#' | grep -v '^\s*$')

local missing_bots=()
if ! echo "$owner_lines" | grep -qE '@petry-projects-pr-review-agent(\s|$)'; then
missing_bots+=("@petry-projects-pr-review-agent")
if [ -z "$owner_lines" ]; then
add_finding "$repo" "settings" "codeowners-empty" "error" \
"CODEOWNERS file has no owner lines (only comments/blank)" \
"standards/codeowners-standard.md"
return
fi
if ! echo "$owner_lines" | grep -qE '@dependabot-automerge-petry(\s|$)'; then
missing_bots+=("@dependabot-automerge-petry")

# Required: @petry-projects/org-leads on every owner line
local lines_missing_team
lines_missing_team=$(echo "$owner_lines" | grep -vE '@petry-projects/org-leads(\s|$)' || true)
Comment thread
don-petry marked this conversation as resolved.
Resolved
if [ -n "$lines_missing_team" ]; then
add_finding "$repo" "settings" "codeowners-missing-team" "error" \
"CODEOWNERS owner lines are missing required \`@petry-projects/org-leads\` team — approvals will not satisfy require_code_owner_review" \
"standards/codeowners-standard.md"
Comment thread
don-petry marked this conversation as resolved.
Resolved
fi
Comment thread
don-petry marked this conversation as resolved.
Resolved

if [ "${#missing_bots[@]}" -gt 0 ]; then
add_finding "$repo" "settings" "codeowners-missing-bots" "error" \
"CODEOWNERS is missing required bot accounts on owner lines: ${missing_bots[*]} — bot approvals will not satisfy require_code_owner_review" \
"standards/github-settings.md#codeowners-standard"
# Forbidden: legacy direct listings (individuals or deprecated bot accounts)
local forbidden_owners=()
for legacy in '@petry-projects-pr-review-agent' '@dependabot-automerge-petry' '@don-petry'; do
if echo "$owner_lines" | grep -qE "${legacy}(\s|$)"; then
Comment thread
don-petry marked this conversation as resolved.
Resolved
forbidden_owners+=("$legacy")
fi
done
if [ "${#forbidden_owners[@]}" -gt 0 ]; then
add_finding "$repo" "settings" "codeowners-legacy-listings" "error" \
"CODEOWNERS contains forbidden legacy direct listings: ${forbidden_owners[*]} — manage membership via the @petry-projects/org-leads team instead" \
"standards/codeowners-standard.md"
fi
}

Expand Down
88 changes: 73 additions & 15 deletions standards/codeowners-standard.md
Original file line number Diff line number Diff line change
@@ -1,42 +1,100 @@
# CODEOWNERS Standard

**Status:** Active
**Status:** Required
**Applies to:** All repos in `petry-projects` org
**Enforced by:** [`compliance-audit.sh`](../scripts/compliance-audit.sh) (`check_codeowners`)
**Migration completed:** 2026-05-04

## Rule

CODEOWNERS files MUST reference the `@petry-projects/org-leads` team rather than individual users or bot accounts.
CODEOWNERS files MUST reference the `@petry-projects/org-leads` team rather
than individual users or bot accounts. The default `*` pattern MUST list
exactly `@petry-projects/org-leads` and no individuals.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved

```text
# Default
* @petry-projects/org-leads
```

Path-specific rules MAY use additional teams or individuals when a narrower owner is appropriate, but the default `*` rule must always include `@petry-projects/org-leads`.
Path-specific rules MAY use additional teams or individuals when a narrower
owner is appropriate, but every owner line MUST include
`@petry-projects/org-leads` (so the team can always satisfy
`require_code_owner_review`).
Comment thread
don-petry marked this conversation as resolved.
Resolved

The legacy direct-listing pattern is **forbidden**:

```text
# DO NOT USE
* @don-petry @petry-projects-pr-review-agent @dependabot-automerge-petry
```

## Why

- **Stable CODEOWNERS files** — adding/removing an owner is a team membership change, not a multi-repo PR
- **CODEOWNERS support for automation** — GitHub Apps cannot be listed in CODEOWNERS
(platform limitation), but a machine user in the team can satisfy
`require_code_owner_review`. See [pr-review-agent issue #27](https://github.com/don-petry/pr-review-agent/issues/27)
- **Centralized control** — team membership is managed in one place via the org admin UI
- **Stable CODEOWNERS files** — adding/removing an owner is a team membership
change, not a multi-repo PR
- **CODEOWNERS support for automation** — GitHub Apps **cannot** be listed in
CODEOWNERS (platform limitation,
[community discussion #23064](https://github.com/orgs/community/discussions/23064)).
A machine user in the team **can** satisfy `require_code_owner_review`. See
[pr-review-agent issue #27](https://github.com/don-petry/pr-review-agent/issues/27)
- **Centralized control** — team membership is managed in one place via the
org admin UI; no PR churn when membership changes

## Team Composition

The `@petry-projects/org-leads` team contains:
The [`@petry-projects/org-leads`](https://github.com/orgs/petry-projects/teams/org-leads)
team contains:

- `@don-petry` — primary maintainer (team maintainer)
- `@donpetry-bot` — automation machine user (used by pr-review-agent and similar tooling)
- `@donpetry-bot` — automation machine user (used by pr-review-agent and
similar tooling)

Add additional human maintainers as needed. Bots/apps that need code-owner
standing MUST be machine-user accounts added to this team, not GitHub Apps.

## Required Setup for New Bots

Add additional human maintainers as needed. Bots/apps that need code owner standing should be machine user accounts added to this team, not GitHub Apps.
When adding a new bot/automation account to the team:

1. Create a GitHub user account (machine user) — not a GitHub App
2. Add the account to the `@petry-projects/org-leads` team
3. Generate a **fine-grained PAT** with **Resource owner = `petry-projects`**
- Resource owner MUST be the org. A PAT scoped to the bot's personal
namespace will show "no access to any repositories" even if the bot is
in the org.
- Required permissions: Contents (read), Pull requests (read+write),
Commit statuses (read), Checks (read), Metadata (read), Members (read)
4. If the org has a fine-grained PAT approval policy, an org admin must
approve the request at
[Settings → Personal access tokens](https://github.com/organizations/petry-projects/settings/personal-access-token-requests)
5. Store the PAT as a repo or org secret for use by GitHub Actions

## Branch Protection

Repos that require code owner review must set `require_code_owner_review: true` on protected branches. Approvals from any member of `@petry-projects/org-leads` will satisfy the requirement.
Repos that require code owner review must set `require_code_owner_review: true`
on protected branches. Approvals from any member of `@petry-projects/org-leads`
will satisfy the requirement.

> **CODEOWNERS approval timing:** GitHub does not retroactively re-evaluate
> existing reviews if CODEOWNERS changes. After a CODEOWNERS edit, the next
> approval (or a re-request) is what counts.

## Verified End-to-End

The standard was validated on 2026-05-04 against
[TalkTerm#159](https://github.com/petry-projects/TalkTerm/pull/159) — a PR
in a repo with `require_code_owner_review: true`. After:

1. Migrating CODEOWNERS to `* @petry-projects/org-leads`
2. Issuing the bot a `petry-projects`-scoped fine-grained PAT
3. Adding `@donpetry-bot` to the team

## Migration
`pr-review-agent` posted an approval that flipped `reviewDecision` from
`REVIEW_REQUIRED` to `APPROVED`, confirming code-owner satisfaction via
team membership.

Repos still using individual owner lists should migrate via PR replacing the legacy `* @don-petry @petry-projects-pr-review-agent @dependabot-automerge-petry` line with `* @petry-projects/org-leads`.
## Migration History

The `petry-projects-pr-review-agent` GitHub App reference is non-functional (GitHub Apps cannot be CODEOWNERS) and should be removed during migration.
| Date | Change |
|------|--------|
| 2026-05-04 | Initial team-based standard adopted; all 6 child repos migrated (ContentTwin#128, TalkTerm#160, broodly#172, google-app-scripts#252, markets#153, bmad-bgreat-suite#133) |
20 changes: 9 additions & 11 deletions standards/dependabot-policy.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ security posture than chasing every minor/patch release.
2. **Version updates weekly** for GitHub Actions, since pinned action versions do not
affect application stability and staying current reduces CI attack surface.
3. **Labels** `security` and `dependencies` on every Dependabot PR for filtering and audit.
4. **Auto-merge** after all CI checks pass, using a GitHub App token to approve
and merge eligible PRs. Both bot accounts (`dependabot-automerge-petry` and
`petry-projects-pr-review-agent`) are listed as code owners in every repo's
`CODEOWNERS` file so their approvals satisfy `require_code_owner_review`
without any bypass. Eligible updates:
4. **Auto-merge** after all CI checks pass, approving and merging eligible PRs.
Approvals come from members of the `@petry-projects/org-leads` team listed
in every repo's `CODEOWNERS` file (see
[codeowners-standard.md](codeowners-standard.md)), so they satisfy
`require_code_owner_review` without any bypass. Eligible updates:
- **GitHub Actions**: all version bumps including major (SHA-pinned, no runtime impact)
- **App ecosystems**: patch and minor security updates only (major requires human review)
- **Indirect (transitive) dependencies**: all updates regardless of version bump
Expand Down Expand Up @@ -276,12 +276,10 @@ The workflow fails if any known vulnerability is found, blocking the PR from mer
— do **not** modify the secrets block or permissions.

> **Note:** The rebase workflow is **not** required for `require_code_owner_review`.
> The correct solution for CODEOWNERS enforcement is to list the bot accounts
> (`@dependabot-automerge-petry`, `@petry-projects-pr-review-agent`) as owners
> in every CODEOWNERS pattern — see the
> [CODEOWNERS Standard](github-settings.md#codeowners-standard). The earlier
> approach of using `gh api .../merge` as a bypass was fragile and has been
> superseded.
> The correct solution for CODEOWNERS enforcement is to list the
> `@petry-projects/org-leads` team in every CODEOWNERS pattern — see the
> [CODEOWNERS Standard](codeowners-standard.md). The earlier approach of
> using `gh api .../merge` as a bypass was fragile and has been superseded.
4. Add `workflows/dependency-audit.yml` to `.github/workflows/`.
5. **GitHub App secrets** — `APP_ID` and `APP_PRIVATE_KEY` are managed at the
**organization level** (`gh secret set <name> --org petry-projects --visibility all`),
Expand Down
40 changes: 15 additions & 25 deletions standards/github-settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -232,8 +232,9 @@ See [CI Standards](ci-standards.md) for workflow templates and patterns.
| App | Purpose | Installed |
|-----|---------|-----------|
| **Claude** | AI code review and PR assistance via Claude Code Action | 2026-03-20 |
| **dependabot-automerge-petry** | Provides approving review for Dependabot auto-merge; listed in CODEOWNERS so its approvals satisfy `require_code_owner_review` | 2026-03-23 |
| **petry-projects-pr-review-agent** | General PR review agent; listed in CODEOWNERS as the org-standard automation reviewer | 2026-04-01 |
| **dependabot-automerge-petry** | Provides approving review for Dependabot auto-merge | 2026-03-23 |
| **petry-projects-pr-review-agent** | (deprecated) GitHub App formerly used for PR review — replaced by `donpetry-bot` machine user in `@petry-projects/org-leads` because Apps cannot be CODEOWNERS | 2026-04-01 |
| **donpetry-bot** | Machine-user account in `@petry-projects/org-leads`; satisfies CODEOWNERS for automated PR review | 2026-05-04 |
| **SonarQube Cloud (SonarCloud)** | Code quality, security hotspots, coverage tracking | 2026-03-25 |
| **CodeRabbit AI** | AI-powered code review on PRs | 2026-03-25 |

Expand Down Expand Up @@ -282,38 +283,27 @@ All repositories MUST have these labels configured:
All repositories MUST have a `CODEOWNERS` file at `.github/CODEOWNERS`
(or `CODEOWNERS` at the repo root for repos with no `.github/` directory).

### Required Bot Accounts
The full policy lives in [`codeowners-standard.md`](codeowners-standard.md).
Summary:

Every CODEOWNERS file MUST include these two bot accounts alongside `@don-petry`
so that automated PR approvals satisfy the `require_code_owner_review` setting
in the `pr-quality` ruleset:

| Account | App | Role |
|---------|-----|------|
| `@petry-projects-pr-review-agent` | `petry-projects-pr-review-agent` | General org PR review bot |
| `@dependabot-automerge-petry` | `dependabot-automerge-petry` | Dependabot auto-merge approver |

The `pr-quality` ruleset requires **1 code owner approval**. With all three
accounts on every pattern, an approval from `@don-petry`, `@petry-projects-pr-review-agent`,
or `@dependabot-automerge-petry` satisfies the requirement — provided the approver
is not also the author of the last push to that branch (`require_last_push_approval`
prevents self-approval after one's own push). For Dependabot PRs this is never an
issue: Dependabot pushes the branch and a separate bot approves it.
- The default owner line MUST be `* @petry-projects/org-leads`
- Direct listings of users or bot accounts (e.g.,
`@petry-projects-pr-review-agent`, `@dependabot-automerge-petry`) are
**forbidden** — manage membership through the team instead
- GitHub Apps cannot be code owners (platform limitation); use machine-user
accounts added to the team

### Standard Template

```gitignore
# CODEOWNERS
# Each line is a pattern followed by one or more owners.
# Owners are matched in order, last matching pattern wins.
# Standard: https://github.com/petry-projects/.github/blob/main/standards/github-settings.md#codeowners-standard
# Standard: https://github.com/petry-projects/.github/blob/main/standards/codeowners-standard.md

# Default owner for all files
* @don-petry @petry-projects-pr-review-agent @dependabot-automerge-petry
* @petry-projects/org-leads
```

Repos with finer-grained path ownership (e.g., `/apps/api/`, `/infra/`) MUST
add the two bot accounts to every path-specific line, not just the default `*`.
Repos with finer-grained path ownership MUST include `@petry-projects/org-leads`
on every owner line so the team can always satisfy `require_code_owner_review`.

---

Expand Down
Loading