diff --git a/action.yml b/action.yml index 1e0e6a95..07d42d35 100644 --- a/action.yml +++ b/action.yml @@ -169,11 +169,62 @@ inputs: outputs: volume: description: 'The Persistent Volume (PV) where the build artifacts have been stored by Kubernetes' + value: '' buildVersion: description: 'The generated version used for the Unity build' + value: '' +runs: + using: 'composite' + steps: + - run: echo "Using GameCI CLI to build project" + shell: bash + - uses: denoland/setup-deno@v1 + with: + deno-version: v1.x + - run: | + deno run --allow-run ./src/index.ts build \ + --targetPlatform="${{ inputs.targetPlatform }}" \ + --unityVersion="${{ inputs.unityVersion }}" \ + --customImage="${{ inputs.customImage }}" \ + --projectPath="${{ inputs.projectPath }}" \ + --buildName="${{ inputs.buildName }}" \ + --buildsPath="${{ inputs.buildsPath }}" \ + --buildMethod="${{ inputs.buildMethod }}" \ + --customParameters="${{ inputs.customParameters }}" \ + --versioning="${{ inputs.versioning }}" \ + --version="${{ inputs.version }}" \ + --androidVersionCode="${{ inputs.androidVersionCode }}" \ + --androidAppBundle="${{ inputs.androidAppBundle }}" \ + --androidKeystoreName="${{ inputs.androidKeystoreName }}" \ + --androidKeystoreBase64="${{ inputs.androidKeystoreBase64 }}" \ + --androidKeystorePass="${{ inputs.androidKeystorePass }}" \ + --androidKeyaliasName="${{ inputs.androidKeyaliasName }}" \ + --androidKeyaliasPass="${{ inputs.androidKeyaliasPass }}" \ + --androidTargetSdkVersion="${{ inputs.androidTargetSdkVersion }}" \ + --sshAgent="${{ inputs.sshAgent }}" \ + --gitPrivateToken="${{ inputs.gitPrivateToken }}" \ + --chownFilesTo="${{ inputs.chownFilesTo }}" \ + --allowDirtyBuild="${{ inputs.allowDirtyBuild }}" \ + --postBuildSteps="${{ inputs.postBuildSteps }}" \ + --preBuildSteps="${{ inputs.preBuildSteps }}" \ + --customJobHooks="${{ inputs.customJobHooks }}" \ + --customJob="${{ inputs.customJob }}" \ + --awsBaseStackName="${{ inputs.awsBaseStackName }}" \ + --cloudRunnerCluster="${{ inputs.cloudRunnerCluster }}" \ + --cloudRunnerCpu="${{ inputs.cloudRunnerCpu }}" \ + --cloudRunnerMemory="${{ inputs.cloudRunnerMemory }}" \ + --cachePushOverrideCommand="${{ inputs.cachePushOverrideCommand }}" \ + --cachePullOverrideCommand="${{ inputs.cachePullOverrideCommand }}" \ + --readInputFromOverrideList="${{ inputs.readInputFromOverrideList }}" \ + --readInputOverrideCommand="${{ inputs.readInputOverrideCommand }}" \ + --kubeConfig="${{ inputs.kubeConfig }}" \ + --kubeVolume="${{ inputs.kubeVolume }}" \ + --kubeStorageClass="${{ inputs.kubeStorageClass }}" \ + --kubeVolumeSize="${{ inputs.kubeVolumeSize }}" \ + --cacheKey="${{ inputs.cacheKey }}" \ + --checkDependencyHealthOverride="${{ inputs.checkDependencyHealthOverride }}" \ + --startDependenciesOverride="${{ inputs.startDependenciesOverride }}" + shell: bash branding: icon: 'box' color: 'gray-dark' -runs: - using: 'node12' - main: 'dist/index.js' diff --git a/src/commands/command-factory.ts b/src/commands/command-factory.ts new file mode 100644 index 00000000..597c6fc7 --- /dev/null +++ b/src/commands/command-factory.ts @@ -0,0 +1,18 @@ +import { NonExistentCommand } from './command/non-existent-command.ts'; +import { BuildCommand } from './command/build-command.ts'; +import { BuildRemoteCommand } from './command/build-remote-command.ts'; + +export class CommandFactory { + constructor() {} + + public createCommand(commandName) { + switch (commandName) { + case 'build': + return new BuildCommand(); + case 'build-remote': + return new BuildRemoteCommand(); + default: + return new NonExistentCommand(commandName); + } + } +} diff --git a/src/commands/command/build-command.ts b/src/commands/command/build-command.ts new file mode 100644 index 00000000..df6683ac --- /dev/null +++ b/src/commands/command/build-command.ts @@ -0,0 +1,60 @@ +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, CloudRunner, Docker, ImageTag, Output } from '../../model/index.ts'; +import PlatformSetup from '../../model/platform-setup.ts'; +import { core, process } from '../../dependencies.ts'; +import MacBuilder from '../../model/mac-builder.ts'; + +export class BuildCommand implements CommandInterface { + public readonly name: string; + + constructor(name: string) { + this.name = name; + } + + public async execute(options: Options): Promise { + try { + log.info('options', options); + const { workspace, actionFolder } = Action; + const { buildParameters } = options; + + Action.checkCompatibility(); + Cache.verify(); + + const baseImage = new ImageTag(buildParameters); + log.debug('baseImage', baseImage); + + if (buildParameters.cloudRunnerCluster !== 'local') { + await CloudRunner.run(buildParameters, baseImage.toString()); + } else { + log.info('Building locally'); + await PlatformSetup.setup(buildParameters, actionFolder); + if (process.platform === 'darwin') { + MacBuilder.run(actionFolder, workspace, buildParameters); + } else { + await Docker.run(baseImage, { workspace, actionFolder, ...buildParameters }); + } + } + + // Set output + await Output.setBuildVersion(buildParameters.buildVersion); + } catch (error) { + log.error(error); + core.setFailed((error as Error).message); + } + + 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; + } +} diff --git a/src/commands/command/build-remote-command.ts b/src/commands/command/build-remote-command.ts new file mode 100644 index 00000000..4b582897 --- /dev/null +++ b/src/commands/command/build-remote-command.ts @@ -0,0 +1,30 @@ +import { CommandInterface } from './command-interface.ts'; +import { Options } from '../../config/options.ts'; +import { CloudRunner, ImageTag, Output } from '../../model/index.ts'; +import { core } from '../../dependencies.ts'; + +// Todo - Verify this entire flow +export class BuildRemoteCommand implements CommandInterface { + public readonly name: string; + + constructor(name: string) { + this.name = name; + } + + public async execute(options: Options): Promise { + try { + const { buildParameters } = options; + const baseImage = new ImageTag(buildParameters); + + const result = await CloudRunner.run(buildParameters, baseImage.toString()); + const { status, output } = result; + + await Output.setBuildVersion(buildParameters.buildVersion); + + return status.success; + } catch (error) { + log.error(error); + core.setFailed((error as Error).message); + } + } +} diff --git a/src/commands/command/command-interface.ts b/src/commands/command/command-interface.ts new file mode 100644 index 00000000..667845f2 --- /dev/null +++ b/src/commands/command/command-interface.ts @@ -0,0 +1,6 @@ +import { Options } from '../../config/options.ts'; + +export interface CommandInterface { + name: string; + execute: (options: Options) => Promise; +} diff --git a/src/commands/command/non-existent-command.ts b/src/commands/command/non-existent-command.ts new file mode 100644 index 00000000..193316ea --- /dev/null +++ b/src/commands/command/non-existent-command.ts @@ -0,0 +1,14 @@ +import { CommandInterface } from './command-interface.ts'; +import { Options } from '../../config/options.ts'; + +export class NonExistentCommand implements CommandInterface { + public name: string; + + constructor(name: string) { + this.name = name; + } + + public async execute(options: Options): Promise { + throw new Error(`Command ${this.name} does not exist`); + } +} diff --git a/src/config/options.ts b/src/config/options.ts new file mode 100644 index 00000000..c1a2b2ee --- /dev/null +++ b/src/config/options.ts @@ -0,0 +1,23 @@ +import { CliArguments } from '../core/cli/cli-arguments.ts'; +import { EnvVariables } from '../core/env/env-variables.ts'; +import { Parameters, Input } from '../model/index.ts'; + +export class Options { + public input: Input; + public parameters: Parameters; + + constructor(env: EnvVariables) { + this.env = env; + + return this; + } + + public async generateParameters(args: CliArguments) { + this.input = new Input(args); + this.parameters = await new Parameters(this.input).parse(); + + log.debug('Parameters generated.'); + + return this; + } +} diff --git a/src/core/cli/arguments-parser.ts b/src/core/cli/arguments-parser.ts new file mode 100644 index 00000000..b0402c2d --- /dev/null +++ b/src/core/cli/arguments-parser.ts @@ -0,0 +1,12 @@ +import { parseArgv } from './parse-argv.ts'; + +export class ArgumentsParser { + public parse(cliArguments: string[]) { + const [commandName, ...args] = cliArguments; + + return { + commandName, + args: parseArgv(args), + }; + } +} diff --git a/src/core/cli/cli-arguments.ts b/src/core/cli/cli-arguments.ts new file mode 100644 index 00000000..107b2e9c --- /dev/null +++ b/src/core/cli/cli-arguments.ts @@ -0,0 +1 @@ +export type CliArguments = Map; diff --git a/src/core/cli/parse-argv.ts b/src/core/cli/parse-argv.ts new file mode 100644 index 00000000..009cdddf --- /dev/null +++ b/src/core/cli/parse-argv.ts @@ -0,0 +1,51 @@ +import { CliArguments } from './cli-arguments.ts'; + +/** + * Parse command line arguments + * + * Usage: + * console.dir(parseArgv(Deno.args)); // Deno + * console.log(parseArgv(process.argv)); // Node + * + * Example: + * deno run my-script -test1=1 -test2 "2" -test3 -test4 false -test5 "one" -test6= -test7=9BX9 + * + * Output: + * Map { + * "test1" => 1, + * "test2" => 2, + * "test3" => true, + * "test4" => false, + * "test5" => "one", + * "test6" => "", + * "test7" => "9BX9" + * } + */ +export const parseArgv = (argv: string[] = [], { verbose = false } = {}): CliArguments => { + const providedArguments = new Map(); + + for (let current = 0, next = 1; current < argv.length; current += 1, next += 1) { + // Detect flag + if (!argv[current].startsWith('-')) continue; + let flag = argv[current].replace(/^-+/, ''); + + // Detect value + const hasNextArgument = next < argv.length && !argv[next].startsWith('-'); + let value: string | number | boolean = hasNextArgument ? argv[next] : 'true'; + + // Split combinations + const isCombination = flag.includes('='); + if (isCombination) [flag, value] = flag.split('='); + + // Parse types + if (['true', 'false'].includes(value)) value = value === 'true'; + else if (!Number.isNaN(Number(value)) && !Number.isNaN(Number.parseInt(value))) value = Number.parseInt(value); + + // Assign + // eslint-disable-next-line no-console + if (verbose) console.log(`Found flag "${flag}" with value "${value}" (${typeof value}).`); + providedArguments.set(flag, value); + } + + return providedArguments; +}; diff --git a/src/core/env/env-variables.ts b/src/core/env/env-variables.ts new file mode 100644 index 00000000..2bf7e0b4 --- /dev/null +++ b/src/core/env/env-variables.ts @@ -0,0 +1 @@ +export type EnvVariables = { [index: string]: string }; diff --git a/src/core/logger/formatter.ts b/src/core/logger/formatter.ts index 96025646..6467a974 100644 --- a/src/core/logger/formatter.ts +++ b/src/core/logger/formatter.ts @@ -19,6 +19,19 @@ export const createFormatter = ({ return pad(value, totalWidth, paddingOptions); }; + const formatValue = (value) => { + switch (typeof value) { + case 'object': + return Deno.inspect(value, { depth }); + case 'undefined': + return 'undefined'; + case 'string': + return value; + default: + return `${value} (${typeof value})`; + } + }; + return ({ level, levelName, msg, args, loggerName }: LogRecord) => { let line = ''; @@ -46,23 +59,12 @@ export const createFormatter = ({ if (msg) { if (line.length > 0) line += ' '; - line += msg; + line += formatValue(msg); } if (args) { if (line.length > 0) line += ' '; - line += args - .map((value) => { - switch (typeof value) { - case 'object': - return Deno.inspect(value, { depth }); - case 'undefined': - return 'undefined'; - default: - return value; - } - }) - .join(' '); + line += args.map((arg) => formatValue(arg)).join(' '); } return line; diff --git a/src/index.ts b/src/index.ts index ef0a0930..525cda03 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,49 +1,38 @@ import './core/logger/index.ts'; -import { core, process } from './dependencies.ts'; -import { Action, BuildParameters, Cache, CloudRunner, Docker, ImageTag, Output } from './model/index.ts'; -import { Cli } from './model/cli/cli.ts'; -import MacBuilder from './model/mac-builder.ts'; -import PlatformSetup from './model/platform-setup.ts'; +import type { CommandInterface } from './commands/command/command-interface.ts'; +import type { EnvVariables } from './core/env/env-variables.ts'; +import { Options } from './config/options.ts'; +import { CommandFactory } from './commands/command-factory.ts'; +import { ArgumentsParser } from './core/cli/arguments-parser.ts'; -async function runMain() { - try { - if (Cli.InitCliMode()) { - // Todo - this is only here for testing the entire flow in deno and making sure I'm hitting the right path - log.error('CloudBuilder CLI mode'); - await Cli.RunCli(); +export class GameCI { + private readonly commandFactory: CommandFactory; + private readonly argumentsParser: ArgumentsParser; + private readonly env: EnvVariables; - return; + private options?: Options; + private command?: CommandInterface; + + constructor(envVariables: EnvVariables) { + this.env = envVariables; + + this.commandFactory = new CommandFactory(); + this.argumentsParser = new ArgumentsParser(); + } + + public async run(cliArguments: string[]) { + try { + const { commandName, args } = this.argumentsParser.parse(cliArguments); + + this.options = await new Options(this.env).generateParameters(args); + this.command = this.commandFactory.createCommand(commandName); + + await this.command.execute(this.options); + } catch (error) { + log.error(error); + Deno.exit(1); } - Action.checkCompatibility(); - Cache.verify(); - - const { workspace, actionFolder } = Action; - log.debug('workspace', workspace, 'actionFolder', actionFolder); - - const buildParameters = await BuildParameters.create(); - log.debug('buildParameters', buildParameters); - - const baseImage = new ImageTag(buildParameters); - log.debug('baseImage', baseImage); - - if (buildParameters.cloudRunnerCluster !== 'local') { - await CloudRunner.run(buildParameters, baseImage.toString()); - } else { - log.info('Building locally'); - await PlatformSetup.setup(buildParameters, actionFolder); - if (process.platform === 'darwin') { - MacBuilder.run(actionFolder, workspace, buildParameters); - } else { - await Docker.run(baseImage, { workspace, actionFolder, ...buildParameters }); - } - } - - // Set output - await Output.setBuildVersion(buildParameters.buildVersion); - } catch (error) { - log.error(error); - core.setFailed((error as Error).message); } } -await runMain(); +await new GameCI(Deno.env.toObject()).run(Deno.args); diff --git a/src/model/build-parameters.test.ts b/src/model/build-parameters.test.ts index 1e24d81b..24a39596 100644 --- a/src/model/build-parameters.test.ts +++ b/src/model/build-parameters.test.ts @@ -1,7 +1,7 @@ import Versioning from './versioning.ts'; import UnityVersioning from './unity-versioning.ts'; import AndroidVersioning from './android-versioning.ts'; -import BuildParameters from './build-parameters.ts'; +import Parameters from './parameters.ts'; import Input from './input.ts'; import Platform from './platform.ts'; @@ -25,52 +25,52 @@ afterEach(() => { describe('BuildParameters', () => { describe('create', () => { it('does not throw', async () => { - await expect(BuildParameters.create()).resolves.not.toThrow(); + await expect(Parameters.create()).resolves.not.toThrow(); }); it('determines the version only once', async () => { - await BuildParameters.create(); + await Parameters.create(); expect(determineVersion).toHaveBeenCalledTimes(1); }); it('determines the unity version only once', async () => { - await BuildParameters.create(); + await Parameters.create(); expect(determineUnityVersion).toHaveBeenCalledTimes(1); }); it('returns the android version code with provided input', async () => { const mockValue = '42'; jest.spyOn(Input, 'androidVersionCode', 'get').mockReturnValue(mockValue); - expect(BuildParameters.create()).resolves.toEqual(expect.objectContaining({ androidVersionCode: mockValue })); + expect(Parameters.create()).resolves.toEqual(expect.objectContaining({ androidVersionCode: mockValue })); }); it('returns the android version code from version by default', async () => { const mockValue = ''; jest.spyOn(Input, 'androidVersionCode', 'get').mockReturnValue(mockValue); - expect(BuildParameters.create()).resolves.toEqual(expect.objectContaining({ androidVersionCode: 1_003_037 })); + expect(Parameters.create()).resolves.toEqual(expect.objectContaining({ androidVersionCode: 1_003_037 })); }); it('determines the android sdk manager parameters only once', async () => { - await BuildParameters.create(); + await Parameters.create(); expect(determineSdkManagerParameters).toHaveBeenCalledTimes(1); }); it('returns the targetPlatform', async () => { const mockValue = 'somePlatform'; jest.spyOn(Input, 'targetPlatform', 'get').mockReturnValue(mockValue); - expect(BuildParameters.create()).resolves.toEqual(expect.objectContaining({ targetPlatform: mockValue })); + expect(Parameters.create()).resolves.toEqual(expect.objectContaining({ targetPlatform: mockValue })); }); it('returns the project path', async () => { const mockValue = 'path/to/project'; jest.spyOn(Input, 'projectPath', 'get').mockReturnValue(mockValue); - expect(BuildParameters.create()).resolves.toEqual(expect.objectContaining({ projectPath: mockValue })); + expect(Parameters.create()).resolves.toEqual(expect.objectContaining({ projectPath: mockValue })); }); it('returns the build name', async () => { const mockValue = 'someBuildName'; jest.spyOn(Input, 'buildName', 'get').mockReturnValue(mockValue); - expect(BuildParameters.create()).resolves.toEqual(expect.objectContaining({ buildName: mockValue })); + expect(Parameters.create()).resolves.toEqual(expect.objectContaining({ buildName: mockValue })); }); it('returns the build path', async () => { @@ -79,13 +79,13 @@ describe('BuildParameters', () => { const expectedBuildPath = `${mockPath}/${mockPlatform}`; jest.spyOn(Input, 'buildsPath', 'get').mockReturnValue(mockPath); jest.spyOn(Input, 'targetPlatform', 'get').mockReturnValue(mockPlatform); - expect(BuildParameters.create()).resolves.toEqual(expect.objectContaining({ buildPath: expectedBuildPath })); + expect(Parameters.create()).resolves.toEqual(expect.objectContaining({ buildPath: expectedBuildPath })); }); it('returns the build file', async () => { const mockValue = 'someBuildName'; jest.spyOn(Input, 'buildName', 'get').mockReturnValue(mockValue); - expect(BuildParameters.create()).resolves.toEqual(expect.objectContaining({ buildFile: mockValue })); + expect(Parameters.create()).resolves.toEqual(expect.objectContaining({ buildFile: mockValue })); }); test.each([Platform.types.StandaloneWindows, Platform.types.StandaloneWindows64])( @@ -93,7 +93,7 @@ describe('BuildParameters', () => { async (targetPlatform) => { jest.spyOn(Input, 'targetPlatform', 'get').mockReturnValue(targetPlatform); jest.spyOn(Input, 'buildName', 'get').mockReturnValue(targetPlatform); - expect(BuildParameters.create()).resolves.toEqual( + expect(Parameters.create()).resolves.toEqual( expect.objectContaining({ buildFile: `${targetPlatform}.exe` }), ); }, @@ -103,7 +103,7 @@ describe('BuildParameters', () => { jest.spyOn(Input, 'targetPlatform', 'get').mockReturnValue(targetPlatform); jest.spyOn(Input, 'buildName', 'get').mockReturnValue(targetPlatform); jest.spyOn(Input, 'androidAppBundle', 'get').mockReturnValue(false); - expect(BuildParameters.create()).resolves.toEqual( + expect(Parameters.create()).resolves.toEqual( expect.objectContaining({ buildFile: `${targetPlatform}.apk` }), ); }); @@ -112,7 +112,7 @@ describe('BuildParameters', () => { jest.spyOn(Input, 'targetPlatform', 'get').mockReturnValue(targetPlatform); jest.spyOn(Input, 'buildName', 'get').mockReturnValue(targetPlatform); jest.spyOn(Input, 'androidAppBundle', 'get').mockReturnValue(true); - expect(BuildParameters.create()).resolves.toEqual( + expect(Parameters.create()).resolves.toEqual( expect.objectContaining({ buildFile: `${targetPlatform}.aab` }), ); }); @@ -120,43 +120,43 @@ describe('BuildParameters', () => { it('returns the build method', async () => { const mockValue = 'Namespace.ClassName.BuildMethod'; jest.spyOn(Input, 'buildMethod', 'get').mockReturnValue(mockValue); - expect(BuildParameters.create()).resolves.toEqual(expect.objectContaining({ buildMethod: mockValue })); + expect(Parameters.create()).resolves.toEqual(expect.objectContaining({ buildMethod: mockValue })); }); it('returns the android keystore name', async () => { const mockValue = 'keystore.keystore'; jest.spyOn(Input, 'androidKeystoreName', 'get').mockReturnValue(mockValue); - expect(BuildParameters.create()).resolves.toEqual(expect.objectContaining({ androidKeystoreName: mockValue })); + expect(Parameters.create()).resolves.toEqual(expect.objectContaining({ androidKeystoreName: mockValue })); }); it('returns the android keystore base64-encoded content', async () => { const mockValue = 'secret'; jest.spyOn(Input, 'androidKeystoreBase64', 'get').mockReturnValue(mockValue); - expect(BuildParameters.create()).resolves.toEqual(expect.objectContaining({ androidKeystoreBase64: mockValue })); + expect(Parameters.create()).resolves.toEqual(expect.objectContaining({ androidKeystoreBase64: mockValue })); }); it('returns the android keystore pass', async () => { const mockValue = 'secret'; jest.spyOn(Input, 'androidKeystorePass', 'get').mockReturnValue(mockValue); - expect(BuildParameters.create()).resolves.toEqual(expect.objectContaining({ androidKeystorePass: mockValue })); + expect(Parameters.create()).resolves.toEqual(expect.objectContaining({ androidKeystorePass: mockValue })); }); it('returns the android keyalias name', async () => { const mockValue = 'secret'; jest.spyOn(Input, 'androidKeyaliasName', 'get').mockReturnValue(mockValue); - expect(BuildParameters.create()).resolves.toEqual(expect.objectContaining({ androidKeyaliasName: mockValue })); + expect(Parameters.create()).resolves.toEqual(expect.objectContaining({ androidKeyaliasName: mockValue })); }); it('returns the android keyalias pass', async () => { const mockValue = 'secret'; jest.spyOn(Input, 'androidKeyaliasPass', 'get').mockReturnValue(mockValue); - expect(BuildParameters.create()).resolves.toEqual(expect.objectContaining({ androidKeyaliasPass: mockValue })); + expect(Parameters.create()).resolves.toEqual(expect.objectContaining({ androidKeyaliasPass: mockValue })); }); it('returns the android target sdk version', async () => { const mockValue = 'AndroidApiLevelAuto'; jest.spyOn(Input, 'androidTargetSdkVersion', 'get').mockReturnValue(mockValue); - expect(BuildParameters.create()).resolves.toEqual( + expect(Parameters.create()).resolves.toEqual( expect.objectContaining({ androidTargetSdkVersion: mockValue }), ); }); @@ -164,7 +164,7 @@ describe('BuildParameters', () => { it('returns the custom parameters', async () => { const mockValue = '-profile SomeProfile -someBoolean -someValue exampleValue'; jest.spyOn(Input, 'customParameters', 'get').mockReturnValue(mockValue); - expect(BuildParameters.create()).resolves.toEqual(expect.objectContaining({ customParameters: mockValue })); + expect(Parameters.create()).resolves.toEqual(expect.objectContaining({ customParameters: mockValue })); }); }); }); diff --git a/src/model/cli/cli.ts b/src/model/cli/cli.ts index 4cc19eca..746c804a 100644 --- a/src/model/cli/cli.ts +++ b/src/model/cli/cli.ts @@ -1,4 +1,4 @@ -import { BuildParameters, CloudRunner, ImageTag, Input } from '../index.ts'; +import { Parameters, CloudRunner, ImageTag, Input } from '../index.ts'; import { Command, core } from '../../dependencies.ts'; import { ActionYamlReader } from '../input-readers/action-yaml.ts'; import CloudRunnerLogger from '../cloud-runner/services/cloud-runner-logger.ts'; @@ -88,7 +88,7 @@ export class Cli { @CliFunction(`cli`, `runs a cloud runner build`) public static async CLIBuild(): Promise { - const buildParameter = await BuildParameters.create(); + const buildParameter = await Parameters.create(); const baseImage = new ImageTag(buildParameter); return await CloudRunner.run(buildParameter, baseImage.toString()); diff --git a/src/model/cloud-runner/cloud-runner.test.ts b/src/model/cloud-runner/cloud-runner.test.ts index 212f48cc..154a8bcc 100644 --- a/src/model/cloud-runner/cloud-runner.test.ts +++ b/src/model/cloud-runner/cloud-runner.test.ts @@ -1,4 +1,4 @@ -import { BuildParameters, ImageTag } from '..'; +import { Parameters, ImageTag } from '..'; import CloudRunner from './cloud-runner.ts'; import Input from '../input.ts'; import { CloudRunnerStatics } from './cloud-runner-statics.ts'; @@ -34,7 +34,7 @@ describe('Cloud Runner', () => { Input.githubInputEnabled = false; // Setup parameters - const buildParameter = await BuildParameters.create(); + const buildParameter = await Parameters.create(); Input.githubInputEnabled = true; const baseImage = new ImageTag(buildParameter); @@ -73,7 +73,7 @@ describe('Cloud Runner', () => { cacheKey: `test-case-${uuidv4()}`, }; Input.githubInputEnabled = false; - const buildParameter = await BuildParameters.create(); + const buildParameter = await Parameters.create(); const baseImage = new ImageTag(buildParameter); const results = await CloudRunner.run(buildParameter, baseImage.toString()); const libraryString = 'Rebuilding Library because the asset database could not be found!'; @@ -81,7 +81,7 @@ describe('Cloud Runner', () => { expect(results).toContain(libraryString); expect(results).toContain(buildSucceededString); CloudRunnerLogger.log(`run 1 succeeded`); - const buildParameter2 = await BuildParameters.create(); + const buildParameter2 = await Parameters.create(); const baseImage2 = new ImageTag(buildParameter2); const results2 = await CloudRunner.run(buildParameter2, baseImage2.toString()); CloudRunnerLogger.log(`run 2 succeeded`); @@ -111,7 +111,7 @@ describe('Cloud Runner', () => { Input.githubInputEnabled = false; // Setup parameters - const buildParameter = await BuildParameters.create(); + const buildParameter = await Parameters.create(); const baseImage = new ImageTag(buildParameter); // Run the job @@ -131,7 +131,7 @@ describe('Cloud Runner', () => { Input.githubInputEnabled = false; // Setup parameters - const buildParameter = await BuildParameters.create(); + const buildParameter = await Parameters.create(); const baseImage = new ImageTag(buildParameter); // Run the job diff --git a/src/model/cloud-runner/cloud-runner.ts b/src/model/cloud-runner/cloud-runner.ts index daf68b63..260f7253 100644 --- a/src/model/cloud-runner/cloud-runner.ts +++ b/src/model/cloud-runner/cloud-runner.ts @@ -1,5 +1,5 @@ import AwsBuildPlatform from './providers/aws/index.ts'; -import { BuildParameters, Input } from '../index.ts'; +import { Parameters, Input } from '../index.ts'; import Kubernetes from './providers/k8s/index.ts'; import CloudRunnerLogger from './services/cloud-runner-logger.ts'; import { CloudRunnerStepState } from './cloud-runner-step-state.ts'; @@ -16,10 +16,10 @@ import LocalDockerCloudRunner from './providers/local-docker/index.ts'; class CloudRunner { public static Provider: ProviderInterface; - static buildParameters: BuildParameters; + static buildParameters: Parameters; public static defaultSecrets: CloudRunnerSecret[]; public static cloudRunnerEnvironmentVariables: CloudRunnerEnvironmentVariable[]; - private static setup(buildParameters: BuildParameters) { + private static setup(buildParameters: Parameters) { CloudRunnerLogger.setup(); CloudRunner.buildParameters = buildParameters; CloudRunner.setupBuildPlatform(); @@ -57,7 +57,7 @@ class CloudRunner { } } - static async run(buildParameters: BuildParameters, baseImage: string) { + static async run(buildParameters: Parameters, baseImage: string) { CloudRunner.setup(buildParameters); try { if (!CloudRunner.buildParameters.isCliMode) core.startGroup('Setup shared cloud runner resources'); diff --git a/src/model/cloud-runner/providers/aws/index.ts b/src/model/cloud-runner/providers/aws/index.ts index b4ab8dcb..b59b3475 100644 --- a/src/model/cloud-runner/providers/aws/index.ts +++ b/src/model/cloud-runner/providers/aws/index.ts @@ -4,7 +4,7 @@ import CloudRunnerEnvironmentVariable from '../../services/cloud-runner-environm import CloudRunnerAWSTaskDef from './cloud-runner-aws-task-def.ts'; import AWSTaskRunner from './aws-task-runner.ts'; import { ProviderInterface } from '../provider-interface.ts'; -import BuildParameters from '../../../build-parameters.ts'; +import Parameters from '../../../parameters.ts'; import CloudRunnerLogger from '../../services/cloud-runner-logger.ts'; import { AWSJobStack } from './aws-job-stack.ts'; import { AWSBaseStack } from './aws-base-stack.ts'; @@ -13,18 +13,18 @@ import { Input } from '../../../index.ts'; class AWSBuildEnvironment implements ProviderInterface { private baseStackName: string; - constructor(buildParameters: BuildParameters) { + constructor(buildParameters: Parameters) { this.baseStackName = buildParameters.awsBaseStackName; } async cleanup( buildGuid: string, - buildParameters: BuildParameters, + buildParameters: Parameters, branchName: string, defaultSecretsArray: { ParameterKey: string; EnvironmentVariable: string; ParameterValue: string }[], ) {} async setup( buildGuid: string, - buildParameters: BuildParameters, + buildParameters: Parameters, branchName: string, defaultSecretsArray: { ParameterKey: string; EnvironmentVariable: string; ParameterValue: string }[], ) {} diff --git a/src/model/cloud-runner/providers/k8s/index.ts b/src/model/cloud-runner/providers/k8s/index.ts index 11f3cc12..454a984c 100644 --- a/src/model/cloud-runner/providers/k8s/index.ts +++ b/src/model/cloud-runner/providers/k8s/index.ts @@ -1,4 +1,4 @@ -import { BuildParameters, Output } from '../../../index.ts'; +import { Parameters, Output } from '../../../index.ts'; import { k8sTypes, k8s, waitUntil } from '../../../../dependencies.ts'; import { ProviderInterface } from '../provider-interface.ts'; import CloudRunnerSecret from '../../services/cloud-runner-secret.ts'; @@ -16,7 +16,7 @@ class Kubernetes implements ProviderInterface { private kubeClient: k8sTypes.CoreV1Api; private kubeClientBatch: k8sTypes.BatchV1Api; private buildGuid: string = ''; - private buildParameters: BuildParameters; + private buildParameters: Parameters; private pvcName: string = ''; private secretName: string = ''; private jobName: string = ''; @@ -26,7 +26,7 @@ class Kubernetes implements ProviderInterface { private cleanupCronJobName: string = ''; private serviceAccountName: string = ''; - constructor(buildParameters: BuildParameters) { + constructor(buildParameters: Parameters) { this.kubeConfig = new k8s.KubeConfig(); this.kubeConfig.loadFromDefault(); this.kubeClient = this.kubeConfig.makeApiClient(k8sTypes.CoreV1Api); @@ -38,7 +38,7 @@ class Kubernetes implements ProviderInterface { } public async setup( buildGuid: string, - buildParameters: BuildParameters, + buildParameters: Parameters, branchName: string, defaultSecretsArray: { ParameterKey: string; EnvironmentVariable: string; ParameterValue: string }[], ) { @@ -171,7 +171,7 @@ class Kubernetes implements ProviderInterface { async cleanup( buildGuid: string, - buildParameters: BuildParameters, + buildParameters: Parameters, branchName: string, defaultSecretsArray: { ParameterKey: string; EnvironmentVariable: string; ParameterValue: string }[], ) { diff --git a/src/model/cloud-runner/providers/k8s/kubernetes-job-spec-factory.ts b/src/model/cloud-runner/providers/k8s/kubernetes-job-spec-factory.ts index ed14a055..dc1e90ab 100644 --- a/src/model/cloud-runner/providers/k8s/kubernetes-job-spec-factory.ts +++ b/src/model/cloud-runner/providers/k8s/kubernetes-job-spec-factory.ts @@ -1,5 +1,5 @@ import { V1EnvVar, V1EnvVarSource, V1SecretKeySelector } from '../../../../dependencies.ts'; -import BuildParameters from '../../../build-parameters.ts'; +import Parameters from '../../../parameters.ts'; import { CloudRunnerBuildCommandProcessor } from '../../services/cloud-runner-build-command-process.ts'; import CloudRunnerEnvironmentVariable from '../../services/cloud-runner-environment-variable.ts'; import CloudRunnerSecret from '../../services/cloud-runner-secret.ts'; @@ -14,7 +14,7 @@ class KubernetesJobSpecFactory { environment: CloudRunnerEnvironmentVariable[], secrets: CloudRunnerSecret[], buildGuid: string, - buildParameters: BuildParameters, + buildParameters: Parameters, secretName, pvcName, jobName, diff --git a/src/model/cloud-runner/providers/k8s/kubernetes-storage.ts b/src/model/cloud-runner/providers/k8s/kubernetes-storage.ts index 8f006e00..c450a756 100644 --- a/src/model/cloud-runner/providers/k8s/kubernetes-storage.ts +++ b/src/model/cloud-runner/providers/k8s/kubernetes-storage.ts @@ -1,10 +1,10 @@ import { k8sTypes, k8s, core, yaml, waitUntil, http } from '../../../../dependencies.ts'; -import BuildParameters from '../../../build-parameters.ts'; +import Parameters from '../../../parameters.ts'; import CloudRunnerLogger from '../../services/cloud-runner-logger.ts'; class KubernetesStorage { public static async createPersistentVolumeClaim( - buildParameters: BuildParameters, + buildParameters: Parameters, pvcName: string, kubeClient: k8sTypes.CoreV1Api, namespace: string, @@ -69,7 +69,7 @@ class KubernetesStorage { private static async createPVC( pvcName: string, - buildParameters: BuildParameters, + buildParameters: Parameters, kubeClient: k8sTypes.CoreV1Api, namespace: string, ) { diff --git a/src/model/cloud-runner/providers/local-docker/index.ts b/src/model/cloud-runner/providers/local-docker/index.ts index 892cabd6..9e866956 100644 --- a/src/model/cloud-runner/providers/local-docker/index.ts +++ b/src/model/cloud-runner/providers/local-docker/index.ts @@ -1,4 +1,4 @@ -import BuildParameters from '../../../build-parameters.ts'; +import Parameters from '../../../parameters.ts'; import { CloudRunnerSystem } from '../../services/cloud-runner-system.ts'; import CloudRunnerEnvironmentVariable from '../../services/cloud-runner-environment-variable.ts'; import CloudRunnerLogger from '../../services/cloud-runner-logger.ts'; @@ -8,13 +8,13 @@ import CloudRunnerSecret from '../../services/cloud-runner-secret.ts'; class LocalDockerCloudRunner implements ProviderInterface { cleanup( buildGuid: string, - buildParameters: BuildParameters, + buildParameters: Parameters, branchName: string, defaultSecretsArray: { ParameterKey: string; EnvironmentVariable: string; ParameterValue: string }[], ) {} setup( buildGuid: string, - buildParameters: BuildParameters, + buildParameters: Parameters, branchName: string, defaultSecretsArray: { ParameterKey: string; EnvironmentVariable: string; ParameterValue: string }[], ) {} diff --git a/src/model/cloud-runner/providers/local/index.ts b/src/model/cloud-runner/providers/local/index.ts index 23e5acfe..d958624e 100644 --- a/src/model/cloud-runner/providers/local/index.ts +++ b/src/model/cloud-runner/providers/local/index.ts @@ -1,4 +1,4 @@ -import BuildParameters from '../../../build-parameters.ts'; +import Parameters from '../../../parameters.ts'; import { CloudRunnerSystem } from '../../services/cloud-runner-system.ts'; import CloudRunnerEnvironmentVariable from '../../services/cloud-runner-environment-variable.ts'; import CloudRunnerLogger from '../../services/cloud-runner-logger.ts'; @@ -8,13 +8,13 @@ import CloudRunnerSecret from '../../services/cloud-runner-secret.ts'; class LocalCloudRunner implements ProviderInterface { cleanup( buildGuid: string, - buildParameters: BuildParameters, + buildParameters: Parameters, branchName: string, defaultSecretsArray: { ParameterKey: string; EnvironmentVariable: string; ParameterValue: string }[], ) {} public setup( buildGuid: string, - buildParameters: BuildParameters, + buildParameters: Parameters, branchName: string, defaultSecretsArray: { ParameterKey: string; EnvironmentVariable: string; ParameterValue: string }[], ) {} diff --git a/src/model/cloud-runner/providers/provider-interface.ts b/src/model/cloud-runner/providers/provider-interface.ts index 6905d1fe..a82b1f90 100644 --- a/src/model/cloud-runner/providers/provider-interface.ts +++ b/src/model/cloud-runner/providers/provider-interface.ts @@ -1,17 +1,17 @@ -import BuildParameters from '../../build-parameters.ts'; +import Parameters from '../../parameters.ts'; import CloudRunnerEnvironmentVariable from '../services/cloud-runner-environment-variable.ts'; import CloudRunnerSecret from '../services/cloud-runner-secret.ts'; export interface ProviderInterface { cleanup( buildGuid: string, - buildParameters: BuildParameters, + buildParameters: Parameters, branchName: string, defaultSecretsArray: { ParameterKey: string; EnvironmentVariable: string; ParameterValue: string }[], ); setup( buildGuid: string, - buildParameters: BuildParameters, + buildParameters: Parameters, branchName: string, defaultSecretsArray: { ParameterKey: string; EnvironmentVariable: string; ParameterValue: string }[], ); diff --git a/src/model/cloud-runner/providers/test/index.ts b/src/model/cloud-runner/providers/test/index.ts index 6a2122c2..c4325fff 100644 --- a/src/model/cloud-runner/providers/test/index.ts +++ b/src/model/cloud-runner/providers/test/index.ts @@ -1,4 +1,4 @@ -import BuildParameters from '../../../build-parameters.ts'; +import Parameters from '../../../parameters.ts'; import CloudRunnerEnvironmentVariable from '../../services/cloud-runner-environment-variable.ts'; import CloudRunnerLogger from '../../services/cloud-runner-logger.ts'; import { ProviderInterface } from '../provider-interface.ts'; @@ -7,13 +7,13 @@ import CloudRunnerSecret from '../../services/cloud-runner-secret.ts'; class TestCloudRunner implements ProviderInterface { cleanup( buildGuid: string, - buildParameters: BuildParameters, + buildParameters: Parameters, branchName: string, defaultSecretsArray: { ParameterKey: string; EnvironmentVariable: string; ParameterValue: string }[], ) {} setup( buildGuid: string, - buildParameters: BuildParameters, + buildParameters: Parameters, branchName: string, defaultSecretsArray: { ParameterKey: string; EnvironmentVariable: string; ParameterValue: string }[], ) {} diff --git a/src/model/cloud-runner/remote-client/caching.test.ts b/src/model/cloud-runner/remote-client/caching.test.ts index 3b5a088d..f8ef9912 100644 --- a/src/model/cloud-runner/remote-client/caching.test.ts +++ b/src/model/cloud-runner/remote-client/caching.test.ts @@ -1,5 +1,5 @@ import { fs, uuid, path, __dirname } from '../../../dependencies.ts'; -import BuildParameters from '../../build-parameters.ts'; +import Parameters from '../../parameters.ts'; import { Cli } from '../../cli/cli.ts'; import Input from '../../input.ts'; import UnityVersioning from '../../unity-versioning.ts'; @@ -21,7 +21,7 @@ describe('Cloud Runner Caching', () => { cacheKey: `test-case-${uuid()}`, }; Input.githubInputEnabled = false; - const buildParameter = await BuildParameters.create(); + const buildParameter = await Parameters.create(); CloudRunner.buildParameters = buildParameter; // Create test folder diff --git a/src/model/cloud-runner/services/cloud-runner-build-command-process.ts b/src/model/cloud-runner/services/cloud-runner-build-command-process.ts index e13f4cec..1358efad 100644 --- a/src/model/cloud-runner/services/cloud-runner-build-command-process.ts +++ b/src/model/cloud-runner/services/cloud-runner-build-command-process.ts @@ -1,10 +1,10 @@ -import { BuildParameters } from '../../index.ts'; +import { Parameters } from '../../index.ts'; import { yaml } from '../../../dependencies.ts'; import CloudRunnerSecret from './cloud-runner-secret.ts'; import CloudRunner from '../cloud-runner.ts'; export class CloudRunnerBuildCommandProcessor { - public static ProcessCommands(commands: string, buildParameters: BuildParameters): string { + public static ProcessCommands(commands: string, buildParameters: Parameters): string { const hooks = CloudRunnerBuildCommandProcessor.getHooks(buildParameters.customJobHooks).filter((x) => x.step.includes(`all`), ); diff --git a/src/model/image-environment-factory.ts b/src/model/image-environment-factory.ts index b870c816..2e162e0b 100644 --- a/src/model/image-environment-factory.ts +++ b/src/model/image-environment-factory.ts @@ -1,4 +1,4 @@ -import BuildParameters from './build-parameters.ts'; +import Parameters from './parameters.ts'; import { ReadLicense } from './input-readers/test-license-reader.ts'; class Parameter { @@ -24,7 +24,7 @@ class ImageEnvironmentFactory { return string; } - public static getEnvironmentVariables(parameters: BuildParameters) { + public static getEnvironmentVariables(parameters: Parameters) { const environmentVariables: Parameter[] = [ { name: 'UNITY_LICENSE', value: Deno.env.get('UNITY_LICENSE') || ReadLicense() }, { name: 'UNITY_LICENSE_FILE', value: Deno.env.get('UNITY_LICENSE_FILE') }, diff --git a/src/model/image-tag.ts b/src/model/image-tag.ts index a9d25ecf..5786ae32 100644 --- a/src/model/image-tag.ts +++ b/src/model/image-tag.ts @@ -1,6 +1,6 @@ import Platform from './platform.ts'; -import BuildParameters from './build-parameters.ts'; +import Parameters from './parameters.ts'; class ImageTag { public repository: string; @@ -13,7 +13,7 @@ class ImageTag { public imageRollingVersion: number; public imagePlatformPrefix: string; - constructor(imageProperties: Partial) { + constructor(imageProperties: Partial) { const { editorVersion = '2019.2.11f1', targetPlatform, customImage, cloudRunnerBuilderPlatform } = imageProperties; if (!ImageTag.versionPattern.test(editorVersion)) { diff --git a/src/model/index.ts b/src/model/index.ts index 9e35789e..a2ae22a8 100644 --- a/src/model/index.ts +++ b/src/model/index.ts @@ -1,5 +1,5 @@ import Action from './action.ts'; -import BuildParameters from './build-parameters.ts'; +import Parameters from './parameters.ts'; import Cache from './cache.ts'; import Docker from './docker.ts'; import Input from './input.ts'; @@ -13,7 +13,7 @@ import CloudRunner from './cloud-runner/cloud-runner.ts'; export { Action, - BuildParameters, + Parameters, Cache, Docker, Input, @@ -23,5 +23,5 @@ export { Project, Unity, Versioning, - CloudRunner as CloudRunner, + CloudRunner, }; diff --git a/src/model/input.ts b/src/model/input.ts index babb9680..376effb6 100644 --- a/src/model/input.ts +++ b/src/model/input.ts @@ -2,6 +2,7 @@ import { fsSync as fs, path, core } from '../dependencies.ts'; import { Cli } from './cli/cli.ts'; import CloudRunnerQueryOverride from './cloud-runner/services/cloud-runner-query-override.ts'; import Platform from './platform.ts'; +import { CliArguments } from '../core/cli/cli-arguments.ts'; /** * Input variables specified in workflows using "with" prop. @@ -11,9 +12,29 @@ import Platform from './platform.ts'; * Todo: rename to UserInput and remove anything that is not direct input from the user / ci workflow */ class Input { + private readonly arguments: CliArguments; + + constructor(argumentsFromCli: CliArguments) { + this.arguments = argumentsFromCli; + + log.debug('Input initialised.'); + + return this; + } + public static githubInputEnabled: boolean = true; - public static getInput(query) { + // Todo - Note that this is now invoked both statically and dynamically - which is a temporary mess. + public getInput(query) { + if (this && this.arguments) { + const value = this.arguments.get(query); + log.warning('arg', query, '=', value); + + return this.arguments.get(query); + } + + // Legacy (static) + log.warn(`Querying static`); if (Input.githubInputEnabled) { const coreInput = core.getInput(query); if (coreInput && coreInput !== '') { @@ -42,24 +63,24 @@ class Input { return; } - static get region(): string { - return Input.getInput('region') || 'eu-west-2'; + public get region(): string { + return this.getInput('region') || 'eu-west-2'; } - static get githubRepo() { - return Input.getInput('GITHUB_REPOSITORY') || Input.getInput('GITHUB_REPO') || undefined; + public get githubRepo() { + return this.getInput('GITHUB_REPOSITORY') || this.getInput('GITHUB_REPO') || undefined; } - static get branch() { - if (Input.getInput(`GITHUB_REF`)) { - return Input.getInput(`GITHUB_REF`).replace('refs/', '').replace(`head/`, '').replace(`heads/`, ''); - } else if (Input.getInput('branch')) { - return Input.getInput('branch'); + 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'); } else { return ''; } } - static get cloudRunnerBuilderPlatform() { - const input = Input.getInput('cloudRunnerBuilderPlatform'); + public get cloudRunnerBuilderPlatform() { + const input = this.getInput('cloudRunnerBuilderPlatform'); if (input) { return input; } @@ -70,32 +91,32 @@ class Input { return; } - static get gitSha() { - if (Input.getInput(`GITHUB_SHA`)) { - return Input.getInput(`GITHUB_SHA`); - } else if (Input.getInput(`GitSHA`)) { - return Input.getInput(`GitSHA`); + public get gitSha() { + if (this.getInput(`GITHUB_SHA`)) { + return this.getInput(`GITHUB_SHA`); + } else if (this.getInput(`GitSHA`)) { + return this.getInput(`GitSHA`); } } - static get runNumber() { - return Input.getInput('GITHUB_RUN_NUMBER') || '0'; + public get runNumber() { + return this.getInput('GITHUB_RUN_NUMBER') || '0'; } - static get targetPlatform() { - return Input.getInput('targetPlatform') || Platform.default; + public get targetPlatform() { + return this.getInput('targetPlatform') || Platform.default; } - static get unityVersion() { - return Input.getInput('unityVersion') || 'auto'; + public get unityVersion() { + return this.getInput('unityVersion') || 'auto'; } - static get customImage() { - return Input.getInput('customImage') || ''; + public get customImage() { + return this.getInput('customImage') || ''; } - static get projectPath() { - let input = Input.getInput('projectPath'); + public get projectPath() { + let input = this.getInput('projectPath'); // Todo - remove hardcoded test project reference const isTestProject = @@ -108,168 +129,169 @@ class Input { return input.replace(/\/$/, ''); } - static get buildName() { - return Input.getInput('buildName') || this.targetPlatform; + public get buildName() { + return this.getInput('buildName') || this.targetPlatform; } - static get buildsPath() { - return Input.getInput('buildsPath') || 'build'; + public get buildsPath() { + return this.getInput('buildsPath') || 'build'; } - static get buildMethod() { - return Input.getInput('buildMethod') || ''; // Processed in docker file + public get buildMethod() { + return this.getInput('buildMethod') || ''; // Processed in docker file } - static get customParameters() { - return Input.getInput('customParameters') || ''; + public get customParameters() { + return this.getInput('customParameters') || ''; } - static get versioningStrategy() { - return Input.getInput('versioning') || 'Semantic'; + public get versioningStrategy() { + return this.getInput('versioning') || 'Semantic'; } - static get specifiedVersion() { - return Input.getInput('version') || ''; + public get specifiedVersion() { + return this.getInput('version') || ''; } - static get androidVersionCode() { - return Input.getInput('androidVersionCode'); + public get androidVersionCode() { + return this.getInput('androidVersionCode'); } - static get androidAppBundle() { - const input = Input.getInput('androidAppBundle') || false; + public get androidAppBundle() { + const input = this.getInput('androidAppBundle') || false; return input === 'true'; } - static get androidKeystoreName() { - return Input.getInput('androidKeystoreName') || ''; + public get androidKeystoreName() { + return this.getInput('androidKeystoreName') || ''; } - static get androidKeystoreBase64() { - return Input.getInput('androidKeystoreBase64') || ''; + public get androidKeystoreBase64() { + return this.getInput('androidKeystoreBase64') || ''; } - static get androidKeystorePass() { - return Input.getInput('androidKeystorePass') || ''; + public get androidKeystorePass() { + return this.getInput('androidKeystorePass') || ''; } - static get androidKeyaliasName() { - return Input.getInput('androidKeyaliasName') || ''; + public get androidKeyaliasName() { + return this.getInput('androidKeyaliasName') || ''; } - static get androidKeyaliasPass() { - return Input.getInput('androidKeyaliasPass') || ''; + public get androidKeyaliasPass() { + return this.getInput('androidKeyaliasPass') || ''; } - static get androidTargetSdkVersion() { - return Input.getInput('androidTargetSdkVersion') || ''; + public get androidTargetSdkVersion() { + return this.getInput('androidTargetSdkVersion') || ''; } - static get sshAgent() { - return Input.getInput('sshAgent') || ''; + public get sshAgent() { + return this.getInput('sshAgent') || ''; } - static get gitPrivateToken() { + public get gitPrivateToken() { return core.getInput('gitPrivateToken') || false; } - static get customJob() { - return Input.getInput('customJob') || ''; + public get customJob() { + return this.getInput('customJob') || ''; } - static customJobHooks() { - return Input.getInput('customJobHooks') || ''; + public customJobHooks() { + return this.getInput('customJobHooks') || ''; } - static cachePushOverrideCommand() { - return Input.getInput('cachePushOverrideCommand') || ''; + public cachePushOverrideCommand() { + return this.getInput('cachePushOverrideCommand') || ''; } - static cachePullOverrideCommand() { - return Input.getInput('cachePullOverrideCommand') || ''; + public cachePullOverrideCommand() { + return this.getInput('cachePullOverrideCommand') || ''; } - static readInputFromOverrideList() { - return Input.getInput('readInputFromOverrideList') || ''; + public readInputFromOverrideList() { + return this.getInput('readInputFromOverrideList') || ''; } - static readInputOverrideCommand() { - return Input.getInput('readInputOverrideCommand') || ''; + public readInputOverrideCommand() { + return this.getInput('readInputOverrideCommand') || ''; } - static get cloudRunnerBranch() { - return Input.getInput('cloudRunnerBranch') || 'cloud-runner-develop'; + public get cloudRunnerBranch() { + return this.getInput('cloudRunnerBranch') || 'cloud-runner-develop'; } - static get chownFilesTo() { - return Input.getInput('chownFilesTo') || ''; + public get chownFilesTo() { + return this.getInput('chownFilesTo') || ''; } - static get allowDirtyBuild() { - const input = Input.getInput('allowDirtyBuild') || false; + public get allowDirtyBuild() { + const input = this.getInput('allowDirtyBuild'); + log.debug('input === ', input); - return input === 'true'; + return input || false === true; } - static get postBuildSteps() { - return Input.getInput('postBuildSteps') || ''; + public get postBuildSteps() { + return this.getInput('postBuildSteps') || ''; } - static get preBuildSteps() { - return Input.getInput('preBuildSteps') || ''; + public get preBuildSteps() { + return this.getInput('preBuildSteps') || ''; } - static get awsBaseStackName() { - return Input.getInput('awsBaseStackName') || 'game-ci'; + public get awsBaseStackName() { + return this.getInput('awsBaseStackName') || 'game-ci'; } - static get cloudRunnerCluster() { + public get cloudRunnerCluster() { if (Cli.isCliMode) { - return Input.getInput('cloudRunnerCluster') || 'aws'; + return this.getInput('cloudRunnerCluster') || 'aws'; } - return Input.getInput('cloudRunnerCluster') || 'local'; + return this.getInput('cloudRunnerCluster') || 'local'; } - static get cloudRunnerCpu() { - return Input.getInput('cloudRunnerCpu'); + public get cloudRunnerCpu() { + return this.getInput('cloudRunnerCpu'); } - static get cloudRunnerMemory() { - return Input.getInput('cloudRunnerMemory'); + public get cloudRunnerMemory() { + return this.getInput('cloudRunnerMemory'); } - static get kubeConfig() { - return Input.getInput('kubeConfig') || ''; + public get kubeConfig() { + return this.getInput('kubeConfig') || ''; } - static get kubeVolume() { - return Input.getInput('kubeVolume') || ''; + public get kubeVolume() { + return this.getInput('kubeVolume') || ''; } - static get kubeVolumeSize() { - return Input.getInput('kubeVolumeSize') || '5Gi'; + public get kubeVolumeSize() { + return this.getInput('kubeVolumeSize') || '5Gi'; } - static get kubeStorageClass(): string { - return Input.getInput('kubeStorageClass') || ''; + public get kubeStorageClass(): string { + return this.getInput('kubeStorageClass') || ''; } - static get checkDependencyHealthOverride(): string { - return Input.getInput('checkDependencyHealthOverride') || ''; + public get checkDependencyHealthOverride(): string { + return this.getInput('checkDependencyHealthOverride') || ''; } - static get startDependenciesOverride(): string { - return Input.getInput('startDependenciesOverride') || ''; + public get startDependenciesOverride(): string { + return this.getInput('startDependenciesOverride') || ''; } - static get cacheKey(): string { - return Input.getInput('cacheKey') || Input.branch; + public get cacheKey(): string { + return this.getInput('cacheKey') || Input.branch; } - static get cloudRunnerTests(): boolean { - return Input.getInput(`cloudRunnerTests`) || false; + public get cloudRunnerTests(): boolean { + return this.getInput(`cloudRunnerTests`) || false; } public static toEnvVarFormat(input: string) { diff --git a/src/model/mac-builder.ts b/src/model/mac-builder.ts index b8fe9615..574a4599 100644 --- a/src/model/mac-builder.ts +++ b/src/model/mac-builder.ts @@ -1,5 +1,5 @@ import { exec } from '../dependencies.ts'; -import { BuildParameters } from './build-parameters.ts'; +import { Parameters } from './parameters.ts'; class MacBuilder { public static async run(actionFolder, workspace, buildParameters: BuildParameters, silent = false) { diff --git a/src/model/build-parameters.ts b/src/model/parameters.ts similarity index 60% rename from src/model/build-parameters.ts rename to src/model/parameters.ts index afa0fdc8..891ad32a 100644 --- a/src/model/build-parameters.ts +++ b/src/model/parameters.ts @@ -10,7 +10,7 @@ import { GitRepoReader } from './input-readers/git-repo.ts'; import { GithubCliReader } from './input-readers/github-cli.ts'; import { Cli } from './cli/cli.ts'; -class BuildParameters { +class Parameters { public editorVersion!: string; public customImage!: string; public unitySerial!: string; @@ -51,7 +51,6 @@ class BuildParameters { public checkDependencyHealthOverride!: string; public startDependenciesOverride!: string; public cacheKey!: string; - public postBuildSteps!: string; public preBuildSteps!: string; public customJob!: string; @@ -66,22 +65,36 @@ class BuildParameters { public cloudRunnerBuilderPlatform!: string | undefined; public isCliMode!: boolean; - static async create(): Promise { - const buildFile = this.parseBuildFile(Input.buildName, Input.targetPlatform, Input.androidAppBundle); + constructor(input: Input) { + this.input = input; + } + + public async parse(): Promise { + const buildFile = Parameters.parseBuildFile( + this.input.buildName, + this.input.targetPlatform, + this.input.androidAppBundle, + ); log.debug('buildFile:', buildFile); - const editorVersion = UnityVersioning.determineUnityVersion(Input.projectPath, Input.unityVersion); + const editorVersion = UnityVersioning.determineUnityVersion(this.input.projectPath, this.input.unityVersion); log.debug('editorVersion:', editorVersion); - const buildVersion = await Versioning.determineBuildVersion(Input.versioningStrategy, Input.specifiedVersion); + const buildVersion = await Versioning.determineBuildVersion( + this.input.versioningStrategy, + this.input.specifiedVersion, + this.input.allowDirtyBuild, + ); log.debug('buildVersion', buildVersion); - const androidVersionCode = AndroidVersioning.determineVersionCode(buildVersion, Input.androidVersionCode); + const androidVersionCode = AndroidVersioning.determineVersionCode(buildVersion, this.input.androidVersionCode); log.debug('androidVersionCode', androidVersionCode); - const androidSdkManagerParameters = AndroidVersioning.determineSdkManagerParameters(Input.androidTargetSdkVersion); + const androidSdkManagerParameters = AndroidVersioning.determineSdkManagerParameters( + this.input.androidTargetSdkVersion, + ); log.debug('androidSdkManagerParameters', androidSdkManagerParameters); // Todo - Don't use process.env directly, that's what the input model class is for. // --- let unitySerial = ''; - if (!Deno.env.get('UNITY_SERIAL') && Input.githubInputEnabled) { + if (!Deno.env.get('UNITY_SERIAL') && this.input.githubInputEnabled) { // No serial was present, so it is a personal license that we need to convert if (!Deno.env.get('UNITY_LICENSE')) { throw new Error(`Missing Unity License File and no Serial was found. If this @@ -96,59 +109,59 @@ class BuildParameters { return { editorVersion, - customImage: Input.customImage, + customImage: this.input.customImage, unitySerial, runnerTempPath: Deno.env.get('RUNNER_TEMP'), - targetPlatform: Input.targetPlatform, - projectPath: Input.projectPath, - buildName: Input.buildName, - buildPath: `${Input.buildsPath}/${Input.targetPlatform}`, + targetPlatform: this.input.targetPlatform, + projectPath: this.input.projectPath, + buildName: this.input.buildName, + buildPath: `${this.input.buildsPath}/${this.input.targetPlatform}`, buildFile, - buildMethod: Input.buildMethod, + buildMethod: this.input.buildMethod, buildVersion, androidVersionCode, - androidKeystoreName: Input.androidKeystoreName, - androidKeystoreBase64: Input.androidKeystoreBase64, - androidKeystorePass: Input.androidKeystorePass, - androidKeyaliasName: Input.androidKeyaliasName, - androidKeyaliasPass: Input.androidKeyaliasPass, - androidTargetSdkVersion: Input.androidTargetSdkVersion, + androidKeystoreName: this.input.androidKeystoreName, + androidKeystoreBase64: this.input.androidKeystoreBase64, + androidKeystorePass: this.input.androidKeystorePass, + androidKeyaliasName: this.input.androidKeyaliasName, + androidKeyaliasPass: this.input.androidKeyaliasPass, + androidTargetSdkVersion: this.input.androidTargetSdkVersion, androidSdkManagerParameters, - customParameters: Input.customParameters, - sshAgent: Input.sshAgent, - gitPrivateToken: Input.gitPrivateToken || (await GithubCliReader.GetGitHubAuthToken()), - chownFilesTo: Input.chownFilesTo, - cloudRunnerCluster: Input.cloudRunnerCluster, - cloudRunnerBuilderPlatform: Input.cloudRunnerBuilderPlatform, - awsBaseStackName: Input.awsBaseStackName, - kubeConfig: Input.kubeConfig, - cloudRunnerMemory: Input.cloudRunnerMemory, - cloudRunnerCpu: Input.cloudRunnerCpu, - kubeVolumeSize: Input.kubeVolumeSize, - kubeVolume: Input.kubeVolume, - postBuildSteps: Input.postBuildSteps, - preBuildSteps: Input.preBuildSteps, - customJob: Input.customJob, - runNumber: Input.runNumber, - branch: Input.branch.replace('/head', '') || (await GitRepoReader.GetBranch()), - cloudRunnerBranch: Input.cloudRunnerBranch.split('/').reverse()[0], - cloudRunnerIntegrationTests: Input.cloudRunnerTests, - githubRepo: Input.githubRepo || (await GitRepoReader.GetRemote()) || 'game-ci/unity-builder', + customParameters: this.input.customParameters, + sshAgent: this.input.sshAgent, + gitPrivateToken: this.input.gitPrivateToken || (await GithubCliReader.GetGitHubAuthToken()), + chownFilesTo: this.input.chownFilesTo, + cloudRunnerCluster: this.input.cloudRunnerCluster, + cloudRunnerBuilderPlatform: this.input.cloudRunnerBuilderPlatform, + awsBaseStackName: this.input.awsBaseStackName, + kubeConfig: this.input.kubeConfig, + cloudRunnerMemory: this.input.cloudRunnerMemory, + cloudRunnerCpu: this.input.cloudRunnerCpu, + kubeVolumeSize: this.input.kubeVolumeSize, + kubeVolume: this.input.kubeVolume, + postBuildSteps: this.input.postBuildSteps, + preBuildSteps: this.input.preBuildSteps, + customJob: this.input.customJob, + runNumber: this.input.runNumber, + branch: this.input.branch.replace('/head', '') || (await GitRepoReader.GetBranch()), + cloudRunnerBranch: this.input.cloudRunnerBranch.split('/').reverse()[0], + cloudRunnerIntegrationTests: this.input.cloudRunnerTests, + githubRepo: this.input.githubRepo || (await GitRepoReader.GetRemote()) || 'game-ci/unity-builder', isCliMode: Cli.isCliMode, - awsStackName: Input.awsBaseStackName, - gitSha: Input.gitSha, + awsStackName: this.input.awsBaseStackName, + gitSha: this.input.gitSha, logId: nanoid.customAlphabet(CloudRunnerConstants.alphabet, 9)(), - buildGuid: CloudRunnerBuildGuid.generateGuid(Input.runNumber, Input.targetPlatform), - customJobHooks: Input.customJobHooks(), - cachePullOverrideCommand: Input.cachePullOverrideCommand(), - cachePushOverrideCommand: Input.cachePushOverrideCommand(), - readInputOverrideCommand: Input.readInputOverrideCommand(), - readInputFromOverrideList: Input.readInputFromOverrideList(), - kubeStorageClass: Input.kubeStorageClass, - checkDependencyHealthOverride: Input.checkDependencyHealthOverride, - startDependenciesOverride: Input.startDependenciesOverride, - cacheKey: Input.cacheKey, + buildGuid: CloudRunnerBuildGuid.generateGuid(this.input.runNumber, this.input.targetPlatform), + customJobHooks: this.input.customJobHooks(), + cachePullOverrideCommand: this.input.cachePullOverrideCommand(), + cachePushOverrideCommand: this.input.cachePushOverrideCommand(), + readInputOverrideCommand: this.input.readInputOverrideCommand(), + readInputFromOverrideList: this.input.readInputFromOverrideList(), + kubeStorageClass: this.input.kubeStorageClass, + checkDependencyHealthOverride: this.input.checkDependencyHealthOverride, + startDependenciesOverride: this.input.startDependenciesOverride, + cacheKey: this.input.cacheKey, }; } @@ -178,4 +191,4 @@ class BuildParameters { } } -export default BuildParameters; +export default Parameters; diff --git a/src/model/platform-setup.ts b/src/model/platform-setup.ts index 56b1addd..c7e537b5 100644 --- a/src/model/platform-setup.ts +++ b/src/model/platform-setup.ts @@ -1,9 +1,9 @@ -import { BuildParameters } from './index.ts'; +import { Parameters } from './index.ts'; import { SetupMac, SetupWindows } from './platform-setup/index.ts'; import ValidateWindows from './platform-validation/validate-windows.ts'; class PlatformSetup { - static async setup(buildParameters: BuildParameters, actionFolder: string) { + static async setup(buildParameters: Parameters, actionFolder: string) { switch (process.platform) { case 'win32': ValidateWindows.validate(buildParameters); diff --git a/src/model/platform-setup/setup-mac.ts b/src/model/platform-setup/setup-mac.ts index 90cc4e6e..e8993086 100644 --- a/src/model/platform-setup/setup-mac.ts +++ b/src/model/platform-setup/setup-mac.ts @@ -1,10 +1,10 @@ -import { BuildParameters } from '../index.ts'; +import { Parameters } from '../index.ts'; import { fsSync as fs, exec, getUnityChangeSet } from '../../dependencies.ts'; class SetupMac { static unityHubPath = `"/Applications/Unity Hub.app/Contents/MacOS/Unity Hub"`; - public static async setup(buildParameters: BuildParameters, actionFolder: string) { + public static async setup(buildParameters: Parameters, actionFolder: string) { const unityEditorPath = `/Applications/Unity/Hub/Editor/${buildParameters.editorVersion}/Unity.app/Contents/MacOS/Unity`; // Only install unity if the editor doesn't already exist @@ -28,7 +28,7 @@ class SetupMac { } } - private static async installUnity(buildParameters: BuildParameters, silent = false) { + private static async installUnity(buildParameters: Parameters, silent = false) { const unityChangeSet = await getUnityChangeSet(buildParameters.editorVersion); const command = `${this.unityHubPath} -- --headless install \ --version ${buildParameters.editorVersion} \ @@ -44,7 +44,7 @@ class SetupMac { } } - private static async setEnvironmentVariables(buildParameters: BuildParameters, actionFolder: string) { + private static async setEnvironmentVariables(buildParameters: 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); diff --git a/src/model/platform-setup/setup-windows.ts b/src/model/platform-setup/setup-windows.ts index ef9313a9..690ddf3d 100644 --- a/src/model/platform-setup/setup-windows.ts +++ b/src/model/platform-setup/setup-windows.ts @@ -1,8 +1,8 @@ import { fsSync as fs, exec } from '../../dependencies.ts'; -import { BuildParameters } from '../index.ts'; +import { Parameters } from '../index.ts'; class SetupWindows { - public static async setup(buildParameters: BuildParameters) { + public static async setup(buildParameters: Parameters) { const { targetPlatform } = buildParameters; await SetupWindows.setupWindowsRun(targetPlatform); diff --git a/src/model/platform-validation/validate-windows.ts b/src/model/platform-validation/validate-windows.ts index 12da2763..a36348a9 100644 --- a/src/model/platform-validation/validate-windows.ts +++ b/src/model/platform-validation/validate-windows.ts @@ -1,8 +1,8 @@ import { fsSync as fs } from '../../dependencies.ts'; -import { BuildParameters } from '../index.ts'; +import { Parameters } from '../index.ts'; class ValidateWindows { - public static validate(buildParameters: BuildParameters) { + public static validate(buildParameters: Parameters) { ValidateWindows.validateWindowsPlatformRequirements(buildParameters.targetPlatform); if (!(Deno.env.get('UNITY_EMAIL') && Deno.env.get('UNITY_PASSWORD'))) { throw new Error(`Unity email and password must be set for Windows based builds to diff --git a/src/model/versioning.ts b/src/model/versioning.ts index 7a60b40b..97b3d641 100644 --- a/src/model/versioning.ts +++ b/src/model/versioning.ts @@ -1,4 +1,3 @@ -import { Buffer } from '../dependencies.ts'; import NotImplementedException from './error/not-implemented-exception.ts'; import ValidationError from './error/validation-error.ts'; import Input from './input.ts'; @@ -9,10 +8,6 @@ export default class Versioning { return Input.projectPath; } - static get isDirtyAllowed() { - return Input.allowDirtyBuild; - } - static get strategies() { return { None: 'None', Semantic: 'Semantic', Tag: 'Tag', Custom: 'Custom' }; } @@ -57,16 +52,6 @@ export default class Versioning { return 60; } - /** - * Log up to maxDiffLines of the git diff. - */ - static async logDiff() { - const diffCommand = `git --no-pager diff | head -n ${this.maxDiffLines.toString()}`; - const result = await System.shellRun(diffCommand); - - log.debug(result.output); - } - /** * Regex to parse version description into separate fields */ @@ -82,7 +67,7 @@ export default class Versioning { return /^v?([\d.]+-\w+\.\d+)-(\d+)-g(\w+)-?(\w+)*/g; } - static async determineBuildVersion(strategy: string, inputVersion: string) { + static async determineBuildVersion(strategy: string, inputVersion: string, allowDirtyBuild: boolean) { // Validate input if (!Object.hasOwnProperty.call(this.strategies, strategy)) { throw new ValidationError(`Versioning strategy should be one of ${Object.values(this.strategies).join(', ')}.`); @@ -99,7 +84,7 @@ export default class Versioning { version = inputVersion; break; case this.strategies.Semantic: - version = await this.generateSemanticVersion(); + version = await this.generateSemanticVersion(allowDirtyBuild); break; case this.strategies.Tag: version = await this.generateTagVersion(); @@ -113,6 +98,16 @@ export default class Versioning { return version; } + /** + * Log up to maxDiffLines of the git diff. + */ + static async logDiff() { + const diffCommand = `git --no-pager diff | head -n ${this.maxDiffLines.toString()}`; + const result = await System.shellRun(diffCommand); + + log.debug(result.output); + } + /** * Automatically generates a version based on SemVer out of the box. * @@ -123,14 +118,14 @@ export default class Versioning { * * @See: https://semver.org/ */ - static async generateSemanticVersion() { + static async generateSemanticVersion(allowDirtyBuild) { if (await this.isShallow()) { await this.fetch(); } await Versioning.logDiff(); - if ((await this.isDirty()) && !this.isDirtyAllowed) { + if ((await this.isDirty()) && !allowDirtyBuild) { throw new Error('Branch is dirty. Refusing to base semantic version on uncommitted changes'); } @@ -209,9 +204,7 @@ export default class Versioning { hash, }; } catch { - log.warning( - `Failed to parse git describe output or version can not be determined through: "${description}".`, - ); + log.warning(`Failed to parse git describe output or version can not be determined through "${description}".`); return false; } @@ -286,14 +279,16 @@ export default class Versioning { * Note: Currently this is run in all OSes, so the syntax must be cross-platform. */ static async hasAnyVersionTags() { - const numberOfTagsAsString = await System.run('sh', undefined, { - input: Buffer.from(`git tag --list --merged HEAD | grep -E '${this.grepCompatibleInputVersionRegex}' | wc -l`), - cwd: this.projectPath, - silent: false, - }); + const command = `git tag --list --merged HEAD | grep -E '${this.grepCompatibleInputVersionRegex}' | wc -l`; + const result = await System.shellRun(command, { cwd: this.projectPath, silent: false }); + log.debug(result); + + const { output: numberOfTagsAsString } = result; const numberOfTags = Number.parseInt(numberOfTagsAsString, 10); + log.debug('numberOfTags', numberOfTags); + return numberOfTags !== 0; }