fix(@angular/cli): gracefully skip unfetchable packages during ng update#32928
fix(@angular/cli): gracefully skip unfetchable packages during ng update#32928maruthang wants to merge 2 commits intoangular:mainfrom
Conversation
When package.json contains packages from non-npmjs registries (JSR, AWS CodeArtifact, private registries, local workspace packages), the registry fetch inside Promise.all would reject and hard-fail the entire update command with a 404 or network error. Wrap each per-package getNpmPackageJson call in a .catch() so that fetch failures emit a warning and return a partial sentinel object. The existing reduce step already handles partial objects correctly: auto-discovered packages are silently skipped while explicitly-requested packages still produce a clear SchematicsException, preserving intentional error visibility. Closes angular#28834
There was a problem hiding this comment.
Code Review
This pull request introduces error handling for package fetching in the Angular CLI update schematic, allowing the process to continue with a warning if a package cannot be retrieved from a registry. This change improves support for private registries and local workspace packages that might otherwise cause the update to fail. The review feedback suggests making the error message extraction more robust to handle non-Error objects and enhancing the regression test to ensure it verifies that other packages are successfully updated when a fetch failure occurs.
| // If the package cannot be fetched (e.g. private registry, JSR, AWS CodeArtifact, | ||
| // or local workspace packages), return a partial object so the reduce below can | ||
| // decide whether to warn or hard-fail based on whether it was explicitly requested. | ||
| const message = error instanceof Error ? error.message : String(error); |
There was a problem hiding this comment.
The error message extraction could be more robust. If error is a plain object with a message property (which can happen with some HTTP clients or custom errors), instanceof Error will be false and String(error) will result in [object Object]. Consider using a fallback to the .message property if it exists to provide a more helpful warning.
| const message = error instanceof Error ? error.message : String(error); | |
| const message = error instanceof Error ? error.message : (error as any)?.message ?? String(error); |
| expect(resultTreeContent.endsWith('}')).toBeTrue(); | ||
| }); | ||
|
|
||
| it('skips packages that cannot be fetched from the registry and continues updating others', async () => { |
There was a problem hiding this comment.
The test case name indicates that it verifies the schematic 'continues updating others', but the current implementation only verifies that the analysis phase completes without throwing when no packages are requested for update. To fully validate the intended behavior, consider requesting an update for a valid package (e.g., @angular-devkit-tests/update-base) and asserting that it is successfully updated despite the fetch failure of the other package.
Problem
ng updatehard-fails whenpackage.jsoncontains packages that passthe
isPkgFromRegistrycheck (i.e., they are specified as plain semverranges) but cannot actually be fetched from the registry. This affects
JSR packages (
@std/toml), private/scoped packages on corporateregistries (AWS CodeArtifact), and local workspace packages referenced
by name. The error looks like:
Root Cause
In
packages/angular/cli/src/commands/update/schematic/index.ts, allpackage metadata is fetched via
Promise.all(deps.map(getNpmPackageJson)).If any single fetch rejects (404, ENOTFOUND, auth error), the entire
Promise.allrejects and kills the update before any package isprocessed.
The code that follows the
Promise.allalready handles the "package notfound" case gracefully (it distinguishes between auto-discovered packages
and explicitly-requested ones), but it never gets a chance to run when
the fetch itself throws.
Fix
Attach a
.catch()to each individualgetNpmPackageJsoncall. Onfailure the catch emits a warning like:
and returns the same sentinel shape (
{ requestedName }) that theexisting reduce block already knows how to handle:
ng update @some/pkg): still produce aSchematicsExceptionso the user gets a clear error.New Behavior
Instead of:
Test
Added a regression test in
index_spec.tsthat puts a non-existentscoped package (
@private-nonexistent/package-ng-update-issue-28834)alongside a real one, runs the schematic, and asserts it completes
without throwing while emitting a warning for the unfetchable package.
Closes #28834