Skip to content

[Epic] PR preview deployments #3042

Description

@dawsontoth

Turn on pull-request previews for an application on a Fabric cluster, and every PR's build is served at its own subdomain, <branch-slug>.<app>.<cluster-domain>, with a valid certificate. Each push updates the preview, and closing the PR removes it. Customer setup should be one toggle in Studio plus one workflow file, with no stored secrets, DNS records or certificates to manage.

Example: a PR from branch david/1778-custom-regions is served at https://david-1778-custom-regions.<app>.<cluster-domain>.

This is the next step after staged component deploys (#2315), and the "preview deployments" item of the Ephemeral Instances roadmap lane, beside #641 and #642. Studio is the first consumer.

Where things stand

Previews already work by hand on 5.3.0. HarperFast/documentation#715 (stacked on HarperFast/documentation#713) documents a recipe, "Per-PR previews with shared databases":

It works, but it takes a lot of setup. This epic removes that cost one mechanism at a time:

What the recipe needs today Removed by
On Fabric, each preview hostname is added, verified and bound as a custom domain, for every PR HarperFast/central-manager#900, HarperFast/central-manager#901, HarperFast/central-manager#902, HarperFast/central-manager#903, HarperFast/host-manager#240, HarperFast/host-manager#241
The first deploy of each preview restarts the shared workers on every node at once #3045
Cleanup calls each node's own URL, with a trust policy per node URL, and checks files and workers itself #3045, #3091
The preview role can deploy and drop any component, and read every component's configuration #3046
An app that needs a build publishes each PR commit to a registry first #3043
A private repository needs an administrator to grant a credential to each preview, and delete it afterwards #3090
Previews share the cluster's data #3071, #3044, #3043, #3053
Nothing removes a preview whose close event was missed #3048
A request with a preview's host on Harper's shared port is answered by production #3052
Each repository carries its own long workflow #3054, #3055

What already exists

Shape

  1. A preview is an isolated application named <component>-pr-<n>, with host: <branch-slug>.<label>.<cluster-domain>. Applications with tables add branchedDatabases once A package deploy's branchedDatabases is ignored at load, so the application runs on its base databases #3071 and deploy_component accepts branchedDatabases on a payload deploy and silently ignores it #3044 are fixed. Until then, previews share the cluster's databases, as the documented recipe does.
  2. CI deploys it straight to the cluster, authenticated by OIDC, as an identity that can only deploy and drop that application's previews.
  3. Central Manager acts once per application, when previews are enabled. It adds a wildcard DNS record, puts the wildcard on the instances' certificates, sends the claim host-manager routes on, and creates the trust policy and scoped role on the cluster.
  4. Closing the PR drops the preview. An expiry and a per-application cap catch anything the close event missed.

The branch slug is one lowercase DNS label:

  • Anything outside [a-z0-9] becomes -, and runs collapse.
  • It is cut to 40 characters, with a hash suffix when cut.
  • Reserved names fall back to pr-<n>.

For example, david/1778-custom-regions becomes david-1778-custom-regions. The documented recipe uses pr-<n>.<suffix>, which needs no slug at all.

Sub-issues

Harper core

CI tooling

Central Manager

  • HarperFast/central-manager#900
  • HarperFast/central-manager#901
  • HarperFast/central-manager#902
  • HarperFast/central-manager#903
  • HarperFast/central-manager#904
  • HarperFast/central-manager#905
  • HarperFast/central-manager#906
  • HarperFast/central-manager#907

Studio as the first consumer

  • HarperFast/central-manager#908
  • HarperFast/central-manager#909
  • HarperFast/central-manager#910
  • HarperFast/central-manager#911
  • HarperFast/central-manager#912

Host Manager

  • HarperFast/host-manager#240
  • HarperFast/host-manager#241
  • HarperFast/host-manager#242
  • HarperFast/host-manager#243

Security model

  • The workflow is trusted, not the PR. The job runs on pull_request_target from the default branch and never checks out PR code. The trust policy pins workflow_ref to that branch's workflow file, event_name: pull_request_target, and an environment, so a PR can't change the privileged workflow. Fork PRs are skipped by the workflow's same-repository condition.
  • The PR's code still runs on Harper, with the preview role's rights. Until Scope deploy permissions to component names #3046, that role can deploy and drop any component. So previews belong on a separate preview cluster, or behind required reviewers when they share a production cluster.
  • Data is shared until branches work. A preview's writes, schema changes and roles apply to the cluster's databases.
  • Public repositories: GitHub's default execution protection blocks pull_request_target for affected public repositories from 2026-11-02. The repository's Actions policy has to allow it for the preview workflow.

Related, tracked elsewhere

Not in this epic

  • Customer-owned preview domains, via _acme-challenge CNAME delegation.
  • A GitHub App mode that needs no workflow file.
  • Promoting a previewed build to production by deployment_id. Builds are bound to their component today.
  • Static-only previews served from the shared workers.
  • Stopping idle previews, and resetting a branch from its base.
  • A cluster per PR (HarperFast/central-manager#570).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Fields

    Priority

    P2

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions