A merge queue for GitHub, inspired by bors. Fila ensures your main branch only contains code that has been tested as a merge result β never untested combinations.
"Fila" means "queue" in Portuguese π§π·
GitHub's merge button has a fundamental problem: it merges code that was tested in isolation, not as the combination that will actually land on main. If PR #1 and PR #2 both pass CI independently but break when combined, you'll only find out after both are on main.
Fila solves this by testing the exact merge result before pushing to main. Every commit on main has passed CI in the exact state it will be deployed.
PR #1 ββ
PR #2 ββ€ ββββββββββββ βββββββ ββββββββ
PR #3 ββΌβββΆβfila/merge ββββββΆβ CI ββββββΆβ main β
PR #4 ββ€ ββββββββββββ βββββββ ββββββββ
PR #5 ββ merge each 1 run fast-forward
- Someone comments
@fila shipon a PR (or includes it in a review) - Fila adds the PR to its queue and confirms with a comment
- The runner picks all queued PRs and creates a temporary
fila/mergebranch from currentmain - It merges each PR sequentially into
fila/merge, producing the exact combined commit that would land on main - CI runs once on
fila/mergeβ testing all PRs together, not in isolation - If CI passes, Fila fast-forwards
mainto the tested commit β one build for all queued PRs - If CI fails, all PRs in the batch are marked as failed and the authors are notified
- If a PR has a merge conflict, it's skipped and marked as failed β the rest of the batch continues
Fila supports two merge strategies, controlled by the MERGE_STRATEGY environment variable:
batch (default) β Merge all queued PRs into fila/merge together, run CI once, fast-forward main once. This saves CI runs and deploy costs. If you have 10 PRs queued, that's 1 build instead of 10. The tradeoff: if CI fails, you can't tell which PR broke it β all get failed and authors retry with @fila ship.
sequential β Process one PR at a time, like bors. Each PR is tested against the exact main it will land on. Slower but guarantees isolation. Use this when you need to know exactly which PR broke the build.
Comment on any PR to interact with Fila:
| Command | Description |
|---|---|
@fila ship |
Add the PR to the merge queue |
@fila cancel |
Remove the PR from the queue |
@fila status |
Show the current queue |
@fila ship works in both regular comments and PR review bodies β approve and ship in one step.
Go to Settings > Developer settings > GitHub Apps > New GitHub App.
Permissions (Repository):
| Permission | Access |
|---|---|
| Contents | Read & write |
| Issues | Read & write |
| Pull requests | Read & write |
| Commit statuses | Read & write |
| Checks | Read |
Subscribe to events: Check suite, Issue comment, Pull request, Pull request review
Generate a private key and note the App ID.
Install the GitHub App on the repositories you want Fila to manage.
If your main branch has branch protection rules (required PRs, required reviews, etc.), you need to add the GitHub App to the bypass list so it can fast-forward main after CI passes. Without this, the fast-forward will be rejected by GitHub.
Go to Settings > Branches > main > Edit and add your app under "Allow specified actors to bypass required pull requests". Or via API:
gh api repos/OWNER/REPO/branches/main/protection -X PUT --input - <<'EOF'
{
"required_pull_request_reviews": {
"required_approving_review_count": 1,
"bypass_pull_request_allowances": {
"apps": ["your-app-slug"]
}
},
"enforce_admins": true,
"restrictions": null,
"required_status_checks": null
}
EOFYour CI workflow must run on the fila/merge branch. This is the branch where Fila tests merge results:
on:
push:
branches: [main, fila/merge]
pull_request:
branches: [main]Fila is a single binary that connects to GitHub via webhooks and stores state in SQLite.
Environment variables:
| Variable | Description | Default |
|---|---|---|
DATABASE_URL |
SQLite connection string | required |
SERVER_PORT |
HTTP port | 8000 |
GITHUB_APP_ID |
GitHub App ID | required |
GITHUB_PRIVATE_KEY |
GitHub App private key (PEM contents) | required |
GITHUB_WEBHOOK_SECRET |
Webhook secret | required |
MERGE_STRATEGY |
batch or sequential |
batch |
BATCH_SIZE |
Max PRs per batch | 5 |
BATCH_INTERVAL_SECS |
How often to check for queued PRs (seconds) | 10 |
CI_TIMEOUT_SECS |
Max time to wait for CI (seconds) | 1800 |
POLL_INTERVAL_SECS |
How often to poll CI status (seconds) | 15 |
DASHBOARD_URL |
URL for queue link in PR comments | β |
# .env
DATABASE_URL=sqlite://fila.db?mode=rwc
GITHUB_APP_ID=12345
GITHUB_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----"
GITHUB_WEBHOOK_SECRET=your-secretfilaThe dashboard is available at http://localhost:8000/.
Fila includes built-in commands to help with setup and troubleshooting:
fila # start the server (default, same as before)
fila doctor # validate config, database connection, and GitHub auth
fila setup # interactive wizard that creates a .env filefila doctor reads each environment variable individually and reports what's present, what's missing, and what's broken β without starting the server. It validates your private key is valid RSA PEM, tests database connectivity, and verifies GitHub API authentication by hitting GET /app. Exit code 0 means everything is good, 1 means something needs attention.
$ fila doctor
.env file found
DATABASE_URL set
GITHUB_APP_ID set
GITHUB_PRIVATE_KEY set
GITHUB_WEBHOOK_SECRET set
SERVER_PORT 8000
HOST 127.0.0.1
MERGE_STRATEGY batch
BATCH_SIZE 5
BATCH_INTERVAL_SECS 10
CI_TIMEOUT_SECS 1800
POLL_INTERVAL_SECS 15
Private key valid RSA PEM
Database connected (sqlite)
GitHub API authenticated as "my-merge-queue"
All checks passed.
fila setup walks you through creating a .env file. It prompts for each value with sensible defaults, reads your private key from a file path, and writes everything properly formatted. Run fila doctor after to verify.
Docker:
FROM rust:1.88-bookworm AS builder
WORKDIR /app
COPY . .
RUN cargo build --release
FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/*
COPY --from=builder /app/target/release/fila /usr/local/bin/fila
CMD ["fila"]Railway:
- Connect your repository
- Add a volume mounted at
/datafor SQLite persistence - Set
DATABASE_URL=sqlite:///data/fila.db?mode=rwcand the other env vars - Set the webhook URL to
https://your-app.up.railway.app/webhooks/github
Fila is a single Rust binary built with Rapina.
βββββββββββββββββββββββββββββββββββββββββββββββββββ
β Fila β
β β
β ββββββββββββ ββββββββββββββββ βββββββββββββ β
β β Webhooks β β Merge Queue β β Dashboard β β
β β (GitHub) β β Runner β β (HTML) β β
β ββββββ¬ββββββ ββββββββ¬ββββββββ βββββββββββββ β
β β β β
β βββββββββ¬ββββββββ β
β βΌ β
β ββββββββββββ ββββββββββββββββ β
β β SQLite β β GitHub API β β
β ββββββββββββ ββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββ
The default batch strategy merges all queued PRs together and runs CI once, saving build time and deploy costs. For teams that need strict isolation, the sequential strategy processes one PR at a time.
# Run tests
cargo test
# Run with hot reload
cargo watch -x run
# Check formatting and lints
cargo fmt --all -- --check
cargo clippy --all-targets -- -D warningsMIT