fix: download Node.js from nodejs.org (#3475)

This commit is contained in:
Zoltan Kochan
2021-05-26 15:22:55 +03:00
committed by GitHub
parent 6310770a9a
commit 6d2ccc9a34
10 changed files with 164 additions and 34 deletions

View File

@@ -0,0 +1,5 @@
---
"@pnpm/plugin-commands-nvm": patch
---
Download Node.js from nodejs.org, not from the npm registry.

View File

@@ -0,0 +1,5 @@
---
"@pnpm/tarball-fetcher": minor
---
Export waitForFilesIndex().

View File

@@ -30,15 +30,23 @@
"homepage": "https://github.com/pnpm/pnpm/blob/master/packages/plugin-commands-nvm#readme",
"dependencies": {
"@pnpm/cli-utils": "workspace:0.6.5",
"@pnpm/config": "workspace:12.3.0",
"@pnpm/fetch": "workspace:3.1.0",
"@pnpm/package-store": "workspace:12.0.7",
"@pnpm/store-path": "^5.0.0",
"@pnpm/tarball-fetcher": "workspace:9.2.2",
"adm-zip": "^0.5.5",
"execa": "^5.0.0",
"load-json-file": "^6.2.0",
"path-name": "^1.0.0",
"rename-overwrite": "^4.0.0",
"render-help": "^1.0.1",
"tempy": "^1.0.0",
"write-json-file": "^4.3.0"
},
"funding": "https://opencollective.com/pnpm",
"devDependencies": {
"@pnpm/prepare": "workspace:0.0.23"
"@pnpm/prepare": "workspace:0.0.23",
"@types/adm-zip": "^0.4.34"
}
}

View File

@@ -1,10 +1,17 @@
import fs from 'fs'
import path from 'path'
import { docsUrl } from '@pnpm/cli-utils'
import fetch from '@pnpm/fetch'
import { Config } from '@pnpm/config'
import fetch, { createFetchFromRegistry, FetchFromRegistry } from '@pnpm/fetch'
import { createCafsStore } from '@pnpm/package-store'
import storePath from '@pnpm/store-path'
import createFetcher, { waitForFilesIndex } from '@pnpm/tarball-fetcher'
import AdmZip from 'adm-zip'
import execa from 'execa'
import PATH from 'path-name'
import renameOverwrite from 'rename-overwrite'
import renderHelp from 'render-help'
import tempy from 'tempy'
import loadJsonFile from 'load-json-file'
import writeJsonFile from 'write-json-file'
@@ -25,19 +32,39 @@ export function help () {
})
}
export type NvmNodeCommandOptions = Pick<Config,
| 'rawConfig'
| 'fetchRetries'
| 'fetchRetryFactor'
| 'fetchRetryMaxtimeout'
| 'fetchRetryMintimeout'
| 'fetchRetryMintimeout'
| 'fetchTimeout'
| 'userAgent'
| 'ca'
| 'cert'
| 'httpProxy'
| 'httpsProxy'
| 'key'
| 'localAddress'
| 'noProxy'
| 'strictSsl'
| 'storeDir'
| 'useNodeVersion'
| 'pnpmHomeDir'
>
export async function handler (
opts: {
opts: NvmNodeCommandOptions & {
argv: {
original: string[]
}
useNodeVersion?: string
pnpmHomeDir: string
}
) {
const nodeDir = await getNodeDir(opts.pnpmHomeDir, opts.useNodeVersion)
const nodeDir = await getNodeDir(opts)
const { exitCode } = await execa('node', opts.argv.original.slice(1), {
env: {
[PATH]: `${path.join(nodeDir, 'node_modules/.bin')}${path.delimiter}${process.env[PATH]!}`,
[PATH]: `${nodeDir}${path.delimiter}${process.env[PATH]!}`,
},
stdout: 'inherit',
stdin: 'inherit',
@@ -45,9 +72,9 @@ export async function handler (
return { exitCode }
}
export async function getNodeDir (pnpmHomeDir: string, nodeVersion?: string) {
const nodesDir = path.join(pnpmHomeDir, 'nodes')
let wantedNodeVersion = nodeVersion ?? (await readNodeVersionsManifest(nodesDir))?.default
export async function getNodeDir (opts: NvmNodeCommandOptions) {
const nodesDir = path.join(opts.pnpmHomeDir, 'nodes')
let wantedNodeVersion = opts.useNodeVersion ?? (await readNodeVersionsManifest(nodesDir))?.default
await fs.promises.mkdir(nodesDir, { recursive: true })
fs.writeFileSync(path.join(nodesDir, 'pnpm-workspace.yaml'), '', 'utf8')
if (wantedNodeVersion == null) {
@@ -62,27 +89,72 @@ export async function getNodeDir (pnpmHomeDir: string, nodeVersion?: string) {
}
const versionDir = path.join(nodesDir, wantedNodeVersion)
if (!fs.existsSync(versionDir)) {
await installNode(wantedNodeVersion, versionDir)
await installNode(wantedNodeVersion, versionDir, opts)
}
return versionDir
return process.platform === 'win32' ? versionDir : path.join(versionDir, 'bin')
}
async function installNode (wantedNodeVersion: string, versionDir: string) {
async function installNode (wantedNodeVersion: string, versionDir: string, opts: NvmNodeCommandOptions) {
await fs.promises.mkdir(versionDir, { recursive: true })
await writeJsonFile(path.join(versionDir, 'package.json'), {})
const { exitCode } = await execa('pnpm', ['add', '--use-stderr', `${getNodePkgName()}@${wantedNodeVersion}`], {
cwd: versionDir,
stdout: 'inherit',
})
if (exitCode !== 0) {
throw new Error(`Couldn't install Node.js ${wantedNodeVersion}`)
const { tarball, pkgName } = getNodeJSTarball(wantedNodeVersion)
const fetchFromRegistry = createFetchFromRegistry(opts)
if (tarball.endsWith('.zip')) {
await downloadAndUnpackZip(fetchFromRegistry, tarball, versionDir, pkgName)
return
}
const getCredentials = () => ({ authHeaderValue: undefined, alwaysAuth: undefined })
const fetch = createFetcher(fetchFromRegistry, getCredentials, {
retry: {
maxTimeout: opts.fetchRetryMaxtimeout,
minTimeout: opts.fetchRetryMintimeout,
retries: opts.fetchRetries,
factor: opts.fetchRetryFactor,
},
timeout: opts.fetchTimeout,
})
const storeDir = await storePath(process.cwd(), opts.storeDir)
const cafsDir = path.join(storeDir, 'files')
const cafs = createCafsStore(cafsDir)
const { filesIndex } = await fetch.tarball(cafs, { tarball }, {
lockfileDir: process.cwd(),
})
await cafs.importPackage(versionDir, {
filesResponse: {
filesIndex: await waitForFilesIndex(filesIndex),
fromStore: false,
},
force: true,
})
}
function getNodePkgName () {
async function downloadAndUnpackZip (
fetchFromRegistry: FetchFromRegistry,
zipUrl: string,
targetDir: string,
pkgName: string
) {
const response = await fetchFromRegistry(zipUrl)
const tmp = path.join(tempy.directory(), 'pnpm.zip')
const dest = fs.createWriteStream(tmp)
await new Promise((resolve, reject) => {
response.body.pipe(dest).on('error', reject).on('close', resolve)
})
const zip = new AdmZip(tmp)
const nodeDir = path.dirname(targetDir)
zip.extractAllTo(nodeDir, true)
await renameOverwrite(path.join(nodeDir, pkgName), targetDir)
await fs.promises.unlink(tmp)
}
function getNodeJSTarball (nodeVersion: string) {
const platform = process.platform === 'win32' ? 'win' : process.platform
const arch = platform === 'win' && process.arch === 'ia32' ? 'x86' : process.arch
return `node-${platform}-${arch}`
const extension = platform === 'win' ? 'zip' : 'tar.gz'
const pkgName = `node-v${nodeVersion}-${platform}-${arch}`
return {
pkgName,
tarball: `https://nodejs.org/download/release/v${nodeVersion}/${pkgName}.${extension}`,
}
}
async function readNodeVersionsManifest (nodesDir: string): Promise<{ default?: string }> {

View File

@@ -10,6 +10,7 @@ test('run specific version of Node.js', async () => {
},
useNodeVersion: '14.0.0',
pnpmHomeDir: process.cwd(),
rawConfig: {},
})
expect(exitCode).toBe(0)
expect(fs.readFileSync('version', 'utf8')).toBe('v14.0.0')
@@ -22,6 +23,7 @@ test('run LTS version of Node.js by default', async () => {
original: ['node', '-e', 'require("fs").writeFileSync("version",process.version, "utf8")'],
},
pnpmHomeDir: process.cwd(),
rawConfig: {},
})
expect(exitCode).toBe(0)
expect(fs.readFileSync('version', 'utf8')).toMatch(/^v[0-9]+\.[0-9]+\.[0-9]+$/)

View File

@@ -15,8 +15,17 @@
{
"path": "../cli-utils"
},
{
"path": "../config"
},
{
"path": "../fetch"
},
{
"path": "../package-store"
},
{
"path": "../tarball-fetcher"
}
]
}

View File

@@ -228,7 +228,7 @@ export default async function run (inputArgv: string[]) {
try {
if (config.useNodeVersion != null) {
const nodePath = path.join(await node.getNodeDir(config.pnpmHomeDir, config.useNodeVersion), 'node_modules/.bin')
const nodePath = await node.getNodeDir(config)
config.extraBinPaths.push(nodePath)
}
let result = pnpmCmds[cmd ?? 'help'](

View File

@@ -211,9 +211,8 @@ function isGitHostedPkgUrl (url: string) {
) && url.includes('tar.gz')
}
async function prepareGitHostedPkg (filesIndex: FilesIndex, cafs: Cafs) {
const tempLocation = await cafs.tempDir()
const filesIndexReady: Record<string, PackageFileInfo> = R.fromPairs(
export async function waitForFilesIndex (filesIndex: FilesIndex): Promise<Record<string, PackageFileInfo>> {
return R.fromPairs(
await Promise.all(
Object.entries(filesIndex).map(async ([fileName, fileInfo]): Promise<[string, PackageFileInfo]> => {
const { integrity, checkedAt } = await fileInfo.writeResult
@@ -228,9 +227,13 @@ async function prepareGitHostedPkg (filesIndex: FilesIndex, cafs: Cafs) {
})
)
)
}
async function prepareGitHostedPkg (filesIndex: FilesIndex, cafs: Cafs) {
const tempLocation = await cafs.tempDir()
await cafs.importPackage(tempLocation, {
filesResponse: {
filesIndex: filesIndexReady,
filesIndex: await waitForFilesIndex(filesIndex),
fromStore: false,
},
force: true,

View File

@@ -17,11 +17,12 @@ import ssri from 'ssri'
import createDownloader, {
DownloadFunction,
TarballIntegrityError,
waitForFilesIndex,
} from './createDownloader'
export { BadTarballError } from './errorTypes'
export { TarballIntegrityError }
export { TarballIntegrityError, waitForFilesIndex }
export default function (
fetchFromRegistry: FetchFromRegistry,

37
pnpm-lock.yaml generated
View File

@@ -1928,25 +1928,41 @@ importers:
packages/plugin-commands-nvm:
specifiers:
'@pnpm/cli-utils': workspace:0.6.5
'@pnpm/config': workspace:12.3.0
'@pnpm/fetch': workspace:3.1.0
'@pnpm/package-store': workspace:12.0.7
'@pnpm/plugin-commands-nvm': 'link:'
'@pnpm/prepare': workspace:0.0.23
'@pnpm/store-path': ^5.0.0
'@pnpm/tarball-fetcher': workspace:9.2.2
'@types/adm-zip': ^0.4.34
adm-zip: ^0.5.5
execa: ^5.0.0
load-json-file: ^6.2.0
path-name: ^1.0.0
rename-overwrite: ^4.0.0
render-help: ^1.0.1
tempy: ^1.0.0
write-json-file: ^4.3.0
dependencies:
'@pnpm/cli-utils': link:../cli-utils
'@pnpm/config': link:../config
'@pnpm/fetch': link:../fetch
'@pnpm/package-store': link:../package-store
'@pnpm/store-path': 5.0.0
'@pnpm/tarball-fetcher': link:../tarball-fetcher
adm-zip: 0.5.5
execa: 5.0.0
load-json-file: 6.2.0
path-name: 1.0.0
rename-overwrite: 4.0.0
render-help: 1.0.2
tempy: 1.0.1
write-json-file: 4.3.0
devDependencies:
'@pnpm/plugin-commands-nvm': 'link:'
'@pnpm/prepare': link:../../privatePackages/prepare
'@types/adm-zip': 0.4.34
packages/plugin-commands-outdated:
specifiers:
@@ -4573,6 +4589,12 @@ packages:
resolution: {integrity: sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==}
engines: {node: '>= 6'}
/@types/adm-zip/0.4.34:
resolution: {integrity: sha512-8ToYLLAYhkRfcmmljrKi22gT2pqu7hGMDtORP1emwIEGmgUTZOsaDjzWFzW5N2frcFRz/50CWt4zA1CxJ73pmQ==}
dependencies:
'@types/node': 15.6.1
dev: true
/@types/archy/0.0.31:
resolution: {integrity: sha512-v+dxizsFVyXgD3EpFuqT9YjdEjbJmPxNf1QIX9ohZOhxh1ZF2yhqv3vYaeum9lg3VghhxS5S0a6yldN9J9lPEQ==}
dev: true
@@ -4773,6 +4795,10 @@ packages:
/@types/node/15.3.0:
resolution: {integrity: sha512-8/bnjSZD86ZfpBsDlCIkNXIvm+h6wi9g7IqL+kmFkQ+Wvu3JrasgLElfiPgoo8V8vVfnEi0QVS12gbl94h9YsQ==}
/@types/node/15.6.1:
resolution: {integrity: sha512-7EIraBEyRHEe7CH+Fm1XvgqU6uwZN8Q7jppJGcqjROMT29qhAuuOxYB1uEY5UMYQKEmA5D+5tBnhdaPXSsLONA==}
dev: true
/@types/nopt/3.0.29:
resolution: {integrity: sha1-8Z3z20yX7hRZonQAKDIKcdcJZM4=}
dev: true
@@ -5295,6 +5321,11 @@ packages:
hasBin: true
dev: true
/adm-zip/0.5.5:
resolution: {integrity: sha512-IWwXKnCbirdbyXSfUDvCCrmYrOHANRZcc8NcRrvTlIApdl7PwE9oGcsYvNeJPAVY1M+70b4PxXGKIf8AEuiQ6w==}
engines: {node: '>=6.0'}
dev: false
/agent-base/4.3.0:
resolution: {integrity: sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==}
engines: {node: '>= 4.0.0'}
@@ -7062,7 +7093,6 @@ packages:
p-map: 4.0.0
rimraf: 3.0.2
slash: 3.0.0
dev: true
/delay/5.0.0:
resolution: {integrity: sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw==}
@@ -9093,12 +9123,10 @@ packages:
/is-path-cwd/2.2.0:
resolution: {integrity: sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==}
engines: {node: '>=6'}
dev: true
/is-path-inside/3.0.3:
resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==}
engines: {node: '>=8'}
dev: true
/is-plain-obj/1.1.0:
resolution: {integrity: sha1-caUMhCnfync8kqOQpKA7OfzVHT4=}
@@ -13215,7 +13243,6 @@ packages:
/temp-dir/2.0.0:
resolution: {integrity: sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==}
engines: {node: '>=8'}
dev: true
/tempy/1.0.1:
resolution: {integrity: sha512-biM9brNqxSc04Ee71hzFbryD11nX7VPhQQY32AdDmjFvodsRFz/3ufeoTZ6uYkRFfGo188tENcASNs3vTdsM0w==}
@@ -13226,7 +13253,6 @@ packages:
temp-dir: 2.0.0
type-fest: 0.16.0
unique-string: 2.0.0
dev: true
/term-size/1.2.0:
resolution: {integrity: sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=}
@@ -13626,7 +13652,6 @@ packages:
/type-fest/0.16.0:
resolution: {integrity: sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==}
engines: {node: '>=10'}
dev: true
/type-fest/0.18.1:
resolution: {integrity: sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==}