GitHub Approval Check
A Cloudflare Worker that creates GitHub check runs to enforce approval requirements on pull requests using organization-level webhooks.
Overview
This worker listens for GitHub organization webhook events and creates/updates check runs based on pull request approval status. It ensures that only authorized team members can approve PRs for merging.
Features
- ✅ Creates a single, consistent check run for PR approval status
- ✅ Validates approvals against a configurable list of authorized users
- ✅ Updates check status in real-time when reviews are submitted
- ✅ Provides detailed feedback about approval requirements
- ✅ Secure webhook signature verification
- ✅ JWT-based GitHub App authentication
- ✅ Dev mode for PR-specific deployments
Setup
1. Create a GitHub App
- Go to your GitHub organization settings → Developer settings → GitHub Apps
- Click "New GitHub App"
- Configure the app:
- Name:
Immich Approval Check(or your preferred name) - Homepage URL: Your organization URL
- Permissions:
- Checks: Read & Write
- Pull requests: Read
- Contents: Read (for accessing repository)
- Events: Leave all unchecked (using org webhooks instead)
- Name:
- After creation, note down:
- App ID
- Generate and download a private key
2. Configure Organization Webhook
- Go to Organization Settings → Webhooks
- Add webhook with:
- Payload URL:
https://your-worker-domain.workers.dev/webhook - Content type:
application/json - Secret: Generate a secure random string
- Events to trigger:
- Check runs
- Check suites
- Pull requests
- Pull request reviews
- Payload URL:
3. Configure the Worker
Local Development
Create .dev.vars file:
GITHUB_APP_ID=your_app_id
GITHUB_APP_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----
your_private_key_content_here
-----END RSA PRIVATE KEY-----"
GITHUB_WEBHOOK_SECRET=your_webhook_secret
Production Deployment
Set secrets using Wrangler:
wrangler secret put GITHUB_APP_PRIVATE_KEY
# Paste your private key when prompted
wrangler secret put GITHUB_WEBHOOK_SECRET
# Enter your webhook secret when prompted
Update wrangler.toml with your App ID:
[vars]
GITHUB_APP_ID = "your_app_id"
4. Deploy the Worker
# Development
pnpm run dev
# Production
wrangler deploy
5. Install the GitHub App
- Go to your GitHub App settings
- Click "Install App"
- Choose the organization/repositories where you want to install it
- The app will automatically start validating pull requests
Configuration
Allowed Users List
The worker reads the list of authorized approvers from a repository, via the GitHub App
installation, so the file can live in a private repository. Configured with
ALLOWED_USERS_REPO and ALLOWED_USERS_PATH, defaulting to
immich-app/core-infra-tf and deployment/data/users.json.
This requires the app to have contents: read on that repository.
The JSON structure should be:
[
{
"github": {
"username": "user1",
"id": 12345
},
"role": "immich_admin"
},
{
"github": {
"username": "user2",
"id": 67890
},
"role": "team"
}
]
Users with role of "immich_admin", "team", or "immich" are authorized to approve pull requests.
How It Works
- Organization webhook received: GitHub sends webhook for PR events across all repos
- Validation: Worker validates webhook signature using org secret
- Check approval: Fetches allowed users and PR reviews
- Update check:
- ✅ Approved: Creates/updates check with success status
- ⚠️ Not approved + previously approved: Updates to action_required
- Not approved + never approved: No check created (keeps PR clean)
The check behavior:
- Clean PR view: No check appears until someone approves
- Blocks merge: Missing required check prevents merging
- Clear feedback: Shows approval status without exposing approver list
Dev Mode
When deployed as part of a pull request (with TF_VAR_stage containing -pr-XXX), the worker automatically enters dev mode:
- Limited scope: Only processes webhooks for the
servicesrepository (hardcoded) - PR-specific: Only responds to events for the PR that created the deployment
- Automatic detection: Extracts PR number from the stage variable
Environment Variables in Dev Mode
ENVIRONMENT=dev # Or automatically detected from stage
STAGE=-pr-123 # Set by Terraform from TF_VAR_stage
The worker automatically:
- Detects dev mode from the
-pr-prefix in the stage - Extracts the PR number (e.g., 123 from
-pr-123) - Limits processing to only the
servicesrepository - Ignores webhooks from other repositories or PRs
This ensures PR deployments don't interfere with production checks and only test against their own changes.
Development
Running Tests
pnpm run test
Type Checking
pnpm run check
Troubleshooting
Check Not Appearing
- Ensure the GitHub App is installed on the repository
- Verify webhook URL is correct in GitHub App settings
- Check worker logs:
pnpm run tail
Authentication Errors
- Verify App ID is correct
- Ensure private key is properly formatted (including headers)
- Check that the private key matches the GitHub App
Webhook Signature Failures
- Ensure webhook secret matches between GitHub and worker config
- Verify the secret doesn't contain any extra whitespace
Security Considerations
- Private keys and webhook secrets are stored as encrypted secrets
- All webhooks are verified using HMAC-SHA256 signatures
- Installation tokens are cached with appropriate TTLs
- Authorized users list is cached to reduce external API calls