Skip to contents

Scripts and hooks customize projr’s build process. Scripts control which documents to render; hooks run custom R code before or after builds.

Both are configured in _projr.yml and have separate settings for production builds (projr_build_patch(), etc.) and development builds (projr_build_dev()).

Build Scripts

Build scripts specify which documents or R scripts projr should render.

Configuration format

Scripts are plain character vectors in _projr.yml. No sub-keys or nested structures are allowed.

build:
  scripts:
    - analysis.qmd
    - report.Rmd
    - data-processing.R

dev:
  scripts:
    - quick-test.qmd

Priority

Production builds resolve scripts in this order:

  1. file parameter passed to the build function
  2. build.scripts in _projr.yml
  3. _quarto.yml or _bookdown.yml project file
  4. Auto-detection of .Rmd, .qmd, .R files

Development builds follow the same order, but check dev.scripts before falling back to build.scripts:

  1. file parameter
  2. dev.scripts in _projr.yml
  3. build.scripts in _projr.yml
  4. _quarto.yml or _bookdown.yml
  5. Auto-detection

When dev.scripts is set, it completely replaces build.scripts for dev builds. To include production scripts in dev builds, list them explicitly under dev.scripts.

All specified scripts are validated before the build starts. Missing files cause an immediate error.

Managing scripts with R

# Add scripts
projr_yml_script_add(c("analysis.qmd", "report.Rmd"))

# Remove a script
projr_yml_script_rm("report.Rmd")

# Remove all scripts
projr_yml_script_rm_all()

Example: separate dev and production scripts

# _projr.yml
build:
  scripts:
    - full-analysis.qmd
    - supplementary.Rmd
    - appendix.Rmd

dev:
  scripts:
    - quick-test.qmd
projr_build_dev()   # renders quick-test.qmd only
projr_build_patch() # renders all three documents

Example: one-off builds

projr_build_dev("exploratory-analysis.Rmd")
projr_build_dev(c("methods.Rmd", "results.Rmd"))

Build Hooks

Hooks run custom R scripts at specific points in the build process.

Configuration format

Hooks are plain character vectors organized by stage. Three stage keys are available: pre (before the build), post (after the build), and both (runs at both stages). File paths are relative to the project root.

build:
  hooks:
    pre:
      - hooks/setup.R
      - hooks/download-data.R
    post:
      - hooks/cleanup.R
    both:
      - hooks/log-timestamp.R

dev:
  hooks:
    pre: hooks/dev-setup.R

build.hooks are ignored in dev builds. dev.hooks are ignored in production builds. Unlike scripts, there is no fallback between them.

Execution details

Hooks within a stage run in the order listed. Each hook runs in an isolated child environment of the global environment, so objects created in one hook are not available in subsequent hooks. To share data between hooks, save to disk (e.g., saveRDS()).

Where hooks run in the build flow:

Production build:
  1. Pre-build hooks
  2. Version bump
  3. Clear output directories
  4. Git commit (if configured)
  5. Render scripts
  6. Git commit outputs (if configured)
  7. Distribute to remotes
  8. Post-build hooks

Development build:
  1. Dev pre-build hooks
  2. Clear output directories
  3. Render scripts
  4. Dev post-build hooks

Like scripts, all hook files are validated before the build starts. A missing hook file causes an immediate error. If a hook fails at runtime, the build stops.

Managing hooks with R

projr_yml_hooks_add(path = "hooks/setup.R", stage = "pre")
projr_yml_hooks_add_pre("hooks/check-auth.R")
projr_yml_hooks_add_post("hooks/cleanup.R")

# Append instead of overwriting
projr_yml_hooks_add("hooks/extra.R", stage = "pre", overwrite = FALSE)

# Remove all hooks
projr_yml_hooks_rm_all()

Example: data preparation hook

# hooks/prepare-data.R
data_url <- Sys.getenv("DATA_URL")
if (!nzchar(data_url)) stop("DATA_URL not set")

download.file(data_url, destfile = "_raw_data/data.csv")
data <- read.csv("_raw_data/data.csv")
stopifnot(nrow(data) > 0)
message("Data preparation complete")
build:
  hooks:
    pre: hooks/prepare-data.R

Example: separate dev and production hooks

build:
  hooks:
    pre: hooks/prod-setup.R

dev:
  hooks:
    pre: hooks/dev-setup.R
# hooks/dev-setup.R
message("Setting up dev environment...")
Sys.setenv(PROJR_OUTPUT_LEVEL = "debug")
# hooks/prod-setup.R
message("Setting up production environment...")
source("hooks/check-auth.R")
source("hooks/prepare-data.R")

Tips

  • Use relative paths for portability.
  • Keep each hook focused on one task. Use descriptive filenames (e.g., validate-credentials.R rather than hook1.R).
  • Wrap external operations in tryCatch() so error messages are actionable.
  • Test hooks outside a build with source("hooks/my-hook.R").

See Also