R/ci_build.R

Defines functions ci_build_site ci_build_markdown

Documented in ci_build_markdown ci_build_site

#' Build and deploy individual site components to a remote branch
#'
#' These are the internal functions which provision and deploy lesson
#' components to orphan git branches. These are called by [ci_deploy()].
#' **These functions have the side-effect of creating and updating orphan
#' branches on a remote repository. It is assumed that you have access to your
#' remote**.
#'
#' @inheritParams ci_deploy
#' @param branch the branch name containing the output:
#'   - `ci_build_markdown()`: defaults to `md-output`, corresponds to
#'     `md_branch` in [ci_deploy()]
#'   - `ci_build_site()`: defaults to `gh-pages`, corresponds to `site_branch`
#'      in [ci_deploy()]
#' @note `ci_build_markdown()` will set the `sandpaper.use_renv`
#'   option to TRUE, which means that it will _always_ use the {renv}
#'   package cache if the lesson uses R Markdown.
#' @returns
#'  - `ci_build_markdown()`: an [expression()] that is evaluated to tear down
#'     the worktree that was created.
#'  - `ci_build_site()`: nothing, it is used for its side-effect.
#'
#' @details
#'
#' The only place these two functions are used are within [ci_deploy()], which
#' has the de-facto use-case for these functions, thus, this page has no
#' examples. Both of these functions do the same general process:
#'
#' 1. provision a git worktree in the appropriate folder with
#'    [git_worktree_setup()]
#' 2. build the requisite content in that folder
#' 3. commit and push the contents to the worktree with
#'    [github_worktree_commit()]
#'
#' There are caveats for each of the two functions as listed in the sections
#' below.
#'
#' ## `ci_build_markdown()`
#'
#' This will not clean up after itself by default. You must save the output in
#' an object and run `eval(obj)` to run the clean up process. This is in place
#' so that `ci_build_site()` can use the worktree from the markdown outputs in
#' order to build the site.
#'
#' @keywords internal
#' @rdname ci_build
ci_build_markdown <- function(path = ".", branch = "md-outputs", remote = "origin", reset = FALSE) {

  options(sandpaper.use_renv = renv_is_allowed())

  # Set episodes to rebuild if the lockfile has changed.
  oc <- package_cache_trigger()
  on.exit(package_cache_trigger(oc), add = TRUE)
  package_cache_trigger(TRUE)

  # step 0: build_lesson defaults to a local build
  path <- set_source_path(path)
  on.exit(reset_build_paths())
  current <- gert::git_branch(repo = path)

  create_site(path)

  built <- path_built(path)

  has_withr <- requireNamespace("withr", quietly = TRUE)

  if (has_git() && has_withr) { withr::with_dir(path, {
    # Set up the worktrees and make sure to remove them when the function exits
    # (gracefully or ungracefully so)
    del_md <- git_worktree_setup(path, built,
      branch = branch, remote = remote
    )
    if (reset) {
      ci_group("Reset Lesson")
      git_clean_everything(built)
      cli::cat_line("::endgroup::")
    }

    ci_group("Build Markdown Sources")
    build_markdown(path = path, quiet = FALSE, rebuild = FALSE)
    cli::cat_line("::endgroup::")

    ci_group("Commit Markdown Sources")
    github_worktree_commit(built,
      message_source("markdown source builds", current, dir = path),
      remote, branch
    )
    cli::cat_line("::endgroup::")

  })}

  return(del_md)
}

#' @param md the branch name that contains the markdown outputs
#'
#' @details
#'
#' ## `ci_build_site()`
#'
#' In addition to the steps listed above, this function needs to verify that it
#' has the materials necessary to build the HTML site.
#'
#' It first checks for the presence of `site/built/`. This folder can be
#' generated by `ci_build_markdown()` or by [build_markdown()]. If the folder
#' does not exist or it is empty, then this function will attempt to fetch the
#' folder from the branch in `md`.
#'
#' Once everything is built and pushed, this function will additionally destroy
#' the work tree.
#'
#' @rdname ci_build
ci_build_site <- function(path = ".", branch = "gh-pages", md = "md-outputs", remote = "origin", reset = FALSE) {

  # step 0: build_lesson defaults to a local build
  path <- set_source_path(path)
  current <- gert::git_branch(path)
  on.exit(reset_build_paths())

  create_site(path)

  built <- path_built(path)
  html  <- fs::path(path_site(path), "docs")

  has_withr <- requireNamespace("withr", quietly = TRUE)

  if (has_git() && has_withr) { withr::with_dir(path, {

    # ------------ markdown worktree
    # We need to first check if the markdown source files exist locally. If they
    # do not, we need to fetch them as a throwaway branch
    need_markdown_sources <- nrow(get_built_db()) == 0L
    if (need_markdown_sources) {
      del_md <- git_worktree_setup(path, built,
        branch = md, remote = remote
      )
      on.exit(eval(del_md), add = TRUE)
    }

    # ------------ site worktree
    del_site <- git_worktree_setup(path, html,
      branch = branch, remote = remote
    )

    # remove the worktree at the end since this is the last step
    on.exit(eval(del_site), add = TRUE)

    if (reset) {
      ci_group("Reset Site")
      git_clean_everything(html)
      cli::cat_line("::endgroup::")
    }

    # Build the site quickly using the markdown files as-is
    ci_group("Build Lesson Website")
    build_site(path = path, quiet = FALSE, preview = FALSE)
    cli::cat_line("::endgroup::")

    # Commit using the markdown branch as a reference
    ci_group("Commit Lesson Website")
    github_worktree_commit(html,
      message_source("site deploy", md, dir = built),
      remote, branch
    )
    cli::cat_line("::endgroup::")
  })}
}
zkamvar/sandpaper documentation built on March 23, 2024, 10:56 p.m.