pkgdown icon indicating copy to clipboard operation
pkgdown copied to clipboard

Consider readthedocs style handling of multiple versions

Open hadley opened this issue 5 years ago • 5 comments
trafficstars

(with a few tweaks for our needs)

  • Latest continues to live in top-level directory
  • Tagged releases built in /version/... (also need to be able to run locally in case automation fails or otherwise needs update)
  • Need to somehow record all versions in central location
  • Default template gets javascript popup for switching versions
  • Need to consider no-index or canonical strategy to ensure that google doesn't find old version of docs.
  • Optional javascript that displays "this isn't the latest version" banner somewhere on page

hadley avatar Jul 31 '20 21:07 hadley

Imagined url structure:

  • /: "stable"/"latest" version
  • /devel: development version
  • /1.0.0/ etc: old versions

Any "versioned" (e.g. devel or tagged release) site would set the canonical url to the root.

How would github builds work? Regular builds go in devel. Tagged builds make two versions — one is versioned in 2.0.0 and the other is unversioned in the root. The versioned site exists mostly for future use — we'd avoid linking to it, but we do want to build at release time to avoid having to rebuild an old version when we release the next version.

Would have ndjson file in root directory that's updated by versioned release — release just appends new version to end (so we can use union strategy to avoid merge conflicts)

hadley avatar Aug 01 '20 13:08 hadley

  • Do we really need tagged builds to make two versions? Creating a tag triggers a separate CI run.
  • Can we do the same for pull requests? Closing a pull request would trigger an action to delete the version, we could also support a "sync" feature that purges versions if really necessary.
  • I'd love to see a "permalink" feature that links to the stable version of the current page.
  • Can we make good use of symlinks in the gh-pages branch?

krlmlr avatar Aug 21 '20 00:08 krlmlr

An example from python: https://pydata-sphinx-theme.readthedocs.io/en/stable/user_guide/version-dropdown.html

hadley avatar Jul 12 '23 22:07 hadley

Hi!

Last week I worked on implementing https://github.com/Appsilon/rhino/issues/530. As a result I created a build script which creates a site with multiple versions of documentation and a switcher. You can view a demo at https://tymekdev.github.io/rhino.

This endeavor gave me a good overview of the issue and how to approach it. I will describe how the build script works and its shortcomings. I will also describe what decisions I think would have to be made before implementing this feature directly in pkgdown.

Hopefully, this gives a good overview on the matter. Let me know what do you think!

Best, Tymek

Build Script

Goals

  • Keep the development version of documentation up to date
  • Default to a specific version (namely, latest CRAN release)
  • Allow for custom URL paths

Description

A version is tied to a git ref (~revision), and defined by:

  • a label to be shown in the switcher for a given version
  • a ref checked out to retrieve code of a given version
  • a href used for a URL path of a given version

[!NOTE] I considered using historical versions from CRAN, however I learned that these might not have vignettes included.

Key components to this solution is a build script and a navbar template template (no, I did not stutter 🙃).

Usage

source("pkgdown/build.R")

build_versioned(
  repo = ".",
  versions = list(
    list(
      ref = "refs/remotes/origin/main",
      href = "/dev",
      label = TRUE # takes version from DESCRIPTION and appends "(dev)" to it
    ),
    list(
      ref = "refs/tags/v1.7.0",
      href = "/",
      label = "1.7.0"
    ),
    list(
      ref = "refs/tags/v1.6.0"
      href = "/v1.6.0",
      label = "1.6.0",
    )
  ),
  url = "https://appsilon.github.io/rhino",
  destination = "docs"
)

[!IMPORTANT] Why not revision instead of ref? Using refs makes this work with GitHub checkout action. The repository is fetched there in a specific way.

Step by Step

  1. Copy repository into a <temp_repo> temporary directory and detach HEAD[^1]
  2. Read the navbar template template
  3. Starting with a version with href = "/"[^2]
    1. Create a git worktree in a <build_dir> temporary directory with version's ref checked out
    2. Create a navbar template. Populate the switcher with a current version and other dropdown options
    3. Write the navbar template to <build_dir>/pkgdown/templates/navbar.html
    4. Copy <temp_repo>/pkgdown/extra.css[^3] to <build_dir>/pkgdown/extra.css
    5. Run pkgdown::build_site_github_pages with two overrides: both url and destination have version's href appended

[^1]: We use git worktrees. This avoids messing up user's repository. Detaching HEAD prevents complaints about a revision being already checked out. [^2]: Otherwise, if we created a version in a subdirectory first, pkgdown would complain about non-empty directory. [^3]: The switcher is styled there. We also get consistent styling.

Result

Running the code presented above would create a directory with a following structure:

docs         <- version from v1.7.0 tag
├── dev      <- version from main branch
│  └── ...
├── v1.6.0   <- version from v1.6.0 tag
│  └── ...
└── ...

This way any static file server will get the job done and get the paths right. As you can see it working on GitHub pages.

Issues and Caveats

  1. Every version has its own sitemap.xml
  2. Every version has separate assets (Bootstrap, favicons, ...)
  3. All versions are indexed by search engines
  4. None of the versions has canonical URLs set up
  5. "Source" link in vignettes and function reference is hardcoded to HEAD (instead of a respective version)
  6. Switching a version redirects to index (instead of staying on the same subpage in target version if it exists)

I am quite sure that all of these could be addressed. However, this would be a significant effort - comparable if not bigger than adding a proper support to pkgdown itself.

Additionally, 6. can be treated as an extra. See how an old Bootstrap versions have a banner that just switches to an index page of the newest version.

Extending pkgdown

Below you can find the burning questions that, in my opinion, would have to be answered before implementing this feature.

Decide how versions are defined

There is quite a lot of room for automation here. A couple ideas:

  • List versions from historical CRAN releases
  • Search git tags by a pattern
  • Give a full control to the user

An MVP could use explicitly listed git revisions (as in the build script above).

Decide how to approach canonical URLs, indexing, and sitemap.xml

This gets tricky with adding a canonical URL to an older versions as content might get removed. There would have to be a detection mechanism to see if something disappeared in a later version (what is a "later" version, though?).

However, implementing this detection mechanism would make the following possible:

  • Permalinks (mentioned by @krlmlr)
  • Switching between versions while staying on the same page (mentioned by me above and by @mccarthy-m-g in https://github.com/r-lib/pkgdown/issues/2392)
  • Adding a notice that something got removed in a newer version

An MVP could simply index only the "stable" version (with href = "/"). The switcher could just land the user on an index page of a given version.

What to override in older versions

URL is a must. Styling would not be necessary[^4]. Perhaps an equivalent of template.includes that's applied to every version?

An MVP could just override the URL.

[^4]: The script had to inject a template. If pkgdown is patched with a new template with a switcher, then using overrides to inject the data should suffice.

TymekDev avatar Apr 22 '24 20:04 TymekDev

Interesting GHA: https://github.com/rstudio/learnr/blob/main/.github/workflows/pkgdown.yaml

Also consider https://pkg.garrickadenbuie.com/epoxy/v0.1.1/reference/use_epoxy_knitr_engines.html + https://github.com/gadenbuie/epoxy/blob/gh-pages/doc-versions.json

hadley avatar Jun 10 '24 16:06 hadley