Skip to content

Fake 'Successfully published' banner when underlying git push fails (silent deploy failure) #205

@JohannesHoppe

Description

@JohannesHoppe

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

  1. 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.
  2. Run the workflow.
  3. 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.

Metadata

Metadata

Assignees

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