Skip to content

Getting Started with Gondolier

Quick setup for hosted gondolier.

  • A Forgejo, Gitea, or Codeberg instance (self-hosted or public)
  • An email address (for Supabase magic-link sign-in)
  • A bot account or PAT with write scope on your forge instance (needed for the queue engine: it creates gondolier/* branches, writes statuses, and merges). To enable webhook wakeups and automatic branch protection the token also needs repo-admin scope — see Provisioning below.

Queue execution is live: gondolier reconciles your managed repos every 5 minutes (cron) with the Go Shunt engine, and optionally on forge webhooks.

  1. Go to gondolier.dev
  2. Click Sign in → enter your email for a magic link
  3. Click the magic link in your email to sign in

Your account is created automatically on first sign-in.

  1. In the dashboard, go to Connections → Add connection
  2. Enter your forge instance URL (e.g. https://git.example.com)
  3. Enter your bot username (e.g. mq-bot)
  4. Paste your PAT or token
  5. Click Add connection — the connection is stored (token encrypted at rest) and verified
Requirement Minimum scope
Queue execution (staging, merging, status writes) write
Webhook registration + branch protection (auto-provisioning) repo-admin (or repo + write:repository)
Discover repos (POST /connections/:id/discover) repo:read

If your token lacks repo-admin scope, gondolier will report provisioning items as Needs admin and show manual instructions in the dashboard.

  1. Go to Repositories → Add repository
  2. Select an existing connection (or create a new one)
  3. Enter the repository slug (e.g. owner/repo)
  4. Configure:
    • Base branch — usually main or master
    • Merge style — merge, squash, or rebase
  5. Click Add

After adding, the dashboard shows the provisioning result:

  • Webhook — Ready | Needs admin | Not required (poll mode)
  • Branch protection — Not protected | Ready | Needs admin | Needs bot username | Waiting on setup PR | Error

If provisioning fails with Needs admin, the dashboard shows manual setup instructions. Grant your bot repo-admin scope and click Provision to retry.

Enrollment does not change your repository’s merge rules. Provisioning configures the webhook (in webhook wake mode) and nothing else. Your base branch is untouched until you click Protect base branch, and you can stop Gondolier managing it at any time — the rule stays on your forge for you to remove yourself, so a branch is never silently reopened.

Before protecting, make sure the repository has CI that runs on gondolier/** pushes. The CI gate below is the assisted way to set that up; it is optional if your existing CI already covers those refs.

Each batch is pushed to gondolier/<base>/staging-<timestamp>-<sequence>. Its Forgejo Actions push run is the gate, so it must match gondolier/**.

On the dashboard’s repository row, choose either Copy CI YAML or Open setup PR. The setup PR adds a separate .forgejo/workflows/gondolier-gate.yaml; it never edits an existing workflow, writes the base branch, or auto-merges.

Its initial workflow is intentionally fail-closed:

name: Gondolier gate
on:
push:
branches: ["gondolier/**"]
permissions: {}
jobs:
gate:
runs-on: ubuntu-latest
steps:
- name: Configure this gate
run: |
echo "Replace this failing stub with this repository's full test command."
exit 1

Merge the setup PR, then replace the stub with the repository’s full validation command (and checkout/setup steps as required). Keep the push trigger and do not apply paths-ignore: a new staging branch can have an empty changed-file set, which can suppress the gate entirely. Gondolier leaves its automatic branch protection in Awaiting CI gate until verification so this setup PR is not blocked by the required check it creates. Existing repository restrictions still require an allowed maintainer to merge the setup PR.

Click Verify CI gate after that change. Gondolier creates a one-file commit on an isolated gondolier/<base>/verify-... ref, deletes the ref, then reports the Gondolier gate run for that exact commit. No PR is queued or merged. A verified state requires a successful run; a failing or pending run remains non-ready.

Do not post a gondolier status from CI: gondolier posts that status on each PR only after the staged run passes.

Each repo has a wakeup mode that controls how gondolier detects new PRs:

Mode How the repository becomes due Setup requirements
poll (default) The reconciliation tick picks it up on a schedule that backs off while it is quiet None. Works with any token scope.
webhook A forge event marks it due immediately, so the next tick takes it repo-admin scope to register the webhook automatically, or manual setup (see below)

Change wake mode from the dashboard’s per-repo wake-mode selector. Switching to webhook triggers provisioning automatically.

Worth being exact, because the two modes differ by more than the labels suggest.

Gondolier reconciles on a five-minute tick. Neither mode processes a pull request the instant it becomes ready — what a webhook changes is when a repository becomes due, not how fast a due repository is handled.

Mode Typical Worst case
webhook ~2.5 minutes 5 minutes
poll, active repository ~2.5 minutes 5 minutes
poll, quiet repository grows with idleness 60 minutes

A poll repository that has seen no queue activity backs off — 5, 10, 20, 40, then 60 minutes — so a pull request opened on a repository that has been idle for an hour can wait up to an hour to be noticed. That is the case webhook mode exists to remove: a forge event resets the schedule to now, so the next tick takes it regardless of how long the repository has been quiet.

If your repository is busy, the two modes perform identically. If it is bursty — quiet for hours, then several PRs at once — webhook mode is the difference between a two-minute wait and an hour-long one.

Why not dispatch on the webhook itself? Because batching is the point. Waiting for the tick is what lets several ready pull requests land in one CI run instead of several, which is where the cost saving comes from. A queue that reacted instantly to every event would be testing one PR at a time.

Provisioning runs in two stages:

  1. On enrollment — best-effort, never blocks repo addition.
  2. On demand — click Provision to retry at any time (e.g. after granting the token more permissions).

What gets set up:

  • Webhook (webhook mode only) — registers a forge webhook pointing to https://api.gondolier.dev/api/v1/webhooks/forgejo for push and pull_request events.
  • Branch protection — not configured at enrollment. Protecting the base branch changes how every pull request in your repository merges, so Gondolier asks first: the repository shows Not protected with a Protect base branch button. Clicking it adds your status context (default gondolier) as a required check, and Gondolier keeps it configured from then on. CI-gate verification is optional assistance and is not a prerequisite; the one exception is that while Gondolier’s own CI-gate setup PR is open, protection is deferred (Waiting on setup PR) so that PR is not blocked by the check it exists to add.
  • Required check — after verification, marks the repo’s status context (default gondolier) as a required check on the base branch.

Provisioning registers the webhook only in webhook wake mode. CI-gate bootstrap is a separate explicit action in Step 4: it can copy a fail-closed workflow or open a normal target-repository setup PR.

If provisioning reports Needs admin, the dashboard shows manual setup instructions (Forgejo webhook URL and branch protection settings). Grant the bot repo-admin scope, click Provision, and gondolier will detect the manual setup as Ready.

When using webhook mode:

  • The webhook URL is https://api.gondolier.dev/api/v1/webhooks/forgejo
  • Required events: push, pull_request
  • Content type: json
  • HTTP method: POST
  • The webhook secret is generated automatically per tenant

If you cannot grant repo-admin scope, set up the webhook manually in your forge instance settings using the values above. Gondolier will detect the existing webhook as Ready on the next check.

  1. Create a PR targeting the base branch
  2. Request auto-merge (Forgejo: click “Merge when ready”)
  3. The PR joins the queue automatically

Gondolier will:

  • Batch eligible PRs
  • Stage them on a gondolier/<branch>/staging-... branch
  • Wait for CI
  • Bisect on failure
  • Auto-merge on success

Gondolier polls your forge instance every 5 minutes (in poll mode) or receives webhooks (in webhook mode). When it sees eligible PRs, it runs the merge queue algorithm:

  1. Discovery — finds PRs with auto-merge scheduled
  2. Batching — groups them into a rollup (configurable size)
  3. Staging — creates a staging branch with all PRs cherry-picked
  4. Gate — waits for CI to pass on the staging branch
  5. Resolution — bisects on failure, merges on success

Queue order and in-flight batch state persist via a Supabase-backed checkpoint store, so the engine resumes in-flight batches across restarts rather than re-staging.

The dashboard at gondolier.dev/dashboard has two peer sections:

Lists your managed repos with per-repo status:

  • Connection badge — clickable tooltip showing the bot, instance URL, and token type
  • Wake mode — inline selector (webhook | poll) with optimistic save
  • Provisioning — chips for webhook and branch protection status (Ready | Needs admin | Not required | Not configured | Error), hover to see details
  • Reconcile — status (ok | error | unknown) with last run timestamp
  • Actions — Reconcile (one-shot), Provision (retry setup), Remove (with confirmation)

Lists your forge connections:

  • Shows bot username, instance URL, and token type
  • Edit — update instance URL, bot username, or rotate the token
  • Remove — with confirmation (managed repos cascade-delete)

Shows a per-repo health panel: queue name, reconcile state, lease state, and a Reconcile button.