From 3dab7f83c2b4287392e2318aa854672b9e1c0ac0 Mon Sep 17 00:00:00 2001 From: Dmitriy Dudkevich Date: Fri, 11 Nov 2022 23:50:56 +0300 Subject: [PATCH] fix: replacing env variables to values in .npmrc (#5623) close #5604 close #2570 Co-authored-by: Zoltan Kochan --- .changeset/chilly-garlics-joke.md | 5 +++ .changeset/grumpy-bats-drum.md | 7 ++++ packages/config/package.json | 5 ++- packages/config/src/index.ts | 2 + packages/config/src/readLocalConfig.ts | 31 +++++++++++++++ .../plugin-commands-installation/package.json | 2 - .../src/recursive.ts | 22 +---------- .../test/miscRecursive.ts | 30 +++++++++++++++ packages/plugin-commands-rebuild/package.json | 2 - .../plugin-commands-rebuild/src/recursive.ts | 22 +---------- pnpm-lock.yaml | 38 ++++++++++--------- 11 files changed, 102 insertions(+), 64 deletions(-) create mode 100644 .changeset/chilly-garlics-joke.md create mode 100644 .changeset/grumpy-bats-drum.md create mode 100644 packages/config/src/readLocalConfig.ts diff --git a/.changeset/chilly-garlics-joke.md b/.changeset/chilly-garlics-joke.md new file mode 100644 index 00000000000..451afa49706 --- /dev/null +++ b/.changeset/chilly-garlics-joke.md @@ -0,0 +1,5 @@ +--- +"@pnpm/config": minor +--- + +New function added: `readLocalConfig(dir: string)`. diff --git a/.changeset/grumpy-bats-drum.md b/.changeset/grumpy-bats-drum.md new file mode 100644 index 00000000000..d01ceee4e41 --- /dev/null +++ b/.changeset/grumpy-bats-drum.md @@ -0,0 +1,7 @@ +--- +"@pnpm/plugin-commands-installation": patch +"@pnpm/plugin-commands-rebuild": patch +"pnpm": patch +--- + +Replace environment variable placeholders with their values, when reading `.npmrc` files in subdirectories inside a workspace [#2570](https://github.com/pnpm/pnpm/issues/2570). diff --git a/packages/config/package.json b/packages/config/package.json index 2f78e2be14c..6b61ef1bb6b 100644 --- a/packages/config/package.json +++ b/packages/config/package.json @@ -32,15 +32,17 @@ }, "homepage": "https://github.com/pnpm/pnpm/blob/main/packages/config#readme", "dependencies": { + "@pnpm/config.env-replace": "1.0.0", "@pnpm/constants": "workspace:*", "@pnpm/error": "workspace:*", "@pnpm/git-utils": "workspace:*", "@pnpm/matcher": "workspace:*", - "@pnpm/npm-conf": "2.0.2", + "@pnpm/npm-conf": "2.0.4", "@pnpm/pnpmfile": "workspace:*", "@pnpm/read-project-manifest": "workspace:*", "@pnpm/types": "workspace:*", "camelcase": "^6.3.0", + "camelcase-keys": "^6.2.2", "can-write-to-dir": "^1.1.1", "is-subdir": "^1.2.0", "is-windows": "^1.0.2", @@ -48,6 +50,7 @@ "path-absolute": "^1.0.1", "path-name": "^1.0.0", "ramda": "npm:@pnpm/ramda@0.28.1", + "read-ini-file": "^3.1.0", "realpath-missing": "^1.1.0", "which": "^2.0.2" }, diff --git a/packages/config/src/index.ts b/packages/config/src/index.ts index 49877d014ef..908d4903fda 100644 --- a/packages/config/src/index.ts +++ b/packages/config/src/index.ts @@ -25,6 +25,8 @@ import { } from './Config' import { getWorkspaceConcurrency } from './concurrency' +export * from './readLocalConfig' + export { Config, UniversalOptions } const npmDefaults = loadNpmConf.defaults diff --git a/packages/config/src/readLocalConfig.ts b/packages/config/src/readLocalConfig.ts new file mode 100644 index 00000000000..1965cb81367 --- /dev/null +++ b/packages/config/src/readLocalConfig.ts @@ -0,0 +1,31 @@ +import path from 'path' +import camelcaseKeys from 'camelcase-keys' +import { envReplace } from '@pnpm/config.env-replace' +import readIniFile from 'read-ini-file' + +export async function readLocalConfig (prefix: string) { + try { + const ini = await readIniFile(path.join(prefix, '.npmrc')) as Record + const config = camelcaseKeys(ini) as (Record & { hoist?: boolean }) + if (config.shamefullyFlatten) { + config.hoistPattern = '*' + // TODO: print a warning + } + if (config.hoist === false) { + config.hoistPattern = '' + } + for (const [key, val] of Object.entries(config)) { + if (typeof val === 'string') { + try { + config[key] = envReplace(val, process.env) + } catch (err) { + // ignore + } + } + } + return config + } catch (err: any) { // eslint-disable-line + if (err.code !== 'ENOENT') throw err + return {} + } +} diff --git a/packages/plugin-commands-installation/package.json b/packages/plugin-commands-installation/package.json index e015a4e0f4d..2c893ae01ed 100644 --- a/packages/plugin-commands-installation/package.json +++ b/packages/plugin-commands-installation/package.json @@ -89,7 +89,6 @@ "@zkochan/rimraf": "^2.1.2", "@zkochan/table": "^1.0.0", "@zkochan/which": "^2.0.3", - "camelcase-keys": "^6.2.2", "chalk": "^4.1.2", "enquirer": "^2.3.6", "is-ci": "^3.0.1", @@ -101,7 +100,6 @@ "path-absolute": "^1.0.1", "path-exists": "^4.0.0", "ramda": "npm:@pnpm/ramda@0.28.1", - "read-ini-file": "^3.1.0", "render-help": "^1.0.2", "version-selector-type": "^3.0.0" }, diff --git a/packages/plugin-commands-installation/src/recursive.ts b/packages/plugin-commands-installation/src/recursive.ts index 60898e67d80..b57be0f983d 100755 --- a/packages/plugin-commands-installation/src/recursive.ts +++ b/packages/plugin-commands-installation/src/recursive.ts @@ -4,7 +4,7 @@ import { RecursiveSummary, throwOnCommandFail, } from '@pnpm/cli-utils' -import { Config } from '@pnpm/config' +import { Config, readLocalConfig } from '@pnpm/config' import { PnpmError } from '@pnpm/error' import { arrayOfWorkspacePackagesToMap } from '@pnpm/find-workspace-packages' import { logger } from '@pnpm/logger' @@ -29,12 +29,10 @@ import { mutateModules, ProjectOptions, } from '@pnpm/core' -import camelcaseKeys from 'camelcase-keys' import isSubdir from 'is-subdir' import mem from 'mem' import pFilter from 'p-filter' import pLimit from 'p-limit' -import readIniFile from 'read-ini-file' import { getOptionsFromRootManifest } from './getOptionsFromRootManifest' import { createWorkspaceSpecs, updateToWorkspacePackagesFromManifest } from './updateWorkspaceDependencies' import { updateToLatestSpecsFromManifest, createLatestSpecs } from './updateToLatestSpecsFromManifest' @@ -425,24 +423,6 @@ async function unlinkPkgs (dependencyNames: string[], manifest: ProjectManifest, ) } -async function readLocalConfig (prefix: string) { - try { - const ini = await readIniFile(path.join(prefix, '.npmrc')) as Record - const config = camelcaseKeys(ini) as (Record & { hoist?: boolean }) - if (config.shamefullyFlatten) { - config.hoistPattern = '*' - // TODO: print a warning - } - if (config.hoist === false) { - config.hoistPattern = '' - } - return config - } catch (err: any) { // eslint-disable-line - if (err.code !== 'ENOENT') throw err - return {} - } -} - function calculateRepositoryRoot ( workspaceDir: string, projectDirs: string[] diff --git a/packages/plugin-commands-installation/test/miscRecursive.ts b/packages/plugin-commands-installation/test/miscRecursive.ts index 87b3467b74b..f979dd611b7 100644 --- a/packages/plugin-commands-installation/test/miscRecursive.ts +++ b/packages/plugin-commands-installation/test/miscRecursive.ts @@ -658,6 +658,36 @@ test('recursive install in a monorepo with different modules directories', async await projects['project-2'].has('is-positive', 'modules_2') }) +test('recursive install in a monorepo with parsing env variables', async () => { + const projects = preparePackages([ + { + name: 'project', + version: '1.0.0', + + dependencies: { + 'is-positive': '1.0.0', + }, + }, + ]) + + process.env['SOME_NAME'] = 'some_name' + // eslint-disable-next-line no-template-curly-in-string + await fs.writeFile('project/.npmrc', 'modules-dir=${SOME_NAME}_modules', 'utf8') + + const { allProjects, allProjectsGraph, selectedProjectsGraph } = await readProjects(process.cwd(), []) + await install.handler({ + ...DEFAULT_OPTS, + allProjects, + allProjectsGraph, + dir: process.cwd(), + recursive: true, + selectedProjectsGraph, + workspaceDir: process.cwd(), + }) + + await projects['project'].has('is-positive', `${process.env['SOME_NAME']}_modules`) +}) + test('prefer-workspace-package', async () => { await addDistTag({ distTag: 'latest', diff --git a/packages/plugin-commands-rebuild/package.json b/packages/plugin-commands-rebuild/package.json index 0ce0c7f15f7..6379bc6902f 100644 --- a/packages/plugin-commands-rebuild/package.json +++ b/packages/plugin-commands-rebuild/package.json @@ -66,13 +66,11 @@ "@pnpm/store-connection-manager": "workspace:*", "@pnpm/store-controller-types": "workspace:*", "@pnpm/types": "workspace:*", - "camelcase-keys": "^6.2.2", "dependency-path": "workspace:*", "load-json-file": "^6.2.0", "mem": "^8.1.1", "p-limit": "^3.1.0", "ramda": "npm:@pnpm/ramda@0.28.1", - "read-ini-file": "^3.1.0", "render-help": "^1.0.2", "run-groups": "^3.0.1", "semver": "^7.3.8" diff --git a/packages/plugin-commands-rebuild/src/recursive.ts b/packages/plugin-commands-rebuild/src/recursive.ts index 45b6542f53b..d01b6cc8abc 100755 --- a/packages/plugin-commands-rebuild/src/recursive.ts +++ b/packages/plugin-commands-rebuild/src/recursive.ts @@ -1,20 +1,18 @@ -import path from 'path' import { RecursiveSummary, throwOnCommandFail, } from '@pnpm/cli-utils' import { Config, + readLocalConfig, } from '@pnpm/config' import { arrayOfWorkspacePackagesToMap } from '@pnpm/find-workspace-packages' import { logger } from '@pnpm/logger' import { sortPackages } from '@pnpm/sort-packages' import { createOrConnectStoreController, CreateStoreControllerOptions } from '@pnpm/store-connection-manager' import { Project, ProjectManifest } from '@pnpm/types' -import camelcaseKeys from 'camelcase-keys' import mem from 'mem' import pLimit from 'p-limit' -import readIniFile from 'read-ini-file' import { rebuildProjects as rebuildAll, RebuildOptions, rebuildSelectedPkgs } from './implementation' type RecursiveRebuildOpts = CreateStoreControllerOptions & Pick - const config = camelcaseKeys(ini) as (Record & { hoist?: boolean }) - if (config.shamefullyFlatten) { - config.hoistPattern = '*' - // TODO: print a warning - } - if (config.hoist === false) { - config.hoistPattern = '' - } - return config - } catch (err: any) { // eslint-disable-line - if (err.code !== 'ENOENT') throw err - return {} - } -} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c1f22bd3290..8c05f838683 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -465,6 +465,9 @@ importers: packages/config: dependencies: + '@pnpm/config.env-replace': + specifier: 1.0.0 + version: 1.0.0 '@pnpm/constants': specifier: workspace:* version: link:../constants @@ -478,8 +481,8 @@ importers: specifier: workspace:* version: link:../matcher '@pnpm/npm-conf': - specifier: 2.0.2 - version: 2.0.2 + specifier: 2.0.4 + version: 2.0.4 '@pnpm/pnpmfile': specifier: workspace:* version: link:../pnpmfile @@ -492,6 +495,9 @@ importers: camelcase: specifier: ^6.3.0 version: 6.3.0 + camelcase-keys: + specifier: ^6.2.2 + version: 6.2.2 can-write-to-dir: specifier: ^1.1.1 version: 1.1.1 @@ -513,6 +519,9 @@ importers: ramda: specifier: npm:@pnpm/ramda@0.28.1 version: /@pnpm/ramda/0.28.1 + read-ini-file: + specifier: ^3.1.0 + version: 3.1.0 realpath-missing: specifier: ^1.1.0 version: 1.1.0 @@ -3355,9 +3364,6 @@ importers: '@zkochan/which': specifier: ^2.0.3 version: 2.0.3 - camelcase-keys: - specifier: ^6.2.2 - version: 6.2.2 chalk: specifier: ^4.1.2 version: 4.1.2 @@ -3391,9 +3397,6 @@ importers: ramda: specifier: npm:@pnpm/ramda@0.28.1 version: /@pnpm/ramda/0.28.1 - read-ini-file: - specifier: ^3.1.0 - version: 3.1.0 render-help: specifier: ^1.0.2 version: 1.0.2 @@ -3897,9 +3900,6 @@ importers: '@pnpm/types': specifier: workspace:* version: link:../types - camelcase-keys: - specifier: ^6.2.2 - version: 6.2.2 dependency-path: specifier: workspace:* version: link:../dependency-path @@ -3915,9 +3915,6 @@ importers: ramda: specifier: npm:@pnpm/ramda@0.28.1 version: /@pnpm/ramda/0.28.1 - read-ini-file: - specifier: ^3.1.0 - version: 3.1.0 render-help: specifier: ^1.0.2 version: 1.0.2 @@ -6869,6 +6866,11 @@ packages: chalk: 4.1.2 dev: false + /@pnpm/config.env-replace/1.0.0: + resolution: {integrity: sha512-ZVPVDi1E8oeXlYqkGRtX0CkzLTwE2zt62bjWaWKaAvI8NZqHzlMvGeSNDpW+JB3+aKanYb4UETJOF1/CxGPemA==} + engines: {node: '>=12.22.0'} + dev: false + /@pnpm/config/16.0.0_kt75g7u7xait7ts3kctxzlcgqq: resolution: {integrity: sha512-/KR8uQ1GWnnk3MciZbSWZG2VYzE/6WomekG7QdZ6iu3KT1YOJ0LGoSfbXCVKyirVZ748k5HIaLL51H7NsDPBLQ==} engines: {node: '>=14.6'} @@ -7477,10 +7479,11 @@ packages: config-chain: 1.1.13 dev: true - /@pnpm/npm-conf/2.0.2: - resolution: {integrity: sha512-vp0T6HQcklEpSILEutr8o4Gxf3lIPJtxbDT1kGNyYo0ZVL1UGgazeaGr9rc4ut8veI7tiVCBr7cJbDgym/q3Kg==} + /@pnpm/npm-conf/2.0.4: + resolution: {integrity: sha512-xWjBhnntYvAjYt1alEoFJiThMe0ZhSY7iZuxBUR+DH3tH2RyGrP2KU75NZfo/jhc3dSBUqZrd1DnIIgkQ0WyKw==} engines: {node: '>=12'} dependencies: + '@pnpm/config.env-replace': 1.0.0 '@pnpm/network.ca-file': 1.0.1 config-chain: 1.1.13 dev: false @@ -17154,13 +17157,14 @@ time: /@commitlint/prompt-cli/17.1.2: '2022-08-29T07:53:18.629Z' /@pnpm/byline/1.0.0: '2021-10-31T23:25:00.031Z' /@pnpm/colorize-semver-diff/1.0.1: '2020-10-25T15:50:17.812Z' + /@pnpm/config.env-replace/1.0.0: '2022-11-11T20:22:17.274Z' /@pnpm/exec/2.0.0: '2020-10-29T23:51:01.271Z' /@pnpm/graph-sequencer/1.0.0: '2022-03-20T20:48:58.797Z' /@pnpm/logger/5.0.0: '2022-10-14T13:56:04.285Z' /@pnpm/meta-updater/0.2.1: '2022-10-17T23:32:30.921Z' /@pnpm/network.agent/0.0.3: '2022-06-12T19:51:53.044Z' /@pnpm/nopt/0.2.1: '2021-06-01T19:45:54.552Z' - /@pnpm/npm-conf/2.0.2: '2022-10-21T14:43:04.271Z' + /@pnpm/npm-conf/2.0.4: '2022-11-11T20:26:48.028Z' /@pnpm/npm-lifecycle/2.0.0-1: '2022-02-21T22:36:47.308Z' /@pnpm/npm-package-arg/1.0.0: '2022-06-28T12:48:31.287Z' /@pnpm/os.env.path-extender/0.2.7: '2022-10-17T23:03:23.674Z'