From 7613be781846a10898188dbf5aab1d404e413bd4 Mon Sep 17 00:00:00 2001 From: mateusz Date: Wed, 8 Apr 2026 15:26:05 +0200 Subject: [PATCH] fix(@angular/cli): copy project config files to temp directory during ng update Fixes #27163 --- .../package-manager-descriptor.ts | 8 +++- .../src/package-managers/package-manager.ts | 3 +- .../package-managers/package-manager_spec.ts | 47 +++++++++++++++++++ 3 files changed, 56 insertions(+), 2 deletions(-) diff --git a/packages/angular/cli/src/package-managers/package-manager-descriptor.ts b/packages/angular/cli/src/package-managers/package-manager-descriptor.ts index 34db06b64c99..fda968f44641 100644 --- a/packages/angular/cli/src/package-managers/package-manager-descriptor.ts +++ b/packages/angular/cli/src/package-managers/package-manager-descriptor.ts @@ -155,6 +155,7 @@ export const SUPPORTED_PACKAGE_MANAGERS = { ignoreScriptsFlag: '--ignore-scripts', ignorePeerDependenciesFlag: '--force', configFiles: ['.npmrc'], + copyConfigFromProject: true, getRegistryOptions: (registry: string) => ({ args: ['--registry', registry] }), versionCommand: ['--version'], listDependenciesCommand: ['list', '--depth=0', '--json=true', '--all=true'], @@ -180,6 +181,7 @@ export const SUPPORTED_PACKAGE_MANAGERS = { noLockfileFlag: '', ignoreScriptsFlag: '--mode=skip-build', configFiles: ['.yarnrc.yml', '.yarnrc.yaml'], + copyConfigFromProject: true, getRegistryOptions: (registry: string) => ({ env: { YARN_NPM_REGISTRY_SERVER: registry } }), versionCommand: ['--version'], listDependenciesCommand: ['info', '--name-only', '--json'], @@ -208,6 +210,7 @@ export const SUPPORTED_PACKAGE_MANAGERS = { noLockfileFlag: '--no-lockfile', ignoreScriptsFlag: '--ignore-scripts', configFiles: ['.yarnrc', '.npmrc'], + copyConfigFromProject: true, getRegistryOptions: (registry: string) => ({ args: ['--registry', registry] }), versionCommand: ['--version'], listDependenciesCommand: ['list', '--depth=0', '--json'], @@ -233,7 +236,10 @@ export const SUPPORTED_PACKAGE_MANAGERS = { noLockfileFlag: '--no-lockfile', ignoreScriptsFlag: '--ignore-scripts', ignorePeerDependenciesFlag: '--strict-peer-dependencies=false', - configFiles: ['.npmrc', 'pnpm-workspace.yaml'], + // Note: pnpm-workspace.yaml is intentionally excluded. Copying it to the + // temporary directory could cause pnpm to treat it as a workspace root. + configFiles: ['.npmrc'], + copyConfigFromProject: true, getRegistryOptions: (registry: string) => ({ args: ['--registry', registry] }), versionCommand: ['--version'], listDependenciesCommand: ['list', '--depth=0', '--json'], diff --git a/packages/angular/cli/src/package-managers/package-manager.ts b/packages/angular/cli/src/package-managers/package-manager.ts index 0dfb89e57371..13e511ae88dd 100644 --- a/packages/angular/cli/src/package-managers/package-manager.ts +++ b/packages/angular/cli/src/package-managers/package-manager.ts @@ -621,7 +621,8 @@ export class PackageManager { // Writing an empty package.json file beforehand prevents this. await this.host.writeFile(join(workingDirectory, 'package.json'), '{}'); - // Copy configuration files if the package manager requires it (e.g., bun). + // Copy configuration files from the project to the temp directory so that + // registry settings and other package manager config are respected. if (this.descriptor.copyConfigFromProject) { for (const configFile of this.descriptor.configFiles) { try { diff --git a/packages/angular/cli/src/package-managers/package-manager_spec.ts b/packages/angular/cli/src/package-managers/package-manager_spec.ts index 8d439d9b3b75..c78bca7d4997 100644 --- a/packages/angular/cli/src/package-managers/package-manager_spec.ts +++ b/packages/angular/cli/src/package-managers/package-manager_spec.ts @@ -113,4 +113,51 @@ describe('PackageManager', () => { expect(manifest).toEqual({ name: 'foo', version: '1.0.0' }); }); }); + + describe('acquireTempPackage', () => { + it('should copy config files from the project when copyConfigFromProject is true', async () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const copyFileSpy = spyOn(host as any, 'copyFile').and.resolveTo(); + spyOn(host, 'createTempDirectory').and.resolveTo('/tmp/angular-cli-packages-test'); + spyOn(host, 'writeFile').and.resolveTo(); + spyOn(host, 'deleteDirectory').and.resolveTo(); + + const pm = new PackageManager(host, '/project', descriptor); + + await pm.acquireTempPackage('foo'); + + expect(copyFileSpy).toHaveBeenCalledWith( + '/project/.npmrc', + '/tmp/angular-cli-packages-test/.npmrc', + ); + }); + + it('should not copy config files when copyConfigFromProject is not set', async () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const copyFileSpy = spyOn(host as any, 'copyFile').and.resolveTo(); + spyOn(host, 'createTempDirectory').and.resolveTo('/tmp/angular-cli-packages-test'); + spyOn(host, 'writeFile').and.resolveTo(); + spyOn(host, 'deleteDirectory').and.resolveTo(); + + const noCopyDescriptor = { ...descriptor, copyConfigFromProject: undefined }; + const pm = new PackageManager(host, '/project', noCopyDescriptor); + + await pm.acquireTempPackage('foo'); + + expect(copyFileSpy).not.toHaveBeenCalled(); + }); + + it('should ignore missing config files gracefully', async () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const copyFileSpy = spyOn(host as any, 'copyFile').and.rejectWith(new Error('ENOENT')); + spyOn(host, 'createTempDirectory').and.resolveTo('/tmp/angular-cli-packages-test'); + spyOn(host, 'writeFile').and.resolveTo(); + spyOn(host, 'deleteDirectory').and.resolveTo(); + + const pm = new PackageManager(host, '/project', descriptor); + + await expectAsync(pm.acquireTempPackage('foo')).toBeResolved(); + expect(copyFileSpy).toHaveBeenCalled(); + }); + }); });