Updating Quarto Website Daily with GitHub Actions

CICD
Quarto
Published

September 7, 2023

Introduction

For my friends who use Quarto, perhaps you need to update a particular blog post daily, weekly, or at some other time interval. You could redeploy your site manually every day, but this would be tedious and inefficient. Another option is to use GitHub actions to help you with this task. For high computational tasks using GitHub Actions can cost money, but rendering a small website and running a small script everything is free!

For those curious, this problem has surfaced with two of my previous posts: my election model and the google trends primary tracker. For my election model, I used the tedious, inefficient approach back in 2020 (manually running and updating a repository), but for the primary tracker, I have implemented an automated approach. Hopefully someone can benefit from this post!

Note

This is not a post detailing every possible approach to using GitHub Actions and Quarto. This is just my implementation given my stack.

My Stack for My Quarto Site

I build a static site with Quarto, then I keep this site in a repository on GitHub. I have connected this repository to Netlify which redeploys my site when new changes are pushed to the repository.

Insert Code that Rerenders in Quarto Document

For this particular project, I wanted to have a daily update of the Google Trends search scores of GOP candidates. So, in my quarto document, i have an Rscript that uses the gtrendsr package to pull the latest data every time the document is rendered. So, to complete our task, we just need the remote environment to simply rerender the site and push to the repository. Here is a snippet of an Rscript in a code chunk inside the quarto document which grabs the data and creates a figure:

library(gtrendsR)
library(ggplot2)
library(ggstream)
library(showtext)
font_add_google("Cairo", family = "Cairo")
showtext_auto()

obj <- gtrends(
    keyword = c("Donald Trump", "Ron DeSantis", "Mike Pence", "Nikki Haley", "Vivek Ramaswamy"),
    time = paste("2023-01-01",as.character(Sys.Date())),
    geo = "US",
    onlyInterest = TRUE
)

df <- obj$interest_over_time

df$hits <- as.numeric(ifelse(df$hits == "<1", "0.5", df$hits))

cols <- c("cadetblue3", "cornsilk3", "coral", "lightgoldenrod2", "palegreen3")

ggplot(df, aes(x = as.Date(date), y = hits, fill = keyword)) +
    geom_stream(type = "mirror") + 
    scale_fill_manual(values = cols) + 
    ggtitle("Search Popularity Since January 2023") +
    xlab("Year 2023") +
    ylab("Google Search Hits") +
    theme(
        axis.ticks = element_blank(),
        axis.title = element_text(size = 18, face = "bold"),
        axis.text.y = element_blank(),
        axis.text.x = element_text(vjust = 2, size = 12, face = "bold"),
        panel.background = element_blank(),
        text = element_text(face = "bold", family = "Cairo"),
        plot.title = element_text(size = 22, face = "bold"),
        legend.title = element_text(face = "bold", size = 16, family = "Cairo"),
        plot.caption = element_text(hjust = 2.55, size = 10, face = "italic")
    ) + 
    scale_x_date(date_labels="%b",date_breaks = "1 month") + 
    guides(fill=guide_legend(title="Keyword"))

GitHub Actions Portion

You’ll notice when you navigate to a github repository, there is a tab entitled Actions. After clicking on this tab, you should see some files that you can choose as starters. I don’t recall which template I used. I believe they had one for R which was the one that I used.

I will paste my .yml file I used for my configuration. Feel free to copy, paste, and edit to your liking.

name: 'Update Stream Graphs'

on:
schedule:
    - cron: "0 12 * * *"
jobs:
quarto-rerender:
    runs-on: macos-latest
    steps:
    - name: Check out repository
        uses: actions/checkout@v3
        with:
        token: ${{ secrets.SECRETS }}
    - name: Set up Quarto
        uses: quarto-dev/quarto-actions/setup@v2
        with:
        version: "1.3.433"
    - name: Install R
        uses: r-lib/actions/setup-r@v2
        with:
        r-version: '4.2.0'
    - uses: r-lib/actions/setup-r-dependencies@v2
        with:
        packages:
            any::readr
            any::ggplot2
            any::ggstream
            any::rmarkdown
            any::showtext
            any::knitr
            any::gtrendsR
    - name: clone personal site
        run: |
        git config --global user.name "github actions"
        git config --global user.email "acbass49@gmail.com"
        git clone "https://github.com/acbass49/personal_quarto_site.git"
        shell: bash
    - name: re-render site
        run: |
        cd personal_quarto_site
        quarto render
        shell: bash
    - name: push new version
        run: |
        cd personal_quarto_site
        git remote set-url origin https://x-access-token:${{ secrets.SECRETS }}@github.com/acbass49/personal_quarto_site.git
        git add .
        git commit -m "StreamGraph Update"
        git push
        shell: bash

This file is found in my website repository.

A few callouts and things I tweeked along the way…

  1. I had a difficult time finding a resource that let me push remotely to a github repository, but this seemed to do the trick git remote set-url origin https://x-access-token:${{ secrets.SECRETS }}@github.com/acbass49/personal_quarto_site.git. The ${{ secrets.SECRETS }} portion is an environment variable that is a github Personal Access Token. You will have to create a personal access token and insert this into a repository environment variable.
  2. Many continuous deployment files run on Ubuntu, but I was having issues getting my figures to be correctly sized as I originally created the figures on a mac, so I used macos-latest instead which solved this problem.
  3. For a time, I was having issues with the deployed version being significantly different from my local version. To solve this problem, I made sure to use the version argument with Quarto and matched it to my local version. So make sure to match those if possible!
  4. If you are unfamiliar with cron timing, there are several calculators online. My cron time is set to run every morning (I think around 6am EST).
Warning

I am by no means an expert at this (feel free to suggest even further efficiencies to what I already have), but I hope that this will benefit someone who was in a similar position as me.