UNPKG

@salesforce/plugin-release-management

Version:
143 lines 6.2 kB
/* * Copyright (c) 2023, salesforce.com, inc. * All rights reserved. * Licensed under the BSD 3-Clause license. * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ /* eslint-disable no-await-in-loop */ import fs from 'node:fs'; import path from 'node:path'; import os from 'node:os'; import { promisify } from 'node:util'; import { exec as execSync } from 'node:child_process'; import { Ux } from '@salesforce/sf-plugins-core'; import chalk from 'chalk'; import { SfError } from '@salesforce/core'; import { parseJson } from '@salesforce/kit'; import stripAnsi from 'strip-ansi'; const exec = promisify(execSync); async function exists(filePath) { try { await fs.promises.access(filePath); return true; } catch { return false; } } export async function testJITInstall(options) { const { jsonEnabled, jitPlugin } = options; let { executable } = options; const ux = new Ux({ jsonEnabled }); if (executable.endsWith('run') && !(await exists(executable))) { // This is a workaround for the ESM branch of sf. // If the executable is bin/run but it doesn't exist, that likely means // that bin/run.js is the executable. executable += '.js'; } const tmpDir = path.join(os.tmpdir(), 'sf-jit-test'); // Clear tmp dir before test to ensure that we're starting from a clean slate await fs.promises.rm(tmpDir, { recursive: true, force: true }); await fs.promises.mkdir(tmpDir, { recursive: true }); ux.styledHeader('Testing JIT installation'); const fileData = await fs.promises.readFile('package.json', 'utf8'); const packageJson = parseJson(fileData); const jitPlugins = jitPlugin ?? Object.keys(packageJson.oclif?.jitPlugins ?? {}); if (jitPlugins.length === 0) return; let manifestData; try { manifestData = options.manifestPath ? await fs.promises.readFile(options.manifestPath, 'utf8') : await fs.promises.readFile('oclif.manifest.json', 'utf8'); } catch { ux.log('No oclif.manifest.json found. Generating one now.'); await exec('yarn oclif manifest'); manifestData = await fs.promises.readFile('oclif.manifest.json', 'utf8'); } const manifest = parseJson(manifestData); const commands = Object.values(manifest.commands); const help = async (command) => { try { await exec(`${executable} ${command} --help`); return true; } catch (e) { return false; } }; const verifyInstall = async (plugin, dataDir) => { const userPjsonRaw = await fs.promises.readFile(path.join(dataDir, 'package.json'), 'utf-8'); const userPjson = parseJson(userPjsonRaw); return Boolean(userPjson.dependencies?.[plugin]); }; const passedInstalls = []; const failedInstalls = []; await Promise.all(jitPlugins.map(async (plugin) => { ux.log(`Testing JIT install for ${plugin}`); const dataDir = path.join(tmpDir, plugin, 'data'); const cacheDir = path.join(tmpDir, plugin, 'cache'); const configDir = path.join(tmpDir, plugin, 'config'); await fs.promises.mkdir(dataDir, { recursive: true }); await fs.promises.mkdir(cacheDir, { recursive: true }); await fs.promises.mkdir(configDir, { recursive: true }); let resultStdout = ''; let resultStderr = ''; try { const firstCommand = commands.find((c) => c.pluginName === plugin); if (!firstCommand) { throw new SfError(`Unable to find command for ${plugin}`); } // Test that --help works on JIT commands const helpResult = await help(firstCommand.id); ux.log(`${executable} ${firstCommand.id} --help ${helpResult ? chalk.green('PASSED') : chalk.red('FAILED')}`); ux.log(`${executable} ${firstCommand.id}`); // Test that executing the command will trigger JIT install // This will likely always fail because we're not providing all the required flags or it depends on some other setup. // However, this is okay because all we need to verify is that running the command will trigger the JIT install const { stdout, stderr } = await exec(`${executable} ${firstCommand.id}`, { env: { ...process.env, SF_DATA_DIR: dataDir, SF_CACHE_DIR: cacheDir, SF_CONFIG_DIR: configDir, }, }); resultStdout = stripAnsi(stdout); resultStderr = stripAnsi(stderr); } catch (e) { const err = e; // @ts-expect-error ExecException type doesn't have a stdout or stderr property // eslint-disable-next-line @typescript-eslint/no-unsafe-argument resultStdout = stripAnsi(err.stdout); // @ts-expect-error ExecException type doesn't have a stdout or stderr property // eslint-disable-next-line @typescript-eslint/no-unsafe-argument resultStderr = stripAnsi(err.stderr); } finally { const result = await verifyInstall(plugin, dataDir); if (result) { ux.log(`✅ ${chalk.green(`Verified installation of ${plugin}\n`)}`); passedInstalls.push(plugin); } else { ux.log(`❌ ${chalk.red(`Failed installation of ${plugin}\n`)}`); failedInstalls.push(plugin); ux.log(`stdout:\n${resultStdout}`); ux.log(`stderr:\n${resultStderr}`); } } })); ux.styledHeader('JIT Installation Results'); ux.log(`Passed (${passedInstalls.length})`); passedInstalls.forEach((msg) => ux.log(`• ${msg}`)); if (failedInstalls.length) { ux.log(); ux.log(`Failed (${failedInstalls.length})`); failedInstalls.forEach((msg) => ux.log(`• ${msg}`)); throw new SfError('Failed JIT installation'); } } //# sourceMappingURL=jit.js.map