From cae5ffaf6c56b8d63121526f66daaffb9feae439 Mon Sep 17 00:00:00 2001 From: Webber Date: Sat, 13 Aug 2022 02:51:26 +0200 Subject: [PATCH] feat: engine-specific commands and better organised environment --- src/commands/command-factory.ts | 23 +++++++++++-- .../command/{ => unity}/build-command.ts | 14 ++++---- .../{ => unity}/build-remote-command.ts | 10 +++--- src/config/options.ts | 16 +++++---- src/core/cli/arguments-parser.ts | 6 ++-- src/core/cli/parse-argv.ts | 23 ++++++++++--- src/core/engine/engine-detector.ts | 15 +++++++++ src/core/env/environment.ts | 29 ++++++++++++++++ src/index.ts | 33 ++++++++----------- src/model/parameters.ts | 6 ++-- 10 files changed, 125 insertions(+), 50 deletions(-) rename src/commands/command/{ => unity}/build-command.ts (81%) rename src/commands/command/{ => unity}/build-remote-command.ts (71%) create mode 100644 src/core/engine/engine-detector.ts create mode 100644 src/core/env/environment.ts diff --git a/src/commands/command-factory.ts b/src/commands/command-factory.ts index b3c296f0..1991de39 100644 --- a/src/commands/command-factory.ts +++ b/src/commands/command-factory.ts @@ -1,11 +1,28 @@ import { NonExistentCommand } from './command/non-existent-command.ts'; -import { BuildCommand } from './command/build-command.ts'; -import { BuildRemoteCommand } from './command/build-remote-command.ts'; +import { BuildCommand } from './command/unity/build-command.ts'; +import { BuildRemoteCommand } from './command/unity/build-remote-command.ts'; +import { CommandInterface } from './command/command-interface.ts'; export class CommandFactory { constructor() {} - public createCommand(commandName: string) { + selectEngine(engine: string, engineVersion: string) { + this.engine = engine; + this.engineVersion = engineVersion; + + return this; + } + + public createCommand(commandName: string): CommandInterface { + switch (this.engine) { + case 'unity': + return this.createUnityCommand(commandName); + default: + throw new Error(`Engine ${this.engine} is not yet supported.`); + } + } + + private createUnityCommand(commandName: string) { switch (commandName) { case 'build': return new BuildCommand(commandName); diff --git a/src/commands/command/build-command.ts b/src/commands/command/unity/build-command.ts similarity index 81% rename from src/commands/command/build-command.ts rename to src/commands/command/unity/build-command.ts index 61874fc9..941d7b24 100644 --- a/src/commands/command/build-command.ts +++ b/src/commands/command/unity/build-command.ts @@ -1,11 +1,11 @@ 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, Input, 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'; -import Parameters from '../../model/parameters.ts'; +import { CommandInterface } from '../command-interface.ts'; +import { Options } from '../../../config/options.ts'; +import { Action, Cache, CloudRunner, Docker, ImageTag, Input, 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'; +import Parameters from '../../../model/parameters.ts'; export class BuildCommand implements CommandInterface { public readonly name: string; diff --git a/src/commands/command/build-remote-command.ts b/src/commands/command/unity/build-remote-command.ts similarity index 71% rename from src/commands/command/build-remote-command.ts rename to src/commands/command/unity/build-remote-command.ts index 326ff5a0..9dd089c1 100644 --- a/src/commands/command/build-remote-command.ts +++ b/src/commands/command/unity/build-remote-command.ts @@ -1,8 +1,8 @@ -import { CommandInterface } from './command-interface.ts'; -import { Options } from '../../config/options.ts'; -import { CloudRunner, ImageTag, Input, Output } from '../../model/index.ts'; -import { core } from '../../dependencies.ts'; -import Parameters from '../../model/parameters.ts'; +import { CommandInterface } from '../command-interface.ts'; +import { Options } from '../../../config/options.ts'; +import { CloudRunner, ImageTag, Input, Output } from '../../../model/index.ts'; +import { core } from '../../../dependencies.ts'; +import Parameters from '../../../model/parameters.ts'; // Todo - Verify this entire flow export class BuildRemoteCommand implements CommandInterface { diff --git a/src/config/options.ts b/src/config/options.ts index c2340581..15288928 100644 --- a/src/config/options.ts +++ b/src/config/options.ts @@ -1,17 +1,15 @@ import { CliArguments } from '../core/cli/cli-arguments.ts'; -import { EnvVariables } from '../core/env/env-variables.ts'; import { Parameters, Input } from '../model/index.ts'; import { CommandInterface } from '../commands/command/command-interface.ts'; +import { Environment } from '../core/env/environment.ts'; export class Options { public input: Input; public parameters: Parameters; - private readonly env: EnvVariables; - private readonly command: CommandInterface; + private readonly env: Environment; + private command: CommandInterface; - constructor(command: CommandInterface, env: EnvVariables) { - this.input = null; - this.parameters = null; + constructor(command: CommandInterface, env: Environment) { this.env = env; this.command = command; @@ -26,4 +24,10 @@ export class Options { return this; } + + registerCommand(command: CommandInterface) { + this.command = command; + + return this; + } } diff --git a/src/core/cli/arguments-parser.ts b/src/core/cli/arguments-parser.ts index b0402c2d..c34e10cf 100644 --- a/src/core/cli/arguments-parser.ts +++ b/src/core/cli/arguments-parser.ts @@ -2,11 +2,13 @@ import { parseArgv } from './parse-argv.ts'; export class ArgumentsParser { public parse(cliArguments: string[]) { - const [commandName, ...args] = cliArguments; + const [commandName, ...rest] = cliArguments; + const { subCommands, args } = parseArgv(rest); return { commandName, - args: parseArgv(args), + subCommands, + args, }; } } diff --git a/src/core/cli/parse-argv.ts b/src/core/cli/parse-argv.ts index 009cdddf..4ed972a6 100644 --- a/src/core/cli/parse-argv.ts +++ b/src/core/cli/parse-argv.ts @@ -8,9 +8,11 @@ import { CliArguments } from './cli-arguments.ts'; * console.log(parseArgv(process.argv)); // Node * * Example: - * deno run my-script -test1=1 -test2 "2" -test3 -test4 false -test5 "one" -test6= -test7=9BX9 + * deno run my-script my-project -test1=1 -test2 "2" -test3 -test4 false -test5 "one" -test6= -test7=9BX9 * * Output: + * [ + * [ 'my-project' ], * Map { * "test1" => 1, * "test2" => 2, @@ -20,11 +22,24 @@ import { CliArguments } from './cli-arguments.ts'; * "test6" => "", * "test7" => "9BX9" * } + * ] */ export const parseArgv = (argv: string[] = [], { verbose = false } = {}): CliArguments => { - const providedArguments = new Map(); + const subCommands: string[] = []; + const args = new Map(); + let hasParsedSubCommands = false; for (let current = 0, next = 1; current < argv.length; current += 1, next += 1) { + // Detect subCommands + if (!hasParsedSubCommands) { + if (argv[current].startsWith('-')) { + hasParsedSubCommands = true; + } else { + subCommands.push(argv[current]); + continue; + } + } + // Detect flag if (!argv[current].startsWith('-')) continue; let flag = argv[current].replace(/^-+/, ''); @@ -44,8 +59,8 @@ export const parseArgv = (argv: string[] = [], { verbose = false } = {}): CliArg // Assign // eslint-disable-next-line no-console if (verbose) console.log(`Found flag "${flag}" with value "${value}" (${typeof value}).`); - providedArguments.set(flag, value); + args.set(flag, value); } - return providedArguments; + return { subCommands, args }; }; diff --git a/src/core/engine/engine-detector.ts b/src/core/engine/engine-detector.ts new file mode 100644 index 00000000..7f864827 --- /dev/null +++ b/src/core/engine/engine-detector.ts @@ -0,0 +1,15 @@ +export class EngineDetector { + private projectPath: string; + + constructor(subCommands: string[], args: string[]) { + this.projectPath = subCommands[0] || args.projectPath || '.'; + } + + public async detect(): Promise<{ engine: string; engineVersion: string }> { + // Todo - detect and return real versions + return { + engine: 'unity', + engineVersion: '2020.1.0f1', + }; + } +} diff --git a/src/core/env/environment.ts b/src/core/env/environment.ts new file mode 100644 index 00000000..a34b4b8c --- /dev/null +++ b/src/core/env/environment.ts @@ -0,0 +1,29 @@ +type EnvVariables = { [index: string]: string }; + +export class Environment implements EnvVariables { + public readonly os: string; + public readonly arch: string; + + constructor(env: Deno.env) { + // Make an immutable copy of the environment variables. + for (const [key, value] of Object.entries(env.toObject())) { + if (value !== undefined) this[key] = value; + } + + // Override specific variables. + this.os = Deno.build.os; + this.arch = Deno.build.arch; + } + + public get(key: string): string | undefined { + return this[key]; + } + + public getOS(): string { + return this.os; + } + + public getArch(): string { + return this.arch; + } +} diff --git a/src/index.ts b/src/index.ts index 76334636..29fa36c3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,34 +1,27 @@ import './core/logger/index.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'; -import System from './model/system.ts'; +import { Environment } from './core/env/environment.ts'; +import { EngineDetector } from './core/engine/engine-detector.ts'; export class GameCI { - private readonly commandFactory: CommandFactory; - private readonly argumentsParser: ArgumentsParser; - private readonly env: EnvVariables; + private readonly env: Environment; - private options?: Options; - private command?: CommandInterface; - - constructor(envVariables: EnvVariables) { - this.env = envVariables; - - this.commandFactory = new CommandFactory(); - this.argumentsParser = new ArgumentsParser(); + constructor() { + this.env = new Environment(Deno.env); + this.args = Deno.args; } - public async run(cliArguments: string[]) { + public async run() { try { - const { commandName, args } = this.argumentsParser.parse(cliArguments); + const { commandName, subCommands, args } = new ArgumentsParser().parse(this.args); + const { engine, engineVersion } = await new EngineDetector(subCommands, args).detect(); - this.options = await new Options(this.env).generateParameters(args); - this.command = this.commandFactory.createCommand(commandName); + const command = new CommandFactory().selectEngine(engine, engineVersion).createCommand(commandName, subCommands); + const options = await new Options(command, this.env).registerCommand(command).generateParameters(args); - await this.command.execute(this.options); + await command.execute(options); } catch (error) { log.error(error); Deno.exit(1); @@ -36,4 +29,4 @@ export class GameCI { } } -await new GameCI(Deno.env.toObject()).run(Deno.args); +await new GameCI().run(); diff --git a/src/model/parameters.ts b/src/model/parameters.ts index 132debc2..22ad8252 100644 --- a/src/model/parameters.ts +++ b/src/model/parameters.ts @@ -9,8 +9,8 @@ import Versioning from './versioning.ts'; import { GitRepoReader } from './input-readers/git-repo.ts'; import { GithubCliReader } from './input-readers/github-cli.ts'; import { Cli } from './cli/cli.ts'; -import { EnvVariables } from '../core/env/env-variables.ts'; import { CommandInterface } from '../commands/command/command-interface.ts'; +import { Environment } from '../core/env/environment.ts'; class Parameters { private command: CommandInterface; @@ -69,9 +69,9 @@ class Parameters { public isCliMode!: boolean; private readonly input: Input; - private readonly env: EnvVariables; + private readonly env: Environment; - constructor(input: Input, env: EnvVariables) { + constructor(input: Input, env: Environment) { this.input = input; this.env = env; }