Nothing
#' Publish the site
#'
#' \code{wflow_publish} is the main workflowr function. Use it when you are
#' ready to publish an analysis to your site. \code{wflow_publish} performs
#' three steps: 1) commit the file(s) (can include both Rmd and non-Rmd files,
#' e.g. \code{_site.yml}), 2) rebuild the R Markdown file(s), 3) commit the
#' generated website file(s). These steps ensure that the version of the HTML
#' file is created by the latest version of the R Markdown file, which is
#' critical for reproducibility.
#'
#' @param files character (default: NULL). R Markdown files and other files to
#' be added and committed with Git (step 1). Any R Markdown files will also be
#' built (step 2) and their output HTML and figures will be subsequently
#' committed (step 3). Supports file
#' \href{https://en.wikipedia.org/wiki/Glob_(programming)}{globbing}.
#' The files are always built in the order they are listed.
#' @inheritParams wflow_git_commit
#' @inheritParams wflow_build
#'
#' @return Returns an object of class \code{wflow_publish}, which is a list with
#' the following elements:
#'
#' \itemize{
#'
#' \item \bold{step1}: An object of class \code{wflow_git_commit} from the first
#' step of committing the files.
#'
#' \item \bold{step2}: An object of class \code{wflow_build} from the second
#' step of building the HTML files.
#'
#' \item \bold{step3}: An object of class \code{wflow_git_commit} from the third
#' step of committing the HTML files.
#'
#' }
#'
#' @seealso \code{\link{wflow_git_commit}}, \code{\link{wflow_build}}
#'
#' @examples
#' \dontrun{
#' # single file
#' wflow_publish("analysis/file.Rmd", "Informative commit message")
#' # All tracked files that have been edited
#' wflow_publish(all = TRUE, message = "Informative commit message")
#' # A new file plus all tracked files that have been edited
#' wflow_publish("analysis/file.Rmd", "Informative commit message", all = TRUE)
#' # Multiple files
#' wflow_publish(c("analysis/file.Rmd", "analysis/another.Rmd"),
#' "Informative commit message")
#' # All R Markdown files that start with the pattern "new_"
#' wflow_publish("analysis/new_*Rmd", "Informative commit message")
#' # Republish all published files even though they haven't been modified.
#' # Useful for changing some universal aspect of the site, e.g. the theme
#' # specified in _site.yml.
#' wflow_publish("analysis/_site.yml", "Informative commit message",
#' republish = TRUE)
#' # Publish all previously published files that have been committed more
#' # recently than their corresponding HTML files. This is useful if you like to
#' # manually commit your R Markdown files.
#' wflow_publish(update = TRUE)
#' }
#'
#' @import rmarkdown
#' @export
wflow_publish <- function(
# args to wflow_git_commit
files = NULL,
message = NULL,
all = FALSE,
force = FALSE,
# args to wflow_build
update = FALSE,
republish = FALSE,
combine = "or",
view = getOption("workflowr.view"),
delete_cache = FALSE,
seed = 12345,
verbose = FALSE,
# general
dry_run = FALSE,
project = "."
) {
# To do:
# * Warning for cache directories
# * Warning if files in docs/ included
# Check for modifications to _site.yml. Refuse to build if it is modified
# Check input arguments ------------------------------------------------------
files <- process_input_files(files, allow_null = TRUE, files_only = FALSE,
convert_to_relative_paths = TRUE)
if (is.null(message)) {
message <- deparse(sys.call())
message <- paste(message, collapse = "\n")
} else if (is.character(message)) {
message <-create_newlines(message)
} else {
stop("message must be NULL or a character vector")
}
assert_is_flag(all)
assert_is_flag(force)
assert_is_flag(update)
assert_is_flag(republish)
combine <- match.arg(combine, choices = c("or", "and"))
assert_is_flag(view)
assert_is_flag(delete_cache)
if (!(is.numeric(seed) && length(seed) == 1))
stop("seed must be a one element numeric vector")
assert_is_flag(verbose)
assert_is_flag(dry_run)
check_wd_exists()
assert_is_single_directory(project)
project <- absolute(project)
if (isTRUE(getOption("workflowr.autosave"))) autosave()
# Assess project status ------------------------------------------------------
s0 <- wflow_status(project = project)
r <- git2r::repository(path = s0$git)
commit_current <- git2r::commits(r, n = 1)[[1]]
if (!dry_run) check_git_config(project, "`wflow_publish`")
# Step 0: Confirm there is something to do -----------------------------------
if (is.null(files) && !all && !update && !republish && !dry_run)
stop("You did not tell wflow_publish() what to publish.\n",
"Unlike wflow_build(), it requires that you name the Rmd files you want to publish.\n")
# Step 1: Commit analysis files ----------------------------------------------
# Decide if wflow_git_commit should be run. At least one of the following
# scenarios must be true:
#
# 1) Rmd files were specified and at least one is scratch (untracked) or has
# unstaged/staged changes
#
# 2) `all == TRUE` and at least one tracked file has unstaged/staged changes
#
# 3) At least one non-Rmd file was specified
scenario1 <- !is.null(files) &&
any(unlist(s0$status[files, c("mod_unstaged", "mod_staged", "scratch")]),
na.rm = TRUE)
scenario2 <- all &&
any(unlist(s0$status[s0$status$tracked, c("mod_unstaged", "mod_staged")]),
na.rm = TRUE)
scenario3 <- !is.null(files) &&
any(!(files %in% rownames(s0$status)))
if (scenario1 || scenario2 || scenario3) {
step1 <- wflow_git_commit_(files = files, message = message,
all = all, force = force,
dry_run = dry_run, project = project)
# If subsequent steps fail, undo this action by resetting the Git repo to
# its initial state.
on.exit(git2r::reset(commit_current, reset_type = "mixed"), add = TRUE)
s1 <- wflow_status(project = project)
} else {
step1 <- NULL
s1 <- s0
}
# Step 2: Build HTML files----------------------------------------------------
# Determine if there are any files to be built.
files_to_build <- character()
# Specified files
files_to_build <- union(files_to_build,
files[files %in% rownames(s1$status)])
# Files committed in Step 1
files_to_build <- union(files_to_build,
step1$commit_files[
step1$commit_files %in% rownames(s1$status)])
# Check if the user wants an intersect build or union build of files
if (combine == "and" && length(files_to_build) == 0) {
stop("combine = \"and\" can only be used when explicitly specifying Rmd files to build with the argument `files`")
}
if (combine == "and") {
combine_files_function <- intersect
} else if (combine == "or") {
combine_files_function <- union
}
# If `republish == TRUE`, all published files
if (republish) {
files_to_build <- combine_files_function(files_to_build,
rownames(s1$status)[s1$status$published])
}
# If `update == TRUE`, all published files with committed modifications
if (update) {
files_to_build <- combine_files_function(files_to_build,
rownames(s1$status)[s1$status$mod_committed])
}
# None of these files can have unstaged/staged changes
files_to_build <- files_to_build[!s1$status[files_to_build, "mod_unstaged"]]
files_to_build <- files_to_build[!s1$status[files_to_build, "mod_staged"]]
if (length(files_to_build) > 0) {
# Create a backup copy of the docs/ directory. If either step 2 (build the
# HTML) or step 3 (commit the HTML) fails, delete docs/ and restore backup
if (fs::dir_exists(s1$docs) && !dry_run) {
docs_backup <- tempfile(pattern = sprintf("docs-backup-%s-",
format(Sys.time(),
"%Y-%m-%d-%Hh-%Mm-%Ss")))
fs::dir_create(docs_backup)
docs_backup <- absolute(docs_backup)
file.copy(from = file.path(s1$docs, "."), to = docs_backup,
recursive = TRUE, copy.date = TRUE)
on.exit(unlink(s1$docs, recursive = TRUE), add = TRUE)
on.exit(fs::dir_create(s1$docs), add = TRUE)
on.exit(file.copy(from = file.path(docs_backup, "."), to = s1$docs,
recursive = TRUE, copy.date = TRUE), add = TRUE)
}
step2 <- wflow_build_(files = files_to_build, make = FALSE,
update = update, republish = republish,
combine = combine,
view = view, clean_fig_files = TRUE,
delete_cache = delete_cache, seed = seed,
local = FALSE, verbose = verbose,
log_dir = use_default_log_dir(),
dry_run = dry_run, project = project)
} else {
step2 <- NULL
}
# Step 3 : Commit HTML files -------------------------------------------------
# Step 3 only needs to be performed if files were built in step 2.
if (length(step2$built) > 0) {
# Have to loop on step2$built as an underlying git2r function requires a
# length 1 character vector
figs_path <- vapply(step2$built, create_figure_path, character(1))
dir_figure <- file.path(s0$docs, figs_path)
site_libs <- file.path(s0$docs, "site_libs")
docs_nojekyll <- file.path(s0$docs, ".nojekyll")
docs_css <- list.files(path = s0$docs, pattern = "css$", full.names = TRUE)
docs_js <- list.files(path = s0$docs, pattern = "js$", full.names = TRUE)
files_to_commit <- c(step2$html, dir_figure, site_libs, docs_nojekyll,
docs_css, docs_js)
# Call directly to internal function `wflow_git_commit_` to bypass input checks.
# In a dry run, some files may not actually exist yet. Also, not every Rmd
# file creates figures, but it's easier to just attempt to add figures for
# every file.
step3 <- wflow_git_commit_(files = files_to_commit, message = "Build site.",
all = FALSE, force = force,
dry_run = dry_run, project = project)
} else {
step3 <- NULL
}
# Prepare output -------------------------------------------------------------
o <- list(step1 = step1, step2 = step2, step3 = step3)
class(o) <- "wflow_publish"
# If everything worked, erase the on.exit code that would have reset
# everything.
on.exit()
return(o)
}
#' @export
print.wflow_publish <- function(x, ...) {
cat("Summary from wflow_publish\n\n")
cat("**Step 1: Commit analysis files**\n\n")
if (is.null(x$step1)) {
cat("No files to commit\n\n")
} else {
print(x$step1)
}
cat("\n**Step 2: Build HTML files**\n\n")
if (is.null(x$step2)) {
cat("No files to build\n\n")
} else {
print(x$step2)
}
cat("\n**Step 3: Commit HTML files**\n\n")
if (is.null(x$step3)) {
cat("No HTML files to commit\n\n")
} else {
print(x$step3)
}
return(invisible(x))
}
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.