fix: clean up leftover gh-pages branch files on deploy (#204)#207
Merged
JohannesHoppe merged 2 commits intomainfrom Apr 22, 2026
Merged
fix: clean up leftover gh-pages branch files on deploy (#204)#207JohannesHoppe merged 2 commits intomainfrom
JohannesHoppe merged 2 commits intomainfrom
Conversation
gh-pages@6.3.0's "Removing files" step calls globby without `dot: true`, so dotfiles (.gitignore, .gitmodules, .github/) and submodule gitlinks from the gh-pages branch are not removed before our dist is copied on top. They then get re-committed and leak into the deploy. Upstream fix tschaub/gh-pages#612 (merged 2025-08-09) adds both `remove: '**/*'` and `dot: true`, but is unreleased as of v6.3.0. We can't reach the globby call from options alone. Workaround: register a `beforeAdd` hook that runs after gh-pages' broken remove + our file copy. The hook asks git what it still has indexed (`git ls-files -z`), diffs against the set of files in our dist, and `git rm`s the leftovers. `git rm` handles submodule gitlinks correctly, so the `build` gitlink from the reporter's scenario is also cleaned up. Skipped when the user opts into `add: true` (additive mode explicitly preserves existing files). Adds a spawn-level regression test in engine.gh-pages-behavior.spec.ts that mocks `git ls-files` to return leftover dotfiles + a submodule gitlink, runs engine.run() end-to-end through real gh-pages, and asserts `git rm` targets the leftovers while leaving dist files alone. Fixes #204
Prior tests covered the cleanup hook at the spawn-mock level only — they verified that the right git commands get issued, but not that the resulting gh-pages commit is actually clean. Add a real-filesystem, real-git test that: 1. Creates a local bare repo. 2. Seeds a gh-pages branch containing the exact conditions from #204: dotfiles (.gitignore, .gitmodules), nested dot-directory contents (.github/workflows/deploy.yml), a submodule gitlink, and a stale non-dot file. 3. Runs engine.run() (no mocks of child_process, fs, or gh-pages). 4. Inspects `git ls-tree -r gh-pages` on the bare repo and asserts the final tree contains ONLY index.html. Also adds a baseline test that calls gh-pages.publish() directly (bypassing our hook) and asserts the dotfiles + submodule DO leak — i.e. the upstream bug is real on the installed gh-pages version. If a future gh-pages release fixes the bug, that baseline test will fail as a clear signal that our workaround can be removed. No production changes; the hook implementation and its PR #206-era callback wrapper are exercised end-to-end by the new tests.
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #204 — when deploying from a repo with submodules (and/or top-level dotfiles like
.github/,.gitignore,.gitmodules), v3 leaves those files behind on thegh-pagesbranch instead of replacing the branch with just the dist contents. v2 did this correctly.Root cause
gh-pages@6.3.0's "Removing files" step calls
globby.sync(options.remove, { cwd })— withoutdot: true— so dotfiles and dot-named directories from thegh-pagesbranch are never matched and never removed. Submodules are also out of scope for a filesystem-based glob. Our dist is then copied on top and committed along with the stale files.Upstream fix tschaub/gh-pages#612 (merged 2025-08-09) addresses this by changing the default
removepattern to'**/*'AND addingdot: trueto the globby call. But that PR is not released:gh-pages@6.3.0is still the latest on npm (latestdist-tag), no newer git tag exists, and nonext/rc/preview dist-tag includes it. Upstreammainis 63 commits ahead of v6.3.0, mixing the fix with a major internal rewrite (globby → tinyglobby), so a drop-in patch release is unlikely any time soon.Approach
Register a
beforeAddhook (gh-pages exposes this as a user-pluggable callback that fires after copy, before git-add). The hook:git ls-files -z— at that point the index still contains everything from the gh-pages branch that gh-pages' broken remove step failed to remove (dotfiles, submodule gitlinks).git rm -- <leftovers>on the difference.git rmhandles submodule gitlinks correctly, so the reporter'sbuildgitlink is also cleaned up. The hook is skipped when the user setsadd: true(that mode explicitly preserves existing files).Because the hook operates at the git-level (not via globby), it's unaffected by upstream's pending globby → tinyglobby swap. When gh-pages eventually ships a release containing #612, this hook becomes a no-op and can be removed.