Title: Pre-Commit Hooks
Version: 0.4.3
Author: Lorenz Walthert
Maintainer: Lorenz Walthert <lorenz.walthert@icloud.com>
Description: Useful git hooks for R building on top of the multi-language framework 'pre-commit' for hook management. This package provides git hooks for common tasks like formatting files with 'styler' or spell checking as well as wrapper functions to access the 'pre-commit' executable.
License: GPL-3
URL: https://lorenzwalthert.github.io/precommit/, https://github.com/lorenzwalthert/precommit
Imports: cli, fs, here, magrittr, purrr, R.cache, rlang, rprojroot, withr, yaml
Suggests: desc, docopt (≥ 0.7.1), git2r, glue, knitr, lintr, pkgload, pkgdown, reticulate (≥ 1.16), rmarkdown, roxygen2, rstudioapi, spelling, styler, testthat (≥ 2.1.0), tibble, usethis (≥ 2.0.0)
VignetteBuilder: knitr
Encoding: UTF-8
RoxygenNote: 7.3.1
SystemRequirements: git
Config/testthat/parallel: true
Config/testthat/edition: 3
NeedsCompilation: no
Packaged: 2024-07-22 13:56:23 UTC; lorenz
Repository: CRAN
Date/Publication: 2024-07-22 22:20:05 UTC

precommit: Pre-Commit Hooks

Description

Useful git hooks for R building on top of the multi-language framework 'pre-commit' for hook management. This package provides git hooks for common tasks like formatting files with 'styler' or spell checking as well as wrapper functions to access the 'pre-commit' executable.

See Also

Useful links:


Auto-update your hooks

Description

Runs ⁠pre-commit autoupdate⁠.

Usage

autoupdate(root = here::here())

Arguments

root

The path to the root directory of your project.

Value

The exit status from ⁠pre-commit autoupdate⁠ (invisibly).

Examples

## Not run: 
autoupdate()

## End(Not run)

Make a call with system2() and capture the effects.

Description

Make a call with system2() and capture the effects.

Usage

call_and_capture(command, args, ..., wait = TRUE)

Arguments

command

The command to issue. A character string of length one.

args

The command line arguments.

...

Arguments passed to system2().

wait

Passed to system2().

Value

A list with:


Call pre-commit

Description

Either via ⁠conda run⁠ (because conda env needs to be activated in general to ensure an executable to runs successfully) or, if the installation method was not conda, as a plain bash command.

Usage

call_precommit(..., wait = TRUE)

Arguments

...

Arguments passed to the command line call pre-commit.

wait

Passed to base::system2().


Communicate a captured call

Description

Communicates a captured call.

Usage

communicate_captured_call(x, preamble = "")

Arguments

x

The output of call_and_capture().


Copy some file to the test directory that must be present, but are not passed to the hook as a file argument.

Description

Copy some file to the test directory that must be present, but are not passed to the hook as a file argument.

Usage

copy_artifacts(artifacts, tempdir)

Arguments

artifacts

Artifacts to copy.

tempdir

The temporary directory.


Check if we should run roxygen.

Description

This is the case if a new or replaced/removed line contains a roxygen2 comment in a file that is staged. This function is only exported for use in hook scripts, but it's not intended to be called by the end-user directly.

Usage

diff_requires_run_roxygenize(root = ".")

Arguments

root

The root of the git repo.

Value

A logical vector of length 1.

See Also

Other hook script helpers: dirs_R.cache(), may_require_permanent_cache(), precommit_docopt(), robust_purl(), roxygen_assert_additional_dependencies(), roxygenize_with_cache()

Examples

## Not run: 
diff_requires_run_roxygenize()

## End(Not run)

Create the path to the precommit R.cache cache

Description

This function is only exported for use in hook scripts, but it's not intended to be called by the end-user directly.

Usage

dirs_R.cache(hook_id)

Arguments

hook_id

The id of the hook for which we want the relative cache directory.

See Also

Other hook script helpers: diff_requires_run_roxygenize(), may_require_permanent_cache(), precommit_docopt(), robust_purl(), roxygen_assert_additional_dependencies(), roxygenize_with_cache()


Reduce a check to the empty string on CRAN

Description

In testing, we don't want to rely on exact error messages or stdout on CRAN for third-party packages, since this would complicate the release process for dependencies of {precommit}.

Usage

empty_on_cran(string)

Arguments

string

The string to test.


Name the input

Description

Name the input

Usage

ensure_named(x, candidate_name = NULL, f = identity)

Arguments

x

A vector.

f

How to transform the input x into a name.


Fallback doc

Description

Fallback doc

Arguments

root

The path to the root directory of your project.


Generate a random package name that is not installed

Description

Generate a random package name that is not installed

Usage

generate_uninstalled_pkg_name(n = 10)

Arguments

n

The number of times we should try


Get the operating System

Description

Can't mock base package (either because it's an .Internal or for some other reason).

Usage

get_os()

Initiate git and configure it

Description

In particular, to avoid CRAN errors lorenzwalthert/precommit#320.

Usage

git_init(path = ".")

Arguments

path

The root of the repo.


Check if the hook produced what you want

Description

Match the resulting state after the hook run with the expected state

Usage

hook_state_assert(
  path_candidate,
  tempdir,
  path_candidate_temp,
  file_transformer,
  path_stdout,
  path_stderr,
  expect_success,
  std_err,
  std_out,
  exit_status
)

Create a hook state

Description

Runs the hook script to create a hook state, i.e. exit code, transformed files and emitted messages of the hook run.

Usage

hook_state_create(
  tempdir,
  path_candidate_temp,
  path_executable,
  cmd_args,
  path_stdout,
  path_stderr,
  env
)

Install pre-commit on your system with conda

Description

Install pre-commit on your system with conda

Usage

install_impl()

Install pre-commit on your system

Description

This installs pre-commit in the conda environment r-precommit. It will be available to use across different git repositories. To update, refer to update_precommit().

Usage

install_precommit(force = FALSE)

Arguments

force

Whether or not to force a re-installation.

Value

The path to the pre-commit executable (invisibly).

See Also

Other executable managers: uninstall_precommit(), update_precommit(), version_precommit()

Examples

## Not run: 
install_precommit()

## End(Not run)

Testing utilities

Description

Similar to the local_() family from {withr}, this function creates a temporary directory and optionally initiates git and pre-commit in it.

Usage

local_test_setup(
  git = TRUE,
  use_precommit = FALSE,
  package = FALSE,
  quiet = TRUE,
  autoupdate = FALSE,
  ...,
  .local_envir = parent.frame()
)

Arguments

git

Whether or not to init git in the local directory.

use_precommit

Whether or not to use_precommit().

autoupdate

Whether or not to run autoupdate() as part of this fixture.

.local_envir

⁠[environment]⁠
The environment to use for scoping.


Issue a warning if {R.cache} uses temporary cache only

Description

This function used to check if a permanent cache was available and issue a warning if not, but since {R.cache} version ⁠0.15.0⁠ (release date 2021-04-27), a permanent directory will be used automatically, so this check if redundant. the function is kept in the package for compatibility, i.e. if someone updates the R package {precommit} but not the hook revisions.

Usage

may_require_permanent_cache(temp_cache_is_enough = FALSE)

Arguments

temp_cache_is_enough

ignored.

See Also

Other hook script helpers: diff_requires_run_roxygenize(), dirs_R.cache(), precommit_docopt(), robust_purl(), roxygen_assert_additional_dependencies(), roxygenize_with_cache()


The testing environment does not use a conda environment if the env variable PRECOMMIT_INSTALLATION_METHOD is not 'conda'.

Description

The testing environment does not use a conda environment if the env variable PRECOMMIT_INSTALLATION_METHOD is not 'conda'.

Usage

not_conda()

Open pre-commit related files

Description

Open pre-commit related files

Usage

open_config(root = here::here())

open_wordlist(root = here::here())

Arguments

root

The path to the root directory of your project.

Details

Value

NULL (invisibly). The function is called for its side effects.

See Also

Other helpers: use_precommit()

Examples

## Not run: 
open_config()

## End(Not run)
## Not run: 
open_wordlist()

## End(Not run)

Derive the path to the pre-commit executable

Description

Returns "" if search was not successful, the path otherwise.

Usage

path_derive_precommit_exec()

Heuristic


Derive the path to the conda pre-commit executable

Description

Only checks the conda env r-precommit. If we can't find the executable, the empty string is returned.

Usage

path_derive_precommit_exec_conda()

Find an executable

Description

Evaluates if the pre-commit executable exists in one or more candidate locations. If so, return one, else return the empty string

Usage

path_derive_precommit_exec_impl(candidate)

Arguments

candidate

A directory to check for the pre-commit executable. The directory may also not exist.


Derive the pre-commit executable from the path

Description

Tries to derive the pre-commit executable from the ⁠$PATH⁠. Returns "" if no executable is found.

Usage

path_derive_precommit_exec_path()

Where are executables on Windows for Python 3 and higher?

Description

Heuristic to determine the directory where the pre-commit executable on Windows lives for Python versions 3 and above.

Usage

path_derive_precommit_exec_win_python3plus_base()

Locate the pre-commit executable

Description

path_precommit_exec() simply reads the R option precommit.executable, path_pre_commit_exec() is the old spelling and deprecated.

Usage

path_precommit_exec(check_if_exists = TRUE)

path_pre_commit_exec(check_if_exists = TRUE)

Arguments

check_if_exists

Whether or not to make sure the returned path also exists.

Value

A character vector of length one with the path to the pre-commit executable.

See Also

path_derive_precommit_exec() for the heuristic to derive it from scratch.

Examples

## Not run: 
path_precommit_exec()

## End(Not run)
## Not run: 
path_pre_commit_exec()

## End(Not run)

Provide a singular interface for hook calls to docopt

Description

docopt provides different processing for a single string than an array/vector. As ⁠"string"`` and ⁠c("string")' are semantically equivalent in R, this can create problems when a single parameter is provided. Thus, this function wraps docopt to ensure that the args will always be interpreted as a vector.

Usage

precommit_docopt(doc, args = commandArgs(trailingOnly = TRUE), ...)

Arguments

doc

character vector with command line specification.

args

character vector of command line arguments. Defaults to commandArgs(trailingOnly=TRUE).

...

Additional parameters passed to docopt.

Details

This function is only exported for use in hook scripts, but it's not intended to be called by the end-user directly.

See Also

Other hook script helpers: diff_requires_run_roxygenize(), dirs_R.cache(), may_require_permanent_cache(), robust_purl(), roxygen_assert_additional_dependencies(), roxygenize_with_cache()


The name of the executable file

Description

This is platform dependent.

Usage

precommit_executable_file()

Complete the release

Description

Bumps the version to devel.

Usage

release_complete(ask = TRUE, is_cran = ask, tag = NULL)

Arguments

tag

The tag to push. NULL will derive the tag from DESCRIPTION.


Create a new release on GitHub

Description

This must be done before a CRAN release.

Usage

release_gh(bump = "dev", is_cran = bump != "dev")

Arguments

bump

The bump increment, either "dev", "patch", "minor" or "major".

is_cran

Is this release a CRAN release?

Details

This function does the following:

CRAN release

If is_cran is TRUE, the workflow is changed slightly:


Read the refs corresponding to a hooks repo

Description

Read the refs corresponding to a hooks repo

Usage

rev_read(path = ".pre-commit-config.yaml", repo = hooks_repo)

Run knitr::purl(), setting the chunk option purl to TRUE if it's not already set to a literal value.

Description

This function is only exported for use in hook scripts, but it's not intended to be called by the end-user directly.

Usage

robust_purl(path)

Arguments

path

The path to the file you want to knitr::purl().

See Also

Other hook script helpers: diff_requires_run_roxygenize(), dirs_R.cache(), may_require_permanent_cache(), precommit_docopt(), roxygen_assert_additional_dependencies(), roxygenize_with_cache()


Assert if all dependencies are installed

Description

This function is only exported for use in hook scripts, but it's not intended to be called by the end-user directly.

Usage

roxygen_assert_additional_dependencies()

See Also

Other hook script helpers: diff_requires_run_roxygenize(), dirs_R.cache(), may_require_permanent_cache(), precommit_docopt(), robust_purl(), roxygenize_with_cache()


Roxygen and add a cache entry

Description

This function is only exported for use in hook scripts, but it's not intended to be called by the end-user directly.

Usage

roxygenize_with_cache(key, dirs)

Arguments

key

An optional object from which a hexadecimal hash code will be generated and appended to the filename.

dirs

A character vector constituting the path to the cache subdirectory (of the cache root directory as returned by getCacheRootPath()) to be used. If NULL, the path will be the cache root path.

See Also

Other hook script helpers: diff_requires_run_roxygenize(), dirs_R.cache(), may_require_permanent_cache(), precommit_docopt(), robust_purl(), roxygen_assert_additional_dependencies()


Run a test

Description

Tests for the executables used as pre-commit hooks via entrypoint in .pre-commit-config.yaml. Set's the env variable R_PRECOMMIT_HOOK_ENV to when running.

Usage

run_test(
  hook_name,
  file_name = hook_name,
  suffix = ".R",
  std_err = NULL,
  std_out = NULL,
  cmd_args = NULL,
  artifacts = NULL,
  file_transformer = function(files) files,
  env = character(),
  expect_success = is.null(std_err),
  read_only = FALSE
)

Arguments

hook_name

The name of the hook in ⁠inst/hooks/exported/⁠, without file extension.

file_name

The file to test in ⁠tests/in⁠ (without extension). Can be a named vector of length one where the name is the target location relative to the temporary location and the value is the source of the file.

suffix

The suffix of file_name.

std_err

An expected error message. If no error is expected, this can be NULL. In that case, the comparator is applied.

std_out

The expected stdout message. If NULL, this check is omitted.

cmd_args

More arguments passed to the file. Pre-commit handles it as described here.

artifacts

Path with artifact files to copy to the temp directory root where the test is run. If you don't target the root, this can be a named vector of length one where the name is the target location relative to the temporary location and the value is the source of the file.

file_transformer

A function that takes the file names as input and is ran right before the hook script is invoked, returning the path to the files, potentially modified (if renamed). This can be useful if you need to make in-place modifications to the file, e.g. to test hooks that operate on .Rprofile. You can't have different names for different tests on that file because it must be called .Rprofile all the time. And R CMD check seems to remove hidden files, so we must also rename it. The transformation is also applied to a temp copy of the reference file before a comparison is made.

env

The environment variables to set with base::system2().

expect_success

Whether or not an exit code 0 is expected. This can be derived from std_err, but sometimes, non-empty stderr does not mean error, but just a message.

read_only

If TRUE, then assert that no new files were created. Additionally, if artifacts are not NULL, then assert that hook did not modify the artifacts.

Details

Two potential outcomes of a hooks are pass or fail. This is reflected on the level of the executable: Fail means the executable fails or the file is changed. Pass means the executable succeeds and the file is unchanged. We check if the executable passes as follows:


Implement a test run

Description

Implement a test run

Usage

run_test_impl(
  path_executable,
  path_candidate,
  std_err,
  std_out,
  cmd_args,
  artifacts,
  file_transformer,
  env,
  expect_success,
  read_only
)

Arguments

path_executable

The path to the executable bash script.

path_candidate

The path to a file that should be modified by the executable.

std_err

An expected error message. If no error is expected, this can be NULL. In that case, the comparator is applied.

std_out

The expected stdout message. If NULL, this check is omitted.

cmd_args

More arguments passed to the file. Pre-commit handles it as described here.

artifacts

Path with artifact files to copy to the temp directory root where the test is run. If you don't target the root, this can be a named vector of length one where the name is the target location relative to the temporary location and the value is the source of the file.

env

The environment variables to set with base::system2().

expect_success

Whether or not an exit code 0 is expected. This can be derived from std_err, but sometimes, non-empty stderr does not mean error, but just a message.

read_only

If TRUE, then assert that no new files were created. Additionally, if artifacts are not NULL, then assert that hook did not modify the artifacts.


Set the location to a config file

Description

If a remote location is specified, the file is downloaded to a temporary location and the path to this location is returned. If NULL, we'll resort to a default config. We'll perform some checks on the existence of the file too.

Usage

set_config_source(config_source, root, verbose = TRUE)

Generate code snippets

Description

Utility function to generate code snippets:

Usage

snippet_generate(
  snippet = "",
  open = rstudioapi::isAvailable(),
  root = here::here()
)

Arguments

snippet

Name of the snippet.

open

Whether or not to open the .pre-commit-config.yaml. The default is TRUE when working in RStudio. Otherwise, we recommend manually opening the file.

root

The path to the root directory of your project.

Details

Currently supported:


Uninstall pre-commit

Description

Remove pre-commit from a repo or from your system.

Usage

uninstall_precommit(scope = "repo", ask = "user", root = here::here())

Arguments

scope

Either "repo" or "user". "repo" removes pre-commit from your project, but you will be able to use it in other projects. With "user", you remove the pre-commit executable in the virtual python environment r-precommit so it won't be available in any project. When you want to do the latter, you should first do the former.

ask

Either "user", "repo" or "none" to determine in which case a prompt should show up to let the user confirm his action.

root

The path to the root directory of your project.

Value

NULL (invisibly). The function is called for its side effects.

See Also

Other executable managers: install_precommit(), update_precommit(), version_precommit()

Examples

## Not run: 
uninstall_precommit()

## End(Not run)

Updates pre-commit on your system with conda

Description

Updates pre-commit on your system with conda

Usage

update_impl()

Update the pre-commit executable

Description

Updates the conda installation of the upstream framework pre-commit. This does not update the R package {precommit} and only works if you choose conda as your installation method. If you have problems updating, we suggest deleting the conda environment r-precommit (if you are sure nothing but pre-commit depend on it) and do a fresh installation with install_precommit().

Usage

update_precommit()

Value

The exit status of the conda update command (invisible).

See Also

Other executable managers: install_precommit(), uninstall_precommit(), version_precommit()


Updates the hook version ref of {precommit} in a .pre-commit-config file

Description

This is useful in the release process because when releasing a new version, we must make sure the template that is used with precommit::use_precommit() is up-to date. Also, after we pushed the release to GitHub, we want to update the hooks from our own hook repo in the source repo too (we could also do that with precommit::autoupdate() though).

Usage

update_rev_in_config(new_version, path = "inst/pre-commit-config.yaml")

Arguments

new_version

The version string of the new version.

path

The path to a pre-commit config file.


Use continuous integration with pre-commit

Description

Sets up continuous integration, or prompts the user to do it manually.

Usage

use_ci(
  ci = getOption("precommit.ci", "native"),
  force = FALSE,
  open = rstudioapi::isAvailable(),
  root = here::here()
)

Arguments

ci

Specifies which continuous integration service to use. See vignette("ci", package = "precommit") for details. Defaults to getOption("precommit.ci", "native"), which is set to "native" on package loading (if unset). "native" sets up pre-commit.ci. Alternatively, "gha" can be used to set up GitHub Actions. Set value to NA if you don't want to use a continuous integration.

force

Whether or not to overwrite an existing ci config file (only relevant for ci = "gha").

open

Whether or not to open pre-commit.ci (if ci = "native"). The default is TRUE when working in RStudio.

root

The path to the root directory of your project.


Get started with pre-commit

Description

This function sets up pre-commit for your git repo.

Usage

use_precommit(
  config_source = getOption("precommit.config_source"),
  force = FALSE,
  legacy_hooks = "forbid",
  open = rstudioapi::isAvailable(),
  install_hooks = TRUE,
  ci = getOption("precommit.ci", "native"),
  autoupdate = install_hooks,
  root = here::here()
)

Arguments

config_source

Path or URL to a .pre-commit-config.yaml. This config file will be hard-copied into root. If NULL, we check if root is a package or project directory using rprojroot::find_package_root_file(), and resort to an appropriate default config. See section 'Copying an existing config file'.

force

Whether or not to overwrite an existing ci config file (only relevant for ci = "gha").

legacy_hooks

How to treat hooks already in the repo which are not managed by pre-commit. "forbid", the default, will cause use_precommit() to fail if there are such hooks. "allow" will run these along with pre-commit. "remove" will delete them.

open

Whether or not to open .pre-commit-config.yaml after it's been placed in your repo as well as pre-commit.ci (if ci = "native"). The default is TRUE when working in RStudio.

install_hooks

Whether to install environments for all available hooks. If FALSE, environments are installed with first commit.

ci

Specifies which continuous integration service to use. See vignette("ci", package = "precommit") for details. Defaults to getOption("precommit.ci", "native"), which is set to "native" on package loading (if unset). "native" sets up pre-commit.ci. Alternatively, "gha" can be used to set up GitHub Actions. Set value to NA if you don't want to use a continuous integration.

autoupdate

Whether or not to run autoupdate() as part of this function call.

root

The path to the root directory of your project.

Value

NULL (invisibly). The function is called for its side effects.

When to call this function?

What does the function do?

Copying an existing config file

You can use an existing .pre-commit-config.yaml file when initializing pre-commit with use_precommit() using the argument config_source to copy an existing config file into your repo. This argument defaults to the R option precommit.config_source, so you may want to set this option in your .Rprofile for convenience. Note that this is not equivalent to the --config option in the CLI command ⁠pre-commit install⁠ and similar, which do not copy a config file into a project root (and allow to put it under version control), but rather link it in some more or less transparent way.

See Also

Other helpers: open_config()

Examples

## Not run: 
use_precommit()

## End(Not run)

Initiate a pre-commit config file

Description

Initiate a pre-commit config file

Usage

use_precommit_config(
  config_source = getOption("precommit.config_source"),
  force = FALSE,
  open = rstudioapi::isAvailable(),
  verbose = FALSE,
  root = here::here()
)

Arguments

config_source

Path or URL to a .pre-commit-config.yaml. This config file will be hard-copied into root. If NULL, we check if root is a package or project directory using rprojroot::find_package_root_file(), and resort to an appropriate default config. See section 'Copying an existing config file'.

force

Whether to replace an existing config file.

open

Whether or not to open the .pre-commit-config.yaml after it's been placed in your repo. The default is TRUE when working in RStudio. Otherwise, we recommend manually inspecting the file.

verbose

Whether or not to communicate what's happening.

root

The path to the root directory of your project.

Value

Character vector of length one with the path to the config file used.

Copying an existing config file

You can use an existing .pre-commit-config.yaml file when initializing pre-commit with use_precommit() using the argument config_source to copy an existing config file into your repo. This argument defaults to the R option precommit.config_source, so you may want to set this option in your .Rprofile for convenience. Note that this is not equivalent to the --config option in the CLI command ⁠pre-commit install⁠ and similar, which do not copy a config file into a project root (and allow to put it under version control), but rather link it in some more or less transparent way.

Examples

## Not run: 
use_precommit_config()

## End(Not run)

Retrieve the version of the pre-commit executable used

Description

Retrieves the version of the pre-commit executable used.

Usage

version_precommit()

See Also

Other executable managers: install_precommit(), uninstall_precommit(), update_precommit()