diff --git a/.changeset/dirty-dryers-lie.md b/.changeset/dirty-dryers-lie.md new file mode 100644 index 0000000000..5cf36e9eae --- /dev/null +++ b/.changeset/dirty-dryers-lie.md @@ -0,0 +1,5 @@ +--- +"@pnpm/plugin-commands-setup": patch +--- + +fix: make `pnpm setup` free of garbled characters. diff --git a/packages/plugin-commands-setup/src/setupOnWindows.ts b/packages/plugin-commands-setup/src/setupOnWindows.ts index 5b239083f5..80381d15e4 100644 --- a/packages/plugin-commands-setup/src/setupOnWindows.ts +++ b/packages/plugin-commands-setup/src/setupOnWindows.ts @@ -12,7 +12,8 @@ function findEnvValuesInRegistry (regEntries: string, envVarName: string): IEnvi } function setEnvVarInRegistry (envVarName: string, envVarValue: string) { - return execa('reg', ['add', REG_KEY, '/v', envVarName, '/t', 'REG_EXPAND_SZ', '/d', envVarValue, '/f']) + // `windowsHide` in `execa` is true by default, which will cause `chcp` to have no effect. + return execa('reg', ['add', REG_KEY, '/v', envVarName, '/t', 'REG_EXPAND_SZ', '/d', envVarValue, '/f'], { windowsHide: false }) } function pathIncludesDir (pathValue: string, dir: string): boolean { @@ -29,9 +30,21 @@ function pathIncludesDir (pathValue: string, dir: string): boolean { export async function setupWindowsEnvironmentPath (pnpmHomeDir: string, opts: { force: boolean }): Promise { // Use `chcp` to make `reg` use utf8 encoding for output. // Otherwise, the non-ascii characters in the environment variables will become garbled characters. - const queryResult = await execa(`chcp 65001>nul && reg query ${REG_KEY}`, undefined, { shell: true }) + + const chcpResult = await execa('chcp') + const cpMatch = /\d+/.exec(chcpResult.stdout) ?? [] + const cpBak = parseInt(cpMatch[0]) + if (chcpResult.failed || cpBak === 0) { + return `exec chcp failed: ${cpBak}, ${chcpResult.stderr}` + } + + await execa('chcp', ['65001']) + + const queryResult = await execa('reg', ['query', REG_KEY], { windowsHide: false }) if (queryResult.failed) { + await execa('chcp', [cpBak.toString()]) + return 'Win32 registry environment values could not be retrieved' } @@ -45,6 +58,8 @@ export async function setupWindowsEnvironmentPath (pnpmHomeDir: string, opts: { if (homeValueMatch.length === 1 && !opts.force) { const currentHomeDir = homeValueMatch[0].groups.data if (currentHomeDir !== pnpmHomeDir) { + await execa('chcp', [cpBak.toString()]) + throw new PnpmError('DIFFERENT_HOME_DIR_IS_SET', `Currently 'PNPM_HOME' is set to '${currentHomeDir}'`, { hint: 'If you want to override the existing PNPM_HOME env variable, use the --force option', }) @@ -87,5 +102,7 @@ export async function setupWindowsEnvironmentPath (pnpmHomeDir: string, opts: { await execa('setx', ['PNPM_HOME', pnpmHomeDir]) } + await execa('chcp', [cpBak.toString()]) + return logger.join('\n') } diff --git a/packages/plugin-commands-setup/test/setupOnWindows/01.test.ts b/packages/plugin-commands-setup/test/setupOnWindows/01.test.ts index d49d5b8587..f24e31d22b 100644 --- a/packages/plugin-commands-setup/test/setupOnWindows/01.test.ts +++ b/packages/plugin-commands-setup/test/setupOnWindows/01.test.ts @@ -26,7 +26,13 @@ afterAll(() => { const regKey = 'HKEY_CURRENT_USER\\Environment' test('Win32 registry environment values could not be retrieved', async () => { - execa['mockResolvedValue']({ + execa['mockResolvedValueOnce']({ + failed: false, + stdout: '活动代码页: 936', + }).mockResolvedValueOnce({ + failed: false, + stdout: '', + }).mockResolvedValue({ failed: true, }) @@ -34,6 +40,6 @@ test('Win32 registry environment values could not be retrieved', async () => { pnpmHomeDir: __dirname, }) - expect(execa).toHaveBeenNthCalledWith(1, `chcp 65001>nul && reg query ${regKey}`, undefined, { shell: true }) + expect(execa).toHaveBeenNthCalledWith(3, 'reg', ['query', regKey], { windowsHide: false }) expect(output).toContain('Win32 registry environment values could not be retrieved') }) diff --git a/packages/plugin-commands-setup/test/setupOnWindows/02.test.ts b/packages/plugin-commands-setup/test/setupOnWindows/02.test.ts index 88a68c3212..8b45a2c07c 100644 --- a/packages/plugin-commands-setup/test/setupOnWindows/02.test.ts +++ b/packages/plugin-commands-setup/test/setupOnWindows/02.test.ts @@ -27,6 +27,12 @@ const regKey = 'HKEY_CURRENT_USER\\Environment' test('Environment PATH is not configured correctly', async () => { execa['mockResolvedValueOnce']({ + failed: false, + stdout: '活动代码页: 936', + }).mockResolvedValueOnce({ + failed: false, + stdout: '', + }).mockResolvedValueOnce({ failed: false, stdout: 'SOME KIND OF ERROR OR UNSUPPORTED RESPONSE FORMAT', }).mockResolvedValue({ @@ -37,6 +43,6 @@ test('Environment PATH is not configured correctly', async () => { pnpmHomeDir: __dirname, }) - expect(execa).toHaveBeenNthCalledWith(1, `chcp 65001>nul && reg query ${regKey}`, undefined, { shell: true }) + expect(execa).toHaveBeenNthCalledWith(3, 'reg', ['query', regKey], { windowsHide: false }) expect(output).toContain('Current PATH is not set. No changes to this environment variable are applied') }) diff --git a/packages/plugin-commands-setup/test/setupOnWindows/03.test.ts b/packages/plugin-commands-setup/test/setupOnWindows/03.test.ts index 59cc6b8051..f7f7b26e96 100644 --- a/packages/plugin-commands-setup/test/setupOnWindows/03.test.ts +++ b/packages/plugin-commands-setup/test/setupOnWindows/03.test.ts @@ -27,11 +27,17 @@ const regKey = 'HKEY_CURRENT_USER\\Environment' test('Environment PATH is empty', async () => { execa['mockResolvedValueOnce']({ + failed: false, + stdout: '活动代码页: 936', + }).mockResolvedValueOnce({ + failed: false, + stdout: '', + }).mockResolvedValueOnce({ failed: false, stdout: ` HKEY_CURRENT_USER\\Environment Path REG_EXPAND_SZ -`, + `, }).mockResolvedValue({ failed: false, }) @@ -40,6 +46,6 @@ HKEY_CURRENT_USER\\Environment pnpmHomeDir: __dirname, }) - expect(execa).toHaveBeenNthCalledWith(1, `chcp 65001>nul && reg query ${regKey}`, undefined, { shell: true }) + expect(execa).toHaveBeenNthCalledWith(3, 'reg', ['query', regKey], { windowsHide: false }) expect(output).toContain('Current PATH is empty. No changes to this environment variable are applied') }) diff --git a/packages/plugin-commands-setup/test/setupOnWindows/04.test.ts b/packages/plugin-commands-setup/test/setupOnWindows/04.test.ts index ffe83dbe95..af2bf81860 100644 --- a/packages/plugin-commands-setup/test/setupOnWindows/04.test.ts +++ b/packages/plugin-commands-setup/test/setupOnWindows/04.test.ts @@ -29,6 +29,12 @@ test('Successful first time installation', async () => { const currentPathInRegistry = '%USERPROFILE%\\AppData\\Local\\Microsoft\\WindowsApps;%USERPROFILE%\\.config\\etc;' execa['mockResolvedValueOnce']({ + failed: false, + stdout: '活动代码页: 936', + }).mockResolvedValueOnce({ + failed: false, + stdout: '', + }).mockResolvedValueOnce({ failed: false, stdout: ` HKEY_CURRENT_USER\\Environment @@ -49,10 +55,10 @@ HKEY_CURRENT_USER\\Environment pnpmHomeDir: __dirname, }) - expect(execa).toHaveBeenNthCalledWith(1, `chcp 65001>nul && reg query ${regKey}`, undefined, { shell: true }) - expect(execa).toHaveBeenNthCalledWith(2, 'reg', ['add', regKey, '/v', 'PNPM_HOME', '/t', 'REG_EXPAND_SZ', '/d', __dirname, '/f']) - expect(execa).toHaveBeenNthCalledWith(3, 'reg', ['add', regKey, '/v', 'Path', '/t', 'REG_EXPAND_SZ', '/d', `${__dirname};${currentPathInRegistry}`, '/f']) - expect(execa).toHaveBeenNthCalledWith(4, 'setx', ['PNPM_HOME', __dirname]) + expect(execa).toHaveBeenNthCalledWith(3, 'reg', ['query', regKey], { windowsHide: false }) + expect(execa).toHaveBeenNthCalledWith(4, 'reg', ['add', regKey, '/v', 'PNPM_HOME', '/t', 'REG_EXPAND_SZ', '/d', __dirname, '/f'], { windowsHide: false }) + expect(execa).toHaveBeenNthCalledWith(5, 'reg', ['add', regKey, '/v', 'Path', '/t', 'REG_EXPAND_SZ', '/d', `${__dirname};${currentPathInRegistry}`, '/f'], { windowsHide: false }) + expect(execa).toHaveBeenNthCalledWith(6, 'setx', ['PNPM_HOME', __dirname]) expect(output).toContain(`Setting 'PNPM_HOME' to value '${__dirname}`) expect(output).toContain('Updating PATH') expect(output).toContain('PNPM_HOME ENV VAR SET') diff --git a/packages/plugin-commands-setup/test/setupOnWindows/05.test.ts b/packages/plugin-commands-setup/test/setupOnWindows/05.test.ts index 079cc5378b..6e0b137fec 100644 --- a/packages/plugin-commands-setup/test/setupOnWindows/05.test.ts +++ b/packages/plugin-commands-setup/test/setupOnWindows/05.test.ts @@ -29,6 +29,12 @@ test('PNPM_HOME is already set, but path is updated', async () => { const currentPathInRegistry = '%USERPROFILE%\\AppData\\Local\\Microsoft\\WindowsApps;%USERPROFILE%\\.config\\etc;' const pnpmHomeDir = '.pnpm\\home' execa['mockResolvedValueOnce']({ + failed: false, + stdout: '活动代码页: 936', + }).mockResolvedValueOnce({ + failed: false, + stdout: '', + }).mockResolvedValueOnce({ failed: false, stdout: ` HKEY_CURRENT_USER\\Environment @@ -45,9 +51,9 @@ HKEY_CURRENT_USER\\Environment const output = await setup.handler({ pnpmHomeDir }) - expect(execa).toHaveBeenNthCalledWith(1, `chcp 65001>nul && reg query ${regKey}`, undefined, { shell: true }) - expect(execa).toHaveBeenNthCalledWith(2, 'reg', ['add', regKey, '/v', 'Path', '/t', 'REG_EXPAND_SZ', '/d', `${'.pnpm\\home'};${currentPathInRegistry}`, '/f']) - expect(execa).toHaveBeenNthCalledWith(3, 'setx', ['PNPM_HOME', '.pnpm\\home']) + expect(execa).toHaveBeenNthCalledWith(3, 'reg', ['query', regKey], { windowsHide: false }) + expect(execa).toHaveBeenNthCalledWith(4, 'reg', ['add', regKey, '/v', 'Path', '/t', 'REG_EXPAND_SZ', '/d', `${'.pnpm\\home'};${currentPathInRegistry}`, '/f'], { windowsHide: false }) + expect(execa).toHaveBeenNthCalledWith(5, 'setx', ['PNPM_HOME', '.pnpm\\home']) expect(output).toContain('Updating PATH') expect(output).toContain('PATH UPDATED') }) diff --git a/packages/plugin-commands-setup/test/setupOnWindows/06.test.ts b/packages/plugin-commands-setup/test/setupOnWindows/06.test.ts index e4b3a0889c..27b8d719ff 100644 --- a/packages/plugin-commands-setup/test/setupOnWindows/06.test.ts +++ b/packages/plugin-commands-setup/test/setupOnWindows/06.test.ts @@ -27,6 +27,12 @@ const regKey = 'HKEY_CURRENT_USER\\Environment' test('setup throws an error if PNPM_HOME is already set to a different directory', async () => { execa['mockResolvedValueOnce']({ + failed: false, + stdout: '活动代码页: 936', + }).mockResolvedValueOnce({ + failed: false, + stdout: '', + }).mockResolvedValueOnce({ failed: false, stdout: ` HKEY_CURRENT_USER\\Environment @@ -47,6 +53,12 @@ HKEY_CURRENT_USER\\Environment test('setup overrides PNPM_HOME, when setup is forced', async () => { execa['mockResolvedValueOnce']({ + failed: false, + stdout: '活动代码页: 936', + }).mockResolvedValueOnce({ + failed: false, + stdout: '', + }).mockResolvedValueOnce({ failed: false, stdout: ` HKEY_CURRENT_USER\\Environment @@ -64,6 +76,6 @@ HKEY_CURRENT_USER\\Environment pnpmHomeDir, }) - expect(execa).toHaveBeenNthCalledWith(1, `chcp 65001>nul && reg query ${regKey}`, undefined, { shell: true }) + expect(execa).toHaveBeenNthCalledWith(3, 'reg', ['query', regKey], { windowsHide: false }) expect(output).toContain(`Setting 'PNPM_HOME' to value '${pnpmHomeDir}'`) }) diff --git a/packages/plugin-commands-setup/test/setupOnWindows/07.test.ts b/packages/plugin-commands-setup/test/setupOnWindows/07.test.ts index 1f52e9eae0..dbfb27cdd4 100644 --- a/packages/plugin-commands-setup/test/setupOnWindows/07.test.ts +++ b/packages/plugin-commands-setup/test/setupOnWindows/07.test.ts @@ -29,6 +29,12 @@ test('Failure to install', async () => { const currentPathInRegistry = '%USERPROFILE%\\AppData\\Local\\Microsoft\\WindowsApps;%USERPROFILE%\\.config\\etc;' execa['mockResolvedValueOnce']({ + failed: false, + stdout: '活动代码页: 936', + }).mockResolvedValueOnce({ + failed: false, + stdout: '', + }).mockResolvedValueOnce({ failed: false, stdout: ` HKEY_CURRENT_USER\\Environment @@ -49,11 +55,11 @@ HKEY_CURRENT_USER\\Environment pnpmHomeDir: __dirname, }) - expect(execa).toHaveBeenNthCalledWith(1, `chcp 65001>nul && reg query ${regKey}`, undefined, { shell: true }) - expect(execa).toHaveBeenNthCalledWith(2, 'reg', ['add', regKey, '/v', 'PNPM_HOME', '/t', 'REG_EXPAND_SZ', '/d', __dirname, '/f']) - expect(execa).toHaveBeenNthCalledWith(3, 'reg', ['add', regKey, '/v', 'Path', '/t', 'REG_EXPAND_SZ', '/d', `${__dirname};${currentPathInRegistry}`, '/f']) + expect(execa).toHaveBeenNthCalledWith(3, 'reg', ['query', regKey], { windowsHide: false }) + expect(execa).toHaveBeenNthCalledWith(4, 'reg', ['add', regKey, '/v', 'PNPM_HOME', '/t', 'REG_EXPAND_SZ', '/d', __dirname, '/f'], { windowsHide: false }) + expect(execa).toHaveBeenNthCalledWith(5, 'reg', ['add', regKey, '/v', 'Path', '/t', 'REG_EXPAND_SZ', '/d', `${__dirname};${currentPathInRegistry}`, '/f'], { windowsHide: false }) expect(output).toContain(`Setting 'PNPM_HOME' to value '${__dirname}`) expect(output).toContain('FAILED TO SET PNPM_HOME') expect(output).toContain('Updating PATH') expect(output).toContain('FAILED TO UPDATE PATH') -}) \ No newline at end of file +})