Skip to content

feat(automerge): add safe-paths reusable for docs/tests-only PRs#22

Merged
topcoder1 merged 1 commit into
mainfrom
feat/safe-paths-automerge
May 3, 2026
Merged

feat(automerge): add safe-paths reusable for docs/tests-only PRs#22
topcoder1 merged 1 commit into
mainfrom
feat/safe-paths-automerge

Conversation

@topcoder1

Copy link
Copy Markdown
Owner

Summary

A new reusable workflow that auto-enables gh pr merge --auto --squash on any PR (any author) whose diff touches only paths matching the safe list:

  • docs/**
  • tests/**
  • test_*.py / *_test.{py,go,js,ts}
  • *.test.{jsx,tsx,js,ts} / *.spec.{jsx,tsx,js,ts}
  • **/__tests__/**

All-or-nothing: if even one changed file is outside the safe list, this workflow no-ops and downstream gating (claude-author-automerge, manual click) decides.

Rationale

Docs have zero runtime impact and tests can't break runtime — failing tests don't ship; passing tests don't change behavior. Maintainers click-merging typo fixes and test additions is alarm-fatigue-by-design. The path classification IS the safety argument.

This is a deliberate carve-out from the existing "human-authored PRs default to manual click-merge" policy. Sensitive surfaces (auth, secrets, migrations, workflows, infra) are still excluded — they don't match any of the safe globs, so this workflow leaves them to the existing gates.

Caller integration

# .github/workflows/safe-paths-automerge.yml in the caller repo
name: Safe-paths Auto-merge

on:
  pull_request:
    types: [opened, synchronize, reopened, ready_for_review]

permissions:
  contents: write
  pull-requests: write

concurrency:
  group: safe-paths-automerge-${{ github.event.pull_request.number }}
  cancel-in-progress: true

jobs:
  automerge:
    uses: topcoder1/ci-workflows/.github/workflows/safe-paths-automerge.yml@main
    secrets: inherit
    with:
      merge_method: squash

Optional extra_safe_globs input lets callers extend the list per-repo (e.g. ^changelogs/.*, ^.devcontainer/.*).

Verification

  • python3 yaml.safe_load parses; actionlint clean.
  • Local smoke test against 7 diff scenarios:
    • docs-only → SAFE ✓
    • tests-only → SAFE ✓
    • mixed test layouts (Python + Go + JS + co-located) → SAFE ✓
    • docs + src → UNSAFE ✓
    • src-only → UNSAFE ✓
    • workflow file → UNSAFE ✓
    • src/.../test_*.py → SAFE (intentional — Python test convention)

Notes

  • Auto-merge uses GitHub native gh pr merge --auto, so required-status-checks on the caller's branch-protection ruleset still apply. This workflow does not bypass branch protection.
  • A bypass hole exists for production code named test_*.py (e.g. src/webcrawl/test_helpers.py). The Python convention is that test_*.py is always test code, but the workflow can't enforce that. Mitigation: existing CI build/lint/Claude-review still runs on every PR, so any production code accidentally classified as a test would still get reviewed.

Auto-merge rationale

Workflow file → manual click-merge per CLAUDE.md.

🤖 Generated with Claude Code

Auto-enables `gh pr merge --auto --squash` on any PR (any author)
whose diff touches ONLY paths matching the safe list:

  - docs/**
  - tests/**
  - test_*.py / *_test.{py,go,js,ts} (Python + Go + JS test conventions)
  - *.test.{js,ts,jsx,tsx} / *.spec.{js,ts,jsx,tsx}
  - **/__tests__/**

The gate is all-or-nothing: if even one changed file is outside the
safe list, this workflow no-ops and downstream gating workflows
(claude-author-automerge, manual click) decide.

Rationale: docs have zero runtime impact and tests can't break
runtime — failing tests don't ship, passing tests don't change
behavior. Maintainers click-merging typo fixes and test additions
is alarm-fatigue-by-design; the path classification IS the safety
argument.

Auto-merge enablement uses GitHub native `gh pr merge --auto`, so
required-status-checks on the caller repo's branch protection still
apply. This workflow does not bypass them.

Caller repos opt in by adding a thin wrapper that calls this
reusable on `pull_request` events. Optional `extra_safe_globs`
input lets callers extend the list with repo-specific safe paths
(e.g. `^changelogs/.*`, `^.devcontainer/.*`).

Smoke-tested classification locally with seven path mixes:
docs-only / tests-only / mixed-test-layouts / mixed-with-src /
src-only / workflow-file / nested-src-test_*.py — all expected
outcomes.
@claude

claude Bot commented May 3, 2026

Copy link
Copy Markdown

No issues found. Logic is sound, failure modes are safe (loud fail, never silent bypass), and the known path-classification trade-off for Python test naming is clearly documented.

@topcoder1
topcoder1 merged commit 8b47e28 into main May 3, 2026
4 checks passed
@topcoder1
topcoder1 deleted the feat/safe-paths-automerge branch May 3, 2026 01:10
topcoder1 added a commit that referenced this pull request May 3, 2026
## Summary

The `safe-paths-automerge.yml` reusable from #22 runs `gh pr diff "\$PR"
--name-only` without a local checkout. gh falls back to spawning git
locally and fails:

```
failed to run git: fatal: not a git repository (or any of the parent directories): .git
##[error]Process completed with exit code 1.
```

Pass `--repo "\$GITHUB_REPOSITORY"` so gh hits the API directly. No
checkout step needed.

## How it surfaced

The very first install of the caller (`topcoder1/webcrawl#130`)
triggered the workflow on the install PR itself. The workflow's diff was
the workflow file — not a safe path — so it should have logged
"deferring" and exited 0. Instead it crashed at the `gh pr diff` line.
Net effect on #130: the workflow shows red but doesn't block merge (it's
not a required check); the PR can still merge manually.

Self-test caught it before the user merged the install — exactly what
the self-test step was for.

## Auto-merge rationale

Workflow file → manual click-merge per CLAUDE.md. Same caveat as #22 —
fleet-wide blast radius if the regex change is wrong, even though this
is a 1-line `--repo` addition.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Jonathan Zhang <[email protected]>
Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant