Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds a detailed example for Quarto within Documenter.jl #81

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
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
5 changes: 3 additions & 2 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@
* [Publishing a single format, publishing without rendering](./example-06-no-render.md)
* [Publishing a single format](./example-07-publish-single-format.md)
* [Publishing to other services](./example-08-publish-to-others-services.md)
* [Render with Quarto to Publish with Documenter.jl](./example-09-render-to-documenter.md)

## Repositories using Quarto actions

- [Earthdata Cloud Cookbook](https://nasa-openscapes.github.io/earthdata-cloud-cookbook/) ([source](https://github.com/NASA-Openscapes/earthdata-cloud-cookbook), [workflow file](https://github.com/NASA-Openscapes/earthdata-cloud-cookbook/blob/main/.github/workflows/quarto-publish.yml)) This book contains `.md` and `.ipynb` files, and is built with Quarto and Python in GHA, and deployed to Github Pages.
- [Earthdata Cloud Cookbook](https://nasa-openscapes.github.io/earthdata-cloud-cookbook/) ([source](https://github.com/NASA-Openscapes/earthdata-cloud-cookbook), [workflow file](https://github.com/NASA-Openscapes/earthdata-cloud-cookbook/blob/main/.github/workflows/quarto-publish.yml)) This book contains `.md` and `.ipynb` files, and is built with Quarto and Python in GHA, and deployed to Github Pages.

- [R Manuals Quarto website](https://rstudio.github.io/r-manuals/) ([source](https://github.com/rstudio/r-manuals), [workflow file](https://github.com/rstudio/r-manuals/blob/main/.github/workflows/build-website.yaml)) This projects uses a workflow to build several books with R and Quarto and organizes them in a website deployed to Github pages.

- [Pathology Atlas](https://www.patolojiatlasi.com/EN) ([source](https://github.com/patolojiatlasi/patolojiatlasi.github.io), [workflow file](https://github.com/patolojiatlasi/patolojiatlasi.github.io/blob/main/.github/workflows/Quarto-Render-Bilingual-Book-Push-Other-Repos-GitLab.yml)) This multilingual website is rendered in two versions and deployed using Github Actions.
- [Pathology Atlas](https://www.patolojiatlasi.com/EN) ([source](https://github.com/patolojiatlasi/patolojiatlasi.github.io), [workflow file](https://github.com/patolojiatlasi/patolojiatlasi.github.io/blob/main/.github/workflows/Quarto-Render-Bilingual-Book-Push-Other-Repos-GitLab.yml)) This multilingual website is rendered in two versions and deployed using Github Actions.
185 changes: 185 additions & 0 deletions examples/example-09-render-to-documenter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
# Quarto Actions: Rendering Julia tutorials to be Published with Documenter.jl

Quarto Markdown files can be used very well to write tutorials that accompany a documentation
of a Julia package.
The advantage is, that the examples in the tutorials can be run (and cached), and the resulting
markdown files can directly be saved and included within the documentation which is
afterwards rendered using [Documenter.jl](https://documenter.juliadocs.org/stable/).

A challenge here are the dependencies, since the CI not only needs Julia and Quarto, but also
Python, Jupyter – and the Julia package. The dependencies to Python and Jupyter can for example
be handled with [CondaPkg.jl](https://github.com/cjdoris/CondaPkg.jl), since that allows a
versioning similar to the `Project.toml` in Julia packages.

## Folder structure

In this example we assume, that

* the documentation is provided in `docs/src` of the repository
* the tutorials are stored within `tutorials/` as well as for example the corresponding `_quarto_yml`
* the folder the results are stored is in this example `docs/src/tutorials`

Both these folders get their own `Project.toml` to have dependencies for the documentation
different from the dependencies for the tutorials. [Ijulia.jl](https://github.com/JuliaLang/IJulia.jl) should be added to both
the tutorial environment, CondaPkg to the documentation.

## Quarto Setup

the `tutorials/_quarto.yml` should use common mark as rendering, here the output
folder is set to fit the caching below.

```yml
project:
title: "Manopt.jl Tutorials"
output-dir: ../docs/src/tutorials
render:
- "*.qmd"

execute:
freeze: auto

format:
commonmark:
variant: -raw_html+tex_math_dollars
wrap: preserve
```

## CondaPkg dependencies

We first specify the versions for Python and Jupyter in `docs/CondaPkg.toml`

```toml
[deps]
jupyter = ""
python = "3.11"
```

## Documenter

We install all necessary python dependencies when the `docs/make.jl` file, that should be
executable, gets the command line argument `--quarto`

A minimal example is for this file is

```julia
#!/usr/bin/env julia
#

# (a) Activate the docs/ environment
if Base.active_project() != joinpath(@__DIR__, "Project.toml")
using Pkg
Pkg.activate(@__DIR__)
Pkg.develop(PackageSpec(; path=(@__DIR__) * "/../")) #put the main package in / into development mode
Pkg.resolve()
Pkg.instantiate()
if "--quarto" ∈ ARGS # if we render,
Pkg.build("IJulia") # activate/add the right kernel
end
end

# (b) Issue the actual rendering
if "--quarto" ∈ ARGS
using CondaPkg #Loads all Python dependencies
CondaPkg.withenv() do # activates the Conda environment
@info "Rendering Quarto"
tutorials_folder = (@__DIR__) * "/../tutorials"
# instantiate the tutorials environment if necessary
Pkg.activate(tutorials_folder)
Pkg.resolve() #resolve / setup the tutorials environment
Pkg.instantiate()
Pkg.activate(@__DIR__) # but return to the docs one before
run(`quarto render $(tutorials_folder)`) # render
return nothing
end
end

# (c) load necessary packages for the documentation
using Documenter#, and all others you need
# (d) call makedocs( to render the docs...
```

For step (d) the [makedocs](https://documenter.juliadocs.org/stable/man/guide/#Building-an-Empty-Document) documentation is recommended

## Documenter CI

For the CI there are four recommended caches

The final `.github/workflow/documenter.yml` might look as follows

```yml
name: Documenter
on:
push:
branches: [master]
tags: [v*]
pull_request:

jobs:
docs:
name: Documentation
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: quarto-dev/quarto-actions/setup@v2
with:
version: 1.3.353
- uses: julia-actions/setup-julia@latest
with:
version: 1.9
- name: "Documenter rendering (including Quarto)"
run: "docs/make.jl --quarto"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
DOCUMENTER_KEY: ${{ secrets.DOCUMENTER_KEY }}
```

It is probably recommended to cache the following

* Julia – maybe even including the precompiled packages
* The CondaPkg installed dependencies
* The Quarto freeze folder
* The resulting Quarto markdown files

This can be achieved with the following steps added after setting up Julia

```yml
- name: Julia Cache
uses: julia-actions/cache@v1
- name: Cache Quarto
env:
cache-name: cache-quarto
with:
path: tutorials/_freeze
key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('tutorials/*.qmd') }}
restore-keys: |
${{ runner.os }}-${{ env.cache-name }}-
- name: Cache Documenter
id: cache-documenter
uses: actions/cache@v3
env:
cache-name: cache-documenter
with:
path: docs/src/tutorials
key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('tutorials/*.qmd') }}
restore-keys: |
${{ runner.os }}-${{ env.cache-name }}-
- name: Cache CondaPkg
id: cache-condaPkg
uses: actions/cache@v3
env:
cache-name: cache-condapkg
with:
path: docs/.CondaPkg
key: ${{ runner.os }}-${{ env.cache-name }}-${{ hashFiles('docs/CondaPkg.toml') }}
restore-keys: |
${{ runner.os }}-${{ env.cache-name }}-```
```

This should be added before running the documenter call. Note that the restore keys even restore old files even in the case that the key (hash) changed. This way also only updated quarto notebooks are rerendered, but due to no exact match, the new ones are cached afterwards.

## Example

Two examples using this scheme are

* [Manopt.jl](https://github.com/JuliaManifolds/Manopt.jl)
* [Manifolds.jl](https://github.com/JuliaManifolds/Manifolds.jl)