chore: remove all previous run/exec commands in favour of one cleaned up version System.run
parent
f4f7aba8ca
commit
bd30a3cd10
|
|
@ -27,19 +27,6 @@ export class BuildCommand extends CommandBase implements CommandInterface {
|
||||||
await Docker.run(baseImage, { workspace, actionFolder, ...parameters });
|
await Docker.run(baseImage, { workspace, actionFolder, ...parameters });
|
||||||
}
|
}
|
||||||
|
|
||||||
// const result = await exec('docker run -it unityci/editor:2020.3.15f2-base-1 /bin/bash -c "echo test"', {
|
|
||||||
// output: OutputMode.Capture,
|
|
||||||
// continueOnError: true,
|
|
||||||
//
|
|
||||||
// // verbose: true,
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// log.info('result', result.output);
|
|
||||||
// const { success } = result.status;
|
|
||||||
// log.info('success', success);
|
|
||||||
//
|
|
||||||
// return success;
|
|
||||||
|
|
||||||
// Set output
|
// Set output
|
||||||
await Output.setBuildVersion(parameters.buildVersion);
|
await Output.setBuildVersion(parameters.buildVersion);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ import { config, configSync } from 'https://deno.land/std@0.151.0/dotenv/mod.ts'
|
||||||
|
|
||||||
// Internally managed packages
|
// Internally managed packages
|
||||||
import waitUntil from './modules/wait-until.ts';
|
import waitUntil from './modules/wait-until.ts';
|
||||||
import { core, exec } from './modules/actions/index.ts';
|
import { core } from './modules/actions/index.ts';
|
||||||
import { dedent } from './modules/dedent.ts';
|
import { dedent } from './modules/dedent.ts';
|
||||||
|
|
||||||
// Polyfill for https://github.com/tc39/proposal-string-dedent
|
// Polyfill for https://github.com/tc39/proposal-string-dedent
|
||||||
|
|
@ -58,7 +58,6 @@ export {
|
||||||
configSync,
|
configSync,
|
||||||
core,
|
core,
|
||||||
crypto,
|
crypto,
|
||||||
exec,
|
|
||||||
fs,
|
fs,
|
||||||
fsSync,
|
fsSync,
|
||||||
getUnityChangeSet,
|
getUnityChangeSet,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { Parameters } from '../../../model/index.ts';
|
import { Parameters } from '../../../model/index.ts';
|
||||||
import { fsSync as fs, exec, getUnityChangeSet } from '../../../dependencies.ts';
|
import { fsSync as fs, getUnityChangeSet } from '../../../dependencies.ts';
|
||||||
|
import System from '../../../model/system.ts';
|
||||||
|
|
||||||
class SetupMac {
|
class SetupMac {
|
||||||
static unityHubPath = `"/Applications/Unity Hub.app/Contents/MacOS/Unity Hub"`;
|
static unityHubPath = `"/Applications/Unity Hub.app/Contents/MacOS/Unity Hub"`;
|
||||||
|
|
@ -19,11 +20,10 @@ class SetupMac {
|
||||||
private static async installUnityHub(silent = false) {
|
private static async installUnityHub(silent = false) {
|
||||||
const command = 'brew install unity-hub';
|
const command = 'brew install unity-hub';
|
||||||
if (!fs.existsSync(this.unityHubPath)) {
|
if (!fs.existsSync(this.unityHubPath)) {
|
||||||
// Ignoring return code because the log seems to overflow the internal buffer which triggers
|
try {
|
||||||
// a false error
|
await System.run(command, { silent, ignoreReturnCode: true });
|
||||||
const { exitCode } = await exec(command, undefined, { silent, ignoreReturnCode: true });
|
} catch (error) {
|
||||||
if (exitCode) {
|
throw new Error(`There was an error installing the Unity Editor. See logs above for details. ${error}`);
|
||||||
throw new Error(`There was an error installing the Unity Editor. See logs above for details.`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -36,11 +36,10 @@ class SetupMac {
|
||||||
--module mac-il2cpp \
|
--module mac-il2cpp \
|
||||||
--childModules`;
|
--childModules`;
|
||||||
|
|
||||||
// Ignoring return code because the log seems to overflow the internal buffer which triggers
|
try {
|
||||||
// a false error
|
await System.run(command, { silent, ignoreReturnCode: true });
|
||||||
const { exitCode } = await exec(command, undefined, { silent, ignoreReturnCode: true });
|
} catch (error) {
|
||||||
if (exitCode) {
|
throw new Error(`There was an error installing the Unity Editor. See logs above for details. ${error}`);
|
||||||
throw new Error(`There was an error installing the Unity Editor. See logs above for details.`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { fsSync as fs, exec } from '../../../dependencies.ts';
|
import { fsSync as fs } from '../../../dependencies.ts';
|
||||||
import { Parameters } from '../../../model/index.ts';
|
import { Parameters } from '../../../model/index.ts';
|
||||||
import ValidateWindows from '../platform-validation/validate-windows.ts';
|
import ValidateWindows from '../platform-validation/validate-windows.ts';
|
||||||
|
import System from '../../../model/system.ts';
|
||||||
|
|
||||||
class SetupWindows {
|
class SetupWindows {
|
||||||
public static async setup(parameters: Parameters) {
|
public static async setup(parameters: Parameters) {
|
||||||
|
|
@ -17,7 +18,7 @@ class SetupWindows {
|
||||||
const copyWinSdkRegistryKeyCommand = `reg export "HKLM\\SOFTWARE\\WOW6432Node\\Microsoft\\Microsoft SDKs\\Windows\\v10.0" ${registryKeysPath}/winsdk.reg /y`;
|
const copyWinSdkRegistryKeyCommand = `reg export "HKLM\\SOFTWARE\\WOW6432Node\\Microsoft\\Microsoft SDKs\\Windows\\v10.0" ${registryKeysPath}/winsdk.reg /y`;
|
||||||
|
|
||||||
await fs.ensureDir(registryKeysPath);
|
await fs.ensureDir(registryKeysPath);
|
||||||
await exec(copyWinSdkRegistryKeyCommand);
|
await System.run(copyWinSdkRegistryKeyCommand);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,67 +1,77 @@
|
||||||
import ImageEnvironmentFactory from './image-environment-factory.ts';
|
import ImageEnvironmentFactory from './image-environment-factory.ts';
|
||||||
import { path, exec, fs } from '../dependencies.ts';
|
import { path, fsSync as fs } from '../dependencies.ts';
|
||||||
|
import System from './system.ts';
|
||||||
|
|
||||||
class Docker {
|
class Docker {
|
||||||
static async run(image, parameters, silent = false) {
|
static async run(image, parameters, silent = false) {
|
||||||
let runCommand = '';
|
log.warning('running docker process for', process.platform, silent);
|
||||||
|
let command = '';
|
||||||
switch (process.platform) {
|
switch (process.platform) {
|
||||||
case 'linux':
|
case 'linux':
|
||||||
runCommand = this.getLinuxCommand(image, parameters);
|
command = await this.getLinuxCommand(image, parameters);
|
||||||
break;
|
break;
|
||||||
case 'win32':
|
case 'win32':
|
||||||
runCommand = this.getWindowsCommand(image, parameters);
|
command = await this.getWindowsCommand(image, parameters);
|
||||||
}
|
}
|
||||||
await exec(runCommand, undefined, { silent });
|
|
||||||
|
const test = await System.newRun(`docker`, command.replace(/\s\s+/, ' ').split(' '), { silent, verbose: true });
|
||||||
|
log.error('test', test);
|
||||||
}
|
}
|
||||||
|
|
||||||
static getLinuxCommand(image, parameters): string {
|
static async getLinuxCommand(image, parameters): string {
|
||||||
const { workspace, actionFolder, runnerTempPath, sshAgent, gitPrivateToken } = parameters;
|
const { workspace, actionFolder, runnerTempPath, sshAgent, gitPrivateToken } = parameters;
|
||||||
|
|
||||||
const githubHome = path.join(runnerTempPath, '_github_home');
|
const githubHome = path.join(runnerTempPath, '_github_home');
|
||||||
if (!fs.existsSync(githubHome)) fs.mkdirSync(githubHome);
|
await fs.ensureDir(githubHome);
|
||||||
const githubWorkflow = path.join(runnerTempPath, '_github_workflow');
|
const githubWorkflow = path.join(runnerTempPath, '_github_workflow');
|
||||||
if (!fs.existsSync(githubWorkflow)) fs.mkdirSync(githubWorkflow);
|
await fs.ensureDir(githubWorkflow);
|
||||||
|
|
||||||
return `docker run \
|
return String.dedent`
|
||||||
--workdir /github/workspace \
|
docker run \
|
||||||
--rm \
|
--workdir /github/workspace \
|
||||||
${ImageEnvironmentFactory.getEnvVarString(parameters)} \
|
--rm \
|
||||||
--env UNITY_SERIAL \
|
${ImageEnvironmentFactory.getEnvVarString(parameters)} \
|
||||||
--env GITHUB_WORKSPACE=/github/workspace \
|
--env UNITY_SERIAL \
|
||||||
${gitPrivateToken ? `--env GIT_PRIVATE_TOKEN="${gitPrivateToken}"` : ''} \
|
--env GITHUB_WORKSPACE=/github/workspace \
|
||||||
${sshAgent ? '--env SSH_AUTH_SOCK=/ssh-agent' : ''} \
|
${gitPrivateToken ? `--env GIT_PRIVATE_TOKEN="${gitPrivateToken}"` : ''} \
|
||||||
--volume "${githubHome}":"/root:z" \
|
${sshAgent ? '--env SSH_AUTH_SOCK=/ssh-agent' : ''} \
|
||||||
--volume "${githubWorkflow}":"/github/workflow:z" \
|
--volume "${githubHome}":"/root:z" \
|
||||||
--volume "${workspace}":"/github/workspace:z" \
|
--volume "${githubWorkflow}":"/github/workflow:z" \
|
||||||
--volume "${actionFolder}/default-build-script:/UnityBuilderAction:z" \
|
--volume "${workspace}":"/github/workspace:z" \
|
||||||
--volume "${actionFolder}/platforms/ubuntu/steps:/steps:z" \
|
--volume "${actionFolder}/default-build-script:/UnityBuilderAction:z" \
|
||||||
--volume "${actionFolder}/platforms/ubuntu/entrypoint.sh:/entrypoint.sh:z" \
|
--volume "${actionFolder}/platforms/ubuntu/steps:/steps:z" \
|
||||||
${sshAgent ? `--volume ${sshAgent}:/ssh-agent` : ''} \
|
--volume "${actionFolder}/platforms/ubuntu/entrypoint.sh:/entrypoint.sh:z" \
|
||||||
${sshAgent ? '--volume /home/runner/.ssh/known_hosts:/root/.ssh/known_hosts:ro' : ''} \
|
${sshAgent ? `--volume ${sshAgent}:/ssh-agent` : ''} \
|
||||||
${image} \
|
${sshAgent ? '--volume /home/runner/.ssh/known_hosts:/root/.ssh/known_hosts:ro' : ''} \
|
||||||
/bin/bash -c /entrypoint.sh`;
|
${image} \
|
||||||
|
/bin/bash -c /entrypoint.sh
|
||||||
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static getWindowsCommand(image: any, parameters: any): string {
|
static async getWindowsCommand(image: any, parameters: any): string {
|
||||||
const { workspace, actionFolder, unitySerial, gitPrivateToken, cliStoragePath } = parameters;
|
const { workspace, actionFolder, unitySerial, gitPrivateToken, cliStoragePath } = parameters;
|
||||||
|
|
||||||
return `docker run \
|
// Todo - get this to work on a non-github runner local machine
|
||||||
--workdir /github/workspace \
|
// return String.dedent`run ${image} powershell c:/steps/entrypoint.ps1`;
|
||||||
--rm \
|
return String.dedent`
|
||||||
${ImageEnvironmentFactory.getEnvVarString(parameters)} \
|
docker run \
|
||||||
--env UNITY_SERIAL="${unitySerial}" \
|
--workdir /github/workspace \
|
||||||
--env GITHUB_WORKSPACE=c:/github/workspace \
|
--rm \
|
||||||
${gitPrivateToken ? `--env GIT_PRIVATE_TOKEN="${gitPrivateToken}"` : ''} \
|
${ImageEnvironmentFactory.getEnvVarString(parameters)} \
|
||||||
--volume "${workspace}":"c:/github/workspace" \
|
--env UNITY_SERIAL="${unitySerial}" \
|
||||||
--volume "${cliStoragePath}/registry-keys":"c:/registry-keys" \
|
--env GITHUB_WORKSPACE=c:/github/workspace \
|
||||||
--volume "C:/Program Files (x86)/Microsoft Visual Studio":"C:/Program Files (x86)/Microsoft Visual Studio" \
|
${gitPrivateToken ? `--env GIT_PRIVATE_TOKEN="${gitPrivateToken}"` : ''} \
|
||||||
--volume "C:/Program Files (x86)/Windows Kits":"C:/Program Files (x86)/Windows Kits" \
|
--volume "${workspace}":"c:/github/workspace" \
|
||||||
--volume "C:/ProgramData/Microsoft/VisualStudio":"C:/ProgramData/Microsoft/VisualStudio" \
|
--volume "${cliStoragePath}/registry-keys":"c:/registry-keys" \
|
||||||
--volume "${actionFolder}/default-build-script":"c:/UnityBuilderAction" \
|
--volume "C:/Program Files (x86)/Microsoft Visual Studio":"C:/Program Files (x86)/Microsoft Visual Studio" \
|
||||||
--volume "${actionFolder}/platforms/windows":"c:/steps" \
|
--volume "C:/Program Files (x86)/Windows Kits":"C:/Program Files (x86)/Windows Kits" \
|
||||||
--volume "${actionFolder}/BlankProject":"c:/BlankProject" \
|
--volume "C:/ProgramData/Microsoft/VisualStudio":"C:/ProgramData/Microsoft/VisualStudio" \
|
||||||
${image} \
|
--volume "${actionFolder}/default-build-script":"c:/UnityBuilderAction" \
|
||||||
powershell c:/steps/entrypoint.ps1`;
|
--volume "${actionFolder}/platforms/windows":"c:/steps" \
|
||||||
|
--volume "${actionFolder}/BlankProject":"c:/BlankProject" \
|
||||||
|
${image} \
|
||||||
|
powershell c:/steps/entrypoint.ps1
|
||||||
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,14 @@ class ImageEnvironmentFactory {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
string += `--env ${p.name}="${p.value}" `;
|
if (Deno.build.os === 'windows') {
|
||||||
|
// The ampersand (&) character is not allowed. The & operator is reserved for future use; wrap an ampersand in
|
||||||
|
// double quotation marks ("&") to pass it as part of a string.
|
||||||
|
const escapedValue = typeof p.value !== 'string' ? p.value : p.value?.replace(/&/, '\\"&\\"');
|
||||||
|
string += `--env ${p.name}='${escapedValue}' `;
|
||||||
|
} else {
|
||||||
|
string += `--env ${p.name}="${p.value}" `;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return string;
|
return string;
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import { exec } from '../dependencies.ts';
|
|
||||||
import { Parameters } from './parameters.ts';
|
import { Parameters } from './parameters.ts';
|
||||||
|
import System from './system.ts';
|
||||||
|
|
||||||
class MacBuilder {
|
class MacBuilder {
|
||||||
public static async run(actionFolder, workspace, buildParameters: BuildParameters, silent = false) {
|
public static async run(actionFolder) {
|
||||||
await exec('bash', [`${actionFolder}/platforms/mac/entrypoint.sh`], {
|
log.warning('running the process');
|
||||||
silent,
|
await System.run(`bash ${actionFolder}/platforms/mac/entrypoint.sh`, {
|
||||||
ignoreReturnCode: true,
|
ignoreReturnCode: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
import { core, exec } from '../../dependencies.ts';
|
import { core } from '../../dependencies.ts';
|
||||||
import System from './system.ts';
|
import System from './system.ts';
|
||||||
|
|
||||||
jest.spyOn(core, 'debug').mockImplementation(() => {});
|
jest.spyOn(core, 'debug').mockImplementation(() => {});
|
||||||
const info = jest.spyOn(core, 'info').mockImplementation(() => {});
|
const info = jest.spyOn(core, 'info').mockImplementation(() => {});
|
||||||
jest.spyOn(core, 'warning').mockImplementation(() => {});
|
jest.spyOn(core, 'warning').mockImplementation(() => {});
|
||||||
jest.spyOn(core, 'error').mockImplementation(() => {});
|
jest.spyOn(core, 'error').mockImplementation(() => {});
|
||||||
const execSpy = jest.spyOn(exec, 'exec').mockImplementation(async () => 0);
|
const execSpy = jest.spyOn(System, 'run').mockImplementation(async () => 0);
|
||||||
|
|
||||||
afterEach(() => jest.clearAllMocks());
|
afterEach(() => jest.clearAllMocks());
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,4 @@
|
||||||
import { exec } from '../dependencies.ts';
|
export interface RunOptions {
|
||||||
|
|
||||||
export interface ShellRunOptions {
|
|
||||||
pwd: string;
|
pwd: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -11,22 +9,45 @@ class System {
|
||||||
*
|
*
|
||||||
* Intended to always be silent and capture the output.
|
* Intended to always be silent and capture the output.
|
||||||
*/
|
*/
|
||||||
static async shellRun(rawCommand: string, options: ShellRunOptions = {}) {
|
static async run(rawCommand: string, options: RunOptions = {}) {
|
||||||
const { pwd } = options;
|
const { pwd } = options;
|
||||||
|
|
||||||
let command = rawCommand;
|
let command = rawCommand;
|
||||||
if (pwd) command = `cd ${pwd} ; ${command}`;
|
if (pwd) command = `cd ${pwd} ; ${command}`;
|
||||||
|
|
||||||
|
const isWindows = Deno.build.os === 'windows';
|
||||||
|
const shellMethod = isWindows ? System.powershellRun : System.shellRun;
|
||||||
|
|
||||||
|
if (log.isVeryVerbose) log.debug(`The following command is run using ${shellMethod.name}`);
|
||||||
|
|
||||||
|
return shellMethod(command, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
static async shellRun(command: string, options: RunOptions = {}) {
|
||||||
return System.newRun('sh', ['-c', command]);
|
return System.newRun('sh', ['-c', command]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async powershellRun(command: string, options: RunOptions = {}) {
|
||||||
|
return System.newRun('powershell', [command]);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Example:
|
* Internal cross-platform run, that spawns a new process and captures its output.
|
||||||
* System.newRun(sh, ['-c', 'echo something'])
|
|
||||||
*
|
*
|
||||||
* private for now, but could become public if this happens to be a great replacement for the other run method.
|
* If any error is written to stderr, this method will throw them.
|
||||||
|
* ❌ new Error(stdoutErrors)
|
||||||
|
*
|
||||||
|
* In case of no errors, this will return an object similar to these examples
|
||||||
|
* ✔️ { status: { success: true, code: 0 }, output: 'output from the command' }
|
||||||
|
* ⚠️ { status: { success: false, code: 1~255 }, output: 'output from the command' }
|
||||||
|
*
|
||||||
|
* Example usage:
|
||||||
|
* System.newRun(sh, ['-c', 'echo something'])
|
||||||
|
* System.newRun(powershell, ['echo something'])
|
||||||
|
*
|
||||||
|
* @deprecated use System.run instead, this method will be private
|
||||||
*/
|
*/
|
||||||
private static async newRun(command, args: string | string[] = []) {
|
public static async newRun(command, args: string | string[] = []) {
|
||||||
if (!Array.isArray(args)) args = [args];
|
if (!Array.isArray(args)) args = [args];
|
||||||
|
|
||||||
const argsString = args.join(' ');
|
const argsString = args.join(' ');
|
||||||
|
|
@ -42,8 +63,8 @@ class System {
|
||||||
|
|
||||||
process.close();
|
process.close();
|
||||||
|
|
||||||
const output = new TextDecoder().decode(outputBuffer).replace(/\n+$/, '');
|
const output = new TextDecoder().decode(outputBuffer).replace(/[\n\r]+$/, '');
|
||||||
const error = new TextDecoder().decode(errorBuffer).replace(/\n+$/, '');
|
const error = new TextDecoder().decode(errorBuffer).replace(/[\n\r]+$/, '');
|
||||||
|
|
||||||
const result = { status, output };
|
const result = { status, output };
|
||||||
|
|
||||||
|
|
@ -61,77 +82,6 @@ class System {
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated use more simplified `shellRun` if possible.
|
|
||||||
*/
|
|
||||||
static async run(command, arguments_: any = [], options = {}, shouldLog = true) {
|
|
||||||
let result = '';
|
|
||||||
let error = '';
|
|
||||||
let debug = '';
|
|
||||||
|
|
||||||
const listeners = {
|
|
||||||
stdout: (dataBuffer) => {
|
|
||||||
result += dataBuffer.toString();
|
|
||||||
},
|
|
||||||
stderr: (dataBuffer) => {
|
|
||||||
error += dataBuffer.toString();
|
|
||||||
},
|
|
||||||
debug: (dataString) => {
|
|
||||||
debug += dataString.toString();
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const showOutput = () => {
|
|
||||||
if (debug !== '' && shouldLog) {
|
|
||||||
log.debug(debug);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result !== '' && shouldLog) {
|
|
||||||
log.info(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (error !== '' && shouldLog) {
|
|
||||||
log.warning(error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const throwContextualError = (message: string) => {
|
|
||||||
let commandAsString = command;
|
|
||||||
if (Array.isArray(arguments_)) {
|
|
||||||
commandAsString += ` ${arguments_.join(' ')}`;
|
|
||||||
} else if (typeof arguments_ === 'string') {
|
|
||||||
commandAsString += ` ${arguments_}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error(`Failed to run "${commandAsString}".\n ${message}`);
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (command.trim() === '') {
|
|
||||||
throw new Error(`Failed to execute empty command`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const { exitCode, success, output } = await exec(command, arguments_, { silent: true, listeners, ...options });
|
|
||||||
showOutput();
|
|
||||||
if (!success) {
|
|
||||||
throwContextualError(`Command returned non-zero exit code (${exitCode}).\nError: ${error}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Todo - remove this after verifying it works as expected
|
|
||||||
const trimmedResult = result.replace(/\n+$/, '');
|
|
||||||
if (!output && trimmedResult) {
|
|
||||||
log.warning('returning result instead of output for backward compatibility');
|
|
||||||
|
|
||||||
return trimmedResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
return output;
|
|
||||||
} catch (inCommandError) {
|
|
||||||
showOutput();
|
|
||||||
throwContextualError(`In-command error caught: ${inCommandError}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default System;
|
export default System;
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import NotImplementedException from './error/not-implemented-exception.ts';
|
||||||
import ValidationError from './error/validation-error.ts';
|
import ValidationError from './error/validation-error.ts';
|
||||||
import Input from './input.ts';
|
import Input from './input.ts';
|
||||||
import System from './system.ts';
|
import System from './system.ts';
|
||||||
|
import { Action } from './index.ts';
|
||||||
|
|
||||||
export default class Versioning {
|
export default class Versioning {
|
||||||
static get projectPath() {
|
static get projectPath() {
|
||||||
|
|
@ -225,7 +226,7 @@ export default class Versioning {
|
||||||
* Returns whether the repository is shallow.
|
* Returns whether the repository is shallow.
|
||||||
*/
|
*/
|
||||||
static async isShallow() {
|
static async isShallow() {
|
||||||
const output = await this.git(['rev-parse', '--is-shallow-repository']);
|
const output = await this.git('rev-parse --is-shallow-repository');
|
||||||
|
|
||||||
return output !== 'false';
|
return output !== 'false';
|
||||||
}
|
}
|
||||||
|
|
@ -239,10 +240,10 @@ export default class Versioning {
|
||||||
*/
|
*/
|
||||||
static async fetch() {
|
static async fetch() {
|
||||||
try {
|
try {
|
||||||
await this.git(['fetch', '--unshallow']);
|
await this.git('fetch --unshallow');
|
||||||
} catch {
|
} catch {
|
||||||
log.warning(`fetch --unshallow did not work, falling back to regular fetch`);
|
log.warning(`fetch --unshallow did not work, falling back to regular fetch`);
|
||||||
await this.git(['fetch']);
|
await this.git('fetch');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -255,14 +256,23 @@ export default class Versioning {
|
||||||
* identifies the current commit.
|
* identifies the current commit.
|
||||||
*/
|
*/
|
||||||
static async getVersionDescription() {
|
static async getVersionDescription() {
|
||||||
return this.git(['describe', '--long', '--tags', '--always', this.sha]);
|
let commitIsh = '';
|
||||||
|
|
||||||
|
// In CI the repo is checked out in detached head mode.
|
||||||
|
// We MUST specify the commitIsh that triggered the job.
|
||||||
|
// Todo - make this compatible with more CI systems
|
||||||
|
if (!Action.isRunningLocally) {
|
||||||
|
commitIsh = this.sha;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.git(`describe --long --tags --always ${commitIsh}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether there are uncommitted changes that are not ignored.
|
* Returns whether there are uncommitted changes that are not ignored.
|
||||||
*/
|
*/
|
||||||
static async isDirty() {
|
static async isDirty() {
|
||||||
const output = await this.git(['status', '--porcelain']);
|
const output = await this.git('status --porcelain');
|
||||||
const isDirty = output !== '';
|
const isDirty = output !== '';
|
||||||
|
|
||||||
if (isDirty) {
|
if (isDirty) {
|
||||||
|
|
@ -279,7 +289,7 @@ export default class Versioning {
|
||||||
* Get the tag if there is one pointing at HEAD
|
* Get the tag if there is one pointing at HEAD
|
||||||
*/
|
*/
|
||||||
static async getTag() {
|
static async getTag() {
|
||||||
return await this.git(['tag', '--points-at', 'HEAD']);
|
return await this.git('tag --points-at HEAD');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -309,7 +319,7 @@ export default class Versioning {
|
||||||
* Note: HEAD should not be used, as it may be detached, resulting in an additional count.
|
* Note: HEAD should not be used, as it may be detached, resulting in an additional count.
|
||||||
*/
|
*/
|
||||||
static async getTotalNumberOfCommits() {
|
static async getTotalNumberOfCommits() {
|
||||||
const numberOfCommitsAsString = await this.git(['rev-list', '--count', this.sha]);
|
const numberOfCommitsAsString = await this.git(`rev-list --count ${this.sha}`);
|
||||||
|
|
||||||
return Number.parseInt(numberOfCommitsAsString, 10);
|
return Number.parseInt(numberOfCommitsAsString, 10);
|
||||||
}
|
}
|
||||||
|
|
@ -318,6 +328,10 @@ export default class Versioning {
|
||||||
* Run git in the specified project path
|
* Run git in the specified project path
|
||||||
*/
|
*/
|
||||||
static async git(arguments_, options = {}) {
|
static async git(arguments_, options = {}) {
|
||||||
return System.run('git', arguments_, { cwd: this.projectPath, ...options });
|
const result = await System.run(`git ${arguments_}`, { cwd: this.projectPath, ...options });
|
||||||
|
|
||||||
|
log.warning(result);
|
||||||
|
|
||||||
|
return result.output;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,61 +0,0 @@
|
||||||
import { exec as originalExec } from 'https://deno.land/x/exec@0.0.5/mod.ts';
|
|
||||||
|
|
||||||
export enum OutputMode {
|
|
||||||
None = 0, // no output, just run the command
|
|
||||||
StdOut, // dump the output to stdout
|
|
||||||
Capture, // capture the output and return it
|
|
||||||
Tee, // both dump and capture the output
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ICommandResult {
|
|
||||||
status?: {
|
|
||||||
code: number;
|
|
||||||
success: boolean;
|
|
||||||
};
|
|
||||||
output: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ISanitisedCommandResult {
|
|
||||||
exitCode: number;
|
|
||||||
success: boolean;
|
|
||||||
output: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IOptions {
|
|
||||||
silent?: boolean;
|
|
||||||
ignoreReturnCode?: boolean;
|
|
||||||
output?: OutputMode;
|
|
||||||
verbose?: boolean;
|
|
||||||
continueOnError?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
const exec = async (
|
|
||||||
command: string,
|
|
||||||
args: string | string[] = [],
|
|
||||||
ghActionsOptions: IOptions = {},
|
|
||||||
): Promise<ISanitisedCommandResult> => {
|
|
||||||
const options = {
|
|
||||||
output: OutputMode.Tee,
|
|
||||||
verbose: false,
|
|
||||||
continueOnError: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
// log.debug('Command:', command, args);
|
|
||||||
|
|
||||||
const { silent = false, ignoreReturnCode } = ghActionsOptions;
|
|
||||||
if (silent) options.output = OutputMode.Capture;
|
|
||||||
if (ignoreReturnCode) options.continueOnError = true;
|
|
||||||
|
|
||||||
const argsString = typeof args === 'string' ? args : args.join(' ');
|
|
||||||
const result: ICommandResult = await originalExec(`${command} ${argsString}`, options);
|
|
||||||
|
|
||||||
const { status, output = '' } = result;
|
|
||||||
const { code: exitCode, success } = status;
|
|
||||||
|
|
||||||
const symbol = success ? '✅' : '❗';
|
|
||||||
log.debug('Command:', command, argsString, symbol, result);
|
|
||||||
|
|
||||||
return { exitCode, success, output: output.replace(/\n+$/, '') };
|
|
||||||
};
|
|
||||||
|
|
||||||
export { exec };
|
|
||||||
|
|
@ -3,4 +3,3 @@
|
||||||
* This substitutes the parts we use in a Deno-compatible way.
|
* This substitutes the parts we use in a Deno-compatible way.
|
||||||
*/
|
*/
|
||||||
export { core } from './core.ts';
|
export { core } from './core.ts';
|
||||||
export { exec } from './exec.ts';
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue