feat: cleanup parameters part 1/*

pull/413/head
Webber 2022-08-24 01:21:00 +02:00
parent bfb847dc81
commit fa479f532a
7 changed files with 239 additions and 196 deletions

View File

@ -5,6 +5,7 @@ import { CommandFactory } from './commands/command-factory.ts';
import { ArgumentsParser } from './core/cli/arguments-parser.ts';
import { Environment } from './core/env/environment.ts';
import { EngineDetector } from './core/engine/engine-detector.ts';
import { ParameterOptions } from './model/parameter-options.ts';
export class GameCI {
private readonly env: Environment;
@ -20,6 +21,10 @@ export class GameCI {
const { commandName, subCommands, args, verbosity } = new ArgumentsParser().parse(this.args);
await configureLogger(verbosity);
// Todo - set default values for parameters to restore functionality.
// Todo - investigate how fitting a CLI lib will be for us
// (now that things are starting to be more separated)
// Determine which command to run
const { engine, engineVersion } = await new EngineDetector(subCommands, args).detect();
const command = new CommandFactory().selectEngine(engine, engineVersion).createCommand(commandName, subCommands);

View File

@ -43,29 +43,29 @@ class SetupMac {
}
}
private static async setEnvironmentVariables(buildParameters: Parameters, actionFolder: string) {
private static async setEnvironmentVariables(parameters: Parameters, actionFolder: string) {
// Need to set environment variables from here because we execute
// the scripts on the host for mac
Deno.env.set('ACTION_FOLDER', actionFolder);
Deno.env.set('UNITY_VERSION', buildParameters.editorVersion);
Deno.env.set('UNITY_SERIAL', buildParameters.unitySerial);
Deno.env.set('PROJECT_PATH', buildParameters.projectPath);
Deno.env.set('BUILD_TARGET', buildParameters.targetPlatform);
Deno.env.set('BUILD_NAME', buildParameters.buildName);
Deno.env.set('BUILD_PATH', buildParameters.buildPath);
Deno.env.set('BUILD_FILE', buildParameters.buildFile);
Deno.env.set('BUILD_METHOD', buildParameters.buildMethod);
Deno.env.set('VERSION', buildParameters.buildVersion);
Deno.env.set('ANDROID_VERSION_CODE', buildParameters.androidVersionCode);
Deno.env.set('ANDROID_KEYSTORE_NAME', buildParameters.androidKeystoreName);
Deno.env.set('ANDROID_KEYSTORE_BASE64', buildParameters.androidKeystoreBase64);
Deno.env.set('ANDROID_KEYSTORE_PASS', buildParameters.androidKeystorePass);
Deno.env.set('ANDROID_KEYALIAS_NAME', buildParameters.androidKeyaliasName);
Deno.env.set('ANDROID_KEYALIAS_PASS', buildParameters.androidKeyaliasPass);
Deno.env.set('ANDROID_TARGET_SDK_VERSION', buildParameters.androidTargetSdkVersion);
Deno.env.set('ANDROID_SDK_MANAGER_PARAMETERS', buildParameters.androidSdkManagerParameters);
Deno.env.set('CUSTOM_PARAMETERS', buildParameters.customParameters);
Deno.env.set('CHOWN_FILES_TO', buildParameters.chownFilesTo);
Deno.env.set('UNITY_VERSION', parameters.editorVersion);
Deno.env.set('UNITY_SERIAL', parameters.unitySerial);
Deno.env.set('PROJECT_PATH', parameters.projectPath);
Deno.env.set('BUILD_TARGET', parameters.targetPlatform);
Deno.env.set('BUILD_NAME', parameters.buildName);
Deno.env.set('BUILD_PATH', parameters.buildPath);
Deno.env.set('BUILD_FILE', parameters.buildFile);
Deno.env.set('BUILD_METHOD', parameters.buildMethod);
Deno.env.set('VERSION', parameters.buildVersion);
Deno.env.set('ANDROID_VERSION_CODE', parameters.androidVersionCode);
Deno.env.set('ANDROID_KEYSTORE_NAME', parameters.androidKeystoreName);
Deno.env.set('ANDROID_KEYSTORE_BASE64', parameters.androidKeystoreBase64);
Deno.env.set('ANDROID_KEYSTORE_PASS', parameters.androidKeystorePass);
Deno.env.set('ANDROID_KEYALIAS_NAME', parameters.androidKeyaliasName);
Deno.env.set('ANDROID_KEYALIAS_PASS', parameters.androidKeyaliasPass);
Deno.env.set('ANDROID_TARGET_SDK_VERSION', parameters.androidTargetSdkVersion);
Deno.env.set('ANDROID_SDK_MANAGER_PARAMETERS', parameters.androidSdkManagerParameters);
Deno.env.set('CUSTOM_PARAMETERS', parameters.customParameters);
Deno.env.set('CHOWN_FILES_TO', parameters.chownFilesTo);
}
}

View File

@ -93,9 +93,7 @@ describe('BuildParameters', () => {
async (targetPlatform) => {
jest.spyOn(Input, 'targetPlatform', 'get').mockReturnValue(targetPlatform);
jest.spyOn(Input, 'buildName', 'get').mockReturnValue(targetPlatform);
expect(Parameters.create()).resolves.toEqual(
expect.objectContaining({ buildFile: `${targetPlatform}.exe` }),
);
expect(Parameters.create()).resolves.toEqual(expect.objectContaining({ buildFile: `${targetPlatform}.exe` }));
},
);
@ -103,18 +101,14 @@ describe('BuildParameters', () => {
jest.spyOn(Input, 'targetPlatform', 'get').mockReturnValue(targetPlatform);
jest.spyOn(Input, 'buildName', 'get').mockReturnValue(targetPlatform);
jest.spyOn(Input, 'androidAppBundle', 'get').mockReturnValue(false);
expect(Parameters.create()).resolves.toEqual(
expect.objectContaining({ buildFile: `${targetPlatform}.apk` }),
);
expect(Parameters.create()).resolves.toEqual(expect.objectContaining({ buildFile: `${targetPlatform}.apk` }));
});
test.each([Platform.types.Android])('appends aab for %s', async (targetPlatform) => {
jest.spyOn(Input, 'targetPlatform', 'get').mockReturnValue(targetPlatform);
jest.spyOn(Input, 'buildName', 'get').mockReturnValue(targetPlatform);
jest.spyOn(Input, 'androidAppBundle', 'get').mockReturnValue(true);
expect(Parameters.create()).resolves.toEqual(
expect.objectContaining({ buildFile: `${targetPlatform}.aab` }),
);
expect(Parameters.create()).resolves.toEqual(expect.objectContaining({ buildFile: `${targetPlatform}.aab` }));
});
it('returns the build method', async () => {
@ -156,9 +150,7 @@ describe('BuildParameters', () => {
it('returns the android target sdk version', async () => {
const mockValue = 'AndroidApiLevelAuto';
jest.spyOn(Input, 'androidTargetSdkVersion', 'get').mockReturnValue(mockValue);
expect(Parameters.create()).resolves.toEqual(
expect.objectContaining({ androidTargetSdkVersion: mockValue }),
);
expect(Parameters.create()).resolves.toEqual(expect.objectContaining({ androidTargetSdkVersion: mockValue }));
});
it('returns the custom parameters', async () => {

View File

@ -3,19 +3,43 @@ import { path, fsSync as fs } from '../dependencies.ts';
import System from './system.ts';
class Docker {
static async run(image, parameters, silent = false) {
log.warning('running docker process for', process.platform, silent);
static async run(image, parameters) {
log.warning('running docker process for', process.platform);
let command = '';
switch (process.platform) {
switch (Deno.build.os) {
case 'windows': {
// Todo: check if docker daemon is set for Windows or Linux containers.
command = await this.getWindowsCommand(image, parameters);
break;
}
case 'linux':
case 'darwin': {
command = await this.getLinuxCommand(image, parameters);
break;
case 'win32':
command = await this.getWindowsCommand(image, parameters);
}
}
const test = await System.newRun(`docker`, command.replace(/\s\s+/, ' ').split(' '), { silent, verbose: true });
log.error('test', test);
try {
const test = await System.run(command, { attach: true });
log.warning('test', test);
} catch (error) {
if (error.message.includes('docker: image operating system "windows" cannot be used on this platform')) {
throw new Error(String.dedent`
Docker daemon is not set to run Windows containers.
To enable the Hyper-V container backend run:
Enable-WindowsOptionalFeature -Online -FeatureName $("Microsoft-Hyper-V", "Containers") -All
To switch the docker daemon to run Windows containers run:
& $Env:ProgramFiles\\Docker\\Docker\\DockerCli.exe -SwitchDaemon .
For more information see:
https://docs.microsoft.com/en-us/virtualization/windowscontainers/quick-start/set-up-environment?tabs=dockerce#prerequisites
`);
}
throw error;
}
}
static async getLinuxCommand(image, parameters): string {
@ -28,8 +52,8 @@ class Docker {
return String.dedent`
docker run \
--workdir /github/workspace \
--rm \
--workdir /github/workspace \
${ImageEnvironmentFactory.getEnvVarString(parameters)} \
--env UNITY_SERIAL \
--env GITHUB_WORKSPACE=/github/workspace \
@ -51,25 +75,23 @@ class Docker {
static async getWindowsCommand(image: any, parameters: any): string {
const { workspace, actionFolder, unitySerial, gitPrivateToken, cliStoragePath } = parameters;
// Todo - get this to work on a non-github runner local machine
// Note: difference between `docker run` and `run`
return String.dedent`run ${image} powershell c:/steps/entrypoint.ps1`;
// Note: the equals sign (=) is needed in Powershell.
return String.dedent`
docker run \
--workdir /github/workspace \
--rm \
--workdir="c:/github/workspace" \
${ImageEnvironmentFactory.getEnvVarString(parameters)} \
--env UNITY_SERIAL="${unitySerial}" \
--env GITHUB_WORKSPACE=c:/github/workspace \
${gitPrivateToken ? `--env GIT_PRIVATE_TOKEN="${gitPrivateToken}"` : ''} \
--volume "${workspace}":"c:/github/workspace" \
--volume "${cliStoragePath}/registry-keys":"c:/registry-keys" \
--volume "C:/Program Files (x86)/Microsoft Visual Studio":"C:/Program Files (x86)/Microsoft Visual Studio" \
--volume "C:/Program Files (x86)/Windows Kits":"C:/Program Files (x86)/Windows Kits" \
--volume "C:/ProgramData/Microsoft/VisualStudio":"C:/ProgramData/Microsoft/VisualStudio" \
--volume "${actionFolder}/default-build-script":"c:/UnityBuilderAction" \
--volume "${actionFolder}/platforms/windows":"c:/steps" \
--volume "${actionFolder}/BlankProject":"c:/BlankProject" \
--env GIT_PRIVATE_TOKEN="${gitPrivateToken}" \
--volume="${workspace}":"c:/github/workspace" \
--volume="${cliStoragePath}/registry-keys":"c:/registry-keys" \
--volume="C:/Program Files (x86)/Microsoft Visual Studio":"C:/Program Files (x86)/Microsoft Visual Studio" \
--volume="C:/Program Files (x86)/Windows Kits":"C:/Program Files (x86)/Windows Kits" \
--volume="C:/ProgramData/Microsoft/VisualStudio":"C:/ProgramData/Microsoft/VisualStudio" \
--volume="${actionFolder}/default-build-script":"c:/UnityBuilderAction" \
--volume="${actionFolder}/platforms/windows":"c:/steps" \
--volume="${actionFolder}/BlankProject":"c:/BlankProject" \
${image} \
powershell c:/steps/entrypoint.ps1
`;

View File

@ -21,15 +21,12 @@ class Input {
return this;
}
// Todo - read something from environment instead and make that into a parameter, then use that.
public static githubInputEnabled: boolean = true;
// Todo - Note that this is now invoked both statically and dynamically - which is a temporary mess.
public getInput(query: string) {
public get(query: string) {
if (this && this.arguments) {
const value = this.arguments.get(query);
if (log.isVeryVerbose) log.debug('arg', query, '=', value);
return this.arguments.get(query);
}
@ -64,25 +61,25 @@ class Input {
}
public get region(): string {
return this.getInput('region') || 'eu-west-2';
return this.get('region');
}
public get githubRepo() {
return this.getInput('GITHUB_REPOSITORY') || this.getInput('GITHUB_REPO') || undefined;
return this.get('GITHUB_REPOSITORY') || this.get('GITHUB_REPO') || undefined;
}
public get branch() {
if (this.getInput(`GITHUB_REF`)) {
return this.getInput(`GITHUB_REF`).replace('refs/', '').replace(`head/`, '').replace(`heads/`, '');
} else if (this.getInput('branch')) {
return this.getInput('branch').replace('/head', '');
if (this.get(`GITHUB_REF`)) {
return this.get(`GITHUB_REF`).replace('refs/', '').replace(`head/`, '').replace(`heads/`, '');
} else if (this.get('branch')) {
return this.get('branch').replace('/head', '');
} else {
return '';
}
}
public get cloudRunnerBuilderPlatform() {
const input = this.getInput('cloudRunnerBuilderPlatform');
const input = this.get('cloudRunnerBuilderPlatform');
if (input) {
return input;
}
@ -94,55 +91,55 @@ class Input {
}
public get gitSha() {
if (this.getInput(`GITHUB_SHA`)) {
return this.getInput(`GITHUB_SHA`);
} else if (this.getInput(`GitSHA`)) {
return this.getInput(`GitSHA`);
if (this.get(`GITHUB_SHA`)) {
return this.get(`GITHUB_SHA`);
} else if (this.get(`GitSHA`)) {
return this.get(`GitSHA`);
}
}
public get runNumber() {
return this.getInput('GITHUB_RUN_NUMBER') || '0';
return this.get('GITHUB_RUN_NUMBER') || '0';
}
public get targetPlatform() {
return this.getInput('targetPlatform') || Platform.default;
return this.get('targetPlatform') || Platform.default;
}
public get unityVersion() {
return this.getInput('unityVersion') || 'auto';
return this.get('unityVersion') || 'auto';
}
public get unityEmail() {
return this.getInput('unityEmail') || '';
return this.get('unityEmail') || '';
}
public get unityPassword() {
return this.getInput('unityPassword') || '';
return this.get('unityPassword') || '';
}
public get unityLicense() {
return this.getInput('unityLicense') || '';
return this.get('unityLicense') || '';
}
public get unityLicenseFile() {
return this.getInput('unityLicenseFile') || '';
return this.get('unityLicenseFile') || '';
}
public get unitySerial() {
return this.getInput('unitySerial') || '';
return this.get('unitySerial') || '';
}
public get usymUploadAuthToken() {
return this.getInput('usymUploadAuthToken') || '';
return this.get('usymUploadAuthToken') || '';
}
public get customImage() {
return this.getInput('customImage') || '';
return this.get('customImage') || '';
}
public get projectPath() {
let input = this.getInput('projectPath');
let input = this.get('projectPath');
// Todo - remove hardcoded test project reference
const isTestProject =
@ -156,172 +153,169 @@ class Input {
}
public get buildName() {
return this.getInput('buildName') || this.targetPlatform;
return this.get('buildName');
}
public get buildsPath() {
return this.getInput('buildsPath') || 'build';
return this.get('buildsPath') || 'build';
}
public get buildMethod() {
return this.getInput('buildMethod') || ''; // Processed in docker file
return this.get('buildMethod') || ''; // Processed in docker file
}
public get customParameters() {
return this.getInput('customParameters') || '';
return this.get('customParameters') || '';
}
public get versioningStrategy() {
return this.getInput('versioning') || 'Semantic';
return this.get('versioning') || 'Semantic';
}
public get specifiedVersion() {
return this.getInput('version') || '';
return this.get('version') || '';
}
public get androidVersionCode() {
return this.getInput('androidVersionCode');
return this.get('androidVersionCode');
}
public get androidAppBundle() {
const input = this.getInput('androidAppBundle') || false;
const input = this.get('androidAppBundle') || false;
return input === 'true';
}
public get androidKeystoreName() {
return this.getInput('androidKeystoreName') || '';
return this.get('androidKeystoreName') || '';
}
public get androidKeystoreBase64() {
return this.getInput('androidKeystoreBase64') || '';
return this.get('androidKeystoreBase64') || '';
}
public get androidKeystorePass() {
return this.getInput('androidKeystorePass') || '';
return this.get('androidKeystorePass') || '';
}
public get androidKeyaliasName() {
return this.getInput('androidKeyaliasName') || '';
return this.get('androidKeyaliasName') || '';
}
public get androidKeyaliasPass() {
return this.getInput('androidKeyaliasPass') || '';
return this.get('androidKeyaliasPass') || '';
}
public get androidTargetSdkVersion() {
return this.getInput('androidTargetSdkVersion') || '';
return this.get('androidTargetSdkVersion') || '';
}
public get sshAgent() {
return this.getInput('sshAgent') || '';
return this.get('sshAgent') || '';
}
public get gitPrivateToken() {
return core.getInput('gitPrivateToken') || false;
return this.get('gitPrivateToken') || '';
}
public get customJob() {
return this.getInput('customJob') || '';
return this.get('customJob') || '';
}
public customJobHooks() {
return this.getInput('customJobHooks') || '';
return this.get('customJobHooks') || '';
}
public cachePushOverrideCommand() {
return this.getInput('cachePushOverrideCommand') || '';
return this.get('cachePushOverrideCommand') || '';
}
public cachePullOverrideCommand() {
return this.getInput('cachePullOverrideCommand') || '';
return this.get('cachePullOverrideCommand') || '';
}
public readInputFromOverrideList() {
return this.getInput('readInputFromOverrideList') || '';
return this.get('readInputFromOverrideList') || '';
}
public readInputOverrideCommand() {
return this.getInput('readInputOverrideCommand') || '';
return this.get('readInputOverrideCommand') || '';
}
public get cloudRunnerBranch() {
return this.getInput('cloudRunnerBranch') || 'cloud-runner-develop';
return this.get('cloudRunnerBranch') || 'cloud-runner-develop';
}
public get chownFilesTo() {
return this.getInput('chownFilesTo') || '';
return this.get('chownFilesTo') || '';
}
public get allowDirtyBuild() {
const input = this.getInput('allowDirtyBuild');
const input = this.get('allowDirtyBuild');
log.debug('input === ', input);
return input || false === true;
}
public get postBuildSteps() {
return this.getInput('postBuildSteps') || '';
return this.get('postBuildSteps') || '';
}
public get preBuildSteps() {
return this.getInput('preBuildSteps') || '';
return this.get('preBuildSteps') || '';
}
public get awsBaseStackName() {
return this.getInput('awsBaseStackName') || 'game-ci';
return this.get('awsBaseStackName') || 'game-ci';
}
public get cloudRunnerCpu() {
return this.getInput('cloudRunnerCpu');
return this.get('cloudRunnerCpu');
}
public get cloudRunnerMemory() {
return this.getInput('cloudRunnerMemory');
return this.get('cloudRunnerMemory');
}
public get kubeConfig() {
return this.getInput('kubeConfig') || '';
return this.get('kubeConfig') || '';
}
public get kubeVolume() {
return this.getInput('kubeVolume') || '';
return this.get('kubeVolume') || '';
}
public get kubeVolumeSize() {
return this.getInput('kubeVolumeSize') || '5Gi';
return this.get('kubeVolumeSize') || '5Gi';
}
public get kubeStorageClass(): string {
return this.getInput('kubeStorageClass') || '';
return this.get('kubeStorageClass') || '';
}
public get checkDependencyHealthOverride(): string {
return this.getInput('checkDependencyHealthOverride') || '';
return this.get('checkDependencyHealthOverride') || '';
}
public get startDependenciesOverride(): string {
return this.getInput('startDependenciesOverride') || '';
return this.get('startDependenciesOverride') || '';
}
public get cacheKey(): string {
return this.getInput('cacheKey') || Input.branch;
return this.get('cacheKey') || Input.branch;
}
public get cloudRunnerTests(): boolean {
return this.getInput(`cloudRunnerTests`) || false;
return this.get(`cloudRunnerTests`) || false;
}
/**
* @deprecated Use Parameter.toEnvFormat
*/
public static toEnvVarFormat(input: string) {
if (input.toUpperCase() === input) {
return input;
}
if (input.toUpperCase() === input) return input;
return input
.replace(/([A-Z])/g, ' $1')
.trim()
.toUpperCase()
.replace(/ /g, '_');
return input.replace(/([\da-z])([A-Z])/g, '$1_$2').toUpperCase();
}
}

View File

@ -7,6 +7,7 @@ import Versioning from './versioning.ts';
import { GitRepoReader } from './input-readers/git-repo.ts';
import { CommandInterface } from '../commands/command/command-interface.ts';
import { Environment } from '../core/env/environment.ts';
import { Parameter } from './parameter.ts';
class Parameters {
private command: CommandInterface;
@ -64,85 +65,70 @@ class Parameters {
public cloudRunnerBuilderPlatform!: string | undefined;
public isCliMode!: boolean;
private defaults: Partial<Parameters> = {
region: 'eu-west-2',
};
private readonly input: Input;
private readonly env: Environment;
constructor(input: Input, env: Environment) {
this.input = input;
this.env = env;
// Todo - ~/.gameci should hold a config with default settings, like cloud region = 'eu-west-2'
// this.config = config;
}
public get(query, useDefault = true) {
const defaultValue = useDefault ? this.default(query) : undefined;
const value = this.input.get(query) || this.env.get(Parameter.toUpperSnakeCase(query)) || defaultValue;
if (log.isVeryVerbose) log.debug('Argument:', query, '=', value);
return value;
}
private default(query) {
return this.defaults[query];
}
public async parse(): Promise<Parameters> {
const cliStoragePath = `${getHomeDir()}/.game-ci`;
const targetPlatform = this.input.get('targetPlatform');
const buildsPath = this.input.get('buildsPath');
const projectPath = this.get('projectPath');
const unityVersion = this.get('unityVersion');
const versioningStrategy = this.get('versioningStrategy');
const specifiedVersion = this.get('specifiedVersion');
const allowDirtyBuild = this.get('allowDirtyBuild');
const androidTargetSdkVersion = this.get('androidTargetSdkVersion');
const buildFile = Parameters.parseBuildFile(
this.input.buildName,
this.input.targetPlatform,
this.input.androidAppBundle,
);
log.debug('buildFile:', buildFile);
const editorVersion = UnityVersioning.determineUnityVersion(this.input.projectPath, this.input.unityVersion);
log.info('Detected editorVersion', editorVersion);
const buildVersion = await Versioning.determineBuildVersion(
this.input.versioningStrategy,
this.input.specifiedVersion,
this.input.allowDirtyBuild,
);
log.debug('buildVersion', buildVersion);
const androidVersionCode = AndroidVersioning.determineVersionCode(buildVersion, this.input.androidVersionCode);
log.debug('androidVersionCode', androidVersionCode);
const androidSdkManagerParameters = AndroidVersioning.determineSdkManagerParameters(
this.input.androidTargetSdkVersion,
);
log.debug('androidSdkManagerParameters', androidSdkManagerParameters);
// Commandline takes precedence over environment variables
const unityEmail = this.input.unityEmail || this.env.get('UNITY_EMAIL');
const unityPassword = this.input.unityPassword || this.env.get('UNITY_PASSWORD');
const unityLicense = this.input.unityLicense || this.env.get('UNITY_LICENSE');
const unityLicenseFile = this.input.unityLicenseFile || this.env.get('UNITY_LICENSE_FILE');
let unitySerial = this.input.unitySerial || this.env.get('UNITY_SERIAL');
// For Windows, we need to use the serial from the license file
if (!unitySerial && this.input.githubInputEnabled) {
// No serial was present, so it is a personal license that we need to convert
if (!unityLicense) {
throw new Error(String.dedent`
Missing Unity License File and no Serial was found. If this is a personal license,
make sure to follow the activation steps and set the UNITY_LICENSE variable or enter
a Unity serial number inside the UNITY_SERIAL variable.
`);
}
unitySerial = this.getSerialFromLicenseFile(this.env.UNITY_LICENSE);
} else {
unitySerial = this.env.UNITY_SERIAL!;
}
const buildName = this.input.get('buildName') || targetPlatform;
const buildFile = Parameters.parseBuildFile(buildName, targetPlatform, this.get('androidAppBundle'));
const buildPath = `${buildsPath}/${targetPlatform}`;
const editorVersion = UnityVersioning.determineUnityVersion(projectPath, unityVersion);
const buildVersion = await Versioning.determineBuildVersion(versioningStrategy, specifiedVersion, allowDirtyBuild);
const androidVersionCode = AndroidVersioning.determineVersionCode(buildVersion, this.get('androidVersionCode'));
const androidSdkManagerParameters = AndroidVersioning.determineSdkManagerParameters(androidTargetSdkVersion);
const branch = (await Versioning.getCurrentBranch()) || (await GitRepoReader.GetBranch());
log.info(`branch: "${branch}"`);
const projectPath = this.input.projectPath;
log.info(`projectPath: "${projectPath}"`);
const targetPlatform = this.input.targetPlatform;
log.info(`targetPlatform: "${targetPlatform}"`);
const parameters = {
branch,
unityEmail: this.get('unityEmail'),
unityPassword: this.get('unityPassword'),
unityLicense: this.get('unityLicense'),
unityLicenseFile: this.get('unityLicenseFile'),
unitySerial: this.getUnitySerial(),
cliStoragePath,
editorVersion,
customImage: this.input.customImage,
unityEmail,
unityPassword,
unityLicense,
unityLicenseFile,
unitySerial,
usymUploadAuthToken: this.input.usymUploadAuthToken || this.env.get('USYM_UPLOAD_AUTH_TOKEN'),
runnerTempPath: this.env.RUNNER_TEMP,
customImage: this.get('customImage'),
usymUploadAuthToken: this.get('usymUploadAuthToken'),
runnerTempPath: this.env.get('RUNNER_TEMP'),
targetPlatform,
projectPath,
buildName: this.input.buildName,
buildPath: `${this.input.buildsPath}/${this.input.targetPlatform}`,
buildName,
buildPath,
buildFile,
buildMethod: this.input.buildMethod,
buildVersion,
@ -152,14 +138,13 @@ class Parameters {
androidKeystorePass: this.input.androidKeystorePass,
androidKeyaliasName: this.input.androidKeyaliasName,
androidKeyaliasPass: this.input.androidKeyaliasPass,
androidTargetSdkVersion: this.input.androidTargetSdkVersion,
androidTargetSdkVersion,
androidSdkManagerParameters,
customParameters: this.input.customParameters,
customParameters: this.get('customParameters'),
sshAgent: this.input.sshAgent,
gitPrivateToken: this.input.gitPrivateToken,
gitPrivateToken: this.get('gitPrivateToken'),
chownFilesTo: this.input.chownFilesTo,
customJob: this.input.customJob,
branch,
};
const commandParameterOverrides = await this.command.parseParameters(this.input, parameters);
@ -183,7 +168,27 @@ class Parameters {
return filename;
}
static getSerialFromLicenseFile(license) {
static getUnitySerial() {
let unitySerial = this.get('unitySerial');
if (!unitySerial && this.env.getOS() === 'windows') {
const unityLicense = this.get('unityLicense');
unitySerial = this.getSerialFromLicense(unityLicense);
}
return unitySerial;
}
static getSerialFromLicense(license) {
if (!license) {
throw new Error(String.dedent`
Missing Unity License File and no Unity Serial was found. If this is a personal license,
make sure to follow the activation steps and set the UNITY_LICENSE variable or enter
a Unity serial number inside the UNITY_SERIAL variable.
`);
}
const startKey = `<DeveloperData Value="`;
const endKey = `"/>`;
const startIndex = license.indexOf(startKey) + startKey.length;

View File

@ -1,5 +1,6 @@
export interface RunOptions {
pwd: string;
attach: boolean;
}
class System {
@ -7,7 +8,7 @@ class System {
* Run any command as if you're typing in shell.
* Make sure it's Windows/MacOS/Ubuntu compatible or has alternative commands.
*
* Intended to always be silent and capture the output.
* Intended to always be silent and capture the output, unless attach is passed.
*
* @returns {string} output of the command on success or failure
*
@ -28,11 +29,15 @@ class System {
}
static async shellRun(command: string, options: RunOptions = {}): Promise<string> {
return System.newRun('sh', ['-c', command]);
const { attach } = options;
return attach ? System.runAndAttach('sh', ['-c', command]) : System.runAndCapture('sh', ['-c', command]);
}
static async powershellRun(command: string, options: RunOptions = {}): Promise<string> {
return System.newRun('powershell', [command]);
const { attach } = options;
return attach ? System.runAndAttach('powershell', [command]) : System.runAndCapture('powershell', [command]);
}
/**
@ -51,7 +56,7 @@ class System {
*
* @deprecated not really deprecated, but please use System.run instead because this method will be made private.
*/
public static async newRun(command, args: string | string[] = []): Promise<string> {
public static async runAndCapture(command, args: string | string[] = []): Promise<string> {
if (!Array.isArray(args)) args = [args];
const argsString = args.join(' ');
@ -92,6 +97,26 @@ class System {
return result;
}
/**
* Output stdout and stderr to the terminal and attach to the process.
*
* Note that the return signature is slightly different from runAndCapture, because we don't have stderrOutput.
*
* Todo - it would be nice to pipe the output to both stdout and capture it in the result object, but this doesn't seem possible yet.
*/
private static async runAndAttach(command, args: string | string[] = []): Promise<string> {
if (!Array.isArray(args)) args = [args];
const process = Deno.run({ cmd: [command, ...args] });
const status = await process.status();
process.close();
if (!status.success) throw new Error(`Command failed with code ${status.code}`);
return { status, output: 'runAndAttach has access to the output stream' };
}
}
export default System;