This article describes the three stages of a projr build and what happens at each step.
projr has two build types:
Production builds create versioned releases:
projr_build_patch(msg = "Fix analysis bug") # 0.0.X
projr_build_minor(msg = "Add new section") # 0.X.0
projr_build_major(msg = "Complete rewrite") # X.0.0Development builds iterate without incrementing the version:
projr_build() is a wrapper that accepts a
bump_component argument:
Both follow a three-stage architecture:
┌─────────────┐
│ Pre-Build │ ─── Preparation, validation, versioning
└──────┬──────┘
│
▼
┌─────────────┐
│ Build │ ─── Document rendering and script execution
└──────┬──────┘
│
▼
┌─────────────┐
│ Post-Build │ ─── Finalization, distribution, commits
└─────────────┘
The build validates the environment before starting:
projr_yml_check() validates
_projr.ymlRun projr_build_check_packages() at any time to check
package requirements.
Creates GitHub releases or local archive directories as configured in
_projr.yml.
renv.lock.gitignore and .Rbuildignoreprojr tracks three version numbers during a build:
Version transitions:
Production (patch) from dev:
0.0.1-1 → 0.0.2 → 0.0.2-1 (success)
0.0.1-1 → 0.0.2 → 0.0.1-1 (failure)
Dev build from release (auto-bumps):
0.0.1 → 0.0.1-1
Dev build from dev (no change):
0.0.1-1 → 0.0.1-1
You can check or set the version directly:
Pre-build hooks run after validation, before rendering.
# Add hooks from R
projr_yml_hooks_add_pre("setup-data.R")
projr_yml_hooks_add_post("cleanup.R")
# Or add to any stage
projr_yml_hooks_add("logger.R", stage = "both")See vignette("scripts-and-hooks") for full configuration
details.
Sets the project to the run version, then clears output directories
based on the clear_output setting:
"pre" (default for production): clear now"post": clear after build completes"never": don’t clearSafe directories (in cache,
e.g. _tmp/projr/v0.0.2/output) are always cleared. Unsafe
directories (final locations, e.g. _output) follow the
setting above.
Commits version files, ignore files, and documentation changes with
the message "Snapshot pre-build". Only runs if
build.git.commit is TRUE.
Hashes all files in input directories (raw-data,
cache) and stores the result in a temporary manifest in the
cache directory. This is merged with output hashes after the build.
| label | fn | version | hash |
|---|---|---|---|
| raw-data | dataset.csv | v0.0.2 | abc123… |
| cache | processed.rds | v0.0.2 | def456… |
Scripts are selected in this priority order:
file parameter passed to the build functiondev.scripts in _projr.yml (dev builds
only)build.scripts in _projr.yml_quarto.yml or
_bookdown.yml).Rmd, .qmd, or
.R files in the project rootThe rendering engine is detected automatically:
.qmd files or Quarto projects_bookdown.yml projects.Rmd filesPass custom arguments to the engine:
Copies files from safe (cache) directories to final (unsafe) directories:
_tmp/projr/v0.0.2/output → _outputdocs directory → docsIf clear_output = "post", the final directories are
cleared before copying.
output and docs
directoriesmanifest.csvSee the Manifest System section below for query functions.
For production builds, projr updates:
.Rd files (if roxygen comments exist)CITATION.cff, codemeta.json,
inst/CITATION (version numbers)README.Rmd (rendered if it exists)BUILDLOG.md (with a change summary)CHANGELOG.md (if configured)Example BUILDLOG entry:
## v0.0.2 (2024-01-15)
Build completed in 45.2 seconds
### Changes from v0.0.1 → v0.0.2
#### Input Changes
- Modified: 2 files
- Added: 1 file
#### Output Changes
- Modified: 5 files
- Added: 3 filesWhen total changes are fewer than 10, individual filenames are listed. Otherwise only counts are shown.
Commits outputs, documentation, manifest, and metadata. The commit
message follows the format "Build v{version}: {message}".
Does not push yet.
Sends artifacts to configured remotes (GitHub, OSF, local). The
behavior is controlled by four parameters in
_projr.yml:
| Parameter | Options | Description |
|---|---|---|
structure |
archive, latest |
Versioned subdirs vs overwrite |
send_cue |
always, if-change, never |
When to send |
send_strategy |
sync-diff, sync-purge,
upload-all, upload-missing |
How to update |
send_inspect |
manifest, file, none |
How to detect changes |
For quick archiving without editing _projr.yml, use the
archive_github or archive_local
parameters:
# Archive all directories to a GitHub release
projr_build_patch(msg = "Release v0.0.2", archive_github = TRUE)
# Archive specific directories
projr_build_patch(msg = "Archive outputs", archive_github = c("output", "docs"))
# Archive locally
projr_build_patch(msg = "Local backup", archive_local = TRUE)See vignette("send-to-remotes") for full configuration
details.
Post-build hooks run after distribution, before the final push. See
vignette("scripts-and-hooks").
Production builds only. Appends a dev suffix (e.g. 0.0.2
→ 0.0.2-1) and commits with the message
"Begin v{version}".
The manifest tracks file hashes across versions in
manifest.csv at the project root.
Pre-build hashes cover input directories (raw-data,
cache). Post-build hashes cover output directories
(output, docs). Both are merged and appended
to the cumulative manifest.
label,fn,version,hash
raw-data,dataset.csv,v0.0.1,abc123def456
output,figure1.png,v0.0.1,jkl678mno901
docs,index.html,v0.0.1,pqr234stu567
This enables:
Query the manifest:
# Changes between two versions
changes <- projr_manifest_changes("0.0.1", "0.0.2")
changes$added
changes$removed
changes$modified
# Changes across multiple versions
projr_manifest_range("0.0.1", "0.0.5")
# Last build's changes
projr_manifest_last_change()Control console verbosity with PROJR_OUTPUT_LEVEL:
| Level | Description |
|---|---|
"none" |
Errors only (default for dev builds) |
"std" |
Major step progress (default for production) |
"debug" |
Detailed operations, file counts, remote plans |
At the "debug" level you will see messages like:
→ Checking required packages
→ Snapshotting renv
→ Setting build version
→ Running build hook: setup.R
→ Remote: github/archive (12 files)
→ Strategy: sync-diff — 3 modified, 2 added, 1 removed
Detailed logs are written to the cache directory:
cache/projr/log/
├── output/ # Production build logs
│ ├── history/builds.md # All build records
│ └── output/YYYY-MMM-DD/
│ └── HH-MM-SS.qmd # Detailed log
└── dev/ # Development build logs
└── ...
History tracking (builds.md) is always maintained.
Detailed per-build logs can be disabled:
View or clear logs:
Review the detailed log file in the cache directory.
Check git status:
A typical workflow from setup through production build:
# 1. Check configuration is valid
projr_yml_check()
# 2. Check the current version
projr_version_get()
# 3. Run a dev build while iterating
projr_build_dev()
# 4. When ready, do a production build with archiving
projr_build_patch(
msg = "Add regression analysis",
archive_github = TRUE,
output_level = "std"
)
# 5. Check what changed
projr_manifest_last_change()
# 6. Review the build log
projr_log_view()After projr_build_patch() completes, the project version
is bumped to a dev version (e.g. 0.0.2-1), all artifacts
are committed and pushed, and remote destinations have received the new
files.
vignette("scripts-and-hooks") – build scripts and
hooksvignette("send-to-remotes") – remote distributionvignette("dest-send-workflow") – destination send
workflow diagramsvignette("environment") – environment variables
referencevignette("how-to-guides") – task-focused guidesvignette("concepts") – core concepts