From 948ebe8ef1f1b25515171bcd8f322aea655560f7 Mon Sep 17 00:00:00 2001 From: almac2022 Date: Sun, 23 Aug 2026 18:36:14 -0700 Subject: [PATCH 1/2] Keep CLAUDE.md out of the published pkgdown site CLAUDE.html, a verbatim CLAUDE.md and the search.json index were all publicly readable. The internal-only conventions are not in them here -- the visibility filter worked -- so this is untidy rather than harmful. Fixed anyway because nothing currently stops the harmful version. The filter rests on an assumption pkgdown breaks, that a repo's CLAUDE.md stays where you put it, and a future INTERNAL.md or a visibility change would publish silently. See NewGraphEnvironment/rfp#180, where the SR&ED section did go public. Removal happens before the build; after would leave the verbatim copy and the search.json entry. Fixes #38 --- .github/workflows/pkgdown.yaml | 38 ++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/.github/workflows/pkgdown.yaml b/.github/workflows/pkgdown.yaml index bfc9f4d..032424d 100644 --- a/.github/workflows/pkgdown.yaml +++ b/.github/workflows/pkgdown.yaml @@ -36,10 +36,48 @@ jobs: extra-packages: any::pkgdown, local::. needs: website + # pkgdown renders every root-level .md except a hardcoded allowlist + # (README, LICENSE, NEWS and two templates). CLAUDE.md is not on it and + # there is no config option to exclude a file, so the site would publish + # our internal working notes -- including, on a private repo, the + # conventions the visibility system marks internal-only. + # + # Repo visibility does not help: GitHub Pages serves publicly regardless, + # and there is no private mode below Enterprise Cloud. + # + # This removes it from the CI checkout only, never from the repo, and it + # has to happen BEFORE the build. Deleting afterwards would still leave + # the verbatim CLAUDE.md copy pkgdown makes and the entry it writes into + # the full-text search.json index. + - name: Keep internal notes out of the published site + run: rm -f CLAUDE.md + - name: Build site run: pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE) shell: Rscript {0} + + # A skip here would be invisible, so assert rather than assume. Any + # top-level page beyond the ones pkgdown is expected to produce means a + # root markdown file reached the site -- the exact shape of the leak this + # workflow exists to prevent, and the shape a future INTERNAL.md would + # take. + - name: Fail if an unexpected page reached the site + run: | + allowed="404 authors index LICENSE LICENSE-text" + unexpected="" + for f in docs/*.html docs/*.md; do + [ -e "$f" ] || continue + b=$(basename "$f"); b="${b%.*}" + case " $allowed " in *" $b "*) ;; *) unexpected="$unexpected $(basename "$f")";; esac + done + if [ -n "$unexpected" ]; then + echo "Unexpected top-level page(s) in docs/:$unexpected" + echo "pkgdown renders every root .md; remove internal ones before the build." + exit 1 + fi + echo "no unexpected top-level pages" + - name: Deploy to GitHub pages 🚀 if: github.event_name != 'pull_request' uses: JamesIves/github-pages-deploy-action@v4.5.0 From a796dfc53c81bfe4a74e8326be9da2ad3c5b693d Mon Sep 17 00:00:00 2001 From: almac2022 Date: Sun, 23 Aug 2026 20:15:06 -0700 Subject: [PATCH 2/2] Deploy with clean: true so removing a file unpublishes it With clean: false the deploy action never deletes. Every file ever published stays on gh-pages regardless of whether the source still produces it, which has two consequences: removing a file from the repo does not unpublish it, and a leak cannot be fixed by fixing the build -- the stale copies need a separate purge that is easy to forget. clean: true makes the deployed site equal to what the build produced. Fixing the build is then sufficient, and the site becomes auditable. Checked before flipping, since clean: true deletes anything on gh-pages not present in docs/: no CNAME on any of these repos (the custom domain comes from the org site repo and project sites inherit it as subpaths), no dev/ versioned docs, and the favicon and webmanifest assets are build output from pkgdown/favicon/ rather than hand-added. The gate's allowlist is now declared per repo rather than assumed, so a new root markdown file fails the build until someone decides whether it should be public. Convention recorded in soul: conventions/pkgdown-publishing.md --- .github/workflows/pkgdown.yaml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pkgdown.yaml b/.github/workflows/pkgdown.yaml index 032424d..bd83a9d 100644 --- a/.github/workflows/pkgdown.yaml +++ b/.github/workflows/pkgdown.yaml @@ -82,6 +82,14 @@ jobs: if: github.event_name != 'pull_request' uses: JamesIves/github-pages-deploy-action@v4.5.0 with: - clean: false + # clean: true so the deployed site equals what the build produced. + # With clean: false the action never deletes, so removing a file from + # the repo does not unpublish it -- measured on gq, where + # task_plan.html and friends were still returning 200 long after the + # PWF documents left the root. Checked before flipping: no CNAME (the + # domain comes from the org site repo), no dev/ versioned docs, and + # the favicon/webmanifest assets are build output rather than + # hand-added. + clean: true branch: gh-pages folder: docs