fix_globals(write = TRUE) no longer flattens the per-function
# <fun>: grouping comments of an existing R/globals.R when a
second run has nothing new to declare. When R CMD check surfaces
only function / operator notes (no remaining variable notes) and
R/globals.R already covers every preserved name, the file is
now left untouched. Closes #123.fix_globals(write = TRUE) no longer creates a degenerate empty
utils::globalVariables(unique(c())) shell on a virgin package
whose only notes are function / operator ones. When there is no
variable to declare, no R/globals.R is written.fix_globals(write = TRUE) now also prints the
"Functions to add in NAMESPACE (with @importFrom ?)" section.
Previously only the operators / pronouns block was surfaced in
write mode, leaving the user blind to bare-function notes (where,
summarise, first, str_detect, bind_cols, ...) that still
needed manual @importFrom wiring. Closes #124.check_n_covr(pkg) runs R CMD check (via
devtools::check(args = "--no-tests")) and code coverage (via
covr::package_coverage(type = "tests")) without running the
unit test suite twice. Returns a named list list(check, coverage).
Closes #67.covr is now in Imports.audit_downloads(pkg) walks R/, tests/, vignettes/ and
inst/, parses every .R / .Rmd / .qmd / .Rnw file, and
surfaces every call to a known download or HTTP function:
download.file() / download.packages() (base / utils),
httr::GET() / POST / PUT / PATCH / DELETE / HEAD,
httr2::req_perform() / req_perform_parallel /
req_perform_iterative, curl::curl_download() /
curl_fetch_memory / curl_fetch_disk / curl_fetch_stream,
and the RCurl::getURL / getURI / getBinaryURL legacy set.
Each hit is paired with a suggestion to wrap the call in
tryCatch() / skip_if_offline() (tests) or move it to
\dontrun{} (examples) so the package degrades gracefully on
offline build farms. Detection is purely static (AST walk via
getParseData()), so user-defined functions that shadow a known
downloader (download.file <- function(...) { ... }) do not
trigger a false positive on the definition site - only call
sites are flagged. Returns a tibble with file, line,
function and suggestion. Closes #27.audit_description(pkg) reads the Description field of
DESCRIPTION, tokenises it, and surfaces every word that matches
an installed package name yet is not wrapped in single quotes.
CRAN incoming pretest emits
Package names should be quoted in the Description field when
this rule is violated. Detection is purely static: no package is
loaded, no namespace is touched. The package's own name and
compound forms (dplyr-style, httr2-based, ...) are
intentionally not flagged. Returns a tibble with word,
position and suggestion. Closes #52.audit_dontrun(pkg) walks man/*.Rd line by line and surfaces
every \dontrun{} block, with the source Rd file, the documented
topic, the line number and a one-line suggestion to switch to
\donttest{} unless the example genuinely cannot be executed
(missing API key, missing system dependency, side effect on the
user's filespace). Detection is purely static: each Rd file is
read line-by-line and never sourced. Closes #72.:=, .SD, .N, .I, .GRP, .BY, .EACHI (data.table),
.data, .env, !!, !!! (rlang) are no longer routed into the
utils::globalVariables(c(...)) block. They are exports from
another package - not undeclared variables - and the right fix is
an @importFrom line, not a globalVariables() entry.audit_globals() / .get_no_visible() now return a third tibble
operators next to globalVariables and functions. The token
is paired with its candidate source package(s).fix_globals() prints a third section
"Operators / pronouns to import via NAMESPACE" with ready-to-paste
#' @importFrom <pkg> <token> lines. When the source is ambiguous
(:= is exported by both data.table and rlang) every candidate is
listed and the user picks one consciously - no silent guessing.fix_globals(write = TRUE) only writes real globals to
R/globals.R. The operators section is printed on stdout so the
user wires the @importFrom lines into NAMESPACE manually.fun = str_extract(fun, ".+(?=:)")) was greedy and ate the
whole prose of := notes. Anchored to the first : so it now
reports the actual caller.fix_globals(write = TRUE) overwrote R/globals.R
with a fresh utils::globalVariables(unique(c(...))) block. That
was unsafe: R CMD check already filters out names covered by an
existing globalVariables() call, so the second time
fix_globals() ran on a curated package, only the uncovered
names showed up in the notes - overwriting then erased every
previously-declared name and re-flagged it on the next check
(circular game).R/globals.R, extracts the
names from any globalVariables() / utils::globalVariables()
calls it finds, and rewrites the file as the deduplicated union
of the freshly detected names and the already-declared ones. The
preserved block is appended under a # previously declared:
banner inside the same unique(c(...)) payload.Run examples step is now wrapped in a tryCatch(). When
devtools::run_examples() fails deep inside pkgload (e.g. the
srcrefs[[1L]]: subscript out of bounds crash on @examplesIf
examples whose body is fully under \donttest{}, on older R +
pkgload combos), the audit no longer aborts: it warns, skips the
examples slice, and still runs the unit tests / full check /
vignettes steps (#93).source = "Run examples (partial)") so files created before the
crash do not slip into the next baseline and disappear from the
report.tests/testthat/test-check_clean_userspace.R no longer hardcodes a
nrow == 5/6/11 cascade. It asserts the invariants the function
promises (the seeded leaks are caught, every row has the right
shape) instead of an exact OS-dependent row count, so the test now
runs on every OS (#54).audit_citation(pkg) parses inst/CITATION statically (no
eval()) and surfaces every call to personList(),
as.personList() or citEntry() that CRAN rejects on submission
(Package CITATION file contains call(s) to old-style ...).
Returns a tibble with call, line and a one-line suggestion
for the modern equivalent (c() on person() objects;
bibentry() instead of citEntry()). Closes #62.R CMD check triggered by audit_globals() and
fix_globals() now passes
build_args = "--no-build-vignettes" and
args = c("--no-manual", "--no-tests", "--no-examples", "--no-vignettes").
The "no visible binding for global variable" /
"no visible global function definition" notes come from R CMD
check's static * checking R code for possible problems step
and never depended on those phases. On a vignette-heavy package
this turns a multi-minute wait into a few seconds. The defaults
are exposed as build_args / args arguments to .get_notes()
so a caller can still opt back in if needed.audit_tags() and find_missing_tags() now flag missing @return
on S3 generics and on S3 methods that have their own Rd file
(block carrying a title or @rdname / @describeIn / @name).
Previously a strict class(object)[1] == "function" filter dropped
these blocks silently, so packages like the one reported in #92
were told "Good!" while CRAN was still asking for \value on
generics' Rd files (e.g. strand_chr.Rd,
dim.gggenomes_layout.Rd).@export blocks (no title, no @rdname / @describeIn /
@name) are intentionally not flagged: they produce no Rd file and
CRAN does not ask for \value on them.create_example_pkg() gains two opt-in flags so the same one-line
fixture can demonstrate every audit:
with_nonascii = TRUE copies a French-flavoured R/nonascii.R
(accents in comments + string literals + a message() body) so
audit_ascii() and fix_ascii() have something to surface.with_undocumented_data = TRUE writes a tiny
data/demo_dataset.rda without a roxygen block so
audit_dataset_doc() flags it as undocumented.FALSE to keep the historic behaviour for tests.
The README Quick start and the "Auditing an R package" vignette
now activate them so a copy-paste demo trips every audit.audit_globals() and fix_globals() gain a checks = argument
that accepts a pre-computed rcmdcheck::rcmdcheck() result. When
supplied, they skip running R CMD check and reuse the existing
output. Lets you run the check once and feed both functions
during a full package audit. See the new vignette
"Auditing an R package you have just received".chk
pattern, plus a per-issue cheatsheet) and "Pre-submission gates"
(heavier audits run before release: audit_check() and
audit_userspace()). The historic per-issue vignettes
(deal-with-check-outputs, check-with-real-cran-settings,
no-files-left-after-check) have been removed; their content lives
in those two and in the function reference.README Quick start now uses the shared-chk workflow as the
default example.The package now exposes a uniform CRAN-oriented API: each category of
R CMD check issue gets one audit_* (read-only) function and, when
an automated fix is safe, one fix_* (action) function. Type
audit_<TAB> or fix_<TAB> in RStudio to discover the surface.
| CRAN issue | Audit | Fix |
|---|---|---|
| Globals to declare (no visible binding) | audit_globals() | fix_globals() |
| Missing roxygen tags | audit_tags() | - |
| Non-ASCII characters | audit_ascii() | fix_ascii() |
| Files left in user space | audit_userspace() | - |
| R CMD check with CRAN settings | audit_check() | - |
| Undocumented datasets | audit_dataset_doc() | fix_dataset_doc() |
The 10 historic functions remain callable but emit
lifecycle::deprecate_warn() and delegate to the new façades:
| Old | → New |
|---|---|
| find_nonascii_files() | audit_ascii() |
| asciify_pkg() | fix_ascii() |
| get_no_visible() | audit_globals() |
| print_globals() | fix_globals() |
| find_missing_tags() | audit_tags() |
| check_as_cran() | audit_check() |
| check_clean_userspace() | audit_userspace() |
| use_data_doc() | fix_dataset_doc() |
| get_notes() | (internal) audit_globals() |
| get_data_info() | (internal) fix_dataset_doc() |
%>% re-export from magrittr is dropped. The pipe is no
longer in the package's exported surface; the native pipe |> is
available since R 4.1.asciify_pkg() now prints a one-line summary of how many files were
scanned, changed, and how many non-ASCII characters were found. In
dry-run mode it also prints how to apply the rewrite and how to
inspect the per-file detail. Use suppressMessages() to silence.asciify_pkg() and asciify_file() gain an n_chars column /
list element: the count of non-ASCII characters in the original
file. n_tokens (number of source locations to rewrite) is kept.asciify_pkg(), asciify_file(), asciify_r_source(),
find_nonascii_tokens() and find_nonascii_files() rewrite
non-ASCII characters in an R package the way CRAN expects: \uXXXX
escapes in string literals, Latin-ASCII transliteration in
comments and roxygen blocks, refusal to auto-rename non-ASCII
identifiers. AST-based via getParseData(). Defaults to a dry run
for whole-package rewrites.find_missing_values only return messages instead of warnings. (@MurielleDelmotte)check_clean_userspace() (#13)create_example_pkg() to get examples in functions documentationcheck_as_cran() (#21)find_missing_tags()print_globals()rcmdcheck()NEWS.md file to track changes to the package.