check_project() audits a project directory and reports whether it follows
the structure and conventions that init_project() creates. It is useful
both for projects initialized with init_project() and for existing
projects that were created independently.
Arguments
- path
Character. Path to the project directory. Defaults to
"."(the current working directory).- config
Character or
NULL. Path to a YAML configuration file produced bygenerate_project_config(). When supplied, thefolders:list in the file replaces the standard toolero folder set for the folder checks. WhenNULL(the default) and the project carries a_toolero.yml, that file is used instead – there is no need to handcheck_project()the same config on every call. Non-folder hygiene checks always run regardless.- error
Logical. Previously controlled whether the function printed a cli report (
TRUE) or returned a tibble visibly without printing (FALSE). Deprecated in v0.5.0 – the cli report now always prints and the tibble is always returned invisibly. Assign the result to access it programmatically:out <- check_project().
Value
A tibble with columns check, status, and message, returned
invisibly. Assign the result to use it programmatically.
Details
Each check records one of four status values: "pass" (the expected
artifact was found), "fail" (a required artifact is missing),
"warn" (a recommended artifact is missing or a problematic file was
found), or "info" (a file was found that warrants attention but is
not necessarily a problem).
README detection is case-insensitive and extension-agnostic: any file
whose stem matches readme (in any capitalization) counts, regardless
of extension or the absence of one. init_project() uses the same
detection when deciding whether it would overwrite an existing README.
Where the folder set comes from
Three sources, in order of precedence.
An explicit config argument wins. Folders it declares and the project
lacks are reported as "fail": the caller named a file and that file
states what the project should look like.
Failing that, a _toolero.yml at the project root is used. init_project()
writes one recording the structure it actually created, so a folder listed
there and missing from disk means something removed it. That is also a
"fail".
Failing both, the built-in standard set is used – data-raw/, data/,
R/, scripts/, output/figures/, output/tables/, and reports/.
Missing folders here are "warn", not "fail": nobody declared anything,
so the standard set is a suggestion rather than a contract.
A _toolero.yml that exists but cannot be parsed is reported as a failing
check and the audit continues against the built-in set. A config that
cannot be parsed is an error, since the caller asked for that file
specifically.
The renv checks
Beyond the presence of renv.lock, two checks guard the failure mode that
costs the most to discover late: a lockfile that does not describe the
analysis, which produces a container image that builds cleanly and then
cannot run.
A .renvignore excluding .qmd files is reported, and the advice is to
remove the entry. It stops renv from seeing the library() calls in a
project whose Quarto document is the source of truth. That the document
will eventually be purled to a .R file does not make up for it: the
snapshot you containerize from may be taken before the purl, and the
.qmd is the file being maintained either way. Versions of
init_project() before v0.5.0 wrote one; projects created by those
versions still carry it.
A renv.lock recording no packages is reported only when the project also
has .R or .qmd source files. A newly scaffolded project legitimately
has an empty lockfile – renv::scaffold() does no dependency discovery,
because there is nothing yet to discover – so the pairing is what makes
the observation worth printing.
Examples
# Audit the current working directory
# \donttest{
check_project()
#>
#> ── Project check ───────────────────────────────────────────────────────────────
#> ! No _toolero.yml found -- create one with `generate_project_config("_toolero.yml")` and edit it to match this project
#> ✖ No .Rproj file found -- use `usethis::create_project()` to initialize one
#> ✖ No renv.lock found -- use `renv::init()` to get started
#> ✖ No git repository found -- use `usethis::use_git()` to initialize one
#> ! No .gitignore found -- consider adding one to avoid committing unwanted files
#> ! No data-raw/ folder found -- consider adding one for raw input data
#> ! No data/ folder found -- consider adding one for cleaned data
#> ! No R/ folder found -- consider adding one for the .R script derived from your .qmd
#> ! No scripts/ folder found -- consider adding one for hand-written scripts
#> ! No output/figures/ folder found -- consider adding one for figures
#> ! No output/tables/ folder found -- consider adding one for tables
#> ! No reports/ folder found -- consider adding one for reports
#> ! No README found -- consider adding one to document the project
# }
# Audit a specific project directory
# \donttest{
project_dir <- withr::local_tempdir()
check_project(path = project_dir)
#> Error in check_project(path = project_dir): Directory /tmp/RtmpAyNNfD/file4e5461b8c215 does not exist.
# }
# Audit against a custom folder structure
# \donttest{
project_dir <- withr::local_tempdir()
config_path <- file.path(tempdir(), "my-config.yml")
generate_project_config("my-config.yml", path = tempdir())
#> ✔ Created /tmp/RtmpAyNNfD/my-config.yml
#> ℹ Edit /tmp/RtmpAyNNfD/my-config.yml to define your custom folder structure,
#> then pass it to `init_project()` via `config =
#> "/tmp/RtmpAyNNfD/my-config.yml"`.
#> ℹ For easy reuse across projects, consider moving this file to /home/runner.
check_project(path = project_dir, config = config_path)
#> Error in check_project(path = project_dir, config = config_path): Directory /tmp/RtmpAyNNfD/file4e54671a407a does not exist.
# }
# Access results programmatically
# \donttest{
project_dir <- withr::local_tempdir()
out <- check_project(path = project_dir)
#> Error in check_project(path = project_dir): Directory /tmp/RtmpAyNNfD/file4e541a8593cd does not exist.
# }
