GitHub Actions

Test quarto render

Since some tests won’t need to rebuild the container, this is ran to verify the quarto is still valid. If the container is being rebuilt, this test is already ran within the container build action.

Publish Container

---
name: Container / Documentation Publish

"on":
  workflow_dispatch:
  push:
    branches:
      - main

jobs:
  # JOB to run change detection
  # yamllint disable-line rule:line-length
  # This is all to include / exclude running jobs when devtcontainer config changes
  changes:
    runs-on: ubuntu-latest
    # Required permissions
    permissions:
      contents: read
    # Set job outputs to values from filter step
    outputs:
      devcontainer: ${{ steps.filter.outputs.devcontainer }}
    steps:
      - name: Checkout source code
        uses: actions/checkout@v4
      - uses: dorny/paths-filter@v2
        id: filter
        with:
          filters: |
            devcontainer:
              - '.devcontainer/dev-build/**'

  publish-devcontainer:
    needs: changes
    # Run if we need to rebuild the container
    if: ${{ needs.changes.outputs.devcontainer == 'true' }}
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write

    steps:
      - name: Checkout (GitHub)
        uses: actions/checkout@v4

      - name: Login to GitHub Container Registry
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.repository_owner }}
          password: ${{ secrets.GITHUB_TOKEN }}

      # https://github.com/devcontainers/ci/issues/212
      # Have to do this manually becuase of issue with subfolders
      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 18

      - name: Install devcontainer CLI
        run: npm install -g @devcontainers/cli

      - name: Build and push devcontainer
        # yamllint disable-line rule:line-length
        run: devcontainer build --push true --image-name ghcr.io/cameronrutherford/quarto-ci --config .devcontainer/dev-build/devcontainer.json --workspace-folder .

  publish-docs:
    needs: publish-devcontainer
    # Run if we need to rebuild the container
    if: ${{ needs.changes.outputs.devcontainer == 'true' }}
    runs-on: ubuntu-latest
    container:
      image: ghcr.io/cameronrutherford/quarto-ci
      credentials:
        username: ${{ github.repository_owner }}
        password: ${{ secrets.GITHUB_TOKEN }}
    permissions:
      contents: write
    steps:
      - name: Check out repository
        uses: actions/checkout@v4
      - name: Quarto Render (with resume profiles)
        run: |
          quarto render
          mkdir ./quarto-cache
          rm -rf ./quarto-cache && mv ./docs ./quarto-cache
          quarto render resume/cpp_resume.qmd --profile cpp
          mv ./docs/resume/cpp_resume.pdf ./quarto-cache/resume
          quarto render resume/python_resume.qmd --profile python
          mv ./docs/resume/python_resume.pdf ./quarto-cache/resume
          quarto render resume/devops_resume.qmd --profile devops
          mv ./docs/resume/devops_resume.pdf ./quarto-cache/resume
          rm -rf ./docs && mv ./quarto-cache ./docs && rm -rf ./quarto-cache

      - name: Quarto Publish
        uses: quarto-dev/quarto-actions/publish@v2
        with:
          render: false
          target: quarto-pub
          QUARTO_PUB_AUTH_TOKEN: ${{ secrets.QUARTO_PUB_TOKEN }}

  publish-docs-no-build:
    needs: changes
    # Run if we need to rebuild the container
    if: ${{ needs.changes.outputs.devcontainer == 'false' }}
    runs-on: ubuntu-latest
    container:
      image: ghcr.io/cameronrutherford/quarto-ci
      credentials:
        username: ${{ github.repository_owner }}
        password: ${{ secrets.GITHUB_TOKEN }}
    permissions:
      contents: write
    steps:
      - name: Check out repository
        uses: actions/checkout@v4

      - name: Quarto Render (with resume profiles)
        run: |
          quarto render
          mkdir ./quarto-cache
          rm -rf ./quarto-cache && mv ./docs ./quarto-cache
          quarto render resume/cpp_resume.qmd --profile cpp
          mv ./docs/resume/cpp_resume.pdf ./quarto-cache/resume
          quarto render resume/onepager_resume.qmd --profile onepager
          mv ./docs/resume/onepager_resume.pdf ./quarto-cache/resume
          quarto render resume/python_resume.qmd --profile python
          mv ./docs/resume/python_resume.pdf ./quarto-cache/resume
          quarto render resume/devops_resume.qmd --profile devops
          mv ./docs/resume/devops_resume.pdf ./quarto-cache/resume
          rm -rf ./docs && mv ./quarto-cache ./docs && rm -rf ./quarto-cache

      - name: Quarto Publish
        uses: quarto-dev/quarto-actions/publish@v2
        with:
          render: false
          target: quarto-pub
          QUARTO_PUB_AUTH_TOKEN: ${{ secrets.QUARTO_PUB_TOKEN }}

pre-commit

I run pre-commit checks in CI using a GitHub action from https://github.com/pre-commit/action:

---
name: pre-commit autofix

"on":
  pull_request:
  push:
    branches:
      - main

jobs:
  pre-commit:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
        with:
          ref: ${{ github.event.pull_request.head.ref }}
      - uses: actions/setup-python@v3
      - uses: pre-commit/action@v3.0.0
      - uses: EndBug/add-and-commit@v9.1.3
        # Only need to try and commit if the action failed
        if: failure()
        with:
          fetch: false
          committer_name: GitHub Actions
          committer_email: actions@github.com
          message: Apply pre-commmit fixes

GitHub Pages Configuration

There are many ways to deploy Quarto pages. You can read more about choices in rendering Quarto in CI here.

Initial Config

I ran this initially per the docs:

quarto publish gh-pages

I then added a GitHub action to auto publish based on what is in the repository based on other people’s publish actions:

---
name: Container / Documentation Publish

"on":
  workflow_dispatch:
  push:
    branches:
      - main

jobs:
  # JOB to run change detection
  # yamllint disable-line rule:line-length
  # This is all to include / exclude running jobs when devtcontainer config changes
  changes:
    runs-on: ubuntu-latest
    # Required permissions
    permissions:
      contents: read
    # Set job outputs to values from filter step
    outputs:
      devcontainer: ${{ steps.filter.outputs.devcontainer }}
    steps:
      - name: Checkout source code
        uses: actions/checkout@v4
      - uses: dorny/paths-filter@v2
        id: filter
        with:
          filters: |
            devcontainer:
              - '.devcontainer/dev-build/**'

  publish-devcontainer:
    needs: changes
    # Run if we need to rebuild the container
    if: ${{ needs.changes.outputs.devcontainer == 'true' }}
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write

    steps:
      - name: Checkout (GitHub)
        uses: actions/checkout@v4

      - name: Login to GitHub Container Registry
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.repository_owner }}
          password: ${{ secrets.GITHUB_TOKEN }}

      # https://github.com/devcontainers/ci/issues/212
      # Have to do this manually becuase of issue with subfolders
      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 18

      - name: Install devcontainer CLI
        run: npm install -g @devcontainers/cli

      - name: Build and push devcontainer
        # yamllint disable-line rule:line-length
        run: devcontainer build --push true --image-name ghcr.io/cameronrutherford/quarto-ci --config .devcontainer/dev-build/devcontainer.json --workspace-folder .

  publish-docs:
    needs: publish-devcontainer
    # Run if we need to rebuild the container
    if: ${{ needs.changes.outputs.devcontainer == 'true' }}
    runs-on: ubuntu-latest
    container:
      image: ghcr.io/cameronrutherford/quarto-ci
      credentials:
        username: ${{ github.repository_owner }}
        password: ${{ secrets.GITHUB_TOKEN }}
    permissions:
      contents: write
    steps:
      - name: Check out repository
        uses: actions/checkout@v4
      - name: Quarto Render (with resume profiles)
        run: |
          quarto render
          mkdir ./quarto-cache
          rm -rf ./quarto-cache && mv ./docs ./quarto-cache
          quarto render resume/cpp_resume.qmd --profile cpp
          mv ./docs/resume/cpp_resume.pdf ./quarto-cache/resume
          quarto render resume/python_resume.qmd --profile python
          mv ./docs/resume/python_resume.pdf ./quarto-cache/resume
          quarto render resume/devops_resume.qmd --profile devops
          mv ./docs/resume/devops_resume.pdf ./quarto-cache/resume
          rm -rf ./docs && mv ./quarto-cache ./docs && rm -rf ./quarto-cache

      - name: Quarto Publish
        uses: quarto-dev/quarto-actions/publish@v2
        with:
          render: false
          target: quarto-pub
          QUARTO_PUB_AUTH_TOKEN: ${{ secrets.QUARTO_PUB_TOKEN }}

  publish-docs-no-build:
    needs: changes
    # Run if we need to rebuild the container
    if: ${{ needs.changes.outputs.devcontainer == 'false' }}
    runs-on: ubuntu-latest
    container:
      image: ghcr.io/cameronrutherford/quarto-ci
      credentials:
        username: ${{ github.repository_owner }}
        password: ${{ secrets.GITHUB_TOKEN }}
    permissions:
      contents: write
    steps:
      - name: Check out repository
        uses: actions/checkout@v4

      - name: Quarto Render (with resume profiles)
        run: |
          quarto render
          mkdir ./quarto-cache
          rm -rf ./quarto-cache && mv ./docs ./quarto-cache
          quarto render resume/cpp_resume.qmd --profile cpp
          mv ./docs/resume/cpp_resume.pdf ./quarto-cache/resume
          quarto render resume/onepager_resume.qmd --profile onepager
          mv ./docs/resume/onepager_resume.pdf ./quarto-cache/resume
          quarto render resume/python_resume.qmd --profile python
          mv ./docs/resume/python_resume.pdf ./quarto-cache/resume
          quarto render resume/devops_resume.qmd --profile devops
          mv ./docs/resume/devops_resume.pdf ./quarto-cache/resume
          rm -rf ./docs && mv ./quarto-cache ./docs && rm -rf ./quarto-cache

      - name: Quarto Publish
        uses: quarto-dev/quarto-actions/publish@v2
        with:
          render: false
          target: quarto-pub
          QUARTO_PUB_AUTH_TOKEN: ${{ secrets.QUARTO_PUB_TOKEN }}

Notice that we also build and publish the devcontainer if necessary, before using that as the base image for the build. Since this only runs once a PR is merged, it doesn’t run quarto render as the build is assumed to have passed by this point.