|
| 1 | +#' Download snapshots from GitHub |
| 2 | +#' |
| 3 | +#' @description |
| 4 | +#' If your snapshots fail on GitHub, it can be a pain to figure out exactly |
| 5 | +#' why, or to incporate them into your local package. This function makes it |
| 6 | +#' easy. |
| 7 | +#' |
| 8 | +#' Note that you should not generally need to fill out this function yourself; |
| 9 | +#' instead copy and paste from the hint emitted on GitHub. |
| 10 | +#' |
| 11 | +#' @param repository Repository name, e.g. `"r-lib/testthat"`. |
| 12 | +#' @param job_id Job ID, e.g. `"47905180716"`. You can find this in the job url. |
| 13 | +#' @param dest_dir Directory to download to. Defaults to the current directory. |
| 14 | +#' @export |
| 15 | +snapshot_download_gh <- function(repository, job_id, dest_dir = ".") { |
| 16 | + check_installed("gh") |
| 17 | + |
| 18 | + dest_snaps <- file.path(dest_dir, "tests", "testthat", "_snaps") |
| 19 | + if (!dir.exists(dest_snaps)) { |
| 20 | + cli::cli_abort("No snapshot directory found in {.file {dest_dir}}.") |
| 21 | + } |
| 22 | + |
| 23 | + artifact_id <- gh_find_artifact(repository, job_id) |
| 24 | + |
| 25 | + path <- withr::local_tempfile(pattern = "gh-snaps-") |
| 26 | + gh_download_artifact(repository, artifact_id, path) |
| 27 | + |
| 28 | + inner_dir <- dir(path, full.names = TRUE)[1] |
| 29 | + src_snaps <- file.path(inner_dir, "tests", "testthat", "_snaps") |
| 30 | + dir_copy(src_snaps, dest_snaps) |
| 31 | +} |
| 32 | + |
| 33 | +snap_download_hint <- function() { |
| 34 | + repository <- Sys.getenv("GITHUB_REPOSITORY") |
| 35 | + job_id <- Sys.getenv("GITHUB_JOB") |
| 36 | + |
| 37 | + if (repository == "" || job_id == "") { |
| 38 | + return() |
| 39 | + } |
| 40 | + |
| 41 | + sprintf( |
| 42 | + "* Call `gh_download_snaps(\"%s\", %s)` to download the snapshots from GitHub.\n", |
| 43 | + repository, |
| 44 | + job_id |
| 45 | + ) |
| 46 | +} |
| 47 | + |
| 48 | + |
| 49 | +gh_find_artifact <- function(repository, job_id) { |
| 50 | + job_logs <- gh::gh( |
| 51 | + "GET /repos/{repository}/actions/jobs/{job_id}/logs", |
| 52 | + repository = repository, |
| 53 | + job_id = job_id, |
| 54 | + .send_headers = c("Accept" = "application/vnd.github.v3+json") |
| 55 | + ) |
| 56 | + |
| 57 | + log_lines <- strsplit(job_logs$message, "\r?\n")[[1]] |
| 58 | + matches <- re_match(log_lines, "Artifact download URL: (?<artifact_url>.*)") |
| 59 | + matches <- matches[!is.na(matches$artifact_url), ] |
| 60 | + |
| 61 | + if (!nrow(matches)) { |
| 62 | + cli::cli_abort("Failed to find artifact") |
| 63 | + } |
| 64 | + |
| 65 | + # Take last artifact URL; if the job has failed the previous artifact will |
| 66 | + # be the R CMD check logs |
| 67 | + artifact_url <- matches$artifact_url[nrow(matches)] |
| 68 | + basename(artifact_url) |
| 69 | +} |
| 70 | + |
| 71 | +gh_download_artifact <- function(repository, artifact_id, path) { |
| 72 | + zip_path <- withr::local_tempfile(pattern = "gh-zip-") |
| 73 | + gh::gh( |
| 74 | + "/repos/{repository}/actions/artifacts/{artifact_id}/{archive_format}", |
| 75 | + repository = repository, |
| 76 | + artifact_id = artifact_id, |
| 77 | + archive_format = "zip", |
| 78 | + .destfile = zip_path |
| 79 | + ) |
| 80 | + unzip(zip_path, exdir = path) |
| 81 | + invisible(path) |
| 82 | +} |
| 83 | + |
| 84 | +# Directory helpers ------------------------------------------------------------ |
| 85 | + |
| 86 | +dir_create <- function(paths) { |
| 87 | + for (path in paths) { |
| 88 | + dir.create(path, recursive = TRUE, showWarnings = FALSE) |
| 89 | + } |
| 90 | + invisible(paths) |
| 91 | +} |
| 92 | + |
| 93 | +dir_copy <- function(src_dir, dst_dir) { |
| 94 | + # First create directories |
| 95 | + dirs <- list.dirs(src_dir, recursive = TRUE, full.names = FALSE) |
| 96 | + dir_create(file.path(dst_dir, dirs)) |
| 97 | + |
| 98 | + # Then copy files |
| 99 | + files <- dir(src_dir, recursive = TRUE) |
| 100 | + src_files <- file.path(src_dir, files) |
| 101 | + dst_files <- file.path(dst_dir, files) |
| 102 | + same <- map_lgl(seq_along(files), \(i) { |
| 103 | + same_file(src_files[[i]], dst_files[[i]]) |
| 104 | + }) |
| 105 | + |
| 106 | + n_new <- sum(!same) |
| 107 | + if (n_new == 0) { |
| 108 | + cli::cli_inform(c(i = "No new snapshots.")) |
| 109 | + } else { |
| 110 | + cli::cli_inform(c( |
| 111 | + v = "Copying {n_new} new snapshots: {.file {files[!same]}}." |
| 112 | + )) |
| 113 | + } |
| 114 | + |
| 115 | + file.copy(src_files[!same], dst_files[!same], overwrite = TRUE) |
| 116 | + invisible() |
| 117 | +} |
| 118 | + |
| 119 | +same_file <- function(x, y) { |
| 120 | + file.exists(x) && file.exists(y) && hash_file(x) == hash_file(y) |
| 121 | +} |
0 commit comments