R/wflow_use_gitlab.R

Defines functions wflow_use_gitlab

Documented in wflow_use_gitlab

#' Deploy site with GitLab
#'
#' \code{wflow_use_gitlab} automates all the local configuration necessary to
#' deploy your workflowr project with
#' \href{https://docs.gitlab.com/ee/ci/yaml/README.html#pages}{GitLab Pages}.
#' Afterwards, you will need to run \code{wflow_git_push} in the R console (or
#' \code{git push} in the terminal) to push the code to GitLab. Note that this
#' will also create the repository if it doesn't exist yet (this requires GitLab
#' 10.5 or greater). Alternatively, you could manually login to your account and
#' create the new repository on GitLab prior to pushing.
#'
#' \code{wflow_use_gitlab} performs the following steps and then commits the
#' changes:
#'
#' \itemize{
#'
#' \item Renames the website directory from \code{docs/} to \code{public/}
#'
#' \item Edits the setting \code{output_dir} in the file \code{_site.yml} to
#' save the website files in \code{public/}
#'
#' \item Adds a link to the GitLab repository in the navigation bar
#'
#' \item Creates the required file \code{.gitlab-ci.yml}
#'
#' \item Configures the Git remote settings to use GitLab
#'
#' }
#'
#' By default the GitLab repository is set to private, so you are the only one
#' that can access it. If you need to keep it private, you can
#' \href{https://gitlab.com/help/user/project/pages/pages_access_control.md}{grant
#' access} to collaborators in Settings->Members. Otherwise, you can make it
#' public in Settings->General->Visibility.
#'
#' For more details, read the documentation provided by
#' \href{https://docs.gitlab.com/ee/ci/yaml/README.html#pages}{GitLab Pages}.
#'
#' @param username character (default: NULL). The GitLab account associated with
#'   the GitLab repository. This is likely your personal GitLab username, but it
#'   could also be the name of a GitLab Group you belong to. It will be
#'   combined with the arguments \code{repository} and \code{domain} to
#'   determine the URL of the new repository, e.g. the default is
#'   https://gitlab.com/username/repository. It will be combined with the
#'   arguments \code{repository}, \code{domain}, and \code{protocol} to
#'   determine the URL for Git to use to push and pull from GitLab, e.g. the
#'   default is https://gitlab.com/username/repository.git. If \code{username}
#'   is not specified, \code{wflow_use_gitlab} will first attempt to guess it
#'   from the current setting for the remote URL named "origin". If you haven't
#'   previously configured a remote for this workflowr project (or you are
#'   unsure what that means), then you should specify your GitLab username when
#'   calling this function.
#' @param repository character (default: NULL). The name of the remote
#'   repository on GitLab. If not specified, workflowr will guess the name of
#'   the repository. First, it will check the current setting for the remote URL
#'   named "origin". Second, it will use the name of the root directory of the
#'   workflowr project.
#' @param navbar_link logical (default: TRUE). Insert a link to the GitLab
#'   repository into the navigation bar.
#' @param protocol character (default: "https"). The protocol for communicating
#'   with GitLab. Must be either "https" or "ssh".
#' @param domain character (default: "gitlab.com"). The domain of the remote
#'   host. You only need to change this if you are using a custom GitLab
#'   instance hosted by your organization. For example, "git.rcc.uchicago.edu"
#'   is the domain for the GitLab instance hosted by the University of Chicago
#'   Research Computing Center.
#' @param project character (default: ".") By default the function assumes the
#'   current working directory is within the project. If this is not true,
#'   you'll need to provide the path to the project directory.
#'
#' @return Invisibly returns a list of class \code{wflow_use_gitlab}. This is
#'   currently for internal use only. Please open an Issue if you'd like to use
#'   this information.
#'
#' @seealso \code{\link{wflow_git_push}}, \code{\link{wflow_git_remote}},
#'          \code{\link{wflow_use_github}}, vignette("wflow-06-gitlab")
#'
#' @examples
#' \dontrun{
#'
#' wflow_use_gitlab("your-username", "name-of-repository")
#' # Login with GitLab account and create new repository
#' wflow_git_push()
#' }
#'
#'@export
wflow_use_gitlab <- function(username = NULL, repository = NULL,
                             navbar_link = TRUE,
                             protocol = "https",
                             domain = "gitlab.com",
                             project = ".") {

  # Check input arguments ------------------------------------------------------

  if (!is.null(username))
    if (!(is.character(username) && length(username) == 1))
      stop("username must be NULL or a one element character vector: ", username)

  if (!is.null(repository))
    if (!(is.character(repository) && length(repository) == 1))
      stop("repository must be NULL or a one element character vector: ", repository)

  assert_is_flag(navbar_link)

  if (!(is.character(protocol) && length(protocol) == 1))
    stop("protocol must be a one element character vector: ", protocol)

  if (!(is.character(domain) && length(domain) == 1))
    stop("domain must be a one element character vector: ", domain)

  check_wd_exists()
  assert_is_single_directory(project)
  project <- absolute(project)
  check_git_config(project, "`wflow_use_gitlab`")

  # Status ---------------------------------------------------------------------

  s <- wflow_status(project = project)
  # Convert to absolute paths to facilitate path manipulation below
  s$analysis <- absolute(s$analysis)
  s$docs <- absolute(s$docs)

  r <- git2r::repository(path = s$git)
  remotes <- wflow_git_remote(verbose = FALSE, project = project)

  message("Summary from wflow_use_gitlab():")

  # Determine username and repository ------------------------------------------

  # guess based on current remote "origin"
  host <- get_host_from_remote(path = project) # returns NA if unavailable
  host_parts <- stringr::str_split(host, "/")[[1]]

  if (is.null(username)) {
    if (is.na(host)) {
      stop("Unable to guess username. Please specify this argument.")
    } else {
      username <- host_parts[length(host_parts) - 1]
    }
  }
  message("username: ", username)

  if (is.null(repository)) {
    if (is.na(host)) {
      # Use root directory name
      repository <- fs::path_file(absolute(s$root))
    } else {
      repository <- host_parts[length(host_parts)]
    }
  }
  message("respository: ", repository)

  # Rename docs/ to public/ ----------------------------------------------------

  if (basename(s$docs) == "public") {
    message("* The website directory is already named public/")
    renamed <- NA
  } else {
    public <- file.path(dirname(s$docs), "public")
    renamed <- wflow_rename(s$docs, public, git = FALSE, project = project)
    git2r_add(r, renamed$files_git)
    message("* Created the website directory public/")
  }

  # Edit output_dir in _site.yml -----------------------------------------------

  site_yml_fname <- file.path(s$analysis, "_site.yml")
  if (!fs::file_exists(site_yml_fname)) {
    stop("The website configuration file _site.yml does not exist.")
  }
  site_yml <- yaml::yaml.load_file(site_yml_fname)
  if (site_yml$output_dir == "../public") {
    message("* Output directory is already set to public/")
  } else {
    site_yml$output_dir <- "../public"
    yaml::write_yaml(site_yml, file = site_yml_fname)
    git2r_add(r, site_yml_fname)
    message("* Set output directory to public/")
  }

  # .gitlab-ci.yml -------------------------------------------------------------

  # The list `gitlab` is defined in R/infrastructure.R
  gitlab_yml <- gitlab[[".gitlab-ci.yml"]]
  gitlab_yml_fname <- file.path(s$root, ".gitlab-ci.yml")
  if (fs::file_exists(gitlab_yml_fname)) {
    message("* .gitlab-ci.yml file already exists")
  } else {
    cat(glue::glue(gitlab_yml), file = gitlab_yml_fname)
    git2r_add(r, gitlab_yml_fname)
    message("* Created the file .gitlab-ci.yml")
  }

  # Configure Git remote -------------------------------------------------------

  # 3 possible scenarios:
  #   1. Remote is already set correctly -> Do nothing
  #   2. Remote "origin" is currently defined -> Update URL with set_url
  #   3. Remote "origin" does not exist -> Add remote "origin"
  url_anticipated <- create_remote_url(user = username, repo = repository,
                                       protocol = protocol, domain = domain)
  url_current <- remotes["origin"]
  if (!is.na(url_current) && url_current == url_anticipated) {
    config_remote <- NA
    message("* Remote \"origin\" already set to ", remotes["origin"])
  } else if ("origin" %in% names(remotes)) {
    config_remote <- wflow_git_remote(remote = "origin", user = username,
                                      repo = repository, protocol = protocol,
                                      action = "set_url", domain = domain,
                                      verbose = FALSE, project = project)
    message("* Changed remote \"origin\" to ", config_remote["origin"])
  } else {
    config_remote <- wflow_git_remote(remote = "origin", user = username,
                                      repo = repository, protocol = protocol,
                                      action = "add", domain = domain,
                                      verbose = FALSE, project = project)
    message("* Set remote \"origin\" to ", config_remote["origin"])
  }

  # Add link in navigation bar -------------------------------------------------

  host <- get_host_from_remote(path = project)
  if (navbar_link && !is.na(host)) {
    site_yml$navbar$right <- list(list(icon = get_fa_brand_icon("gitlab"),
                                       text = "Source code",
                                       href = host))
    yaml::write_yaml(site_yml, file = site_yml_fname)
    git2r_add(r, site_yml_fname)
    message("* Added GitLab link to navigation bar")
  }

  # Commit changes -------------------------------------------------------------

  # Obtain staged files
  files_git <- git2r::status(r, staged = TRUE, unstaged = FALSE, untracked = FALSE)
  files_git <- unlist(files_git$staged)
  names(files_git) <- NULL
  if (length(files_git) > 0) {
    commit <- git2r::commit(r, message = "Host with GitLab.")
    message("* Committed the changes to Git")
  } else {
    commit <- NA
  }


  # Prepare output -------------------------------------------------------------

  o <- list(username = username, repository = repository,
            renamed = renamed, files_git = files_git, commit = commit,
            config_remote = config_remote)
  class(o) <- "wflow_use_gitlab"

  message("To do: Run wflow_git_push() to send your project to GitLab")
  message("Note: The push will create the new repository if it doesn't exist yet")

  return(invisible(o))
}

Try the workflowr package in your browser

Any scripts or data that you put into this service are public.

workflowr documentation built on Aug. 23, 2023, 1:09 a.m.