R package workflow

Rpkg
RSE

A step-to-step guide to make CRAN-worthy R packages

Author

Chi Zhang

Published

May 19, 2023

This checklist is being updated over time. Mostly for my own use; but great if it helps you as well!

For a complete treatment, please refer to R Packages (2e) by Hadley Wickham and Jennifer Bryan.

Overview of my current workflow
  • initialize project, edit DESCRIPTION, search and replace with your package name
  • create package planning documentation, put inside dev/
  • create a minimal function with documentation, test and vignette
  • build package, check
  • create data related files and documentation
  • build pkgdown website
  • build package, check
  • push to github, deploy pkgdown website
  • github action configure lifecycle and R CMD check

Start building your package

The usethis package will help with the procedure when you do not have a package yet.

You can also create a template, and copy paste from it in the future.

Initialize the project

usethis::create_package('path_to_pkg/pkgname') 

It opens a new R project (directory) named pkgname, with the following items:

  • DESCRIPTION
  • NAMESPACE
  • directory R/
  • .Rbuildignore and .gitignore
  • and the project icon, pkgname.Rproj.

If you have an existing R project but wish to build a package there, copy everything but pkgname.Rproj, and modify the files in your existing pkg directory. Pay extra attention to the hidden files like .Rbuildignore.

usethis::use_mit_license() # modify name to yours
usethis::use_readme_md() # if you do not have this already
usethis::use_news_md()
usethis::use_test()

# create a folder for future data documentation
x <- 1 
usethis::use_data() 

In addition, URL and bug reports should be added in the DESCRIPTION.

Planning

It is good practice to start with planning the package, rather than directly start coding.

Create a folder called dev. To prevent it from being built, add the following line in .Rbuildignore

Write, test and document

Create exported functions in R/, development code in script/ (or somewhere else, such as dev/).

Data: raw and processed

Need to be clear in mind where the data files go. There are a few data related folders:

  • raw data files, in the format of excel sheets or csv. Usually placed as inst/data_name.csv
  • R scripts to process the raw data so that we create data object inside the package, put inside data-raw
  • data objects that can be called as pkg::data_name, are placed in data. These files are usually directly generated by executing write.rda().
  • data documentation, usually placed in R/data_documentation.R. These are Roxygen2 documents for the data.

Documentation

You need to configure the Build tools.

These three things should be done:

Function documentation

Create a function f1, and put your cursor on it. Go to Code -> Insert Roxygen Skeleton to create the template.

Alternatively, use #' to start.

#' A simple placehold function 
#'
#' @param x a numeric value
#'
#' @return a value 3 greater than the input
#' @export
#'
#' @examples 
#' f1(5)
f1 <- function(x){
  x+3
}

Data documentation

It can be beneficial to create a separate file to document data only, say data_documentation.R under the R/ directory.

#' Placeholder data x
#'
#' This dataset contains one value, x
#'
#' @format
#' \describe{
#' \item{x}{The placeholder data x}
#' }
#' @examples
#' print(x)
"x"

Vignette documentation

usethis::use_vignette('your_vignette')

Deploy to pkgdown

Check this reference here

Build package and check

It is possible that your checks don’t pass on the first try.

What to ignore when build?

^.*\.Rproj$
^\.Rproj\.user$
^dev$
^_pkgdown\.yml$
^license\.md$
Makefile
data-raw
cran-comments.md
^\.github$

Don’t forget to check these!

These aspects are quite important to check as well: documentation, code quality, exceptions among others.

Documentation

Sufficient, working examples

  • README.md
  • Help pages
  • Vignettes

Example code coverage, covr::package_coverage(), use GHA workflow to check if code coverage is above a threshold.

Detect broken README examples by generating README.md on every commit.

In help pages, some examples have tags:

  • \dontrun{}: not run by example(), not run by R CMD check
  • \donttest{}: run by example() but not checked
  • \dontshow{}: run and checked
# code coverage
covr::package_coverage(type = c('examples', 'vignettes'), commentDonttest = F, commentDontrun = F)

# readme examples
rmarkdown::render("README.rmd", output_format = rmarkdown::github_document())

# help page examples
devtools::run_examples(run_dontrun = T, run_donttest = T)

# check vignette examples
pkgdown::build_site()

Handling exceptions

Error > warning > message

Code quality

Style guide, styler::style_pkg() enforces tidyverse style guide.

Lint (code smells). Can be removed with styler

# code quality assessment
lint(text = 'x = 1') # with lint
lint(text = 'x <- 1') # no lint
lintr::lint_package()

CRAN Submission

Final checks

When you are almost ready to submit your package to CRAN, you need to do a few checks.

A good practice is to check while you build your package, so that you’ve already fixed most of the problems by the time you do the final check.

These are a few final checks to do.

# spell check
devtools::spell_check()

# windows check
devtools::check_win_devel()

# rhub cran check 
rhub::check_for_cran()

Remember to check your email and fix potential problems! Some problems pop up in different places, so make sure you fix as many as possible.

Submit

There are a few ways to submit. I found this one to be the most convenient way:

devtools::release()

It asks a few questions to remind you whether all the checks havve passed, and whether you’ve updated the relevant information in DESCRIPTION, NEWS. In the end, it creates a tar ball and submits automatically to CRAN.

You need to check your email to confirm submission, just like when you do it manually via the webform.

Alternatively, you can either

  • write R CMD build PKGNAME in the terminal;
  • in Rstudio, in Build tab select build binary.

Afterwards, submit manually via the webform.