A Probot GitHub App that validates Pull Requests using configurable checks.
Runs in two modes:
- App mode — long-running webhook server (Probot). Install once as a GitHub App, validates every PR in every subscribed repo.
- Action mode — drop-in GitHub Action triggered inside a repo's workflow. No deployment, uses the workflow's
GITHUB_TOKEN.
Both modes share the same rule engine and the same config schema.
- Config-driven validation rules (all checks can be enabled/disabled)
- Default config committed in the repo
- Custom config file path via env var
- Rich PR checks: labels, title/body, reviews, commits, branch freshness, and security
- GitHub Check Run output directly on PRs
- GitHub Actions for manual release creation and Docker Hub publishing
Default config is stored at:
config/default.json
The app loads this file automatically.
To override defaults, point PR_CHECKER_CONFIG_PATH to a JSON file:
PR_CHECKER_CONFIG_PATH=./config/my-config.jsonCustom config is deep-merged with config/default.json, so you can override only the fields you need.
You can start from config/custom.example.json.
Merge semantics: objects are deep-merged, arrays are replaced. If you override
blockedLabels.labelsyou must list every label you want — the defaults are not concatenated. Custom path is resolved againstprocess.cwd(); in containers this is theWORKDIR.
Example override:
{
"checkRun": {
"name": "My PR Gate"
},
"checks": {
"minApprovals": {
"enabled": true,
"required": 2
},
"bigPrWarning": {
"maxChanges": 800
},
"requiredLabels": {
"groups": [
{
"anyOf": ["review passed", "code review passed", "approved by tech lead"],
"message": "❌ Code review approval label is missing"
},
{
"anyOf": ["qa passed", "qa skipped"],
"message": "❌ QA label is missing"
}
]
}
}
}All checks below support enabled: true|false and additional options from config/default.json:
labelsRequired: minimum number of labelsblockedLabels: labels that must block mergetitlePatternBlock: regex for blocked title patterns (for example WIP/Draft)descriptionRequired: minimum PR description lengthblockedReviewLabels: labels that block review status (for example product review needed)requiredLabels: required label groups (anyOf)minApprovals: minimum number of active APPROVED reviewsbaseBranchAllowed: allowed target branchesbigPrWarning: warning threshold for changed lineswipCommitMessages: regex for blocked commit messagesmergeCommits: regex for blocked merge commitsfixupCommits: regex for blockedfixup!/squash!commitsmeaningfulCommitMessages: minimum commit subject length + disallowed subjects/prefixesbranchUpToDate: max commits behind base branchsensitiveFiles: blocked file extensionssensitiveInfoInBody: regex for sensitive data patterns in PR body- Pattern-based — may false-positive on PR bodies that mention
password:/token:in prose. Disable per repo via"sensitiveInfoInBody": { "enabled": false }or tighten the regex.
- Pattern-based — may false-positive on PR bodies that mention
Additional config sections:
checkRun.name: GitHub Check Run nameapi.listPerPage: page size for PR-related API list calls
- Go to GitHub Settings -> Developer settings -> GitHub Apps -> New GitHub App.
- Configure:
- Homepage URL: your project URL
- Webhook URL:
https://<your-host>/api/github/webhooks - Webhook secret: strong random string
- Permissions (Repository):
- Checks: Read & Write
- Contents: Read-only
- Metadata: Read-only
- Pull requests: Read-only
- Subscribe to events:
- Pull request
- Pull request review
- Check run
- Generate and download the private key.
Copy and edit env file:
cp .env.example .envRequired env vars:
APP_ID=123456
WEBHOOK_SECRET=your-webhook-secret
# Production: prefer mounting the .pem file and pointing PRIVATE_KEY_PATH at it
PRIVATE_KEY_PATH=/run/secrets/github-app.pem
# Development fallback: inline key (escape newlines with \n)
# PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----"
WEBHOOK_SECRETmust be set. If left empty, Probot will not verify webhook signatures and the endpoint will accept arbitrary payloads.
Optional:
WEBHOOK_PROXY_URLLOG_LEVELPORTPR_CHECKER_CONFIG_PATH
npm install
npm run devUse directly inside a repository workflow. No GitHub App, no deployment.
# .github/workflows/pr-checker.yml
name: PR Checker
on:
pull_request:
types: [opened, synchronize, reopened, edited, labeled, unlabeled]
pull_request_review:
types: [submitted, dismissed]
permissions:
contents: read
pull-requests: read
checks: write
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: alexremn/github-prchecker@v1
with:
# All inputs are optional.
config-path: .github/pr-checker.json
check-run-name: PR Checker
fail-on-failure: "true"| Input | Default | Notes |
|---|---|---|
github-token |
${{ github.token }} |
Needs pull-requests: read + checks: write. |
config-path |
(empty) | Path to a custom rules JSON file relative to the workspace. |
check-run-name |
PR Checker |
Name shown on the PR. |
fail-on-failure |
true |
Exit non-zero when a rule fails (also fails the job). |
| Output | Description |
|---|---|
passed |
true when no rules failed. |
failure-count |
Number of failing rules. |
warning-count |
Number of warnings emitted. |
A Markdown step summary is also written to $GITHUB_STEP_SUMMARY.
The action pulls the prebuilt image
alexremn/prcheckerbot:latest
from Docker Hub — no per-run image build.
Forks:
pull_requestevents from forks ship a read-onlyGITHUB_TOKEN, so the check run cannot be created. Usepull_request_targetif you need to validate fork PRs — review the security implications first.
Build:
docker build -f .docker/Dockerfile -t prcheckerbot .Run:
docker run -d \
--name prcheckerbot \
-p 3000:3000 \
-e APP_ID=<app-id> \
-e WEBHOOK_SECRET=<webhook-secret> \
-e PRIVATE_KEY="$(cat path/to/private-key.pem)" \
prcheckerbotWorkflow: .github/workflows/release.yml
- Trigger: push of a tag matching
v*(for examplev1.2.3) - Auto-generates release notes from commits/PRs since the previous tag
- Tags containing
-(for examplev1.2.3-rc.1) are marked as prereleases
Cut a release:
git tag v1.2.3
git push origin v1.2.3The Release workflow creates the GitHub Release, which in turn triggers
Publish Docker Image to build and push the container.
Workflow: .github/workflows/publish-docker.yml
- Trigger:
release.published - Builds
.docker/Dockerfile - Pushes multi-arch image (
linux/amd64,linux/arm64)
Required repository secrets:
DOCKERHUB_USERNAMEDOCKERHUB_TOKEN
Optional repository variable:
DOCKERHUB_REPOSITORY(example:your-org/prcheckerbot)- If not set, defaults to
<DOCKERHUB_USERNAME>/prcheckerbot
- If not set, defaults to
The repository includes:
- MIT license (
LICENSE) - Contribution guide (
CONTRIBUTING.md) - Code of conduct (
CODE_OF_CONDUCT.md) - Security policy (
SECURITY.md) - Issue templates (
.github/ISSUE_TEMPLATE/*) - Pull request template (
.github/pull_request_template.md)
GET /healthzGET /readyz
MIT