Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 30 additions & 6 deletions .github/workflows/pkgdown.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,21 @@ on:
- main
workflow_dispatch:

permissions:
contents: write

name: pkgdown

jobs:
pkgdown:
name: 🌍 Build and publish website
runs-on: ubuntu-latest
env:
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 0

- uses: r-lib/actions/setup-pandoc@v2

Expand All @@ -25,11 +31,29 @@ jobs:

- uses: r-lib/actions/setup-r-dependencies@v2
with:
extra-packages: any::pkgdown, local::.
extra-packages: |
any::pkgdown
any::yaml
any::fs
any::purrr
any::withr
any::desc
any::rlang
local::.
needs: website

- name: Deploy to gh-pages branch
- name: Build site
shell: Rscript {0}
run: |
git config --local user.name "$GITHUB_ACTOR"
git config --local user.email "$GITHUB_ACTOR@users.noreply.github.com"
Rscript -e 'pkgdown::deploy_to_branch(new_process = FALSE)'
source("pkgdown/build.R")
build_versioned(
repo = ".",
root_url = sprintf("https://%s.github.io/shiny.telemetry", Sys.getenv("GITHUB_REPOSITORY_OWNER")),
destination = "docs"
)

- name: Deploy
uses: JamesIves/github-pages-deploy-action@v4
with:
folder: docs
branch: gh-pages
174 changes: 174 additions & 0 deletions pkgdown/build.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
#' @param repo The path to the git repository to build.
#' @param versions A list of lists. Each sublist should contain the following
#' keys:
#' - `git_ref`: The git ref to build.
#' - `url`: The URL path for the version.
#' - `label`: The label to display in the navbar. To use the version
#' from DESCRIPTION provide `TRUE`.
#' Additonally, exactly one version should have `url` set to "/".
#' @param root_url The root URL for all versions of the website.
#' @param destination The destination directory for the built website.
build_versioned <- function(repo, versions, root_url, destination) {
versions <- rlang::maybe_missing(versions, github_version_tags())

validate_versions(versions)
# Prepare a repo for building
temp_repo <- withr::local_tempdir(pattern = "versioned-build-repo-")
fs::dir_copy(repo, temp_repo)

# NOTE: detach to avoid git worktree complaining about the current ref being
# checked out
system2("git", c("-C", temp_repo, "switch", "--detach", "@"))
build_version <- build_version_factory(
repo = temp_repo,
versions = versions,
root_url = root_url,
destination = destination
)

# NOTE: building the root URL first, so pkgdown doesn't complain about a
# non-empty destination directory
root_index <- purrr::detect_index(versions, function(x) isTRUE(x$url == "/"))
purrr::walk(c(versions[root_index], versions[-root_index]), build_version)
}

validate_versions <- function(versions) {
expected_names <- c("git_ref", "url", "label")
n_root <- vapply(
versions,
function(version) {
diff <- setdiff(expected_names, names(version))
if (length(diff) > 0) {
stop(
"A version is missing the following keys: ",
paste(diff, collapse = ", ")
)
}
isTRUE(version$url == "/")
},
logical(1L)
)

if (sum(n_root) != 1L) stop("Exactly one version should have url set to '/'")
}

github_version_tags <- function() {
tags <- purrr::keep(
system2("git", c("tag", "-l"), stdout = TRUE),
function(x) grepl("^v([0-9]+[.]?)+$", x)
)

tags_ordered <- sprintf(
"v%s",
sort(package_version(gsub("^v", "", tags)), decreasing = TRUE)
)

versions <- purrr::map(
tags_ordered,
function(x) {
list(
git_ref = sprintf("refs/tags/%s", x),
url = sprintf("/%s", ifelse(identical(x, tags_ordered[[1]]), "", x)),
label = gsub("^v", "", x)
)
}
)

# Add developer version
append(
list(list(git_ref = "refs/remotes/origin/main", url = "/dev", label = TRUE)),
versions
)
}

build_version_factory <- function(repo, versions, root_url, destination) {
navbar_template <- navbar_template_factory(versions, root_url)
destination <- fs::path_abs(destination)
extra_css_path <- fs::path_join(c(repo, "pkgdown", "extra.css"))

function(version) {
# Prepare a worktree for building
build_dir <- withr::local_tempdir(pattern = "versioned-build-worktree-")

status <- system2(
"git",
c("-C", repo, "worktree", "add", build_dir, version$git_ref)
)
on.exit(
# NOTE: --force because we add the navbar file
system2("git", c("-C", repo, "worktree", "remove", "--force", build_dir))
)
if ((status) != 0) {
stop("Failed to create a worktree for ref ", version$git_ref)
}

# Overwrite the pkgdown folder with latest changes
config <- yaml::read_yaml(fs::path_join(c(repo, "pkgdown", "_pkgdown.yml")))

# Write the navbar template and extra.css
template_dir <- fs::path_join(c(build_dir, "pkgdown", "templates"))
fs::dir_create(template_dir)
writeLines(
navbar_template(version),
fs::path_join(c(template_dir, "navbar.html"))
)
fs::file_copy(
extra_css_path,
fs::path_join(c(build_dir, "pkgdown", "extra.css")),
overwrite = TRUE
)

# NOTE: providing an absolute path to build_site won't work: https://github.com/r-lib/pkgdown/issues/2172
withr::with_dir(
build_dir,
pkgdown::build_site_github_pages(
override = list(
url = sub("/$", "", url_join(root_url, version$url)),
template = config$template,
navbar = list(
type = config$navbar$type,
bg = config$navbar$bg,
fg = config$navbar$fg,
structure = config$navbar$structure
)
),
dest_dir = fs::path_join(c(destination, version$url))
)
)
}
}

url_join <- function(url, path) {
sprintf("%s/%s", sub("/$", "", url), sub("^/", "", path))
}

navbar_template_factory <- function(versions, root_url) {
navbar_code <- readLines("pkgdown/navbar.html")
index_current <- grep("___CURRENT_PLACEHOLDER___", navbar_code)
index_options <- grep("___OPTIONS_PLACEHOLDER___", navbar_code)
stopifnot(index_current < index_options)
wrap_label <- function(label) {
if (isTRUE(label)) {
label <- paste(desc::desc_get_version(), "(dev)")
}
label
}
function(version) {
c(
navbar_code[1:(index_current - 1)],
wrap_label(version$label),
navbar_code[(index_current + 1):(index_options - 1)],
purrr::map_chr(
versions,
function(ver) {
sprintf(
'<li><a class="dropdown-item" href="%s">%s</a></li>',
url_join(root_url, ver$url),
wrap_label(ver$label)
)
}
),
navbar_code[(index_options + 1):length(navbar_code)]
)
}
}
47 changes: 47 additions & 0 deletions pkgdown/navbar.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{{#navbar}}
<nav class="navbar navbar-expand-lg fixed-top {{#bg}}bg-{{{.}}}{{/bg}}" {{#type}}data-bs-theme="{{{.}}}"{{/type}} aria-label="{{#translate}}{{site_nav}}{{/translate}}">
<div class="container">
{{#includes}}{{{before_title}}}{{/includes}}
<a class="navbar-brand me-2" href="{{#site}}{{root}}{{/site}}index.html">{{#site}}{{title}}{{/site}}</a>

<div id="version-switcher" class="dropdown">
<a
href="#"
class="nav-link dropdown-toggle"
data-bs-toggle="dropdown"
role="button"
aria-expanded="false"
aria-haspopup="true"
>
<small class="nav-text {{#development}}text-{{version_label}}{{/development}} me-auto" data-bs-toggle="tooltip" data-bs-placement="bottom" title="{{#development}}{{version_tooltip}}{{/development}}">
___CURRENT_PLACEHOLDER___
</small>
</a>
<ul class="dropdown-menu">
___OPTIONS_PLACEHOLDER___
</ul>
</div>

{{#includes}}{{{before_navbar}}}{{/includes}}
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbar" aria-controls="navbar" aria-expanded="false" aria-label="{{#translate}}{{toggle_nav}}{{/translate}}">
<span class="navbar-toggler-icon"></span>
</button>

<div id="navbar" class="collapse navbar-collapse ms-3">
{{#left}}
<ul class="navbar-nav me-auto">
{{{.}}}
</ul>
{{/left}}

{{#right}}
<ul class="navbar-nav">
{{{.}}}
</ul>
{{/right}}
</div>

{{#includes}}{{{after_navbar}}}{{/includes}}
</div>
</nav>
{{/navbar}}