Skip to content

Repository files navigation

branchpol

Branch naming and protection policy checker for teams. Scans your remotes, reports violations, exits non-zero for CI.

Zero runtime dependencies · Works offline (with --local) · Open-core (MIT)

Node Test npm license


Every team agrees on a branch convention in the retro. Two sprints later the remote has wip, TEMP-fix, john-backup-final2, and nobody remembers which protection rules got dropped when the repo was migrated.

branchpol makes your branch policy executable:

  • Naming policy — required patterns (feat/*, fix/*, release/*), forbidden names (master, wip, anything matching deny-lists), lowercase enforcement, max length.
  • Protection policy — required branches must exist on the remote, banned branches must not, and the remote's default branch must match your policy.
  • Staleness sweep — flag remote-tracking branches with no commits for N days.
  • CI-native — human output locally, JSON for tooling, GitHub Actions annotations in CI, and a non-zero exit code when violations are found.

The problem

Branch sprawl is a governance problem that shows up as real risk: stale release/ branches nobody patched, unprotected default branches, personal branches holding unreviewed work. Code review catches code problems; nothing catches branch problems — until now.

Installation

npm install --global branchpol

Requires Node 18+ and git on your PATH. Works on macOS, Linux and Windows.

Quick start (30 seconds)

cd my-project
branchpol init        # create branchpol.config.json with sensible defaults
branchpol check       # scan origin's branches against policy
echo $?               # 0 = clean, 1 = violations, 2 = error

Example output:

branchpol — origin · 14 branches scanned

  wip
    ✗ forbidden pattern: matches forbidden pattern ^wip$
    ! naming: does not match any required pattern (^(feat|fix|...)/.+)
  TEMP-auth-retry
    ! case: should be lowercase (a-z, 0-9, . _ / -)

1 error(s), 2 warning(s)

In GitHub Actions, use --format github (or just run it in CI — it's detected automatically) and violations appear inline on the PR:

- run: npx branchpol check --format github

How scanning works

By default, branchpol talks to the remote directly with read-only git plumbing:

  • git ls-remote --heads <remote> — every branch, no fetch, no checkout, no working-tree changes. Safe to run anywhere.
  • git ls-remote --symref <remote> HEAD — the true remote default branch.

Prefer fully offline? Use local remote-tracking refs instead:

git fetch --prune && branchpol check --local   # or let branchpol fetch:
branchpol check --fetch                        # also enables the staleness sweep

branchpol never mutates your repository.

Configuration

branchpol.config.json (created by branchpol init):

{
  "project": "my-project",
  "remote": "origin",
  "defaultBranch": "main",
  "naming": {
    // Branch must match at least ONE require pattern (default branch exempt).
    "require": ["^(feat|fix|chore|docs|refactor|perf|test|build|ci|release|hotfix)/.+"],
    // Branch must match NONE of these. "(?i)" prefix = case-insensitive.
    "forbid": ["^master$", "^wip$", "^tmp$", "^temp$", "(?i)^dont.?touch"],
    "case": "lower",       // or "any"
    "maxLength": 60
  },
  "protection": {
    "required": ["main"],  // must exist on the remote
    "forbidden": []        // must NOT exist on the remote
  },
  "stale": {
    "enabled": false,      // requires --local/--fetch tracking refs
    "days": 90
  }
}

Rules reference

Rule Severity Meaning
require error Branch matches no required naming pattern
forbid error Branch matches a forbidden pattern
case warning Branch name has characters outside a-z0-9._/-
maxLength warning Branch name longer than naming.maxLength
defaultBranch error Remote's HEAD doesn't match defaultBranch
required error A protected branch is missing from the remote
forbiddenBranch error A banned branch exists on the remote
stale warning Tracking branch idle beyond stale.days

CLI

branchpol <command> [options]

Commands
  init        Create branchpol.config.json
  check       Scan remote branches against policy (default)
  protect     Print suggested GitHub branch-protection payloads

Options
  --remote <name>   Remote to scan (default: origin)
  --format <f>      text | json | github
  --local           Scan local remote-tracking refs (no network)
  --fetch           Run `git fetch --prune` first; enables staleness
  --out <file>      Write report to file
  --strict          Treat warnings as errors
  --cwd <dir>       Repository directory

Exit codes: 0 clean · 1 violations (or warnings with --strict) · 2 error.

CI recipes

GitHub Actions:

name: branch-policy
on: [push, schedule]
jobs:
  policy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npx branchpol check --format github

Any other CI: parse --format json's .summary.errors, or trust the exit code.

Pro

Need branchpol across an organization rather than a repo? Branchpol Pro ($9/month) adds org-wide scheduled scans with drift alerts, a policy dashboard per repository, Slack/email notifications when protection rules change or get deleted, and ready-to-apply GitHub ruleset templates generated from your config. License via Gumroad — link placeholder.

The CLI itself is free and MIT-licensed forever.

Development

npm test
node bin/branchpol.js help

License

MIT — see LICENSE.


Part of the stealth-alpha toolkit — eight zero-dependency CLIs for release automation, agent security, and repo hygiene.

About

Branch naming & protection policy checker for teams. Scans remotes, reports violations, exits non-zero for CI.

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages