From 88f68071fc0fffc33d24d4e9059c50c8c0171577 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Fri, 24 Jun 2022 10:33:30 +0200 Subject: [PATCH 1/5] feat: download the latest version instead of a pinned one Fixes: https://github.com/nodejs/corepack/issues/93 --- sources/Engine.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/sources/Engine.ts b/sources/Engine.ts index e08bb1ee1..f9c105e25 100644 --- a/sources/Engine.ts +++ b/sources/Engine.ts @@ -7,6 +7,7 @@ import defaultConfig from '../config.js import * as corepackUtils from './corepackUtils'; import * as folderUtils from './folderUtils'; +import {fetchAsJson} from './httpUtils'; import * as semverUtils from './semverUtils'; import {Config, Descriptor, Locator} from './types'; import {SupportedPackageManagers, SupportedPackageManagerSet} from './types'; @@ -69,17 +70,16 @@ export class Engine { // Ignore errors; too bad } - if (typeof lastKnownGood !== `object` || lastKnownGood === null) - return definition.default; - - if (!Object.prototype.hasOwnProperty.call(lastKnownGood, packageManager)) - return definition.default; - - const override = (lastKnownGood as any)[packageManager]; - if (typeof override !== `string`) - return definition.default; + if (typeof lastKnownGood === `object` && lastKnownGood !== null && + Object.prototype.hasOwnProperty.call(lastKnownGood, packageManager)) { + const override = (lastKnownGood as any)[packageManager]; + if (typeof override === `string`) { + return override; + } + } - return override; + const latest = await fetchAsJson(`https://registry.npmjs.org/${packageManager}`); + return latest[`dist-tags`].latest; } async activatePackageManager(locator: Locator) { From 6c9e041693a0b3fc7c2b212f6a7d1f7def8429b6 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Sun, 26 Jun 2022 19:59:44 +0200 Subject: [PATCH 2/5] add `COREPACK_NO_LOOKUP` env flag --- README.md | 2 ++ sources/Engine.ts | 3 +++ tests/main.test.ts | 1 + 3 files changed, 6 insertions(+) diff --git a/README.md b/README.md index 6fc940814..07e5bf636 100644 --- a/README.md +++ b/README.md @@ -106,6 +106,8 @@ This command will retrieve the given package manager from the specified archive - `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 `corepack hydrate`). +- `COREPACK_NO_LOOKUP` can be set in order to instruct Corepack not to lookup on the remote registry for the latest version of the selected package manager. + - `COREPACK_HOME` can be set in order to define where Corepack should install the package managers. By default it is set to `$HOME/.node/corepack`. - `COREPACK_ROOT` has no functional impact on Corepack itself; it's automatically being set in your environment by Corepack when it shells out to the underlying package managers, so that they can feature-detect its presence (useful for commands like `yarn init`). diff --git a/sources/Engine.ts b/sources/Engine.ts index f9c105e25..e5d2ec53c 100644 --- a/sources/Engine.ts +++ b/sources/Engine.ts @@ -78,6 +78,9 @@ export class Engine { } } + if (process.env.COREPACK_NO_LOOKUP) + return definition.default; + const latest = await fetchAsJson(`https://registry.npmjs.org/${packageManager}`); return latest[`dist-tags`].latest; } diff --git a/tests/main.test.ts b/tests/main.test.ts index 24e9be813..f758c6788 100644 --- a/tests/main.test.ts +++ b/tests/main.test.ts @@ -6,6 +6,7 @@ import {runCli} from './_runCli'; beforeEach(async () => { process.env.COREPACK_HOME = npath.fromPortablePath(await xfs.mktempPromise()); + process.env.COREPACK_NO_LOOKUP = `1`; }); const testedPackageManagers: Array<[string, string]> = [ From 0668fb04ce7f61507050144beb5fbdbede220ce0 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Mon, 27 Jun 2022 18:53:24 +0200 Subject: [PATCH 3/5] Use KGR to cache the version --- README.md | 11 +++++++++-- sources/Engine.ts | 11 +++++++++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 07e5bf636..553f4ab36 100644 --- a/README.md +++ b/README.md @@ -46,9 +46,16 @@ Just use your package managers as you usually would. Run `yarn install` in Yarn ## Known Good Releases -When running Yarn or pnpm within projects that don't list a supported package manager, Corepack will default to a set of Known Good Releases. In a way, you can compare this to Node.js, where each version ships with a specific version of npm. +When running Corepack within projects that don't list a supported package +manager, it will default to a set of Known Good Releases. In a way, you can +compare this to Node.js, where each version ships with a specific version of npm. -The Known Good Releases can be updated system-wide using the `--activate` flag from the `corepack prepare` and `corepack hydrate` commands. +If there is no Known Good Release for the requested package manager, Corepack +looks up the npm registry for the latest available version and cache it for +future use. + +The Known Good Releases can be updated system-wide using the `--activate` flag +from the `corepack prepare` and `corepack hydrate` commands. ## Offline Workflow diff --git a/sources/Engine.ts b/sources/Engine.ts index e5d2ec53c..f98c049e2 100644 --- a/sources/Engine.ts +++ b/sources/Engine.ts @@ -81,8 +81,15 @@ export class Engine { if (process.env.COREPACK_NO_LOOKUP) return definition.default; - const latest = await fetchAsJson(`https://registry.npmjs.org/${packageManager}`); - return latest[`dist-tags`].latest; + const {[`dist-tags`]: {latest}, versions: {[latest]: {dist: {shasum}}}} = await fetchAsJson(`https://registry.npmjs.org/${packageManager}`); + const reference = `${latest}+sha1.${shasum}`; + + await this.activatePackageManager({ + name: packageManager, + reference, + }); + + return reference; } async activatePackageManager(locator: Locator) { From c33fa1d7de68b837038af23acfc5ccc0219b37b6 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Tue, 28 Jun 2022 17:05:25 +0200 Subject: [PATCH 4/5] don't hardcode the registry --- config.json | 12 ++++++++++++ sources/Engine.ts | 4 +--- sources/corepackUtils.ts | 17 +++++++++++++++++ sources/types.ts | 5 +++++ 4 files changed, 35 insertions(+), 3 deletions(-) diff --git a/config.json b/config.json index 593915097..afb6ccdac 100644 --- a/config.json +++ b/config.json @@ -2,6 +2,10 @@ "definitions": { "npm": { "default": "8.13.1", + "fetchLatestFrom": { + "type": "npm", + "package": "npm" + }, "transparent": { "commands": [ [ @@ -29,6 +33,10 @@ }, "pnpm": { "default": "7.3.0", + "fetchLatestFrom": { + "type": "npm", + "package": "pnpm" + }, "transparent": { "commands": [ [ @@ -71,6 +79,10 @@ }, "yarn": { "default": "1.22.19", + "fetchLatestFrom": { + "type": "npm", + "package": "yarn" + }, "transparent": { "default": "3.2.1", "commands": [ diff --git a/sources/Engine.ts b/sources/Engine.ts index f98c049e2..1ce9a1576 100644 --- a/sources/Engine.ts +++ b/sources/Engine.ts @@ -7,7 +7,6 @@ import defaultConfig from '../config.js import * as corepackUtils from './corepackUtils'; import * as folderUtils from './folderUtils'; -import {fetchAsJson} from './httpUtils'; import * as semverUtils from './semverUtils'; import {Config, Descriptor, Locator} from './types'; import {SupportedPackageManagers, SupportedPackageManagerSet} from './types'; @@ -81,8 +80,7 @@ export class Engine { if (process.env.COREPACK_NO_LOOKUP) return definition.default; - const {[`dist-tags`]: {latest}, versions: {[latest]: {dist: {shasum}}}} = await fetchAsJson(`https://registry.npmjs.org/${packageManager}`); - const reference = `${latest}+sha1.${shasum}`; + const reference = await corepackUtils.fetchLatestStableVersion(definition.fetchLatestFrom); await this.activatePackageManager({ name: packageManager, diff --git a/sources/corepackUtils.ts b/sources/corepackUtils.ts index acd74e575..09c31ec93 100644 --- a/sources/corepackUtils.ts +++ b/sources/corepackUtils.ts @@ -9,6 +9,23 @@ import * as httpUtils from './httpUtils import * as nodeUtils from './nodeUtils'; import {RegistrySpec, Descriptor, Locator, PackageManagerSpec} from './types'; +export async function fetchLatestStableVersion(spec: RegistrySpec) { + switch (spec.type) { + case `npm`: { + const {[`dist-tags`]: {latest}, versions: {[latest]: {dist: {shasum}}}} = + await httpUtils.fetchAsJson(`https://registry.npmjs.org/${spec.package}`); + return `${latest}+sha1.${shasum}`; + } + case `url`: { + const data = await httpUtils.fetchAsJson(spec.url); + return data[spec.fields.tags].stable; + } + default: { + throw new Error(`Unsupported specification ${JSON.stringify(spec)}`); + } + } +} + export async function fetchAvailableTags(spec: RegistrySpec): Promise> { switch (spec.type) { case `npm`: { diff --git a/sources/types.ts b/sources/types.ts index 8f19f294c..8ed2cfd19 100644 --- a/sources/types.ts +++ b/sources/types.ts @@ -62,6 +62,11 @@ export interface Config { */ default: string; + /** + * Defines how to fetch the latest version from a remote registry. + */ + fetchLatestFrom: RegistrySpec; + /** * Defines a set of commands that are fine to run even if the user isn't * in a project configured for the specified package manager. For instance, From 4ce5e2c23a1e087e0a50749588d49236058fdbda Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Sat, 23 Jul 2022 15:07:28 +0200 Subject: [PATCH 5/5] `COREPACK_NO_LOOKUP` -> `COREPACK_DEFAULT_TO_LATEST` --- README.md | 4 +++- sources/Engine.ts | 3 ++- tests/main.test.ts | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 553f4ab36..f1a3b9bf1 100644 --- a/README.md +++ b/README.md @@ -113,7 +113,9 @@ This command will retrieve the given package manager from the specified archive - `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 `corepack hydrate`). -- `COREPACK_NO_LOOKUP` can be set in order to instruct Corepack not to lookup on the remote registry for the latest version of the selected package manager. +- `COREPACK_DEFAULT_TO_LATEST` can be set to `0` in order to instruct Corepack + not to lookup on the remote registry for the latest version of the selected + package manager. - `COREPACK_HOME` can be set in order to define where Corepack should install the package managers. By default it is set to `$HOME/.node/corepack`. diff --git a/sources/Engine.ts b/sources/Engine.ts index e7b2c3a19..93dc7faf8 100644 --- a/sources/Engine.ts +++ b/sources/Engine.ts @@ -1,6 +1,7 @@ import {UsageError} from 'clipanion'; import fs from 'fs'; import path from 'path'; +import process from 'process'; import semver from 'semver'; import defaultConfig from '../config.json'; @@ -77,7 +78,7 @@ export class Engine { } } - if (process.env.COREPACK_NO_LOOKUP) + if (process.env.COREPACK_DEFAULT_TO_LATEST === `0`) return definition.default; const reference = await corepackUtils.fetchLatestStableVersion(definition.fetchLatestFrom); diff --git a/tests/main.test.ts b/tests/main.test.ts index 33df5c4cb..2d93a013b 100644 --- a/tests/main.test.ts +++ b/tests/main.test.ts @@ -6,7 +6,7 @@ import {runCli} from './_runCli'; beforeEach(async () => { process.env.COREPACK_HOME = npath.fromPortablePath(await xfs.mktempPromise()); - process.env.COREPACK_NO_LOOKUP = `1`; + process.env.COREPACK_DEFAULT_TO_LATEST = `0`; }); it(`should refuse to download a package manager if the hash doesn't match`, async () => {