File: github-pages.R

package info (click to toggle)
r-cran-usethis 3.1.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,228 kB
  • sloc: sh: 26; makefile: 17; cpp: 6; ansic: 3
file content (170 lines) | stat: -rw-r--r-- 5,834 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
#' Configure a GitHub Pages site
#'
#' Activates or reconfigures a GitHub Pages site for a project hosted on GitHub.
#' This function anticipates two specific usage modes:
#' * Publish from the root directory of a `gh-pages` branch, which is assumed to
#'   be only (or at least primarily) a remote branch. Typically the `gh-pages`
#'   branch is managed by an automatic "build and deploy" job, such as the one
#'   configured by [`use_github_action("pkgdown")`][use_github_action()].
#' * Publish from the `"/docs"` directory of a "regular" branch, probably the
#'   repo's default branch. The user is assumed to have a plan for how they will
#'   manage the content below `"/docs"`.
#'

#' @param branch,path Branch and path for the site source. The default of
#'   `branch = "gh-pages"` and `path = "/"` reflects strong GitHub support for
#'   this configuration: when a `gh-pages` branch is first created, it is
#'   *automatically* published to Pages, using the source found in `"/"`. If a
#'   `gh-pages` branch does not yet exist on the host, `use_github_pages()`
#'   creates an empty, orphan remote branch.
#'
#'   The most common alternative is to use the repo's default branch, coupled
#'   with `path = "/docs"`. It is the user's responsibility to ensure that this
#'   `branch` pre-exists on the host.
#'
#'   Note that GitHub does not support an arbitrary `path` and, at the time of
#'   writing, only `"/"` or `"/docs"` are accepted.

#' @param cname Optional, custom domain name. The `NA` default means "don't set
#'   or change this", whereas a value of `NULL` removes any previously
#'   configured custom domain.
#'
#'   Note that this *can* add or modify a CNAME file in your repository. If you
#'   are using Pages to host a pkgdown site, it is better to specify its URL in
#'   the pkgdown config file and let pkgdown manage CNAME.
#'

#' @seealso
#' * [use_pkgdown_github_pages()] combines `use_github_pages()` with other
#' functions to fully configure a pkgdown site
#' * <https://docs.github.com/en/pages>
#' * <https://docs.github.com/en/rest/pages>

#' @return Site metadata returned by the GitHub API, invisibly
#' @export
#'
#' @examples
#' \dontrun{
#' use_github_pages()
#' use_github_pages(branch = git_default_branch(), path = "/docs")
#' }
use_github_pages <- function(branch = "gh-pages", path = "/", cname = NA) {
  check_name(branch)
  check_name(path)
  check_string(cname, allow_empty = FALSE, allow_na = TRUE, allow_null = TRUE)
  tr <- target_repo(github_get = TRUE, ok_configs = c("ours", "fork"))
  check_can_push(tr = tr, "to turn on GitHub Pages")

  gh <- gh_tr(tr)
  safe_gh <- purrr::safely(gh)

  if (branch == "gh-pages") {
    new_branch <- create_gh_pages_branch(tr, branch = "gh-pages")
    if (new_branch) {
      # merely creating gh-pages branch automatically activates publishing
      # BUT we need to give the servers time to sync up before a new GET
      # retrieves accurate info... ask me how I know
      Sys.sleep(2)
    }
  }

  site <- safe_gh("GET /repos/{owner}/{repo}/pages")[["result"]]

  if (is.null(site)) {
    ui_bullets(c(
      "v" = "Activating GitHub Pages for {.val {tr$repo_spec}}."
    ))
    site <- gh(
      "POST /repos/{owner}/{repo}/pages",
      source = list(branch = branch, path = path),
      .accept = "application/vnd.github.switcheroo-preview+json"
    )
  }

  need_update <-
    site$source$branch != branch ||
    site$source$path != path ||
    (is.null(cname) && !is.null(site$cname)) ||
    (is_string(cname) && (is.null(site$cname) || cname != site$cname))

  if (need_update) {
    args <- list(
      endpoint = "PUT /repos/{owner}/{repo}/pages",
      source = list(branch = branch, path = path)
    )
    if (is.null(cname) && !is.null(site$cname)) {
      # this goes out as a JSON `null`, which is necessary to clear cname
      args$cname <- NA
    }
    if (is_string(cname) && (is.null(site$cname) || cname != site$cname)) {
      args$cname <- cname
    }
    Sys.sleep(2)
    exec(gh, !!!args)
    Sys.sleep(2)
    site <- safe_gh("GET /repos/{owner}/{repo}/pages")[["result"]]
  }

  ui_bullets(c("v" = "GitHub Pages is publishing from:"))
  if (!is.null(site$cname)) {
    kv_line("Custom domain", site$cname)
  }
  kv_line("URL", site$html_url)
  kv_line("Branch", site$source$branch)
  kv_line("Path", site$source$path)

  invisible(site)
}

# returns FALSE if it does NOT create the branch (because it already exists)
# returns TRUE if it does create the branch
create_gh_pages_branch <- function(tr, branch = "gh-pages") {
  gh <- gh_tr(tr)
  safe_gh <- purrr::safely(gh)

  branch_GET <- safe_gh(
    "GET /repos/{owner}/{repo}/branches/{branch}",
    branch = branch
  )

  if (!inherits(branch_GET$error, "http_error_404")) {
    return(FALSE)
  }

  ui_bullets(c(
    "v" = "Initializing empty, orphan branch {.val {branch}} in GitHub repo
           {.val {tr$repo_spec}}."
  ))

  # GitHub no longer allows you to directly create an empty tree
  # hence this roundabout method of getting an orphan branch with no files
  tree <- gh(
    "POST /repos/{owner}/{repo}/git/trees",
    tree = list(list(
      path = "_temp_file_ok_to_delete",
      mode = "100644",
      type = "blob",
      content = ""
    ))
  )
  commit <- gh(
    "POST /repos/{owner}/{repo}/git/commits",
    message = "Init orphan branch",
    tree = tree$sha
  )
  ref <- gh(
    "POST /repos/{owner}/{repo}/git/refs",
    ref = glue("refs/heads/{branch}"),
    sha = commit$sha
  )
  # this should succeed, but if somehow it does not, it's not worth failing and
  # leaving pkgdown + GitHub Pages setup half-done --> why I use safe_gh()
  safe_gh(
    "DELETE /repos/{owner}/{repo}/contents/_temp_file_ok_to_delete",
    message = "Remove temp file",
    sha = purrr::pluck(tree, "tree", 1, "sha"),
    branch = branch
  )

  TRUE
}