Efficient deployment of Quarto presentations on a website using git hooks
Recently I have been writing my slides using Quarto . Here I describe the workflow I have converged on to render and deploy them on this website in an efficient and streamlined fashion.
In a previous post I wrote about how I manage my website as a git repository and use a post-recieve git hook to render and deploy it whenever changes are pushed.
Recently (if 2 years is ‘recent’) I have been using Quarto to write my presentations. More specifically, using Quarto to render my markdown presentations into revealjs html files. You can see examples on my slides page.
This was for a few reasons:
- Primarily, I wanted to place my presentations online:
- Easily accessible and available whenever I want.
- Viewable by anyone with a browser – no worrying about presentation software compatibility as plagues every conference.
- Easily (and immediately) shareable with the audience via a web link.
- I can write them in plaintext markdown
- Nice portabiity - see No Boilerplate’s video The Unreasonable Effectiveness Of Plain Text
- Low footprint for version control
But this is not an article primarily about Quarto, rather about how to deploy it efficiently online, so we digress.
Now, Quarto revealjs allows you to
embed everything into a single html file
using the embed-resources: true
option.
That is, the images, the animations, the style etc.
The nice side of this is that it gives you a single html item to upload and point at
(as opposed to having style files and artifacts generated during render in separate
files that also need to be tracked).
However this comes at the cost of generating HUGE final html files, somewhat defeating
the above objectives.
It eventually occurred to me that “surely I could just check in the basic
files and render them server-side!”
My first attempts at this went fine, including a segment in the hook that navigates to
the slides and runs quarto render
on each slidedeck before deploying the site.
However, as I added more than a couple of slidedecks the time taken to render every
one with each rebuild really began to stack up. There are also additional complications
once you start exploring the deeper features of Quarto.
To resolve this I decided I would only re-render ‘updated’ slides with each push. This required me to change my build process. Instead of building from a fresh git clone every time I now have an intermediate clone that persists and to which I pull the latest changes. Slides that have been previously rendered are left unchanged, with only those that I specify being re-rendered. After this has been done the site is re-built using hugo and deployed to the public-facing location as before.
Additional tips and tricks
Quarto suggests
adding extensions to version control
.
If we were to do this separately for every slidedeck this would start to add up
and be redundant, especially as I often use the same extensions in every presentation.
Fortunately we can address this by making the slides/
directory into a
Quarto project
with an
extremely minimal _quarto.yml
file.
Extensions can now be shared between all sub-folders of the project.
Using a project also allows rendering of the entire slidedeck in a directory by running
quarto render slideshow/
on the directory.
Quarto will, by default, render slideshow/slideshow.qmd
to slideshow/slideshow.html
.
This will then be viewable at jackatkinson.net/slides/slideshow/slideshow.html.
To make these viewable instead at jackatkinson.net/slides/slideshow we can render
instead to index.html
by adding output-file: index
to the .qmd
file header.
This means we can point a browser at slideshow/
to view the files instead of
slideshow/slideshow.html
.
Avoiding file duplication can also be extended to style files (.scss
) and
references (.bib
) by pointing to a shared version from the .qmd
file where appropriate.
These are stored at the top level of the Quarto project.
In a similar manner I have a shared directory of images/
as I often re-use the same ones
between presentations.
Quarto allows you to execute Python at build time to create images and output.
I set up a single shared environment for all slidedecks with requirements listed in a
requirements.txt
file for easy installation and version control.
Also useful, I have recently begun to break presentations into separate parts using includes . This becomes particularly handy when you want to re-use a specific section of a previous presentation, or add/remove and entire section.
Finally, I control which slidedecks are rendered or re-rendered at each push by
listing them in a text file slidelist.txt
.
Yes, this is a somewhat hacky solution and I could probably do something cleverer by
checking locations that have changed in git, but it works for now.
Folder structure
Taking the above points into consideration we have a directory structure as follows:
|
|
The post-receive hook
So without any further ado, here is the post-receive hook I am now using:
|
|
It will be run after every push to main sending feedback back over ssh.
Note the extra feature I added whereby the output of quarto is redirected to a logfile
in /tmp
. This stops it from polluting the feedback over ssh, but means that we can
examine output on the server if something goes wrong.
Note also that a side effect of pulling to a local clone as part of the post-recieve hook is that we can’t do any naughty force-pushes. And if we do we need a little manual intervention. Fortunately, if the hook fails for some reason we can always execute it on the server as a bash script.
Footnote - GitHub Actions
It is worth noting that if you only have a single slideshow to deploy this can be done from a GitHub repository using a workflow to deploy to a github.io webpage.
An example of this can be seen in my
RSE skills workshop
where quarto slides in slides/
are deployed using the
deploy_slides.yml
workflow.