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 128 129 130 131 132
|
import {Command, Option, UsageError} from 'clipanion';
import fs from 'fs';
import path from 'path';
import * as folderUtils from '../folderUtils';
import * as specUtils from '../specUtils';
import {Descriptor, Locator, isSupportedPackageManager} from '../types';
import {BaseCommand} from './Base';
export class InstallGlobalCommand extends BaseCommand {
static paths = [
[`install`],
];
static usage = Command.Usage({
description: `Install package managers on the system`,
details: `
Download the selected package managers and install them on the system.
Package managers thus installed will be configured as the new default when calling their respective binaries outside of projects defining the 'packageManager' field.
`,
examples: [[
`Install the latest version of Yarn 1.x and make it globally available`,
`corepack install -g yarn@^1`,
], [
`Install the latest version of all available package managers, and make them globally available`,
`corepack install -g --all`,
]],
});
global = Option.Boolean(`-g,--global`, {
required: true,
});
all = Option.Boolean(`--all`, false, {
description: `If true, all available default package managers will be installed`,
});
cacheOnly = Option.Boolean(`--cache-only`, false, {
description: `If true, the package managers will only be cached, not set as new defaults`,
});
args = Option.Rest();
async execute() {
if (this.args.length === 0 && !this.all)
throw new UsageError(`No package managers specified; use --all to install all available package managers, or specify one or more package managers to proceed`);
if (!this.all) {
for (const arg of this.args) {
if (arg.endsWith(`.tgz`)) {
await this.installFromTarball(path.resolve(this.context.cwd, arg));
} else {
await this.installFromDescriptor(specUtils.parseSpec(arg, `CLI arguments`, {enforceExactVersion: false}));
}
}
} else {
for (const descriptor of await this.context.engine.getDefaultDescriptors()) {
await this.installFromDescriptor(descriptor);
}
}
}
log(locator: Locator) {
if (this.cacheOnly) {
this.context.stdout.write(`Adding ${locator.name}@${locator.reference} to the cache...\n`);
} else {
this.context.stdout.write(`Installing ${locator.name}@${locator.reference}...\n`);
}
}
async installFromDescriptor(descriptor: Descriptor) {
const resolved = await this.context.engine.resolveDescriptor(descriptor, {allowTags: true, useCache: false});
if (resolved === null)
throw new UsageError(`Failed to successfully resolve '${descriptor.range}' to a valid ${descriptor.name} release`);
this.log(resolved);
await this.context.engine.ensurePackageManager(resolved);
if (!this.cacheOnly) {
await this.context.engine.activatePackageManager(resolved);
}
}
async installFromTarball(p: string) {
const installFolder = folderUtils.getInstallFolder();
const archiveEntries = new Map<string, Set<string>>();
const {default: tar} = await import(`tar`);
let hasShortEntries = false;
await tar.t({file: p, onentry: entry => {
const segments = entry.path.split(/\//g);
if (segments.length > 0 && segments[segments.length - 1] !== `.corepack`)
return;
if (segments.length < 3) {
hasShortEntries = true;
} else {
let references = archiveEntries.get(segments[0]);
if (typeof references === `undefined`)
archiveEntries.set(segments[0], references = new Set());
references.add(segments[1]);
}
}});
if (hasShortEntries || archiveEntries.size < 1)
throw new UsageError(`Invalid archive format; did it get generated by 'corepack pack'?`);
for (const [name, references] of archiveEntries) {
for (const reference of references) {
if (!isSupportedPackageManager(name))
throw new UsageError(`Unsupported package manager '${name}'`);
this.log({name, reference});
// Recreate the folder in case it was deleted somewhere else:
await fs.promises.mkdir(installFolder, {recursive: true});
await tar.x({file: p, cwd: installFolder}, [`${name}/${reference}`]);
if (!this.cacheOnly) {
await this.context.engine.activatePackageManager({name, reference});
}
}
}
}
}
|