Skip to content

ci: add GitHub Actions deploy workflow for GitHub Pages#199

Merged
mmcky merged 4 commits into
mainfrom
fix/198-github-actions-deploy
Jun 10, 2026
Merged

ci: add GitHub Actions deploy workflow for GitHub Pages#199
mmcky merged 4 commits into
mainfrom
fix/198-github-actions-deploy

Conversation

@mmcky

@mmcky mmcky commented May 5, 2026

Copy link
Copy Markdown
Collaborator

Summary

Switches production publishing from the legacy "Deploy from a branch" method (which pins to the GitHub Pages gem sandbox / Jekyll 3.10) to a proper GitHub Actions workflow so the Gemfile's Jekyll 4.4 is used in production.

What changed

Added .github/workflows/deploy.yml using the standard GitHub Pages Actions pattern:

  1. ruby/setup-ruby — same Ruby 3.2 + bundler-cache: true config as the existing build.yml
  2. actions/configure-pages — emits the correct base_path for the site URL
  3. bundle exec jekyll build with JEKYLL_ENV=production
  4. actions/upload-pages-artifactactions/deploy-pages

Triggers on push to main and workflow_dispatch. The deploy job is guarded by if: github.ref == 'refs/heads/main' and deployment credentials (pages: write, id-token: write) are scoped to the deploy job only. The existing build.yml (PR preview) is left untouched.

Pre-merge review fixes

  • _config.yml: added url: "https://quantecon.org". The legacy Pages builder injects site.url automatically (the live site's og:url is absolute today); a plain jekyll build in Actions does not, which would have silently broken og:url meta tags and post share links after the switch.
  • sitemap.xml: emit absolute <loc> URLs via absolute_url as the sitemap spec requires. The live sitemap is currently relative — a pre-existing bug this fixes as a side benefit.
  • actions/checkout bumped to v6 to match build.yml on main.

Required manual step after merge

Settings → Pages → Source → change from "Deploy from a branch" to "GitHub Actions"
https://github.com/QuantEcon/website/settings/pages

After this lands we can

  • Restore @use / @forward Dart Sass directives in assets/main.scss (currently reverted to @import as a workaround)
  • Use Jekyll 4.x-only Liquid filters safely in templates
  • Drop the github-pages gem dependency entirely

Verification checklist

All verified against the live production site after the Pages source flip (2026-06-10):

  • After flipping Pages source: confirm quantecon.org CNAME resolves correctly — Pages API reports build_type: workflow, cname: quantecon.org, https_enforced: true; DNS check successful; apex resolves to the GitHub Pages anycast IPs and the site serves HTTP/2 200. The Jekyll 4.4 Actions build is confirmed live via the /assets/main.css.map fingerprint (200, vs 404 for the legacy-only /assets/css/style.css).
  • Confirm redirects in pages/ still work — spot-checked live: /python-lectures/, /form/visitor-application/ (external redirect_to), and /quantecon-rse-joint-intitiative/ all emit correct http-equiv="refresh" stubs.
  • Verify all collections build identically — /team/ and /lectures/ are byte-identical to a local Jekyll 4.4 build except the CSS cache-busting timestamp; full sitemap page-inventory diff vs the pre-switch site showed 167 vs 168 paths with only benign differences (/archive/lectures.html — source has commented-out front matter, correctly skipped now; /assets/css/style.css — unreferenced legacy-theme artifact; /assets/main.css.map — new Dart Sass sourcemap).
  • Check sitemap.xml and feed.xml output — sitemap <loc> entries are now absolute (fixing a pre-existing spec violation); feed item links are absolute and correct.

Pre-existing issues discovered during verification (not regressions)

  • feed.xml is invalid XML: the template does {{ post.content | escape | truncate: '400' }}, and truncating after escaping slices entities like &quot; in half. Fix is to reorder: strip_html | truncate | escape.
  • /projects/ 404s and always has (absent from the pre-switch sitemap too), but it is the redirect_to target of /python-lectures/.

Closes #198

Switch production publishing from the legacy 'Deploy from branch' method
(Jekyll 3.10 sandbox) to a GitHub Actions workflow so the Gemfile's
Jekyll 4.4 version is used in production.

- Add .github/workflows/deploy.yml using the standard Pages pattern:
  actions/configure-pages → jekyll build → upload-pages-artifact → deploy-pages
- Trigger on push to main and workflow_dispatch
- Mirror ruby/setup-ruby config from existing build.yml (Ruby 3.2, bundler-cache)
- Set JEKYLL_ENV=production and pass base_path from configure-pages

After merging, go to Settings → Pages → Source and change from
'Deploy from a branch' to 'GitHub Actions' to activate this workflow.

Closes #198
Copilot AI review requested due to automatic review settings May 5, 2026 03:03
@netlify

netlify Bot commented May 5, 2026

Copy link
Copy Markdown

Deploy Preview for grand-swan-ca5201 ready!

Name Link
🔨 Latest commit 113c9e2
🔍 Latest deploy log https://app.netlify.com/projects/grand-swan-ca5201/deploys/6a28a72050289800088eee88
😎 Deploy Preview https://deploy-preview-199--grand-swan-ca5201.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a dedicated GitHub Actions deployment workflow for the QuantEcon Jekyll site so production publishing uses the repository’s configured Jekyll 4.4 toolchain instead of the legacy GitHub Pages branch build.

Changes:

  • Adds a new Pages deployment workflow that builds the site with Ruby 3.2 and Bundler cache.
  • Uses actions/configure-pages, uploads the generated _site artifact, and deploys it via actions/deploy-pages.
  • Keeps the existing PR preview workflow separate from production deployment.

Comment thread .github/workflows/deploy.yml
Comment thread .github/workflows/deploy.yml Outdated
- Scope pages:write and id-token:write to the deploy job only;
  top-level permissions reduced to contents:read so the build job
  (which runs repo code and third-party actions) cannot use deployment
  credentials even if compromised.
- Add if: github.ref == 'refs/heads/main' guard to the deploy job so
  a manual workflow_dispatch triggered from a non-main branch builds
  but never publishes to production.
@mmcky

mmcky commented May 5, 2026

Copy link
Copy Markdown
Collaborator Author

Copilot feedback addressed

Two commits pushed to address the two Copilot security notes:

  1. Permissions scoped to deploy job onlypages: write and id-token: write moved off the top-level block and onto the deploy job. Top-level now carries only contents: read. This means the build job (which checks out repo code and runs third-party actions) cannot use deployment credentials even if a step were compromised.

  2. workflow_dispatch branch guard — Added if: github.ref == 'refs/heads/main' to the deploy job. A manual trigger from a non-main branch will still run the build job (useful for testing the build) but will never push to the production Pages environment.


Why this migration is worth doing

The core problem

The legacy "Deploy from a branch" Pages setup runs builds inside GitHub's gem sandbox, which is pinned to Jekyll 3.10 and LibSass (via jekyll-sass-converter 1.x) regardless of what's in Gemfile. That's a two-major-version gap from what's specified locally.

What Jekyll 4.x unlocks

Feature Jekyll 3.10 (current production) Jekyll 4.4 (after this PR)
where_exp / group_by_exp Liquid filters ✅ — already used in #204 (workshops collection)
Dart Sass (@use / @forward) ❌ LibSass only
@import deprecation warnings None (LibSass ignores them) Shown — pushes toward modern CSS
Incremental build (--incremental) Partial More reliable
Better Liquid error messages

The @use / @forward directives were already written in assets/main.scss as part of the site refresh (#194) but had to be reverted (65163f9) because they fail on the LibSass sandbox in production. Once this PR is merged and Pages is switched to GitHub Actions, those can be restored.

Gemfile control

With the legacy setup, the github-pages gem silently overrides individual gem pins. Every dependency is locked to whatever GitHub chose for their sandbox at build time — meaning a gem update GitHub makes on their end can silently change production behaviour. After this migration we own the full dependency graph via Gemfile.lock.

Reproducibility

Local builds, Netlify preview builds, and production builds all use identical Ruby + Jekyll + gem versions. The "it works locally but fails in production" class of bug goes away.

Summary

This is a small workflow file that removes a large hidden constraint on the tooling we can use. The immediate unblock is Dart Sass @use; longer term it means we can adopt any Jekyll 4.x feature without worrying about the sandbox ceiling.

mmcky and others added 2 commits June 10, 2026 00:34
The legacy GitHub Pages builder injects site.url automatically; a plain
'jekyll build' in Actions does not, which would silently break og:url
meta tags and post share links after the deployment switch. Set
url: https://quantecon.org explicitly in _config.yml.

Also fix sitemap.xml to emit absolute <loc> URLs as the sitemap spec
requires (the live sitemap is currently relative — pre-existing bug),
and bump actions/checkout to v6 to match build.yml on main.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@mmcky mmcky merged commit 1dcce18 into main Jun 10, 2026
5 checks passed
@mmcky mmcky deleted the fix/198-github-actions-deploy branch June 10, 2026 00:14
mmcky added a commit that referenced this pull request Jun 10, 2026
…#208)

Two pre-existing bugs found while verifying the GitHub Actions deploy
migration (#199):

- feed.xml truncated post content *after* escaping it, slicing XML
  entities like &quot; in half and producing an invalid feed. Reorder
  to strip_html | truncate | escape so truncation happens on plain
  text and escaping is applied last.

- /python-lectures/ redirected to /projects/#filter=lecture, a page
  that has never existed (404). Point it at /lectures/, where the
  lecture series are listed today.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
mmcky added a commit that referenced this pull request Jun 10, 2026
Sass @import is deprecated and will be removed in Dart Sass 3.0. The
two partials define no variables and share none with main.scss, so
@use is a drop-in: compiled main.css is byte-identical and the build
log loses all deprecation warnings.

Completes the Sass follow-up from #199, now that production builds
with the Gemfile's Jekyll 4.4 / Dart Sass toolchain.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Migrate GitHub Pages publishing to GitHub Actions workflow

2 participants