ci: add GitHub Actions deploy workflow for GitHub Pages#199
Conversation
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
✅ Deploy Preview for grand-swan-ca5201 ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
There was a problem hiding this comment.
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_siteartifact, and deploys it viaactions/deploy-pages. - Keeps the existing PR preview workflow separate from production deployment.
- 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.
Copilot feedback addressedTwo commits pushed to address the two Copilot security notes:
Why this migration is worth doingThe core problemThe legacy "Deploy from a branch" Pages setup runs builds inside GitHub's gem sandbox, which is pinned to Jekyll 3.10 and LibSass (via What Jekyll 4.x unlocks
The Gemfile controlWith the legacy setup, the ReproducibilityLocal 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. SummaryThis is a small workflow file that removes a large hidden constraint on the tooling we can use. The immediate unblock is Dart Sass |
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>
…#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 " 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>
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>
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.ymlusing the standard GitHub Pages Actions pattern:ruby/setup-ruby— same Ruby 3.2 +bundler-cache: trueconfig as the existingbuild.ymlactions/configure-pages— emits the correctbase_pathfor the site URLbundle exec jekyll buildwithJEKYLL_ENV=productionactions/upload-pages-artifact→actions/deploy-pagesTriggers on push to
mainandworkflow_dispatch. The deploy job is guarded byif: github.ref == 'refs/heads/main'and deployment credentials (pages: write,id-token: write) are scoped to the deploy job only. The existingbuild.yml(PR preview) is left untouched.Pre-merge review fixes
_config.yml: addedurl: "https://quantecon.org". The legacy Pages builder injectssite.urlautomatically (the live site'sog:urlis absolute today); a plainjekyll buildin Actions does not, which would have silently brokenog:urlmeta tags and post share links after the switch.sitemap.xml: emit absolute<loc>URLs viaabsolute_urlas the sitemap spec requires. The live sitemap is currently relative — a pre-existing bug this fixes as a side benefit.actions/checkoutbumped to v6 to matchbuild.ymlonmain.Required manual step after merge
After this lands we can
@use/@forwardDart Sass directives inassets/main.scss(currently reverted to@importas a workaround)github-pagesgem dependency entirelyVerification checklist
All verified against the live production site after the Pages source flip (2026-06-10):
quantecon.orgCNAME resolves correctly — Pages API reportsbuild_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.mapfingerprint (200, vs 404 for the legacy-only/assets/css/style.css).pages/still work — spot-checked live:/python-lectures/,/form/visitor-application/(externalredirect_to), and/quantecon-rse-joint-intitiative/all emit correcthttp-equiv="refresh"stubs./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).sitemap.xmlandfeed.xmloutput — 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.xmlis invalid XML: the template does{{ post.content | escape | truncate: '400' }}, and truncating after escaping slices entities like"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 theredirect_totarget of/python-lectures/.Closes #198