From 6b8d87f2374f79855b24d659f2a2579d6b39f54f Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Tue, 30 Jan 2024 23:38:33 +0100 Subject: [PATCH] feat!: prompt user before downloading software (#360) --- README.md | 8 ++++++++ mkshims.ts | 2 ++ sources/httpUtils.ts | 24 +++++++++++++++++++++--- 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 18bcf88e9..5037b06f5 100644 --- a/README.md +++ b/README.md @@ -223,6 +223,14 @@ same major line. Should you need to upgrade to a new major, use an explicit not to lookup on the remote registry for the latest version of the selected package manager. +- `COREPACK_ENABLE_DOWNLOAD_PROMPT` can be set to `0` to + prevent Corepack showing the URL when it needs to download software, or can be + set to `1` to have the URL shown. By default, when Corepack is called + explicitly (e.g. `corepack pnpm …`), it is set to `0`; when Corepack is called + implicitely (e.g. `pnpm …`), it is set to `1`. + When standard input is a TTY and no CI environment is detected, Corepack will + ask for user input before starting the download. + - `COREPACK_ENABLE_NETWORK` can be set to `0` to prevent Corepack from accessing the network (in which case you'll be responsible for hydrating the package manager versions that will be required for the projects you'll run, using diff --git a/mkshims.ts b/mkshims.ts index fde6dacac..4adcd62fe 100644 --- a/mkshims.ts +++ b/mkshims.ts @@ -21,6 +21,7 @@ async function main() { const corepackPath = path.join(distDir, `corepack.js`); fs.writeFileSync(corepackPath, [ `#!/usr/bin/env node`, + `process.env.COREPACK_ENABLE_DOWNLOAD_PROMPT??='0';`, `require('./lib/corepack.cjs').runMain(process.argv.slice(2));`, ].join(`\n`)); fs.chmodSync(corepackPath, 0o755); @@ -32,6 +33,7 @@ async function main() { const entryPath = path.join(distDir, `${binaryName}.js`); const entryScript = [ `#!/usr/bin/env node`, + `process.env.COREPACK_ENABLE_DOWNLOAD_PROMPT??='1'`, `require('./lib/corepack.cjs').runMain(['${binaryName}', ...process.argv.slice(2)]);`, ].join(`\n`); diff --git a/sources/httpUtils.ts b/sources/httpUtils.ts index 94598dfbe..dc73b96c3 100644 --- a/sources/httpUtils.ts +++ b/sources/httpUtils.ts @@ -1,6 +1,8 @@ -import {UsageError} from 'clipanion'; -import {RequestOptions} from 'https'; -import {IncomingMessage, ClientRequest} from 'http'; +import {UsageError} from 'clipanion'; +import {once} from 'events'; +import type {RequestOptions} from 'https'; +import type {IncomingMessage, ClientRequest} from 'http'; +import {stderr, stdin} from 'process'; export async function fetchUrlStream(url: string, options: RequestOptions = {}) { if (process.env.COREPACK_ENABLE_NETWORK === `0`) @@ -12,6 +14,22 @@ export async function fetchUrlStream(url: string, options: RequestOptions = {}) const proxyAgent = new ProxyAgent(); + if (process.env.COREPACK_ENABLE_DOWNLOAD_PROMPT === `1`) { + console.error(`Corepack is about to download ${url}.`); + if (stdin.isTTY && !process.env.CI) { + stderr.write(`\nDo you want to continue? [Y/n] `); + stdin.resume(); + const chars = await once(stdin, `data`); + stdin.pause(); + if ( + chars[0][0] === 0x6e || // n + chars[0][0] === 0x4e // N + ) { + throw new UsageError(`Aborted by the user`); + } + } + } + return new Promise((resolve, reject) => { const createRequest = (url: string) => { const request: ClientRequest = https.get(url, {...options, agent: proxyAgent}, response => {