diff --git a/packages/angular/cli/src/commands/update/schematic/index.ts b/packages/angular/cli/src/commands/update/schematic/index.ts index e110480d09a5..4741ff6e8661 100644 --- a/packages/angular/cli/src/commands/update/schematic/index.ts +++ b/packages/angular/cli/src/commands/update/schematic/index.ts @@ -854,6 +854,16 @@ export default function (options: UpdateSchema): Rule { registry: options.registry, usingYarn, verbose: options.verbose, + }).catch((error: unknown) => { + // 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 as { message?: string }).message ?? String(error); + logger.warn( + `Package '${depName}' could not be fetched from the registry: ${message}`, + ); + + return { requestedName: depName } as Partial; }), ), ); diff --git a/packages/angular/cli/src/commands/update/schematic/index_spec.ts b/packages/angular/cli/src/commands/update/schematic/index_spec.ts index 11b2a0b5855e..8de48f0d4fc9 100644 --- a/packages/angular/cli/src/commands/update/schematic/index_spec.ts +++ b/packages/angular/cli/src/commands/update/schematic/index_spec.ts @@ -336,6 +336,53 @@ describe('@schematics/update', () => { expect(resultTreeContent.endsWith('}')).toBeTrue(); }); + it('continues updating others when one package fetch fails', async () => { + // Regression test for https://github.com/angular/angular-cli/issues/28834 + // Packages from private registries, JSR, AWS CodeArtifact, or local workspaces + // may resolve as npm-registry packages (pass isPkgFromRegistry) but fail to fetch + // with a 404. The schematic should warn and skip them rather than hard-failing, + // and other explicitly-requested packages should still be updated. + const inputTree = new UnitTestTree( + new HostTree( + new virtualFs.test.TestHost({ + '/package.json': JSON.stringify({ + name: 'blah', + dependencies: { + // A real package that should be updated: + '@angular-devkit-tests/update-base': '1.0.0', + // A scoped package that does not exist on the npm registry (simulates a + // private / JSR / CodeArtifact package that passes the registry specifier + // check but returns a 404 when fetched): + '@private-nonexistent/package-ng-update-issue-28834': '1.0.0', + }, + }), + }), + ), + ); + + const messages: string[] = []; + schematicRunner.logger.subscribe((x) => messages.push(x.message)); + + // Should NOT throw even though one package cannot be fetched. + const resultTree = await schematicRunner.runSchematic( + 'update', + { packages: ['@angular-devkit-tests/update-base'] }, + inputTree, + ); + + // The unfetchable package should produce a warning. + expect( + messages.some((m) => m.includes('@private-nonexistent/package-ng-update-issue-28834')), + ).toBeTrue(); + + // The valid package should have been updated despite the other package failing. + const { dependencies } = resultTree.readJson('/package.json') as { + dependencies: Record; + }; + expect(dependencies['@angular-devkit-tests/update-base']).toBe('1.1.0'); + expect(dependencies['@private-nonexistent/package-ng-update-issue-28834']).toBe('1.0.0'); + }, 45000); + it('updates group members to the same version as the targeted package', async () => { const packageJsonContent = `{ "name": "test",