
Package Setup & Maintenace
rpkgkit 0.1.23
Yuxi Yang
2026-11-06
Source:vignettes/pkg_maintain.Rmd
pkg_maintain.Rmd
library(rpkgkit)
#> ✔ rpkgkit v0.1.24 loaded [2 ms]
dir <- tempdir()
usethis::create_package(path = dir, open = FALSE)
#> ✔ Setting active project to "/tmp/RtmpFX7Op9".
#> ✔ Creating R/.
#> ✔ Writing DESCRIPTION.
#> Package: RtmpFX7Op9
#> Title: What the Package Does (One Line, Title Case)
#> Version: 0.0.0.9000
#> Authors@R (parsed):
#> * First Last <first.last@example.com> [aut, cre]
#> Description: What the package does (one paragraph).
#> License: `use_mit_license()`, `use_gpl3_license()` or friends to
#> pick a license
#> Config/roxygen2/version: 8.1.1
#> Encoding: UTF-8
#> Roxygen: list(markdown = TRUE)
#> ✔ Writing NAMESPACE.
#> ✔ Setting active project to "<no active project>".
usethis::use_pkgdown()
#> ✔ Setting active project to "/home/runner/work/rpkgkit/rpkgkit".
#> ℹ Leaving _pkgdown.yml unchanged.
#> ☐ Edit _pkgdown.yml.Create package infrastructure
A startup message
use_zzz() creates a package documentation file with
.onLoad, .onAttach, and %||%. It
is similar to usethis::use_package_doc() while also
providing a useful starting template.
use_zzz(path = dir, open = FALSE)
#> ✔ Setting active project to "/tmp/RtmpFX7Op9".
#> ✔ Created /tmp/RtmpFX7Op9/R/RtmpFX7Op9-package.R from template.
#> ✔ Adding cli to Imports field in DESCRIPTION.
#>
#> ☐ Refer to functions with `cli::fun()`.Check missing function in _pkgdown reference
check_pkgdown_reference() identifies exported functions
that are absent from the reference section of
_pkgdown.yml.
check_pkgdown_reference(pkg = dir)Configure the clangd language server
use_clangd() writes a .clangd configuration
file that lets the clangd language server find the system R headers and
the include/ directory of the packages you pass in
include (Rcpp, RcppEigen and
RcppArmadillo by default). Packages that are not installed
are skipped with a warning.
use_clangd(path = dir, overwrite = TRUE)
#> Warning: Package RcppEigen is not installed; skipping its
#> include path.
#> Warning: Package RcppArmadillo is not installed; skipping its
#> include path.
#> ✔ Created /tmp/RtmpFX7Op9/.clangd.When path is a package root, the file also disables
background indexing for the R/ directory (which holds no
C++ code) and adds .clangd to
.Rbuildignore.
use_clangd(path = dir, include = c("Rcpp", "BH"), overwrite = TRUE)
#> ! Overwriting /tmp/RtmpFX7Op9/.clangd.
#> Warning: Package BH is not installed; skipping its include path.
#> ✔ Created /tmp/RtmpFX7Op9/.clangd.Configure the jarl linter
use_jarl() writes a jarl.toml file for jarl, the linter written in
Rust. The file pins down the linting behaviour of every
jarl invocation, so the command line, your editor and CI
all agree. It enables the opt-in TESTTHAT group of rules,
skips vendored files, and lints the code inside @examples
and @examplesIf tags.
use_jarl(path = dir, overwrite = TRUE)
#> ✔ Created /tmp/RtmpFX7Op9/jarl.toml.
#> • Read jarl's editor support
#> (<https://jarl.etiennebacher.com/reference/config-file>) to learn how to
#> invoke jarl in your preferred editor.
#>
#> • Read jarl's continuous integration
#> (<https://jarl.etiennebacher.com/howto/ci>) to learn about GHA workflows that
#> continuously run linting checks.jarl reads a single configuration file: the closest
jarl.toml in the current directory or any of its parents,
and finally the one in your user configuration directory. When
path is a package root, jarl.toml is also
added to .Rbuildignore so it stays out of the built
tarball.
readLines(file.path(dir, "jarl.toml")) |> cli::cli_code()
#> # jarl configuration file
#> # Reference: https://jarl.etiennebacher.com/reference/config-file
#> [lint]
#> # Rules enabled on top of jarl's default rule set. The `TESTTHAT` group is
#> # opt-in, so it has to be requested explicitly.
#> extend-select = ["TESTTHAT"]
#> # Files or directories that jarl should not lint.
#> exclude = ["R/vendor-*.R"]
#> # Also lint the R code inside `@examples` and `@examplesIf` tags
#> # (the default).
#> check-roxygen = true
#> # Rules that `jarl check --fix` is allowed to fix (all rules by default).
#> # fixable = ["PERF"]
#> # Rules that `jarl check --fix` must leave untouched.
#> # unfixable = ["undesirable_function"]Read the configuration
reference for options such as select,
ignore and per-file-ignores, and the continuous integration
guide to run jarl check on every push with the
etiennebacher/setup-jarl action.
RStudio addins
An addin is an exported function registered in
inst/rstudio/addins.dcf; RStudio then lists it in the
Addins menu and runs it in the current session.
use_tidy_addin() writes that entry for you:
use_tidy_addin(
fn_name = "check_my_pkg",
desc = "Check my package",
path = dir
)The entry is appended above the existing ones, so the newest addin
comes first, and a Binding that is already registered is
never added twice:
Name: check_my_pkg
Description: Check my package
Binding: check_my_pkg
Interactive: false
Before writing, the function checks that path is a
package and that fn_name is exported in
NAMESPACE, reads the body of the function (from the
installed package, or from R/ when the package has not been
documented yet), and warns when that body calls no
rstudioapi function, or, for an
Interactive = TRUE addin, no
shiny/miniUI function. In an interactive
session the warning asks whether to continue; a non-interactive session
only reports it and writes the entry.
rpkgkit registers its own addins in
inst/rstudio/addins.dcf, so every detector can be run from
the menu without typing the call:
| Addin | What it checks |
|---|---|
detect_lost_glue_brace /
package_lost_glue_brace
|
unmatched {} in glue()/cli
strings |
detect_print_and_cat /
package_print_and_cat
|
CRAN-unsafe print()/cat() calls |
detect_fn_arg_path /
package_fn_arg_path
|
path-like arguments of exported functions that default to a concrete location |
package_unmatched_nocov_marker |
unmatched # nocov start / # nocov end
markers |
check_pkgdown_reference |
exported functions missing from the _pkgdown.yml
reference |
The detect_* addins work on the active editor, the
package-wide ones on the current package directory. They only report;
none of them rewrites a file. In Positron, addins need a third-party
extension such as Positron R Wizard.
Vendor permissively licensed code
use_vendor() records attribution and licensing
information when incorporating selected source files from a permissively
licensed GitHub R package. It creates an inst/vendor/
directory, a R/vendor-*.R file, and updates relevant
DESCRIPTION fields.
try(use_vendor(
pkg = "WangLabCSU/rpkgkit",
"43_use_vendor.R",
branch = "main",
path = dir
))
#> ℹ Fetching repository information for WangLabCSU/rpkgkit...
#> ✔ Vendor package uses MIT license.
#> ✔ Created directory /tmp/RtmpFX7Op9/inst/vendor/rpkgkit.
#> ✔ Copied LICENSE.
#> ✔ Copied LICENSE.md.
#> ✔ Created inst/vendor/rpkgkit/README.md.
#> ✔ Created /tmp/RtmpFX7Op9/R/vendor-rpkgkit.R.
#> ✔ Added rpkgkit authors to Authors@R.
#> ✔ Updated DESCRIPTION.
#> ☐ Consider pasting the following statement into README.md
#>
#>
#> ## Acknowledgements
#>
#> We would like to thank the following people and projects:
#>
#> - The authors of the [rpkgkit](https://github.com/WangLabCSU/rpkgkit) package — **Yuxi Yang** — whose code is included (under MIT license) in files named `vendor-rpkgkit.*`.Only packages with a supported permissive license can be vendored. Review the acknowledgement text printed by the function and add it to your README when appropriate.
usethis extensions
Use the following helpers from the package root:
Use R 4.1.0 version
R 4.1.0 introduced the pipe (|>) and anonymous
function \(). If these syntaxes need to be used in a
package, the R package must require at least R version 4.1.0.
use_r_v4.1.0(path = dir)
#> ✔ Adding R to Depends field in DESCRIPTION.Add URL in DESCRIPTION
use_url(dir)Add bug reports URL in DESCRIPTION
use_bugreports(dir)Open CFF
CITATION.cff files are plain text files with human- and machine-readable citation information for software and datasets.
Code developers can include such files in their source code repositories to let others know how to correctly cite their software.
This cannot replace
usethis::use_citation(), it just complements
project.
Global ignore-file templates
Use add_global_rbuildignore() and
add_global_gitgnore() to add the corresponding global
ignore-file templates to a package.
add_global_rbuildignore(path = dir)
#> ✔ Added 38 pattern(s) to .Rbuildignore.
add_global_gitgnore(pkg = dir)GitHub Actions workflows
Each use_workflow_*() function copies a workflow
template bundled in inst/actions/ into the
.github/ directory of the target package. All of them
create the directories they need, abort instead of overwriting unless
overwrite = TRUE, and return the written path
invisibly.
Bump the version from commit messages
use_workflow_version_update() installs
version_update.yml together with the
version-bumper composite action. The version is bumped from
the commit message (major/breaking,
feat/minor,
patch/fix), or explicitly through
workflow_dispatch. A bump is skipped when the new version
would exceed the highest version documented in NEWS.md, and
the version field of src/rust/Cargo.toml is
kept in sync when the package ships Rust code.
use_workflow_version_update(path = dir)
#> ✔ Created workflow: /tmp/RtmpFX7Op9/.github/workflows/version_update.yml
#> → It triggers on push to "main/master" and via workflow_dispatch. Version bump
#> is inferred from commit messages or manual input.
#> ☐ It is recommended to add a devel version badge to your README.md via
#> `badger::badge_devel(color = "blue")`Run R CMD check on packages with Rust code
use_workflow_rust_compatible_check() installs a
R-CMD-check.yaml that can download, compile and link Rust
crates, which is what packages built on
extendr/rextendr need. It replaces
usethis::use_github_action("check-standard") for such
packages.
use_workflow_rust_compatible_check(path = dir)
#> ✔ Created workflow: /tmp/RtmpFX7Op9/.github/workflows/R-CMD-check.yaml
#> → It triggers on push to "main" and on pull requests targeting "main".
#> → Rust crates are compiled with a pinned toolchain and the
#> "x86_64-pc-windows-gnu" target on Windows, so they link against Rtools.The difference lies in the toolchain. The usethis
workflow only sets up R and leaves the runner default in place; on
Windows that default is an MSVC toolchain, while R and Rtools are GNU
based, so the static library produced by cargo cannot be
linked and the resulting build noise makes R CMD check
report a WARNING. The template used here follows the official
extendr CI:
-
dtolnay/rust-toolchaininstalls the requested toolchain and, on Windows, additionally thex86_64-pc-windows-gnutarget that Rtools can link. - The matrix covers Windows, macOS, Ubuntu release and Ubuntu devel
with
fail-fast: false, so one broken platform does not hide the others. -
R_REMOTES_NO_ERRORS_FROM_WARNINGSkeeps download noise fromremotesandpakfrom surfacing as a check failure.
Sync main to a test branch
use_workflow_test_branch() adds a workflow that runs
after R-CMD-check.yaml succeeds on
main/master and force-pushes the checked
commit to test. The sync is skipped when test
holds commits that are not reachable from main, so unmerged
work is never overwritten. Push experimental changes to
test and each push to main resets it
again.
use_workflow_test_branch(path = dir)
#> ✔ Created workflow: /tmp/RtmpFX7Op9/.github/workflows/sync_test_branch.yml
#> → It runs after "R-CMD-check.yaml" succeeds for "main/master", then syncs the
#> checked commit to "test".
#> → If "test" has unmerged work, the sync is skipped automatically.Check system dependencies
Some packages need system development libraries (graphics and font
libraries, for example) to compile. check_sysdeps() detects
the package manager in use (dpkg, rpm,
pacman, apk or brew), checks each
dependency with it, and reports every hit and miss together with a
ready-to-paste install command for the missing ones.
With deps = NULL (the default), a built-in list of
graphics and font libraries (freetype,
libjpeg, libpng, libtiff,
libwebp, harfbuzz, fribidi,
fontconfig) is checked using the names that the detected
package manager expects. Pass your own names in deps, and
optionally set manager to check against a manager other
than the detected one:
try(check_sysdeps(c("freetype-devel", "libwebp-devel"), manager = "rpm"))The result is returned invisibly as a named logical vector, so it can
be reused in scripts. Set verbose = FALSE to keep the run
silent:
ok <- check_sysdeps(verbose = FALSE)
ok[!ok]Detect the Positron version
get_positron_version() reports the version of the
Positron IDE that hosts the current R session. It is useful for
diagnostics, bug reports, or for enabling IDE-specific behaviour only on
recent builds.
get_positron_version() # e.g. "2025.01.0"
get_positron_version(long = TRUE) # e.g. "2025.01.0-123"The version is looked up in three steps, in this order:
- the
POSITRON_VERSIONenvironment variable set by Positron for its R sessions (POSITRON_LONG_VERSION, which also carries the build number, whenlong = TRUE); - the
.ps.positron_version()helper that the IDE injects into the session; - the
positron --versioncommand-line tool.
If the session does not run inside Positron and no
positron executable is on PATH, the function
returns NA_character_ instead of throwing an error, so it
is safe to call in any session, including CI.