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:
content of stderr
content of stdout
exit status
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
-
open_config(): opens the pre-commit config file. -
open_wordlist(): opens the the WORDLIST file for the check-spelling hook in inst/WORDLIST.
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
First check if there is an executable on the
$PATHusingpath_derive_precommit_exec_path()Search os dependent for other possible locations for common installation methods.
If not, check if we can find one in a conda environment with
path_derive_precommit_exec_conda(). Do this last as it's the slowest.
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:
bump description.
update default config in inst/
commit
git tag
run
inst/hooks/local/consistent-release-tag.Rhook with –release-mode (passing args to hooks not possible interactively, hence we run in advance).commit and push with skipping
inst/hooks/local/consistent-release-tag.R.autoupdate own config file
bump description with dev
commit and push DESCRIPTION and .pre-commit-config.yaml
CRAN release
If is_cran is TRUE, the workflow is changed slightly:
push to release branch, not main.
doesn't run
release_complete(). This must be done manually after accepted on CRAN.
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:
If we expect success (by setting
std_errtoNULL), we make sure nothing was written to sterr and the file content does not change.If we expect failure, it can be due to changed file or due to failed executable. To check for failed executable, we set
std_errto the message we expect. To check changed file content, we setstd_errtoNA.
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:
additional-deps-roxygenize: Code to paste into
.pre-commit-config.yamlfor the additional dependencies required by the roxygenize hook.
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?
You want to add pre-commit support to a git repo which does not have a
.pre-commit-config.yaml. This involves adding a pre-commit config file and making sure git will call the hooks before the next commit.You cloned a repo that has a
.pre-commit-config.yamlalready. You need to make sure git calls the hooks before the next commit.
What does the function do?
Sets up a template
.pre-commit-config.yaml.Autoupdates the template to make sure you get the latest versions of the hooks.
Installs the pre-commit script along with the hook environments with
$ pre-commit install --install-hooks.Opens the config file if RStudio is running.
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()