--- title: "Scripts and Hooks" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Scripts and Hooks} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` 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. ```yaml 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 ```{r eval=FALSE} # 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 ```yaml # _projr.yml build: scripts: - full-analysis.qmd - supplementary.Rmd - appendix.Rmd dev: scripts: - quick-test.qmd ``` ```{r eval=FALSE} projr_build_dev() # renders quick-test.qmd only projr_build_patch() # renders all three documents ``` ### Example: one-off builds ```{r eval=FALSE} 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. ```yaml 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 ```{r eval=FALSE} 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 ```{r eval=FALSE} # 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") ``` ```yaml build: hooks: pre: hooks/prepare-data.R ``` ### Example: separate dev and production hooks ```yaml build: hooks: pre: hooks/prod-setup.R dev: hooks: pre: hooks/dev-setup.R ``` ```{r eval=FALSE} # hooks/dev-setup.R message("Setting up dev environment...") Sys.setenv(PROJR_OUTPUT_LEVEL = "debug") ``` ```{r eval=FALSE} # 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 - `?projr_yml_hooks_add` - Add hooks programmatically - `?projr_yml_script_add` - Add scripts programmatically - `?projr_build_dev` - Development builds - `?projr_build_patch` - Production builds