Skip to content
CodeFloe

Preview Environments

Preview environments give every pull request its own short-lived deployment, so reviewers can open the proposed change in a browser instead of building it locally. They are provided by roost, deployed when a pull request opens and torn down again when it closes or merges.

Each preview is served at its own subdomain of preview.codefloe.com:

https://<owner>-<repo>-pr-<number>-<hash>.preview.codefloe.com

For example, pull request 27 in codefloe/docs is served at:

https://codefloe-docs-pr-27-053ebd.preview.codefloe.com

The parts are:

  • <owner>-<repo> identifies the repository.
  • pr-<number> is the pull request number.
  • <hash> is a short hash of the repository, so two repositories that would otherwise slugify to the same name never collide on a shared daemon.

The name is deterministic: the same pull request always maps to the same URL, so redeploys reuse it and the teardown removes the right one.

  • On a pull request the preview is deployed (or updated on every new push).
  • On close or merge the preview is destroyed.

When the preview is deployed, a bot comment posts the link on the pull request and keeps it up to date. On teardown the comment is struck through and suffixed with “→ torn down”, leaving a record that a preview existed.

Add a roost step to your Crow CI pipeline that runs on the pull request lifecycle events. The roost_server and roost_token secrets already exist as global secrets in Crow CI, so you do not need to create them yourself: just reference them with from_secret.

when:
  - event: [pull_request, pull_request_closed, pull_request_merged]

steps:
  build:
    image: node:lts-alpine
    commands:
      - npm ci
      # emits ./dist
      - npm run build
    when:
      - event: pull_request

  preview:
    image: codefloe.com/crow-plugins/roost:<version>
    settings:
      server:
        from_secret: roost_server
      token:
        from_secret: roost_token
      dir: ./dist
      ttl: 168h
    when:
      - event: [pull_request, pull_request_closed, pull_request_merged]

The single step covers the whole lifecycle: it deploys on pull_request and destroys on pull_request_closed and pull_request_merged. The ttl is a safety net so the daemon reclaims a preview even if the teardown event never fires.