From d29d55664f723b222d5e03a3d02db22fca3b8877 Mon Sep 17 00:00:00 2001 From: zanminkian Date: Thu, 10 Apr 2025 08:19:15 +0000 Subject: [PATCH 1/5] feat: COREPACK_NPM_REGISTRY defaults to the regitry of npm --- README.md | 4 +++- package.json | 1 + sources/corepackUtils.ts | 13 +++++++------ sources/httpUtils.ts | 4 ++-- sources/npmRegistryUtils.ts | 15 +++++++++++++-- yarn.lock | 25 +++++++++++++++++++++++++ 6 files changed, 51 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 67d396d5c..3b24c3508 100644 --- a/README.md +++ b/README.md @@ -341,7 +341,9 @@ same major line. Should you need to upgrade to a new major, use an explicit (useful for commands like `yarn init`). - `COREPACK_NPM_REGISTRY` sets the registry base url used when retrieving - package managers from npm. Default value is `https://registry.npmjs.org` + package managers from npm. Default value is the result of command + `npm config get registry`, which is typically `https://registry.npmjs.org` + unless it has been customized. - `COREPACK_NPM_TOKEN` sets a Bearer token authorization header when connecting to a npm type registry. diff --git a/package.json b/package.json index 6d8f3dd15..1b7edefb6 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "esbuild": "^0.25.0", "eslint": "^9.22.0", "proxy-from-env": "^1.1.0", + "registry-url": "^7.1.0", "semver": "^7.6.3", "supports-color": "^10.0.0", "tar": "^7.4.0", diff --git a/sources/corepackUtils.ts b/sources/corepackUtils.ts index 50a808808..05926967d 100644 --- a/sources/corepackUtils.ts +++ b/sources/corepackUtils.ts @@ -224,6 +224,7 @@ export async function installVersion(installTarget: string, locator: Locator, {s let signatures: Array<{keyid: string, sig: string}>; let integrity: string; let binPath: string | null = null; + const registryUrl = npmRegistryUtils.getRegistryUrl(); if (locatorIsASupportedPackageManager) { url = spec.url.replace(`{}`, version); if (process.env.COREPACK_NPM_REGISTRY) { @@ -234,17 +235,17 @@ export async function installVersion(installTarget: string, locator: Locator, {s binPath = registry.bin; } } - url = url.replace( - npmRegistryUtils.DEFAULT_NPM_REGISTRY_URL, - () => process.env.COREPACK_NPM_REGISTRY!, - ); } + url = url.replace( + npmRegistryUtils.DEFAULT_NPM_REGISTRY_URL, + () => registryUrl, + ); } else { url = decodeURIComponent(version); - if (process.env.COREPACK_NPM_REGISTRY && url.startsWith(npmRegistryUtils.DEFAULT_NPM_REGISTRY_URL)) { + if (url.startsWith(npmRegistryUtils.DEFAULT_NPM_REGISTRY_URL)) { url = url.replace( npmRegistryUtils.DEFAULT_NPM_REGISTRY_URL, - () => process.env.COREPACK_NPM_REGISTRY!, + () => registryUrl, ); } } diff --git a/sources/httpUtils.ts b/sources/httpUtils.ts index 6c2dc9040..8f1313fd8 100644 --- a/sources/httpUtils.ts +++ b/sources/httpUtils.ts @@ -4,7 +4,7 @@ import {once} from 'events'; import {stderr, stdin} from 'process'; import {Readable} from 'stream'; -import {DEFAULT_NPM_REGISTRY_URL} from './npmRegistryUtils'; +import {getRegistryUrl} from './npmRegistryUtils'; async function fetch(input: string | URL, init?: RequestInit) { if (process.env.COREPACK_ENABLE_NETWORK === `0`) @@ -29,7 +29,7 @@ async function fetch(input: string | URL, init?: RequestInit) { input.username = input.password = ``; } - if (input.origin === (process.env.COREPACK_NPM_REGISTRY || DEFAULT_NPM_REGISTRY_URL) && process.env.COREPACK_NPM_TOKEN) { + if (process.env.COREPACK_NPM_TOKEN && input.origin === getRegistryUrl()) { headers = { ...headers, authorization: `Bearer ${process.env.COREPACK_NPM_TOKEN}`, diff --git a/sources/npmRegistryUtils.ts b/sources/npmRegistryUtils.ts index a7d110e95..46599f31d 100644 --- a/sources/npmRegistryUtils.ts +++ b/sources/npmRegistryUtils.ts @@ -1,5 +1,6 @@ import {UsageError} from 'clipanion'; import {createVerify} from 'crypto'; +import registryUrl, {defaultUrl} from 'registry-url'; import defaultConfig from '../config.json'; @@ -11,10 +12,20 @@ import * as httpUtils from './httpUtils'; export const DEFAULT_HEADERS: Record = { [`Accept`]: `application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8`, }; -export const DEFAULT_NPM_REGISTRY_URL = `https://registry.npmjs.org`; + +const normalize = (url: string) => url.endsWith(`/`) ? url.slice(0, -1) : url; + +export const DEFAULT_NPM_REGISTRY_URL = normalize(defaultUrl); + +export function getRegistryUrl() { + if (process.env.COREPACK_NPM_REGISTRY) { + return process.env.COREPACK_NPM_REGISTRY; + } + return normalize(registryUrl()); +} export async function fetchAsJson(packageName: string, version?: string) { - const npmRegistryUrl = process.env.COREPACK_NPM_REGISTRY || DEFAULT_NPM_REGISTRY_URL; + const npmRegistryUrl = getRegistryUrl(); if (process.env.COREPACK_ENABLE_NETWORK === `0`) throw new UsageError(`Network access disabled by the environment; can't reach npm repository ${npmRegistryUrl}`); diff --git a/yarn.lock b/yarn.lock index b2a26d9ad..b0f8ab41d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1315,6 +1315,7 @@ __metadata: esbuild: "npm:^0.25.0" eslint: "npm:^9.22.0" proxy-from-env: "npm:^1.1.0" + registry-url: "npm:^7.1.0" semver: "npm:^7.6.3" supports-color: "npm:^10.0.0" tar: "npm:^7.4.0" @@ -2003,6 +2004,13 @@ __metadata: languageName: node linkType: hard +"find-up-simple@npm:^1.0.1": + version: 1.0.1 + resolution: "find-up-simple@npm:1.0.1" + checksum: 10c0/ad34de157b7db925d50ff78302fefb28e309f3bc947c93ffca0f9b0bccf9cf1a2dc57d805d5c94ec9fc60f4838f5dbdfd2a48ecd77c23015fa44c6dd5f60bc40 + languageName: node + linkType: hard + "find-up@npm:^5.0.0": version: 5.0.0 resolution: "find-up@npm:5.0.0" @@ -2377,6 +2385,13 @@ __metadata: languageName: node linkType: hard +"ini@npm:^5.0.0": + version: 5.0.0 + resolution: "ini@npm:5.0.0" + checksum: 10c0/657491ce766cbb4b335ab221ee8f72b9654d9f0e35c32fe5ff2eb7ab8c5ce72237ff6456555b50cde88e6507a719a70e28e327b450782b4fc20c90326ec8c1a8 + languageName: node + linkType: hard + "ini@npm:~1.3.0": version: 1.3.8 resolution: "ini@npm:1.3.8" @@ -3443,6 +3458,16 @@ __metadata: languageName: node linkType: hard +"registry-url@npm:^7.1.0": + version: 7.1.0 + resolution: "registry-url@npm:7.1.0" + dependencies: + find-up-simple: "npm:^1.0.1" + ini: "npm:^5.0.0" + checksum: 10c0/b9ccddc4d079cc3e6cc9b137aef1a71aaab02d04785890b8f86d1b63eb158e0198e17a444b3bba2986eadc0beff18fae1be058665b4197deec9422650100fb24 + languageName: node + linkType: hard + "resolve-from@npm:^4.0.0": version: 4.0.0 resolution: "resolve-from@npm:4.0.0" From 1435643a31062c3a85a68cb37b90d49a7561a007 Mon Sep 17 00:00:00 2001 From: zanminkian Date: Mon, 14 Apr 2025 03:19:55 +0000 Subject: [PATCH 2/5] test: fix testing error --- tests/npmRegistryUtils.test.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/npmRegistryUtils.test.ts b/tests/npmRegistryUtils.test.ts index 728d6f977..b90d54fb7 100644 --- a/tests/npmRegistryUtils.test.ts +++ b/tests/npmRegistryUtils.test.ts @@ -20,6 +20,7 @@ describe(`npm registry utils fetchAsJson`, () => { }); it(`loads from DEFAULT_NPM_REGISTRY_URL by default`, async () => { + process.env.npm_config_registry = `https://registry.npmjs.org`; await fetchAsJson(`package-name`); expect(httpFetchAsJson).toBeCalled(); @@ -38,6 +39,7 @@ describe(`npm registry utils fetchAsJson`, () => { it(`adds authorization header with bearer token if COREPACK_NPM_TOKEN is set`, async () => { // `process.env` is reset after each tests in setupTests.js. process.env.COREPACK_NPM_TOKEN = `foo`; + process.env.npm_config_registry = `https://registry.npmjs.org`; await fetchAsJson(`package-name`); @@ -53,6 +55,7 @@ describe(`npm registry utils fetchAsJson`, () => { process.env.COREPACK_NPM_TOKEN = `foo`; process.env.COREPACK_NPM_USERNAME = `bar`; process.env.COREPACK_NPM_PASSWORD = `foobar`; + process.env.npm_config_registry = `https://registry.npmjs.org`; await fetchAsJson(`package-name`); @@ -68,6 +71,7 @@ describe(`npm registry utils fetchAsJson`, () => { // `process.env` is reset after each tests in setupTests.js. process.env.COREPACK_NPM_USERNAME = `foo`; process.env.COREPACK_NPM_PASSWORD = `bar`; + process.env.npm_config_registry = `https://registry.npmjs.org`; const encodedCreds = Buffer.from(`${process.env.COREPACK_NPM_USERNAME}:${process.env.COREPACK_NPM_PASSWORD}`, `utf8`).toString(`base64`); @@ -80,9 +84,10 @@ describe(`npm registry utils fetchAsJson`, () => { }}); }); - it(`does not add authorization header if COREPACK_NPM_USERNAME is set and COREPACK_NPM_PASSWORD is not.`, async () => { + it(`does not add authorization header if COREPACK_NPM_USERNAME is set and COREPACK_NPM_PASSWORD is not`, async () => { // `process.env` is reset after each tests in setupTests.js. process.env.COREPACK_NPM_USERNAME = `foo`; + process.env.npm_config_registry = `https://registry.npmjs.org`; await fetchAsJson(`package-name`); From b6b489ab80d174b6ac655ea436e3ae54e9226f55 Mon Sep 17 00:00:00 2001 From: zanminkian Date: Mon, 14 Apr 2025 04:07:31 +0000 Subject: [PATCH 3/5] test: add more test --- tests/main.test.ts | 24 ++++++++++++++++++++++++ tests/npmRegistryUtils.test.ts | 23 +++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/tests/main.test.ts b/tests/main.test.ts index 256008943..6e525c1f3 100644 --- a/tests/main.test.ts +++ b/tests/main.test.ts @@ -1314,6 +1314,30 @@ it(`should download latest pnpm from custom registry`, async () => { }); }); +it(`should download from npm registry url by default`, async () => { + await xfs.mktempPromise(async cwd => { + process.env.npm_config_registry = `https://registry.npmmirror.com`; + process.env.COREPACK_ENABLE_DOWNLOAD_PROMPT = `1`; + + await xfs.writeJsonPromise(ppath.join(cwd, `package.json` as Filename), { + packageManager: `pnpm@10.1.0`, + }); + + await expect(runCli(cwd, [`pnpm`, `--version`])).resolves.toMatchObject({ + exitCode: 0, + stdout: `10.1.0\n`, + stderr: `! Corepack is about to download https://registry.npmmirror.com/pnpm/-/pnpm-10.1.0.tgz\n`, + }); + + // Should keep working with cache + await expect(runCli(cwd, [`pnpm`, `--version`])).resolves.toMatchObject({ + exitCode: 0, + stdout: `10.1.0\n`, + stderr: ``, + }); + }); +}); + describe(`should pick up COREPACK_INTEGRITY_KEYS from env`, () => { beforeEach(() => { process.env.AUTH_TYPE = `COREPACK_NPM_TOKEN`; // See `_registryServer.mjs` diff --git a/tests/npmRegistryUtils.test.ts b/tests/npmRegistryUtils.test.ts index b90d54fb7..752505ff3 100644 --- a/tests/npmRegistryUtils.test.ts +++ b/tests/npmRegistryUtils.test.ts @@ -36,6 +36,15 @@ describe(`npm registry utils fetchAsJson`, () => { expect(httpFetchAsJson).lastCalledWith(`${process.env.COREPACK_NPM_REGISTRY}/package-name`, {headers: DEFAULT_HEADERS}); }); + it(`loads from npm registry url`, async () => { + // `process.env` is reset after each tests in setupTests.js. + process.env.npm_config_registry = `https://registry.example.org`; + await fetchAsJson(`package-name`); + + expect(httpFetchAsJson).toBeCalled(); + expect(httpFetchAsJson).lastCalledWith(`${process.env.npm_config_registry}/package-name`, {headers: DEFAULT_HEADERS}); + }); + it(`adds authorization header with bearer token if COREPACK_NPM_TOKEN is set`, async () => { // `process.env` is reset after each tests in setupTests.js. process.env.COREPACK_NPM_TOKEN = `foo`; @@ -50,6 +59,20 @@ describe(`npm registry utils fetchAsJson`, () => { }}); }); + it(`uses npm registry url if COREPACK_NPM_TOKEN is set`, async () => { + // `process.env` is reset after each tests in setupTests.js. + process.env.COREPACK_NPM_TOKEN = `foo`; + process.env.npm_config_registry = `https://registry.example.org`; + + await fetchAsJson(`package-name`); + + expect(httpFetchAsJson).toBeCalled(); + expect(httpFetchAsJson).lastCalledWith(`${process.env.npm_config_registry}/package-name`, {headers: { + ...DEFAULT_HEADERS, + authorization: `Bearer ${process.env.COREPACK_NPM_TOKEN}`, + }}); + }); + it(`only adds authorization header with bearer token if COREPACK_NPM_TOKEN and COREPACK_NPM_USERNAME are set`, async () => { // `process.env` is reset after each tests in setupTests.js. process.env.COREPACK_NPM_TOKEN = `foo`; From 80ce9a0c0b817d3db347004668f04694b0937b30 Mon Sep 17 00:00:00 2001 From: zanminkian Date: Mon, 14 Apr 2025 04:08:14 +0000 Subject: [PATCH 4/5] style: format --- sources/httpUtils.ts | 12 ++++++------ sources/npmRegistryUtils.ts | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/sources/httpUtils.ts b/sources/httpUtils.ts index 8f1313fd8..71411bda4 100644 --- a/sources/httpUtils.ts +++ b/sources/httpUtils.ts @@ -1,10 +1,10 @@ -import assert from 'assert'; -import {UsageError} from 'clipanion'; -import {once} from 'events'; -import {stderr, stdin} from 'process'; -import {Readable} from 'stream'; +import assert from 'assert'; +import {UsageError} from 'clipanion'; +import {once} from 'events'; +import {stderr, stdin} from 'process'; +import {Readable} from 'stream'; -import {getRegistryUrl} from './npmRegistryUtils'; +import {getRegistryUrl} from './npmRegistryUtils'; async function fetch(input: string | URL, init?: RequestInit) { if (process.env.COREPACK_ENABLE_NETWORK === `0`) diff --git a/sources/npmRegistryUtils.ts b/sources/npmRegistryUtils.ts index 46599f31d..8e503159a 100644 --- a/sources/npmRegistryUtils.ts +++ b/sources/npmRegistryUtils.ts @@ -18,9 +18,9 @@ const normalize = (url: string) => url.endsWith(`/`) ? url.slice(0, -1) : url; export const DEFAULT_NPM_REGISTRY_URL = normalize(defaultUrl); export function getRegistryUrl() { - if (process.env.COREPACK_NPM_REGISTRY) { + if (process.env.COREPACK_NPM_REGISTRY) return process.env.COREPACK_NPM_REGISTRY; - } + return normalize(registryUrl()); } From 8c074274a56f04cd7b60d5ef20bd7bdda0f06ec3 Mon Sep 17 00:00:00 2001 From: zanminkian Date: Wed, 16 Apr 2025 03:22:49 +0000 Subject: [PATCH 5/5] chore: upgrade deps --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 1b7edefb6..ac6242b4d 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "esbuild": "^0.25.0", "eslint": "^9.22.0", "proxy-from-env": "^1.1.0", - "registry-url": "^7.1.0", + "registry-url": "^7.2.0", "semver": "^7.6.3", "supports-color": "^10.0.0", "tar": "^7.4.0", diff --git a/yarn.lock b/yarn.lock index b0f8ab41d..4b0d03c77 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1315,7 +1315,7 @@ __metadata: esbuild: "npm:^0.25.0" eslint: "npm:^9.22.0" proxy-from-env: "npm:^1.1.0" - registry-url: "npm:^7.1.0" + registry-url: "npm:^7.2.0" semver: "npm:^7.6.3" supports-color: "npm:^10.0.0" tar: "npm:^7.4.0" @@ -3458,13 +3458,13 @@ __metadata: languageName: node linkType: hard -"registry-url@npm:^7.1.0": - version: 7.1.0 - resolution: "registry-url@npm:7.1.0" +"registry-url@npm:^7.2.0": + version: 7.2.0 + resolution: "registry-url@npm:7.2.0" dependencies: find-up-simple: "npm:^1.0.1" ini: "npm:^5.0.0" - checksum: 10c0/b9ccddc4d079cc3e6cc9b137aef1a71aaab02d04785890b8f86d1b63eb158e0198e17a444b3bba2986eadc0beff18fae1be058665b4197deec9422650100fb24 + checksum: 10c0/176c4054f6b9f3ad38b9a682f87c44c1ff56046921fdd66b3af9ff81f4a4d512d6cb5f7a8f3213824c9bbb5838334a176321b000488bbf5342bcda7b258a47ec languageName: node linkType: hard