Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Getting started vignette #60

Merged
merged 16 commits into from
Oct 18, 2021
Merged
2 changes: 2 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ repos:
inst/script\.R|
README\.Rmd|
tests/testthat/test-prepare\.R|
inst/script\.R
vignettes/touchstone\.Rmd
)$
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.0.1
Expand Down
6 changes: 0 additions & 6 deletions R/source.R
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,6 @@ run_script <- function(path = "touchstone/script.R",
action = "prefix"
)

head_asset_dir <- fs::path_temp("head")
base_asset_dir <- fs::path_temp("base")
options(
touchstone.dir_assets_head = head_asset_dir,
touchstone.dir_assets_base = base_asset_dir
)
temp_file <- fs::file_temp()
fs::file_copy(path, temp_file)

Expand Down
4 changes: 2 additions & 2 deletions R/testing.R
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,12 @@ path_temp_pkg <- function(name) {
#' @inheritParams withr::defer
#' @family testers
#' @keywords internal
local_package <- function(pkg_name = fs::path_file(tempfile("pkg")),
local_package <- function(pkg_name = fs::path_file(fs::file_temp("pkg")),
branches = c("main", "devel"),
r_sample = NULL,
setwd = TRUE,
envir = parent.frame()) {
path <- fs::path(tempfile(""), pkg_name)
path <- fs::path(fs::file_temp(""), pkg_name)
fs::dir_create(path)
withr::local_options(
usethis.quiet = TRUE,
Expand Down
7 changes: 7 additions & 0 deletions R/use.R
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
#' Initiate {touchstone}
#'
#' This function will initialize {touchstone} in your package repository, use
#' from root directory.
#' @param cancel Whether or not to also introduce a GitHub Actions
#' [cancel workflow](https://github.com/marketplace/actions/cancel-workflow-action)
#' for canceling old runs when new ones are started. This makes sense because
#' touchstone runs can take a lot of time and compute resources and you usually
#' don't care about old runs when you pushed new code.
#' @details
#' For more information see the 'Using touchstone' vignette:
#' `vignette("touchstone", package = "touchstone")
#' @return
#' The function is called for its side effects and returns `NULL` (invisibly).
#' @export
Expand Down Expand Up @@ -47,6 +52,8 @@ use_touchstone <- function(cancel = TRUE) {
fs::path(workflows, "touchstone-comment.yaml")
)

append_rbuildignore("touchstone")

if (cancel) {
target <- fs::path(workflows, "cancel.yaml")
if (fs::file_exists(target)) {
Expand Down
108 changes: 92 additions & 16 deletions R/utils.R
Original file line number Diff line number Diff line change
Expand Up @@ -191,24 +191,24 @@ is_windows <- function() {
#'
#' Pin files or directories that need to be available on both branches when
#' running the `touchstone_script`. During [benchmark_run_ref()] they will
#' available via [path_pinned_asset()].
#' available via [path_pinned_asset()]. This is only possible for assets
#' *within* the git repository.
#' @param ... Any number of directories or files, as strings, that you want to
#' access in your [touchstone_script].
#' @param ref The branch the passed assets are copied from.
#' @inheritParams fs::path
#' @inheritParams fs::dir_copy
#' @details When passing nested directories or files within nested directories
#' only the file/last directory will be copied. Directories will be copied
#' recursively. See examples.
#' the path will be copied recursively. See examples.
#' @return The asset directory invisibly.
#' @examples
#' \dontrun{
#' # In the touchstone script
#' # In the touchstone script within the repo "/home/user/pkg"
#'
#' pin_assets(c("bench", "inst/setup.R", "some/nested/dir"))
#' pin_assets(c("/home/user/pkg/bench", "inst/setup.R", "some/nested/dir"))
#'
#' source(path_pinned_asset("setup.R"))
#' load(path_pinned_asset("dir/data.RData"))
#' source(path_pinned_asset("inst/setup.R"))
#' load(path_pinned_asset("some/nested/dir/data.RData"))
#'
#' touchstone::benchmark_run_ref(
#' expr_before_benchmark = {
Expand Down Expand Up @@ -236,20 +236,54 @@ pin_assets <- function(...,
))
}

dirs[valid_dirs] %>% purrr::walk(
~ purrr::when(
.x,
fs::is_dir(.)[[1]] ~ fs::dir_copy(.x,
fs::path_join(c(asset_dir, fs::path_file(.x))),
create_and_copy <- function(asset) {
git_root <- get_git_root()
asset <- fs::path_real(asset)

if (!fs::path_has_parent(asset, git_root)) {
cli::cli_abort(c(
"Can only pin assets within the git repository!",
"i" = "Current repo: {.path {git_root}}"
))
}

rel_asset <- fs::path_rel(asset, git_root)
new_path <- fs::path_join(c(asset_dir, rel_asset))

if (fs::is_dir(asset)[[1]]) {
fs::dir_copy(
asset,
new_path,
overwrite = overwrite
),
fs::is_file(.)[[1]] ~ fs::file_copy(.x,
fs::path_join(c(asset_dir, fs::path_file(.x))),
)
} else if (fs::is_file(asset)[[1]]) {
fs::path_dir(rel_asset) %>%
fs::path(asset_dir, .) %>%
fs::dir_create()

fs::file_copy(
asset,
new_path,
overwrite = overwrite
)
)
}
}

dirs[valid_dirs] %>% purrr::walk(
create_and_copy
# ~ purrr::when(
# .x,
# fs::is_dir(.)[[1]] ~ fs::dir_copy(.x,
# fs::path_join(c(asset_dir, fs::path_file(.x))),
# overwrite = overwrite
# ),
# fs::is_file(.)[[1]] ~ fs::file_copy(.x,
# fs::path_join(c(asset_dir, fs::path_file(.x))),
# overwrite = overwrite
# )
)


if (any(valid_dirs)) {
cli::cli_alert_success(
paste0(
Expand Down Expand Up @@ -319,3 +353,45 @@ get_asset_dir <- function(ref, verb = "find") {
path_pr_comment <- function() {
fs::path(dir_touchstone(), "pr-comment/info.txt")
}

append_rbuildignore <- function(dir) {
ignore <- ".Rbuildignore"
if (fs::file_exists(ignore)) {
cat(
glue::glue("^{dir}$"),
sep = "\n", file = ignore, append = TRUE
)
cli::cli_alert_success("Added {.path {dir}} to {.file {ignore}}.")
} else {
cli::cli_alert_warning(
"Could not find {.file {ignore}} to add {.path {dir}}."
)
}
}

find_git_root <- function(path = ".") {
tryCatch(
gert::git_find(path),
error = function(err) {
cli::cli_alert_danger(
"Could not find git repository from current working directory!"
)
cli::cli_alert_info(
"Please manually set the option {.val touchstone.git_root}."
)
NULL
}
)
}

get_git_root <- function() {
git_root <- getOption("touchstone.git_root")

if (is.null(git_root)) {
cli::cli_abort(c("Option {.val touchstone.git_root} not set!",
"i" = 'Set it with {.code options(touchstone.git_root = "path to repo")}'
))
}

git_root
}
7 changes: 6 additions & 1 deletion R/zzz.R
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,18 @@
cache <- tibble::tibble(
ref = character(), md5_hashes = list(), path_pkg = character()
)

op.touchstone <- list(
"touchstone.skip_install" = FALSE,
"touchstone.git_root" = find_git_root(),
"touchstone.dir" = "touchstone",
# how many times should inner loop be ran in benchmark_run_iteration
"touchstone.n_iterations" = 1,
"touchstone.hash_source_package" = cache
"touchstone.hash_source_package" = cache,
"touchstone.dir_assets_head" = fs::path_temp("head"),
"touchstone.dir_assets_base" = fs::path_temp("base")
)

toset <- !(names(op.touchstone) %in% names(op))
if (any(toset)) options(op.touchstone[toset])
invisible()
Expand Down
80 changes: 15 additions & 65 deletions README.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,21 @@ You can install the package from CRAN:
install.packages("touchstone")
```

And the development version from [GitHub](https://github.com/lorenzwalthert/touchstone){target="_blank"} with:

``` r
# install.packages("devtools")
devtools::install_github("lorenzwalthert/touchstone")
```

## Getting Started
You can start using {touchstone} in your package repository with:
```{r, eval = FALSE}
touchstone::use_touchstone()
```
For a detailed explanation on how to configure and use {touchstone} see the
["Using touchstone"](https://lorenzwalthert.github.io/touchstone/articles/touchstone.html) vignette.

## Motivation

The motivation for touchstone is to provide accurate benchmarking results for
Expand Down Expand Up @@ -83,71 +98,6 @@ Once done with the measurements, it will
* create visualizations as Github Action artifacts that show how the
distribution of the timings for both branches.

## Proposed Workflow

Initialize {touchstone} in your repo with

```{r, eval = FALSE}
touchstone::use_touchstone()
```

This will:

- create a `touchstone` directory in the repo root with:

* `config.json` that defines how to run your benchmark. In particular, you can
define a `benchmarking_repo`, that is, a repo you need to run your bench mark
(use `""` if you don't need an additional git repo checked out for your
benchmark).
This repo will be cloned into `benchmarking_path`. For example to benchmark
{styler}, we format the {here} package with `style_pkg()` that is not part
of the {styler} repo, but with the below config, will be located at
`touchstone/sources/here` during benchmarking. The code you want to
benchmark comes from the benchmarked repo, which in our case is the root
from where you call `use_touchstone()` and hence does not need to be defined
explicitly.

```json

{
"benchmarking_repo": "lorenzwalthert/here",
"benchmarking_ref": "ca9c8e69c727def88d8ba1c8b85b0e0bcea87b3f",
"benchmarking_path": "touchstone/sources/here",
"os": "ubuntu-18.04",
"r": "4.0.0",
"rspm": "https://packagemanager.rstudio.com/all/__linux__/bionic/291"
}

```


* `script.R`, the script that runs the benchmark.

```{r}
touchstone::refs_install() # installs branches to benchmark

# benchmark a function call from your package (two calls per branch)
touchstone::benchmark_run_ref(
random_test = yourpkg::fun(),
n = 2
)

# create artifacts used downstream in the GitHub Action
touchstone::benchmarks_analyze()
```

- write the workflow files you need for touchstone into `.github/workflows/` to
invoke your touchstone script.

Note that these files must be committed to the default branch before
{touchstone} continuous benchmarking will be triggered for new PRs.

If you want to call the script interactively, use `run_script()`(an
enhanced version of `base::source()`) for various technical reasons
described in the help file. Note that `benchmark_run_ref()` will check
out different branches and should therefore be the only process running
in the directory and only in a clean git working directory.

## Status

This package is experimental. You can see an example usage in
Expand Down
Loading