# GitHub Template Sync

Allegro supports syncing templates from a GitHub repository. When connected, templates are automatically synced whenever commits are pushed to the configured default branch.

## How It Works[​](#how-it-works "Direct link to How It Works")

1. An organization admin installs the Allegro GitHub App on their GitHub account or organization.
2. After installation, Allegro records a sync link pointing the tenant at that installation.
3. The admin selects a repository and default branch to sync from, which completes the link and registers a push webhook on the repository.
4. On every push to that branch, a sync job is queued to update templates from the repository.

A single GitHub App installation can serve **many** repositories across **many** tenants. Each tenant syncs from at most one repository, but the same repository can be shared by several tenants. The installation, the per-tenant sync link, and the per-repository webhook are stored separately so one installation (for example on an `alleyinteractive` organization) can map different repositories to different tenants. See [Mapping Repositories at the Platform Level](#mapping-repositories-at-the-platform-level).

***

## Setting Up the GitHub App[​](#setting-up-the-github-app "Direct link to Setting Up the GitHub App")

### 1. Create a GitHub App[​](#1-create-a-github-app "Direct link to 1. Create a GitHub App")

In your GitHub organization or personal account, go to **Settings → Developer settings → GitHub Apps** and create a new app with the following settings:

| Setting                                    | Value                                                             |
| ------------------------------------------ | ----------------------------------------------------------------- |
| **GitHub App name**                        | Your app name (e.g. `Allegro Template Sync`)                      |
| **Homepage URL**                           | Your Allegro instance URL                                         |
| **Webhook URL**                            | `https://your-app.com/github/webhooks`                            |
| **Webhook secret**                         | A random secret string (set as `GITHUB_WEBHOOK_SECRET` in `.env`) |
| **Post installation → Setup URL**          | `https://your-app.com/github/callback`                            |
| **Post installation → Redirect on update** | ✅ Enabled                                                        |

**Permissions required:**

* Repository contents: Read-only

**Subscribe to events:**

* Push
* Installation
* Installation repositories

### 2. Configure Environment Variables[​](#2-configure-environment-variables "Direct link to 2. Configure Environment Variables")

After creating the GitHub App, add these values to your `.env`:

```dotenv
# GitHub App credentials
GITHUB_APP_ID=<your_app_id>
GITHUB_APP_PRIVATE_KEY="<your_pem_private_key>"
GITHUB_WEBHOOK_SECRET=<your_webhook_secret>

# The installation URL shown on the GitHub Sync settings page
# Format: https://github.com/apps/<app-slug>/installations/new
GITHUB_APP_URL=https://github.com/apps/your-app-name/installations/new

```

note

`GITHUB_APP_PRIVATE_KEY` should be the full PEM content of the private key generated in your GitHub App settings.

***

## Installation Flow[​](#installation-flow "Direct link to Installation Flow")

When an admin visits **Organization Settings → GitHub Sync** and clicks **Connect GitHub Repository**:

1. They are sent to `GITHUB_APP_URL?state={encrypted_tenant_id}` on GitHub.
2. They install the app on their GitHub account/organization.
3. GitHub fires an `installation.created` webhook to `/github/webhooks`, which creates a tenant-less `GitHubAppInstallation` record on the landlord database.
4. GitHub redirects the user back to `/github/callback?installation_id=XXX&state={encrypted_tenant_id}`.
5. The callback decrypts the state to identify the tenant and records a `GitHubRepoSync` row linking that tenant to the installation (with no repository selected yet).
6. The admin is redirected to the GitHub Sync settings page where they select the repository and default branch.
7. Upon submitting the form, the sync link is completed, a push webhook is registered on the repository, and an initial sync is queued.

important

The `Setup URL` in your GitHub App settings **must** be set to `https://your-app.com/github/callback`. Without this, the installation cannot be linked to the tenant.

***

## Mapping Repositories at the Platform Level[​](#mapping-repositories-at-the-platform-level "Direct link to Mapping Repositories at the Platform Level")

The self-serve flow above always returns the admin to the tenant they started from, so it can only ever connect one repository to that one tenant. To map several repositories from a single shared installation to several tenants — for example, when one organization's installation hosts a separate template repository per tenant — super admins use the landlord tenant dashboard.

From **Tenants** in the landlord navigation, open a tenant's detail page. The GitHub Template Sync section lists every known installation; pick an installation, choose one of its repositories, set the branch, and connect. This creates the same `GitHubRepoSync` link as the self-serve flow, without a GitHub round-trip, so different repositories under one installation can be assigned to different tenants.

Installations only appear here after the GitHub App has been installed on the organization (which fires the `installation.created` webhook). There is no manual way to register an installation ID.

***

## Sync Process[​](#sync-process "Direct link to Sync Process")

Template sync is handled by the `SyncGitHubTemplates` job. It:

* Fetches each top-level directory containing an `index.html` from the repository at the configured branch (with optional `index.css`, `index.js`, and `template.json`).
* Creates or updates corresponding `Template` records (matched by `github_path`).
* Force-deletes synced templates no longer present in the repository.

Syncs are triggered:

* **Automatically** when a push arrives on a configured default branch (via webhook). A push to a shared repository **fans out**: the job is dispatched once for every tenant whose sync link points at that repository and whose branch matches.
* **Manually** via the **Sync Now** button on the GitHub Sync settings page (rate-limited to once per minute).

***

## Disconnecting[​](#disconnecting "Direct link to Disconnecting")

Admins can disconnect GitHub sync from **Organization Settings → GitHub Sync → Disconnect**. This:

* Deletes the tenant's `GitHubRepoSync` link.
* Deletes the repository's push webhook **only if no other tenant still syncs that repository** (the webhook is reference-counted per repository).
* Removes the `sync_source` from all templates, making them editable again.

Templates are **not deleted** when disconnecting.

Disconnecting a tenant from the landlord dashboard removes the sync link and reference-counted webhook the same way, but leaves that tenant's templates untouched.

Lifecycle teardown

When the GitHub App is uninstalled (`installation.deleted`) or loses access to a repository (`installation_repositories` removed), Allegro tears down the affected sync links and webhooks but never deletes synced templates — they remain in each tenant exactly as they were.

***

## Database Schema[​](#database-schema "Direct link to Database Schema")

All three tables live on the landlord database. The installation, the per-tenant sync link, and the per-repository webhook are stored separately so one installation can map many repositories to many tenants.

### `github_app_installations`[​](#github_app_installations "Direct link to github_app_installations")

The GitHub App installation itself — one row per GitHub account/organization.

| Column            | Type                | Description                   |
| ----------------- | ------------------- | ----------------------------- |
| `installation_id` | `bigint` (unique)   | GitHub's installation ID      |
| `account_login`   | `string` (nullable) | GitHub account/org login name |

### `github_repo_syncs`[​](#github_repo_syncs "Direct link to github_repo_syncs")

The tenant ↔ repository link — one row per tenant. `repository` and `default_branch` are null between installing the app and selecting a repository.

| Column             | Type                   | Description                            |
| ------------------ | ---------------------- | -------------------------------------- |
| `tenant_id`        | `bigint` (unique)      | The linked Allegro tenant              |
| `installation_id`  | `bigint`               | GitHub installation this link uses     |
| `repository`       | `string` (nullable)    | Full repository name (e.g. `org/repo`) |
| `default_branch`   | `string` (nullable)    | Branch to sync from                    |
| `last_sync_at`     | `timestamp` (nullable) | Time of last successful sync           |
| `last_sync_hash`   | `string` (nullable)    | Commit SHA of last sync                |
| `last_sync_errors` | `json` (nullable)      | Errors from the last sync attempt      |

### `github_repo_webhooks`[​](#github_repo_webhooks "Direct link to github_repo_webhooks")

The push webhook registered on a repository — one row per `(installation, repository)`, shared by every tenant syncing that repository and reference-counted on disconnect.

| Column            | Type     | Description                            |
| ----------------- | -------- | -------------------------------------- |
| `installation_id` | `bigint` | GitHub installation that owns it       |
| `repository`      | `string` | Full repository name (e.g. `org/repo`) |
| `webhook_id`      | `bigint` | GitHub's webhook ID on the repository  |
