chore: sanitise more parameters
parent
0379bceff3
commit
2574cad919
|
|
@ -8,3 +8,5 @@ yarn-error.log
|
|||
*.log
|
||||
logs/*
|
||||
!**/.gitkeep
|
||||
.env*
|
||||
!.env*.dist
|
||||
|
|
|
|||
|
|
@ -6,4 +6,6 @@ export interface CommandInterface {
|
|||
name: string;
|
||||
execute: (options: Options) => Promise<boolean>;
|
||||
parseParameters: (input: Input, parameters: Parameters) => Promise<Partial<Parameters>>;
|
||||
configure(options: Options): this;
|
||||
validate(): Promise<this>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,24 +1,18 @@
|
|||
import { exec, OutputMode } from 'https://deno.land/x/exec@0.0.5/mod.ts';
|
||||
import { CommandInterface } from '../command-interface.ts';
|
||||
import { Options } from '../../../config/options.ts';
|
||||
import { Action, Cache, Docker, ImageTag, Input, Output } from '../../../model/index.ts';
|
||||
import PlatformSetup from '../../../model/platform-setup.ts';
|
||||
import MacBuilder from '../../../model/mac-builder.ts';
|
||||
import Parameters from '../../../model/parameters.ts';
|
||||
import { CommandBase } from './command-base.ts';
|
||||
|
||||
export class BuildCommand implements CommandInterface {
|
||||
public readonly name: string;
|
||||
|
||||
constructor(name: string) {
|
||||
this.name = name;
|
||||
export class BuildCommand extends CommandBase implements CommandInterface {
|
||||
public async validate() {
|
||||
await super.validate();
|
||||
}
|
||||
|
||||
public async parseParameters(input: Input, parameters: Parameters) {}
|
||||
|
||||
public async execute(options: Options): Promise<boolean> {
|
||||
public async execute(): Promise<boolean> {
|
||||
try {
|
||||
const { workspace, actionFolder } = Action;
|
||||
const { parameters, env } = options;
|
||||
const { parameters, env } = this.options;
|
||||
|
||||
Action.checkCompatibility();
|
||||
Cache.verify();
|
||||
|
|
@ -35,24 +29,24 @@ export class BuildCommand implements CommandInterface {
|
|||
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
|
||||
await Output.setBuildVersion(parameters.buildVersion);
|
||||
} catch (error) {
|
||||
log.error(error);
|
||||
Deno.exit(1);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,24 +8,27 @@ import { Cli } from '../../../model/cli/cli.ts';
|
|||
import CloudRunnerConstants from '../../../model/cloud-runner/services/cloud-runner-constants.ts';
|
||||
import CloudRunnerBuildGuid from '../../../model/cloud-runner/services/cloud-runner-guid.ts';
|
||||
import { GithubCliReader } from '../../../model/input-readers/github-cli.ts';
|
||||
import { CommandBase } from './command-base.ts';
|
||||
|
||||
// Todo - Verify this entire flow
|
||||
export class BuildRemoteCommand implements CommandInterface {
|
||||
public readonly name: string;
|
||||
|
||||
constructor(name: string) {
|
||||
this.name = name;
|
||||
export class BuildRemoteCommand extends CommandBase implements CommandInterface {
|
||||
public async validate() {
|
||||
await super.validate();
|
||||
}
|
||||
|
||||
public async parseParameters(input: Input, parameters: Parameters) {
|
||||
public async parseParameters(input: Input, parameters: Parameters): Promise<Partial<Parameters>> {
|
||||
const cloudRunnerCluster = Cli.isCliMode
|
||||
? this.input.getInput('cloudRunnerCluster') || 'aws'
|
||||
: this.input.getInput('cloudRunnerCluster') || 'local';
|
||||
|
||||
return {
|
||||
cloudRunnerCluster,
|
||||
cloudRunnerBranch: input.cloudRunnerBranch.split('/').reverse()[0],
|
||||
cloudRunnerIntegrationTests: input.cloudRunnerTests,
|
||||
githubRepo: input.githubRepo || (await GitRepoReader.GetRemote()) || 'game-ci/unity-builder',
|
||||
gitPrivateToken: parameters.gitPrivateToken || (await GithubCliReader.GetGitHubAuthToken()),
|
||||
isCliMode: Cli.isCliMode,
|
||||
awsStackName: input.awsBaseStackName,
|
||||
cloudRunnerCluster: input.cloudRunnerCluster,
|
||||
cloudRunnerBuilderPlatform: input.cloudRunnerBuilderPlatform,
|
||||
awsBaseStackName: input.awsBaseStackName,
|
||||
kubeConfig: input.kubeConfig,
|
||||
|
|
@ -52,19 +55,14 @@ export class BuildRemoteCommand implements CommandInterface {
|
|||
}
|
||||
|
||||
public async execute(options: Options): Promise<boolean> {
|
||||
try {
|
||||
const { buildParameters } = options;
|
||||
const baseImage = new ImageTag(buildParameters);
|
||||
const { buildParameters } = options;
|
||||
const baseImage = new ImageTag(buildParameters);
|
||||
|
||||
const result = await CloudRunner.run(buildParameters, baseImage.toString());
|
||||
const { status, output } = result;
|
||||
const result = await CloudRunner.run(buildParameters, baseImage.toString());
|
||||
const { status, output } = result;
|
||||
|
||||
await Output.setBuildVersion(buildParameters.buildVersion);
|
||||
await Output.setBuildVersion(buildParameters.buildVersion);
|
||||
|
||||
return status.success;
|
||||
} catch (error) {
|
||||
log.error(error);
|
||||
Deno.exit(1);
|
||||
}
|
||||
return status.success;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
import { Options } from '../../../config/options.ts';
|
||||
import { Input } from '../../../model/index.ts';
|
||||
import Parameters from '../../../model/parameters.ts';
|
||||
|
||||
export class CommandBase {
|
||||
public readonly name: string;
|
||||
private options: Options;
|
||||
|
||||
constructor(name: string) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public configure(options: Options): this {
|
||||
this.options = options;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public async validate(): Promise<this> {
|
||||
return this;
|
||||
}
|
||||
|
||||
public async parseParameters(input: Input, parameters: Parameters): Promise<Partial<Parameters>> {
|
||||
return {};
|
||||
}
|
||||
|
||||
public async execute(): Promise<boolean> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
}
|
||||
|
|
@ -4,10 +4,21 @@ export class Environment implements EnvVariables {
|
|||
public readonly os: string;
|
||||
public readonly arch: string;
|
||||
|
||||
constructor(env: Deno.env) {
|
||||
constructor(env: Deno.env, envFile: EnvVariables) {
|
||||
// Make an immutable copy of the environment variables.
|
||||
for (const [key, value] of Object.entries(env.toObject())) {
|
||||
if (value !== undefined) this[key] = value;
|
||||
// Todo - check if this ever happens at all
|
||||
if (value === undefined) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(`Environment variable ${key} is undefined.`);
|
||||
}
|
||||
|
||||
this[key] = value;
|
||||
}
|
||||
|
||||
// Override any env variables that are set in a .env file.
|
||||
for (const [key, value] of Object.entries(envFile)) {
|
||||
this[key] = value;
|
||||
}
|
||||
|
||||
// Override specific variables.
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import * as string from 'https://deno.land/std@0.36.0/strings/mod.ts';
|
|||
import { Command } from 'https://deno.land/x/cmd@v1.2.0/commander/index.ts';
|
||||
import { getUnityChangeset as getUnityChangeSet } from 'https://deno.land/x/unity_changeset@2.0.0/src/index.ts';
|
||||
import { Buffer } from 'https://deno.land/std@0.151.0/io/buffer.ts';
|
||||
import { config, configSync } from 'https://deno.land/std@0.151.0/dotenv/mod.ts';
|
||||
|
||||
// Internally managed packages
|
||||
import waitUntil from './modules/wait-until.ts';
|
||||
|
|
@ -53,6 +54,8 @@ export {
|
|||
Buffer,
|
||||
Command,
|
||||
compress,
|
||||
config,
|
||||
configSync,
|
||||
core,
|
||||
crypto,
|
||||
exec,
|
||||
|
|
|
|||
13
src/index.ts
13
src/index.ts
|
|
@ -1,4 +1,4 @@
|
|||
import './dependencies.ts';
|
||||
import { configSync } from './dependencies.ts';
|
||||
import { configureLogger } from './core/logger/index.ts';
|
||||
import { Options } from './config/options.ts';
|
||||
import { CommandFactory } from './commands/command-factory.ts';
|
||||
|
|
@ -10,23 +10,26 @@ export class GameCI {
|
|||
private readonly env: Environment;
|
||||
|
||||
constructor() {
|
||||
this.env = new Environment(Deno.env);
|
||||
this.env = new Environment(Deno.env, configSync());
|
||||
this.args = Deno.args;
|
||||
}
|
||||
|
||||
public async run() {
|
||||
try {
|
||||
// Infallible configuration
|
||||
const { commandName, subCommands, args, verbosity } = new ArgumentsParser().parse(this.args);
|
||||
|
||||
await configureLogger(verbosity);
|
||||
|
||||
// Determine the command and its options
|
||||
const { engine, engineVersion } = await new EngineDetector(subCommands, args).detect();
|
||||
const command = new CommandFactory().selectEngine(engine, engineVersion).createCommand(commandName, subCommands);
|
||||
const options = await new Options(command, this.env).registerCommand(command).generateParameters(args);
|
||||
await command.configure(options).validate();
|
||||
|
||||
// Execute
|
||||
if (log.isVerbose) log.info('Executing', command.name);
|
||||
|
||||
await command.execute(options);
|
||||
const success = await command.execute();
|
||||
if (!success) log.warning(`Command ${command.name} failed.`);
|
||||
} catch (error) {
|
||||
log.error(error);
|
||||
Deno.exit(1);
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@ import { fsSync as fs } from '../../../dependencies.ts';
|
|||
import { Parameters } from '../../../model/index.ts';
|
||||
|
||||
class ValidateWindows {
|
||||
public static validate(buildParameters: Parameters) {
|
||||
ValidateWindows.validateWindowsPlatformRequirements(buildParameters.targetPlatform);
|
||||
if (!(Deno.env.get('UNITY_EMAIL') && Deno.env.get('UNITY_PASSWORD'))) {
|
||||
public static validate(parameters: Parameters) {
|
||||
ValidateWindows.validateWindowsPlatformRequirements(parameters.targetPlatform);
|
||||
if (!parameters.unityEmail || !parameters.unityPassword) {
|
||||
throw new Error(String.dedent`
|
||||
Unity email and password must be set for Windows based builds to authenticate the license.
|
||||
|
||||
Make sure to set them inside UNITY_EMAIL and UNITY_PASSWORD in Github Secrets and pass them into the environment.
|
||||
Please make sure to set the unityEmail (UNITY_EMAIL) and unityPassword (UNITY_PASSWORD) parameters.
|
||||
`);
|
||||
}
|
||||
}
|
||||
|
|
@ -38,7 +38,7 @@ class ValidateWindows {
|
|||
const windows10SDKPathExists = fs.existsSync('C:/Program Files (x86)/Windows Kits');
|
||||
if (!windows10SDKPathExists) {
|
||||
throw new Error(String.dedent`
|
||||
Windows 10 SDK not found in default location. Make sure this machine has a Windows 10 SDK installed.
|
||||
Windows 10 SDK not found in default location. Please make sure this machine has a Windows 10 SDK installed.
|
||||
|
||||
Download here: https://developer.microsoft.com/en-us/windows/downloads/windows-sdk/
|
||||
`);
|
||||
|
|
@ -54,7 +54,7 @@ class ValidateWindows {
|
|||
throw new Error(String.dedent`
|
||||
Visual Studio not found at the default location.
|
||||
|
||||
Make sure the runner has Visual Studio installed in the default location
|
||||
Please make sure the runner has Visual Studio installed in the default location
|
||||
|
||||
Download here: https://visualstudio.microsoft.com/downloads/
|
||||
`);
|
||||
|
|
|
|||
|
|
@ -25,14 +25,17 @@ class ImageEnvironmentFactory {
|
|||
return string;
|
||||
}
|
||||
public static getEnvironmentVariables(parameters: Parameters) {
|
||||
// Todo - replace with simple for of loop, mapping parameters to this specific format
|
||||
// All parameters should be straight forward at this point in the process.
|
||||
// We can convert between camelCase and UPPER_SNAKE_CASE relatively easily.
|
||||
const environmentVariables: Parameter[] = [
|
||||
{ name: 'UNITY_LICENSE', value: Deno.env.get('UNITY_LICENSE') || ReadLicense() },
|
||||
{ name: 'UNITY_LICENSE_FILE', value: Deno.env.get('UNITY_LICENSE_FILE') },
|
||||
{ name: 'UNITY_EMAIL', value: Deno.env.get('UNITY_EMAIL') },
|
||||
{ name: 'UNITY_PASSWORD', value: Deno.env.get('UNITY_PASSWORD') },
|
||||
{ name: 'UNITY_LICENSE', value: parameters.unityLicense || ReadLicense(parameters) },
|
||||
{ name: 'UNITY_LICENSE_FILE', value: parameters.unityLicenseFile },
|
||||
{ name: 'UNITY_EMAIL', value: parameters.unityEmail },
|
||||
{ name: 'UNITY_PASSWORD', value: parameters.unityPassword },
|
||||
{ name: 'UNITY_SERIAL', value: parameters.unitySerial },
|
||||
{ name: 'UNITY_VERSION', value: parameters.editorVersion },
|
||||
{ name: 'USYM_UPLOAD_AUTH_TOKEN', value: Deno.env.get('USYM_UPLOAD_AUTH_TOKEN') },
|
||||
{ name: 'USYM_UPLOAD_AUTH_TOKEN', value: parameters.uploadAuthToken },
|
||||
{ name: 'PROJECT_PATH', value: parameters.projectPath },
|
||||
{ name: 'BUILD_TARGET', value: parameters.targetPlatform },
|
||||
{ name: 'BUILD_NAME', value: parameters.buildName },
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
import { fsSync as fs, path, yaml, __dirname } from '../../dependencies.ts';
|
||||
import Input from '../input.ts';
|
||||
|
||||
export function ReadLicense() {
|
||||
if (Input.cloudRunnerCluster === 'local') {
|
||||
export function ReadLicense(parameters) {
|
||||
if (parameters.cloudRunnerCluster === 'local') {
|
||||
return '';
|
||||
}
|
||||
|
||||
const pipelineFile = path.join(__dirname, `.github`, `workflows`, `cloud-runner-k8s-pipeline.yml`);
|
||||
|
||||
return fs.existsSync(pipelineFile) ? yaml.parse(Deno.readTextFileSync(pipelineFile, 'utf8')).env.UNITY_LICENSE : '';
|
||||
|
|
|
|||
|
|
@ -113,6 +113,30 @@ class Input {
|
|||
return this.getInput('unityVersion') || 'auto';
|
||||
}
|
||||
|
||||
public get unityEmail() {
|
||||
return this.getInput('unityEmail') || '';
|
||||
}
|
||||
|
||||
public get unityPassword() {
|
||||
return this.getInput('unityPassword') || '';
|
||||
}
|
||||
|
||||
public get unityLicense() {
|
||||
return this.getInput('unityLicense') || '';
|
||||
}
|
||||
|
||||
public get unityLicenseFile() {
|
||||
return this.getInput('unityLicenseFile') || '';
|
||||
}
|
||||
|
||||
public get unitySerial() {
|
||||
return this.getInput('unitySerial') || '';
|
||||
}
|
||||
|
||||
public get usymUploadAuthToken() {
|
||||
return this.getInput('usymUploadAuthToken') || '';
|
||||
}
|
||||
|
||||
public get customImage() {
|
||||
return this.getInput('customImage') || '';
|
||||
}
|
||||
|
|
@ -248,15 +272,6 @@ class Input {
|
|||
return this.getInput('awsBaseStackName') || 'game-ci';
|
||||
}
|
||||
|
||||
// Todo - move to parameters
|
||||
public static get cloudRunnerCluster() {
|
||||
if (Cli.isCliMode) {
|
||||
return this.getInput('cloudRunnerCluster') || 'aws';
|
||||
}
|
||||
|
||||
return this.getInput('cloudRunnerCluster') || 'local';
|
||||
}
|
||||
|
||||
public get cloudRunnerCpu() {
|
||||
return this.getInput('cloudRunnerCpu');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -98,15 +98,24 @@ class Parameters {
|
|||
);
|
||||
log.debug('androidSdkManagerParameters', androidSdkManagerParameters);
|
||||
|
||||
let unitySerial = '';
|
||||
if (!this.env.UNITY_SERIAL && this.input.githubInputEnabled) {
|
||||
// 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 (!this.env.UNITY_LICENSE) {
|
||||
throw new Error(`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 GitHub secret or enter a Unity
|
||||
serial number inside the UNITY_SERIAL GitHub secret.`);
|
||||
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!;
|
||||
|
|
@ -124,7 +133,12 @@ class Parameters {
|
|||
const parameters = {
|
||||
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,
|
||||
targetPlatform,
|
||||
projectPath,
|
||||
|
|
|
|||
|
|
@ -3,14 +3,14 @@ import { SetupMac, SetupWindows } from '../logic/unity/platform-setup/index.ts';
|
|||
import ValidateWindows from '../logic/unity/platform-validation/validate-windows.ts';
|
||||
|
||||
class PlatformSetup {
|
||||
static async setup(buildParameters: Parameters, actionFolder: string) {
|
||||
static async setup(parameters: Parameters, actionFolder: string) {
|
||||
switch (process.platform) {
|
||||
case 'win32':
|
||||
ValidateWindows.validate(buildParameters);
|
||||
SetupWindows.setup(buildParameters);
|
||||
ValidateWindows.validate(parameters);
|
||||
SetupWindows.setup(parameters);
|
||||
break;
|
||||
case 'darwin':
|
||||
await SetupMac.setup(buildParameters, actionFolder);
|
||||
await SetupMac.setup(parameters, actionFolder);
|
||||
break;
|
||||
|
||||
// Add other baseOS's here
|
||||
|
|
|
|||
Loading…
Reference in New Issue