Skip to content

Commit 12bd990

Browse files
committed
.corepack.env
1 parent b1facaf commit 12bd990

File tree

5 files changed

+305
-32
lines changed

5 files changed

+305
-32
lines changed

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,7 @@ same major line. Should you need to upgrade to a new major, use an explicit
297297
set to `1` to have the URL shown. By default, when Corepack is called
298298
explicitly (e.g. `corepack pnpm …`), it is set to `0`; when Corepack is called
299299
implicitly (e.g. `pnpm …`), it is set to `1`.
300+
The default value cannot be overridden in a `.corepack.env` file.
300301
When standard input is a TTY and no CI environment is detected, Corepack will
301302
ask for user input before starting the download.
302303

@@ -322,6 +323,14 @@ same major line. Should you need to upgrade to a new major, use an explicit
322323
project. This means that it will always use the system-wide package manager
323324
regardless of what is being specified in the project's `packageManager` field.
324325

326+
- `COREPACK_ENV_FILE` can be set to `0` to request Corepack to not attempt to
327+
load `.corepack.env`; it can be set to a path to specify a different env file.
328+
Only keys that start with `COREPACK_` and are not in the exception list
329+
(`COREPACK_ENABLE_DOWNLOAD_PROMPT` and `COREPACK_ENV_FILE` are ignored)
330+
will be taken into account.
331+
For Node.js 18.x users, this setting has no effect as that version doesn't
332+
support parsing of `.env` files.
333+
325334
- `COREPACK_HOME` can be set in order to define where Corepack should install
326335
the package managers. By default it is set to `%LOCALAPPDATA%\node\corepack`
327336
on Windows, and to `$HOME/.cache/node/corepack` everywhere else.

sources/npmRegistryUtils.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ export function verifySignature({signatures, integrity, packageName, version}: {
3838
packageName: string;
3939
version: string;
4040
}) {
41+
if (signatures == null) throw new Error(`No compatible signature found in package metadata for ${packageName}@${version}`);
42+
4143
const {npm: keys} = process.env.COREPACK_INTEGRITY_KEYS ?
4244
JSON.parse(process.env.COREPACK_INTEGRITY_KEYS) as typeof defaultConfig.keys :
4345
defaultConfig.keys;

sources/specUtils.ts

Lines changed: 53 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
1-
import {UsageError} from 'clipanion';
2-
import fs from 'fs';
3-
import path from 'path';
4-
import semverSatisfies from 'semver/functions/satisfies';
5-
import semverValid from 'semver/functions/valid';
6-
import semverValidRange from 'semver/ranges/valid';
7-
8-
import {PreparedPackageManagerInfo} from './Engine';
9-
import * as debugUtils from './debugUtils';
10-
import {NodeError} from './nodeUtils';
11-
import * as nodeUtils from './nodeUtils';
12-
import {Descriptor, isSupportedPackageManager} from './types';
1+
import {UsageError} from 'clipanion';
2+
import fs from 'fs';
3+
import path from 'path';
4+
import semverSatisfies from 'semver/functions/satisfies';
5+
import semverValid from 'semver/functions/valid';
6+
import semverValidRange from 'semver/ranges/valid';
7+
import {parseEnv} from 'util';
8+
9+
import type {PreparedPackageManagerInfo} from './Engine';
10+
import * as debugUtils from './debugUtils';
11+
import type {NodeError} from './nodeUtils';
12+
import * as nodeUtils from './nodeUtils';
13+
import {isSupportedPackageManager} from './types';
14+
import type {LocalEnvFile, Descriptor} from './types';
1315

1416
const nodeModulesRegExp = /[\\/]node_modules[\\/](@[^\\/]*[\\/])?([^@\\/][^\\/]*)$/;
1517

@@ -138,10 +140,11 @@ export async function setLocalPackageManager(cwd: string, info: PreparedPackageM
138140
};
139141
}
140142

143+
type FoundSpecResult = {type: `Found`, target: string, spec: Descriptor, range?: Descriptor, envFilePath?: string};
141144
export type LoadSpecResult =
142145
| {type: `NoProject`, target: string}
143146
| {type: `NoSpec`, target: string}
144-
| {type: `Found`, target: string, spec: Descriptor, range?: Descriptor};
147+
| FoundSpecResult;
145148

146149
export async function loadSpec(initialCwd: string): Promise<LoadSpecResult> {
147150
let nextCwd = initialCwd;
@@ -150,6 +153,8 @@ export async function loadSpec(initialCwd: string): Promise<LoadSpecResult> {
150153
let selection: {
151154
data: any;
152155
manifestPath: string;
156+
envFilePath?: string;
157+
localEnv: LocalEnvFile;
153158
} | null = null;
154159

155160
while (nextCwd !== currCwd && (!selection || !selection.data.packageManager)) {
@@ -177,23 +182,55 @@ export async function loadSpec(initialCwd: string): Promise<LoadSpecResult> {
177182
if (typeof data !== `object` || data === null)
178183
throw new UsageError(`Invalid package.json in ${path.relative(initialCwd, manifestPath)}`);
179184

180-
selection = {data, manifestPath};
185+
let localEnv: LocalEnvFile;
186+
const envFilePath = path.resolve(currCwd, process.env.COREPACK_ENV_FILE ?? `.corepack.env`);
187+
if (process.env.COREPACK_ENV_FILE == `0`) {
188+
debugUtils.log(`Skipping env file as configured with COREPACK_ENV_FILE`);
189+
localEnv = process.env;
190+
} else if (typeof parseEnv !== `function`) {
191+
// TODO: remove this block when support for Node.js 18.x is dropped.
192+
debugUtils.log(`Skipping env file as it is not supported by the current version of Node.js`);
193+
localEnv = process.env;
194+
} else {
195+
debugUtils.log(`Checking ${envFilePath}`);
196+
try {
197+
localEnv = {
198+
...Object.fromEntries(Object.entries(parseEnv(await fs.promises.readFile(envFilePath, `utf8`))).filter(e => e[0].startsWith(`COREPACK_`))),
199+
...process.env,
200+
};
201+
debugUtils.log(`Successfully loaded env file found at ${envFilePath}`);
202+
} catch (err) {
203+
if ((err as NodeError)?.code !== `ENOENT`)
204+
throw err;
205+
206+
debugUtils.log(`No env file found at ${envFilePath}`);
207+
localEnv = process.env;
208+
}
209+
}
210+
211+
selection = {data, manifestPath, localEnv, envFilePath};
181212
}
182213

183214
if (selection === null)
184215
return {type: `NoProject`, target: path.join(initialCwd, `package.json`)};
185216

217+
let envFilePath: string | undefined;
218+
if (selection.localEnv !== process.env) {
219+
envFilePath = selection.envFilePath;
220+
process.env = selection.localEnv;
221+
}
222+
186223
const rawPmSpec = parsePackageJSON(selection.data);
187224
if (typeof rawPmSpec === `undefined`)
188225
return {type: `NoSpec`, target: selection.manifestPath};
189226

190227
debugUtils.log(`${selection.manifestPath} defines ${rawPmSpec} as local package manager`);
191228

192-
const spec = parseSpec(rawPmSpec, path.relative(initialCwd, selection.manifestPath));
193229
return {
194230
type: `Found`,
195231
target: selection.manifestPath,
196-
spec,
232+
envFilePath,
233+
spec: parseSpec(rawPmSpec, path.relative(initialCwd, selection.manifestPath)),
197234
range: selection.data.devEngines?.packageManager?.version && {
198235
name: selection.data.devEngines.packageManager.name,
199236
range: selection.data.devEngines.packageManager.version,

sources/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,3 +160,5 @@ export interface LazyLocator {
160160
*/
161161
reference: () => Promise<string>;
162162
}
163+
164+
export type LocalEnvFile = Record<string, string | undefined>;

0 commit comments

Comments
 (0)