setup-node/src/util.ts

109 lines
3.0 KiB
TypeScript

import * as core from '@actions/core';
import * as exec from '@actions/exec';
import * as io from '@actions/io';
import fs from 'fs';
import path from 'path';
export function getNodeVersionFromFile(versionFilePath: string): string | null {
if (!fs.existsSync(versionFilePath)) {
throw new Error(
`The specified node version file at: ${versionFilePath} does not exist`
);
}
const contents = fs.readFileSync(versionFilePath, 'utf8');
// Try parsing the file as an NPM `package.json` file.
try {
const manifest = JSON.parse(contents);
// Presume package.json file.
if (typeof manifest === 'object' && !!manifest) {
// Support Volta.
// See https://docs.volta.sh/guide/understanding#managing-your-project
if (manifest.volta?.node) {
return manifest.volta.node;
}
if (manifest.engines?.node) {
return manifest.engines.node;
}
// Support Volta workspaces.
// See https://docs.volta.sh/advanced/workspaces
if (manifest.volta?.extends) {
const extendedFilePath = path.resolve(
path.dirname(versionFilePath),
manifest.volta.extends
);
core.info('Resolving node version from ' + extendedFilePath);
return getNodeVersionFromFile(extendedFilePath);
}
// If contents are an object, we parsed JSON
// this can happen if node-version-file is a package.json
// yet contains no volta.node or engines.node
//
// If node-version file is _not_ JSON, control flow
// will not have reached these lines.
//
// And because we've reached here, we know the contents
// *are* JSON, so no further string parsing makes sense.
return null;
}
} catch {
core.info('Node version file is not JSON file');
}
const found = contents.match(/^(?:node(js)?\s+)?v?(?<version>[^\s]+)$/m);
return found?.groups?.version ?? contents.trim();
}
export async function printEnvDetailsAndSetOutput() {
core.startGroup('Environment details');
const promises = ['node', 'npm', 'yarn'].map(async tool => {
const pathTool = await io.which(tool, false);
const output = pathTool ? await getToolVersion(tool, ['--version']) : '';
return {tool, output};
});
const tools = await Promise.all(promises);
tools.forEach(({tool, output}) => {
if (tool === 'node') {
core.setOutput(`${tool}-version`, output);
}
core.info(`${tool}: ${output}`);
});
core.endGroup();
}
async function getToolVersion(tool: string, options: string[]) {
try {
const {stdout, stderr, exitCode} = await exec.getExecOutput(tool, options, {
ignoreReturnCode: true,
silent: true
});
if (exitCode > 0) {
core.info(`[warning]${stderr}`);
return '';
}
return stdout.trim();
} catch (err) {
return '';
}
}
export const unique = () => {
const encountered = new Set();
return (value: unknown): boolean => {
if (encountered.has(value)) return false;
encountered.add(value);
return true;
};
};