Skip to main content

preview.yml

The build half of the PR-preview family. Render the Quarto site read-only in the possibly-forked PR context and upload it plus PR metadata as the pr-preview-site artifact. The deploy half (preview-deploy.yml) publishes it to gh-pages. Keep this workflow’s name: in sync with the workflows: list in the deploy stub. The R and renv machinery is parameterized so non-rme repos can reuse it; turn it off for a pure-markdown site.

Inputs

Input Type Default Description
runs-on string empty (CI_RUNNER variable, else ubuntu-latest) Runner label for every job. Use a self-hosted label only for a private repository, never a public one. A self-hosted runner without root cannot run steps that call apt or sudo, such as apt-packages.
path string '.' Path in the caller repo to the Quarto project to render (the directory with _quarto.yml). Defaults to the repo root; set it for a subdirectory site (matches quarto-publish’s path).
r-version string '4.6.0' R version to install with r-lib/actions/setup-r.
apt-packages string (long; see workflow file) System apt packages to install before R/Quarto setup, space-separated. Defaults to the shared renv stack plus build helpers; set to an empty string to skip the apt step.
use-renv boolean true Restore R dependencies with renv. Set false for non-renv repos.
r-packages string '' R packages to install when use-renv is false (space-, comma-, or newline-separated).
install-package boolean true Run R CMD INSTALL <path> to install the caller repo as a local package before rendering.
setup-chrome boolean true Install Chrome (needed by Quarto for revealjs and screenshot rendering).
tinytex boolean false Install TinyTeX and enable PDF rendering. When false (default), TinyTeX is installed and PDF is rendered only when the PR is labeled preview:pdf.
setup-julia boolean false Install Julia (julia-actions/setup-julia) before rendering, for pages that execute {julia} chunks; under knitr these run through the R package JuliaCall, so add any::JuliaCall to r-packages. The Julia depot is cached between runs via julia-actions/cache. When JuliaCall is installed, its Julia dependencies (RCall, Suppressor) are set up in their own step before the render, and R_LD_LIBRARY_PATH is set to R’s own lib directory so Julia started by R does not load the system libunwind, which segfaults on any Julia exception. @v3 and later.
julia-version string '1' Julia version to install, in setup-julia’s version syntax. Only used when setup-julia is true. @v3 and later.
preinstalled-toolchain boolean false The runner already provides R, Quarto, TinyTeX and Julia and has no sudo, as a self-hosted runner does. Skips the steps that install them (apt packages, Chrome, Quarto, R, Julia and its cache) and installs r-packages with pak into the runner’s user library. With an empty r-packages nothing is installed. The runner must already have R with the pak and tinytex packages, Quarto, TinyTeX, Julia and (if needed) Chrome; tinytex is then ignored for the install. renv, the JuliaCall setup and the freeze cache still run.
submodules string 'recursive' Passed to actions/checkout submodules ('recursive', 'true', or 'false').
render-profile string 'website' Quarto --profile to render with.
output-dir string '_site' Directory the rendered site is written to, relative to path.
formats string '' Space-separated list of formats to render (e.g. 'html docx pdf revealjs'). When empty (default), renders HTML plus any formats requested via PR labels (preview:docx, preview:pdf, preview:revealjs). Set to 'default' to run a single bare quarto render letting _quarto.yml decide formats (unlike quarto-publish where empty string defaults to bare render).
skip-docx boolean true Leave Word (docx) out of the PR preview render so it builds faster; the publish build still renders it. Applies to an explicit formats list only: formats: default is a bare render, and quarto render --to takes one format. A PR labelled preview:docx opts back in, and so does docx-tracked-changes: true, which needs the Word files. Set to false to render docx as before.
skip-formats string '' Space-separated formats (for example pdf docx) to leave out of the PR preview, including under formats: default, where skip-docx cannot apply. A PR labelled preview:<format> keeps that format, and docx-tracked-changes: true keeps docx. Slide decks are untouched unless named. Empty removes nothing; the publish build is unaffected.
extra-preview-labels string '[]' JSON array string of additional PR label names that trigger a preview re-render on add or remove (e.g. '["preview:tracked-changes"]'), beyond the built-in set (clear freezer, preview:pdf, preview:docx, preview:revealjs).
fail-on-render-warning boolean false Fail the build if Quarto emits any render warnings (WARN in the render log).
forbid-log-patterns string '' Newline-separated fixed string patterns that fail the build if found in the render log (e.g. missing listing files).
timezone string '' IANA time zone (e.g. America/Los_Angeles) exported as TZ for the render and later steps, so date: last-modified stamps are not in UTC. Empty leaves the runner’s time zone unchanged. A value containing a newline or = is rejected.
timeout-minutes number 45 Timeout, in minutes, for the render (build job). The default is the hang ceiling, not a budget; raise it for a site whose full render (every page in every format) takes longer. A caller cannot set timeout-minutes on a job that uses uses:.
detect-changed-chapters boolean false Compare this run’s render against the copy published on the deployed branch and expose which chapters differ as outputs. Implied by changed-chapters-banner and highlight-changes.
changed-chapters-banner boolean false Add a banner to the preview home page naming the chapters that differ from the published render.
highlight-changes boolean false Highlight added and modified text in the rendered HTML chapters that differ from the published render.
changed-chapters-glob string 'chapters/*.html' Glob, relative to the rendered site root, selecting the files the comparison treats as chapters.
deployed-branch string 'gh-pages' Branch holding the published render to compare against.
deployed-subdir string '' Path prefix, within the deployed branch, at which the published site root lives. Defaults to the branch root.
changed-chapters-normalize-patterns string '' Newline-separated regexes whose matches are blanked before comparison, in addition to the built-in ones (htmlwidget element ids and ISO-8601 datetimes).
max-elements-for-pairwise string '500' Maximum candidate elements per page for pairwise SequenceMatcher diffing; exceeding this skips element-level diffing to prevent quadratic runtime.
banner-index string 'index.html' Home page the banner is inserted into, relative to the rendered site root.
docx-tracked-changes boolean false Generate Word DOCX documents with tracked changes against the published version on the deployed branch.
docx-tracked-changes-glob string 'chapters/*.docx' Glob, relative to the rendered site root, selecting the DOCX files to generate tracked changes for.

Outputs

Set only when detect-changed-chapters, changed-chapters-banner, or highlight-changes is on.

Output Description
changed-chapters JSON array of chapter ids that differ from the published render. A chapter id is the rendered file’s path relative to the site root, with its extension removed.
any-changed true when at least one chapter differs, false otherwise.
detection-status compared when the published render was available, skipped when it was not.
skip-reason Why the comparison was skipped; empty when it ran.
docx-status generated when DOCX tracked changes were produced, skipped when the comparison was skipped.
docx-skip-reason Why DOCX tracked changes generation was skipped; empty when it ran.
docx-tracked-changes-files JSON array of generated tracked changes DOCX file paths relative to the site root.
any-docx-changed true when at least one DOCX file differs from published render, false otherwise.

Read detection-status before acting on an empty changed-chapters. An empty list means “nothing changed” only under compared; under skipped it means the comparison never ran, because nothing has been deployed to deployed-branch yet. Collapsing the two is the failure this capability was rewritten to avoid.

The comparison is against the deployed render, not the source diff, so a page whose content changed because shared code beneath it changed is reported, and a source edit that changes no rendered output is not.

Permissions

Only contents: read (the default). The build half is read-only and must never write to the base repo.

Example

# Copy to .github/workflows/preview.yml in your repo.
# Build half of the PR-preview family: renders the Quarto site in the (possibly
# fork) PR context and uploads it + PR metadata as an artifact. The deploy half
# (preview-deploy.yml) publishes it to gh-pages. This job is read-only
# (contents: read) --- it must never write to the base repo.
#
# IMPORTANT: keep this workflow's `name:` in sync with the `workflows:` list in
# preview-deploy.yml --- that's how the deploy half finds this run.
name: Quarto Preview Build

on:
  pull_request:
    types: [opened, reopened, synchronize, labeled, closed]
    # Trim/extend these globs to the source paths that affect your rendered site.
    paths:
      - 'man/**'
      - 'vignettes/**'
      - '_extensions/**'
      - '_quarto*.yml'
      - '*.qmd'
      - 'chapters/**/*.qmd'
      - '_subfiles/**'
      - '*.scss'

jobs:
  build:
    permissions:
      contents: read
    uses: Morrison-Lab/gha/.github/workflows/preview.yml@v2
    # with:
    #   path: website                      # Quarto project lives in a subdir (default: repo root)
    #   r-version: '4.6.0'
    #   apt-packages: 'jags libglpk-dev'   # override the default system-deps list
    #   use-renv: true                     # set false for non-renv repos
    #   r-packages: 'rmarkdown knitr'      # packages to install when use-renv: false
    #   install-package: true              # `R CMD INSTALL <path>` before rendering
    #   setup-chrome: true                 # needed for revealjs / screenshots
    #   tinytex: false                     # install TinyTeX unconditionally (default: label-gated)
    #   setup-julia: true                  # run {julia} chunks (add any::JuliaCall to r-packages; needs @v3)
    #   preinstalled-toolchain: true       # self-hosted runner with R, Quarto, TinyTeX, Julia installed
    #   julia-version: '1'
    #   submodules: recursive
    #   render-profile: website
    #   output-dir: docs                   # render to a dir other than _site
    #   formats: 'html docx pdf'           # space-separated list, or 'default' for bare render
    #   skip-docx: false                   # render docx in PR previews too (default true skips it)
    #   skip-formats: 'pdf docx'           # leave these formats out of PR previews, even under formats: default
    #   extra-preview-labels: '["preview:tracked-changes"]' # JSON array string of additional labels that trigger preview rebuilds
    #   fail-on-render-warning: true       # fail if Quarto emits WARN in render log
    #   forbid-log-patterns: |             # fail on specific forbidden substrings in render log
    #     doesn't match any files or folders
    #   timezone: America/Los_Angeles     # export TZ for the render (e.g. for date: last-modified)
    #   timeout-minutes: 90                # raise the 45-minute render cap for a site that renders slowly
    #   detect-changed-chapters: true      # expose which chapters differ from the deployed render
    #   changed-chapters-banner: true      # and list them on the preview home page
    #   highlight-changes: true            # and highlight modified/added text in rendered chapters
    #   changed-chapters-glob: 'chapters/*.html'
    #   deployed-branch: gh-pages
    #   deployed-subdir: ''                # set when gh-pages nests the site under a prefix
    #   banner-index: index.html
    #   changed-chapters-normalize-patterns: |   # blank anything else that changes every render
    #     build-[0-9a-f]+
    #   max-elements-for-pairwise: '500'   # cap candidate elements to avoid quadratic diffing
    #   docx-tracked-changes: true         # generate Word DOCX with tracked changes against published render
    #   docx-tracked-changes-glob: 'chapters/*.docx'

See examples/preview.yml for the full caller stub.

Partial adoption for bespoke builds

If your site requires custom post-render processing or a build sequence that preview.yml does not model, you can maintain a custom build workflow and still use preview-deploy.yml and cleanup-pr-previews.yml. See Partial adoption and artifact contract for details on the three-file pr-preview-site artifact handoff.