Summary
When the underlying gh-pages library fails to push (for example, because the authentication token used for HTTPS auth is invalid/revoked), angular-cli-ghpages still prints its success banner and exits 0. CI systems therefore report green builds while nothing is actually deployed.
Observed behavior in a real CI run
🚀 Uploading via git, please wait...
Cloning ***github.com/owner/repo.git into .../node_modules/.cache/gh-pages/...
Cloning into '.../node_modules/.cache/gh-pages/...'...
remote: Invalid username or token. Password authentication is not supported for Git operations.
fatal: Authentication failed for 'https://github.com/owner/repo.git/'
🌟 Successfully published via angular-cli-ghpages! Have a nice day!
The job exits 0. GitHub Actions shows a green checkmark. No commit lands on the target branch.
Impact
In our setup, a GitHub Actions GH_TOKEN secret had silently expired. For about four weeks every Build Backend and Build Frontend run reported success while the build repos received zero new commits. Our downstream auto-deploy (Koyeb pulling from the build repo) therefore served stale code for a month, completely invisibly, including after we pushed fixes that looked successful in Actions logs.
The failure class is: git child process exited non-zero (authentication/push error), but the outer promise resolved.
Root cause (best guess)
src/engine/engine.ts calls:
await ghPages.publish(dir, ghPagesOptions);
and, on resolution, unconditionally logs:
logger.info('🌟 Successfully published via angular-cli-ghpages! Have a nice day!');
There is even a comment noting that a historic Promise bug in gh-pages was believed to be fixed in v5+:
// gh-pages v5+ fixed the Promise bug where errors didn't reject properly
// We can now safely await the promise directly
The evidence above (with gh-pages@6.3.0, as pinned by angular-cli-ghpages@3.0.2) suggests at least one failure class still resolves instead of rejecting — specifically the git clone / auth-failure path invoked before any git push. If gh-pages emits the error only to stderr and resolves, angular-cli-ghpages has no way to know the deploy failed.
Reproducer
- Create a GitHub Actions workflow that calls
npx angular-cli-ghpages --repo=https://github.com/<owner>/<repo>.git ... using a GH_TOKEN env var whose token is expired or scoped away from the target repo.
- Run the workflow.
- Observe:
fatal: Authentication failed in the log, followed by the 🌟 success banner, and job exit code 0.
Suggested directions (non-prescriptive)
- Defensive check at the end of
publishViaGhPages: capture the child process's exit code (or scan its stderr for fatal: / error:) and throw when seen, instead of trusting the outer promise alone.
- Optional post-deploy verification: after
ghPages.publish() resolves, do a shallow git ls-remote (or the GitHub API) and confirm the branch tip moved to the expected commit. If not, throw.
- Upstream: open a companion issue at
tschaub/gh-pages with the same reproducer so the underlying promise-rejection gap can be tracked there too.
Even a simple "loud warning" (e.g. logger.warn('git reported an error above — aborting') when the stderr stream contained fatal: or error:) would have saved a month of silent stale deploys here.
Happy to help test a fix against the original failing CI if useful.
Summary
When the underlying
gh-pageslibrary fails to push (for example, because the authentication token used for HTTPS auth is invalid/revoked),angular-cli-ghpagesstill prints its success banner and exits0. CI systems therefore report green builds while nothing is actually deployed.Observed behavior in a real CI run
The job exits
0. GitHub Actions shows a green checkmark. No commit lands on the target branch.Impact
In our setup, a GitHub Actions
GH_TOKENsecret had silently expired. For about four weeks everyBuild BackendandBuild Frontendrun reported success while the build repos received zero new commits. Our downstream auto-deploy (Koyeb pulling from the build repo) therefore served stale code for a month, completely invisibly, including after we pushed fixes that looked successful in Actions logs.The failure class is: git child process exited non-zero (authentication/push error), but the outer promise resolved.
Root cause (best guess)
src/engine/engine.tscalls:and, on resolution, unconditionally logs:
There is even a comment noting that a historic Promise bug in
gh-pageswas believed to be fixed in v5+:The evidence above (with
gh-pages@6.3.0, as pinned byangular-cli-ghpages@3.0.2) suggests at least one failure class still resolves instead of rejecting — specifically thegit clone/ auth-failure path invoked before anygit push. Ifgh-pagesemits the error only to stderr and resolves,angular-cli-ghpageshas no way to know the deploy failed.Reproducer
npx angular-cli-ghpages --repo=https://github.com/<owner>/<repo>.git ...using aGH_TOKENenv var whose token is expired or scoped away from the target repo.fatal: Authentication failedin the log, followed by the 🌟 success banner, and job exit code0.Suggested directions (non-prescriptive)
publishViaGhPages: capture the child process's exit code (or scan its stderr forfatal:/error:) and throw when seen, instead of trusting the outer promise alone.ghPages.publish()resolves, do a shallowgit ls-remote(or the GitHub API) and confirm the branch tip moved to the expected commit. If not, throw.tschaub/gh-pageswith the same reproducer so the underlying promise-rejection gap can be tracked there too.Even a simple "loud warning" (e.g.
logger.warn('git reported an error above — aborting')when the stderr stream containedfatal:orerror:) would have saved a month of silent stale deploys here.Happy to help test a fix against the original failing CI if useful.