From 6d406ecc2a237eb1365fbbcf5c57a8934c894d19 Mon Sep 17 00:00:00 2001 From: Matt McKay Date: Tue, 5 May 2026 13:02:56 +1000 Subject: [PATCH 1/2] ci: add GitHub Actions deploy workflow for GitHub Pages (#198) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- .github/workflows/deploy.yml | 51 ++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 .github/workflows/deploy.yml diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..a8c20cf --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,51 @@ +name: Deploy to GitHub Pages + +on: + push: + branches: [main] + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +# Only one deployment at a time; don't cancel in-flight deploys. +concurrency: + group: pages + cancel-in-progress: false + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Setup Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.2' + bundler-cache: true + + - name: Setup Pages + id: pages + uses: actions/configure-pages@v5 + + - name: Build Jekyll site + run: bundle exec jekyll build --baseurl "${{ steps.pages.outputs.base_path }}" + env: + JEKYLL_ENV: production + + - name: Upload Pages artifact + uses: actions/upload-pages-artifact@v3 + + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 From a919804a14910ced8d43b28e3db6017506a8522b Mon Sep 17 00:00:00 2001 From: Matt McKay Date: Tue, 5 May 2026 13:53:09 +1000 Subject: [PATCH 2/2] ci: address Copilot security feedback on deploy workflow - 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. --- .github/workflows/deploy.yml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index a8c20cf..76bfb1f 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -5,10 +5,11 @@ on: branches: [main] workflow_dispatch: +# Minimal top-level permissions: build job only needs to read the repo. +# Deployment credentials (pages: write, id-token: write) are scoped to +# the deploy job only, so a compromised build step cannot use them. permissions: contents: read - pages: write - id-token: write # Only one deployment at a time; don't cancel in-flight deploys. concurrency: @@ -40,11 +41,17 @@ jobs: uses: actions/upload-pages-artifact@v3 deploy: + # Guard: only deploy from main, even when triggered via workflow_dispatch + # on a different branch. + if: github.ref == 'refs/heads/main' environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} runs-on: ubuntu-latest needs: build + permissions: + pages: write + id-token: write steps: - name: Deploy to GitHub Pages id: deployment