Skip to content

Remove dependency on gh-pages #215

Open

Description

@JohannesHoppe

gh-pages is the last big avoidable runtime dependency of angular-cli-ghpages. It is pinned to 6.3.0 and brings its own dependency tree into every project that installs angular-cli-ghpages:

async, globby, fs-extra, commander, filenamify, find-cache-dir, email-addresses

Short-term trigger

Through globby → fast-glob → micromatch, this tree includes braces, which currently has an unpatched advisory (CVE-2026-93687 / GHSA-vfj7-8cjw-p6xm, tracked upstream in micromatch/braces#73; gh-pages was notified in tschaub/gh-pages#658). Even the latest releases of that chain still depend on it. The practical impact is negligible: angular-cli-ghpages doesn't pass any glob patterns to gh-pages, so only its fixed defaults (**/* and .) reach braces. But security scanners still flag it in every downstream project.

Long-term reason

Every package in this tree is part of the supply chain of every project that uses angular-cli-ghpages. Fewer third-party packages mean a smaller attack surface.

We only use a small part of what gh-pages offers: publish(), clean() and the internal gh-pages/lib/git module (for getRemoteUrl()). On top of that, the engine already has to work around several gh-pages internals (util.debuglog setup, the find-cache-dir fallback, and a beforeAdd hook that fixes stale-file removal). Replacing that small surface with our own code is a manageable amount of work.

angular-cli-ghpages already took this approach with commander: the standalone CLI uses a minimal fork (src/commander-fork) instead of the npm package (#200). However, commander still ends up in downstream projects as a dependency of gh-pages. Removing gh-pages would finish that job.

Goal

Implement the publishing logic directly in angular-cli-ghpages and drop the gh-pages dependency.

Context: #214

Activity

  1. JohannesHoppe commented on Oct 7, 2026

    @JohannesHoppe
    MemberAuthor

    Notes on the scope:

    Globbing is the easy part. We never pass src, remove or only, so gh-pages always runs with its defaults. Both are already covered by our own code in engine.prepare-options-helpers.ts:

    • **/* (list all files to publish) → collectDistFiles() walks the dist directory recursively and honors dotfiles the same way.
    • . (remove everything from the branch) → createCleanupBeforeAddHook() asks git via git ls-files -z and removes what's no longer in dist. Git already knows what's tracked, no glob needed.

    Replacing only the globbing doesn't help, though: gh-pages calls globby internally either way, so braces only disappears once gh-pages is replaced as a whole.

    What publish() does beyond that (gh-pages@6.3.0, lib/index.js):

    1. Clone the target repo into the cache dir, or clean/fetch an existing clone
    2. Check out the branch (or create it as an orphan branch)
    3. Remove existing files (see above)
    4. Write .nojekyll and CNAME
    5. Copy files
    6. git add, set the user, commit
    7. Optionally tag
    8. Push

    Token injection into the repo URL is already done on our side. At its core this is a handful of git calls via child_process, plus proper error handling, which is exactly where gh-pages keeps causing trouble (#205). Doable, but clearly more than just the two globs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions