The release pipeline

Every repository releases in the same order: check the files, bring the environment up to date, build, publish the static sites, deploy the workers and the runtime, check that it works. That order is one workflow we keep, replicator-release.yml. Your repository keeps only what is yours: which events start a release, which stages it targets and the commands that build and check it. A repository’s release file is about thirty lines.

name: Release
on:
  push:
    branches: [main]
  pull_request:
    types: [opened, synchronize, reopened, closed]
permissions:
  contents: read
jobs:
  release:
    # A pull request from this repository only: a fork never receives the credential.
    if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository
    uses: clankerlabs/CLANKERNET/.github/workflows/replicator-release.yml@<40-character SHA> # v1
    permissions:
      contents: read
    with:
      replicator_ref: <40-character SHA> # v1
      action: ${{ github.event.action == 'closed' && 'remove' || 'release' }}
      stage: ${{ github.event_name == 'push' && 'production' || format('pr-{0}', github.event.number) }}
      ref: ${{ github.event.pull_request.head.sha || github.sha }}
      state_account: 0123456789abcdef0123456789abcdef
      build_command: npm ci && npm run build
      workers_command: ./scripts/deploy-workers.sh
      smoke_command: ./scripts/smoke.sh
    secrets:
      REPLICATOR_STATE_TOKEN: ${{ secrets.REPLICATOR_STATE_TOKEN }}

Pin the 40-character SHA on the uses: line and in replicator_ref, as the quickstart does. The one secret is REPLICATOR_STATE_TOKEN. The engine is read with the workflow’s own token, so the repository that holds it has to be readable by yours.

The steps

StepWhat it does
validateReads replicator.config.ts and says what the stage is (a listed stage or a preview, protected or not), and checks the shape of the static file.
infraDeploys the stage with replicator infra deploy (only the diff with action: diff), and writes the stage’s outputs to a private file.
buildRuns your build_command. The state credential is not in its environment.
staticWhen the config has a static block: builds the toolkit’s contract from it, seals the build, loads the credential, publishes (Static sites).
workersRuns your workers_command.
runtimeRuns your runtime_command.
smokeRuns your smoke_command.

A command left empty is skipped. A failing step stops the release, and nothing after it runs. Your commands run with bash -euo pipefail and see REPLICATOR_STAGE, REPLICATOR_OUTPUTS_FILE (the stage’s addresses and names, never a secret) and FACTORY_BIN (the engine, so a command can read a stored value with replicator infra secrets get). The workers and runtime commands also receive REPLICATOR_STATE_TOKEN; the build and the smoke do not.

The commands are reviewed code in your workflow file. Never build one from a dispatch input or from pull-request text.

Inputs

All strings; only stage is required.

InputMeaning
actionrelease (default), diff (the infra diff and nothing else) or remove.
stageThe stage: staging, production, or a preview’s name such as pr-12.
config, state_account, policyThe environment file, the account that holds the state, and a fleet file whose policy: the diff is judged by.
imports, allow_destroy, confirmAs for the infra actions.
build_command, workers_command, runtime_command, smoke_commandYour own commands.
static_environment, static_branch, static_envWhich environment of the static file, which branch, and plain STATIC_SITE_<NAME>=value lines (an origin it requires).
ref, replicator_refThe caller’s ref to check out, and the engine’s ref.

By default a release of production uses the static file’s production environment, a preview uses preview, and any other stage uses the environment of its own name. The Pages branch is main, or the stage’s name for a preview.

Protected stages and removing one

A release of a protected stage is the same release: the diff must pass the policy, so nothing is deleted that you did not name in allow_destroy. Putting a person in front of it is your workflow’s choice (a branch rule, or a manual trigger).

action: remove takes a stage down. A preview stage is removed with no confirmation, because it holds nothing that outlives it. Any other stage needs confirm: remove <stage>, and a protected one also allow_destroy: '*', exactly as the infra actions do. The run stops before it starts if either is missing.

A preview whose pull request closed without the release workflow seeing it is removed once it is older than its ttl by the scheduled preview expiry; a scheduled drift check of each stage is a separate reusable workflow. Both are called from a cron of your own and use the one secret, REPLICATOR_STATE_TOKEN.

Runs queue, never cancel

Releases and manual environment runs of one repository and stage share one queue (infra:<repository>:<stage>): they run one at a time and are never cancelled half way.

Pull requests

A pull request never receives publishing credentials, so on a pull_request the static step is skipped with a notice and the preview is its environment. If you want the static site of a preview published, start the same call from a manual trigger on a trusted branch, with stage: pr-<number>.