-
Notifications
You must be signed in to change notification settings - Fork 28
Expand file tree
/
Copy pathsetupPackageManagers.js
More file actions
127 lines (119 loc) · 5.84 KB
/
Copy pathsetupPackageManagers.js
File metadata and controls
127 lines (119 loc) · 5.84 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
'use strict'
import fs from 'fs'
import path from 'path'
import pathKey from 'path-key'
import spawn from 'cross-spawn'
/**
* pnpm 12 installs the other package managers natively: `pnpm add --global`
* resolves npm, Yarn, and Bun through their trusted release channels and
* verifies each npm-published one against npm's signature, and
* `pnpm self-update` does the same for pnpm itself. The benchmark uses that
* instead of installing whatever npm package shares a manager's name — which
* for Yarn 6 (a Rust binary published on GitHub, not npm) used to take a
* downloader of its own.
*
* Every manager is provisioned into a `PNPM_HOME` of its own, so its
* executable, its packages, and the store they came from all live under the
* one directory the scenarios put on `PATH` — `<managersDir>/bin` is where
* the executables land.
*/
// The pnpm every other manager is installed with. It is itself installed the
// only way that works before any pnpm 12 exists on the machine: as a package,
// by whatever pnpm the machine has.
const INSTALLER_SPEC = 'pnpm@next-12'
/**
* Installs the pnpm 12 that provisions every package manager, and returns the
* path to its executable. It lives in a directory of its own that no scenario
* ever puts on `PATH`, so the installer can never be what a benchmark measures.
*/
export function bootstrapInstaller (setupDir) {
fs.mkdirSync(setupDir, { recursive: true })
fs.writeFileSync(path.join(setupDir, 'package.json'), '{}', 'utf8')
// pnpm holds versions younger than `minimumReleaseAge` back, and the
// benchmark exists to measure the newest release, not the newest mature one.
fs.writeFileSync(
path.join(setupDir, 'pnpm-workspace.yaml'),
'packages:\n - "."\nminimumReleaseAge: 0\n',
'utf8'
)
// pnpm 12 ships its Rust binary via an install script, so the build must be
// allowed; otherwise the `pnpm` bin is left as a placeholder that errors out.
run('pnpm', ['add', INSTALLER_SPEC, '--allow-build=pnpm'], { cwd: setupDir, stdio: 'inherit' })
return path.join(setupDir, 'node_modules/.bin/pnpm')
}
/**
* Provisions every benchmarked package manager into its directory.
*
* A package manager that fails to install is worse than a failed benchmark:
* the scenarios still find the machine's own `npm`/`pnpm`/`bun` further down
* PATH and quietly measure that version instead of the one being benchmarked.
* That is why every install here throws on failure.
*/
export function provisionPackageManagers (installerPnpm, managersDirs) {
provision(installerPnpm, managersDirs.npm, ['add', '--global', 'npm@latest'])
provisionPnpm11(installerPnpm, managersDirs.pnpm11)
provisionPnpm12(installerPnpm, managersDirs.pnpm12)
// `yarn@6` rather than `yarn@latest`: a specifier that doesn't commit to a
// major resolves to Yarn Berry from npm, while the benchmark measures the
// current Yarn line — the Rust rewrite released on GitHub.
provision(installerPnpm, managersDirs.yarn, ['add', '--global', 'yarn@6'])
provision(installerPnpm, managersDirs.bun, ['add', '--global', 'bun@latest'])
}
/**
* The pnpm the registry's populate pass installs with. Exported because
* `populatePnprCache.js` provisions this one alone: nothing it does is
* timed, so the managers the benchmark measures would be installed for
* no one.
*
* pnpm refuses `pnpm add --global pnpm`: switching pnpm is `self-update`'s
* job, so that is what installs both pnpm lines. The v11 column pins the
* line's own dist-tag rather than `latest`, which would follow the
* registry's default tag onto pnpm 12 the moment that becomes the stable
* line — and this column exists to measure the previous major.
*/
export function provisionPnpm11 (installerPnpm, managersDir) {
provision(installerPnpm, managersDir, ['self-update', 'latest-11'])
}
/**
* The pnpm the registry's populate pass installs with. Exported for
* `populatePnprCache.js`, which provisions this one alone: nothing it
* does is timed, so the managers the benchmark measures would be
* installed for no one.
*
* pnpm 12 rather than pnpm 11 because the populate pass resolves the
* whole graph, and the two resolvers are not in the same league on a
* large one: the `alotta-packages` graph takes pnpm 12 six seconds and
* pnpm 11 twenty-one minutes.
*/
export function provisionPnpm12 (installerPnpm, managersDir) {
provision(installerPnpm, managersDir, ['self-update', 'next-12'])
}
function provision (installerPnpm, managersDir, args) {
const pathEnv = pathKey()
const env = Object.create(process.env)
// The manager's own pnpm home: executables in `<managersDir>/bin`, packages
// and store next to them, nothing shared with the machine or the other
// managers.
env.PNPM_HOME = managersDir
// pnpm refuses to install globally when the global bin directory is not in
// PATH.
env[pathEnv] = [path.join(managersDir, 'bin'), process.env[pathEnv]].join(path.delimiter)
// Left to itself, pnpm links a globally installed package manager as a shim
// that re-resolves the version on every invocation, so a project's pin wins
// over the global install. The benchmark measures the package manager, not
// the shim, so the executables have to be linked directly.
env.PNPM_CONFIG_GLOBAL_SHIMS = 'false'
// `self-update` deliberately ignores what a project's `pnpm-workspace.yaml`
// says about release maturity — a repository must not decide whether the
// binary gets replaced — so the benchmark's `minimumReleaseAge: 0` has to
// arrive through the trusted env-var layer instead.
env.PNPM_CONFIG_MINIMUM_RELEASE_AGE = '0'
run(installerPnpm, args, { cwd: managersDir, env, stdio: 'inherit' })
}
function run (command, args, opts) {
const result = spawn.sync(command, args, opts)
if (result.error) throw result.error
if (result.status !== 0) {
throw new Error(`\`${path.basename(command)} ${args.join(' ')}\` failed with status code ${result.status}`)
}
}