Prototype single files for web and slides #31
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Builds a preview of the palaeoverse website with this PR's module(s) swapped | |
| # in, and publishes it to the gh-pages branch of this repo under | |
| # pr-preview/pr-<N>/, served at: | |
| # | |
| # https://palaeoverse.github.io/modules/pr-preview/pr-<N>/ | |
| # | |
| # Rendering a module executes the R code it contains, so we only do this for PRs | |
| # whose author is a member of the palaeoverse org (see the `if` on the `preview` | |
| # job). | |
| on: | |
| pull_request_target: | |
| types: [opened, synchronize, reopened, closed] | |
| name: PR preview | |
| permissions: | |
| contents: write # push the rendered preview to gh-pages | |
| pull-requests: write # comment the preview URL on the PR | |
| jobs: | |
| preview: | |
| # Only members of the palaeoverse org (which owns this repo) get a preview. | |
| # The job — including the checkout of PR code — is skipped entirely for | |
| # outside contributors, so their code never runs here. | |
| if: > | |
| github.event.action != 'closed' && | |
| contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), | |
| github.event.pull_request.author_association) | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| steps: | |
| - name: "Check out PR head (trusted: author is an org member)" | |
| uses: actions/checkout@v7 | |
| with: | |
| repository: ${{ github.event.pull_request.head.repo.full_name }} | |
| ref: ${{ github.event.pull_request.head.sha }} | |
| path: modules | |
| - name: Check out the palaeoverse website | |
| uses: actions/checkout@v7 | |
| with: | |
| repository: palaeoverse/palaeoverse.github.io | |
| path: site | |
| - name: Set up R | |
| uses: r-lib/actions/setup-r@v2 | |
| with: | |
| use-public-rspm: true | |
| - name: Sync the Lua filter into each module | |
| # Mirrors _quarto.yml's pre-render hook that runs locally. | |
| working-directory: modules | |
| run: Rscript sync_filter.r | |
| - name: Copy PR module(s) into the website | |
| shell: Rscript {0} | |
| env: | |
| PR_NUMBER: ${{ github.event.pull_request.number }} | |
| run: | | |
| # Mirrors the copy logic of sync-modules.yml in palaeoverse.github.io: | |
| # upstream modules are the top-level directories of this repo (loose | |
| # files such as README.md / LICENSE.md and .git/.github are ignored). | |
| src_root <- "modules" | |
| dest <- "site/training/modules" | |
| upstream <- setdiff( | |
| list.dirs(src_root, full.names = FALSE, recursive = FALSE), | |
| c(".git", ".github") | |
| ) | |
| message("Modules in this PR: ", paste(upstream, collapse = ", ")) | |
| # Replace each module in the website checkout with the PR version. | |
| # `freeze: auto` means only modules whose source changed are | |
| # re-executed at render time; the rest reuse the website's committed | |
| # freeze. | |
| for (name in upstream) { | |
| target <- file.path(dest, name) | |
| unlink(target, recursive = TRUE) | |
| dir.create(target, recursive = TRUE, showWarnings = FALSE) | |
| src <- list.files(file.path(src_root, name), full.names = TRUE, | |
| all.files = TRUE, no.. = TRUE) | |
| file.copy(src, target, recursive = TRUE) | |
| } | |
| # The website's _quarto.yml sets `site-url: https://palaeoverse.github.io`, | |
| # which makes Quarto bake root-absolute paths (/training/..., /site_libs/...) | |
| # into every page. Served from the preview sub-path those would point back | |
| # at the production site, so internal links and assets break (404). Rewrite | |
| # site-url to the preview's own URL so all paths resolve under it. | |
| preview_url <- sprintf( | |
| "https://palaeoverse.github.io/modules/pr-preview/pr-%s", | |
| Sys.getenv("PR_NUMBER") | |
| ) | |
| cfg_path <- "site/_quarto.yml" | |
| cfg <- readLines(cfg_path) | |
| cfg <- sub("^(\\s*site-url:\\s*).*$", paste0("\\1", preview_url), cfg) | |
| writeLines(cfg, cfg_path) | |
| - name: Install pak | |
| run: install.packages("pak") | |
| shell: Rscript {0} | |
| - name: Install R dependencies | |
| working-directory: site | |
| run: | | |
| deps <- pak::scan_deps(".") | |
| pkgs <- setdiff(unique(deps$package), "R") | |
| pak::pak(pkgs) | |
| shell: Rscript {0} | |
| - name: Set up Quarto | |
| uses: quarto-dev/quarto-actions/setup@v2 | |
| - name: Render the website | |
| uses: quarto-dev/quarto-actions/render@v2 | |
| with: | |
| path: site | |
| - name: Publish preview to gh-pages | |
| uses: peaceiris/actions-gh-pages@v4 | |
| with: | |
| github_token: ${{ secrets.GITHUB_TOKEN }} | |
| publish_dir: site/_site | |
| destination_dir: pr-preview/pr-${{ github.event.pull_request.number }} | |
| keep_files: true # don't wipe other PRs' previews | |
| - name: Comment the preview link on the PR | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const pr = context.payload.pull_request.number; | |
| const url = `https://palaeoverse.github.io/modules/pr-preview/pr-${pr}/training/modules`; | |
| const marker = '<!-- pr-preview -->'; | |
| const body = `${marker}\nSee preview at ${url}`; | |
| // Reuse the existing preview comment (if any) instead of adding a new | |
| // one on every push. | |
| const { data: comments } = await github.rest.issues.listComments({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: pr, | |
| }); | |
| const existing = comments.find(c => c.body.includes(marker)); | |
| if (existing) { | |
| await github.rest.issues.updateComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| comment_id: existing.id, | |
| body, | |
| }); | |
| } else { | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: pr, | |
| body, | |
| }); | |
| } | |
| cleanup: | |
| # Remove a PR's preview when it is closed or merged. This only deletes a | |
| # directory from gh-pages and never runs PR code, so it is safe to run for | |
| # any author. | |
| if: github.event.action == 'closed' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Check out gh-pages | |
| uses: actions/checkout@v7 | |
| with: | |
| ref: gh-pages | |
| - name: Remove this PR's preview | |
| run: | | |
| dir="pr-preview/pr-${{ github.event.pull_request.number }}" | |
| if [ ! -d "$dir" ]; then | |
| echo "No preview to remove at $dir" | |
| exit 0 | |
| fi | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| git rm -r "$dir" | |
| git commit -m "cleanup: remove preview for PR #${{ github.event.pull_request.number }}" | |
| git push |