feat: engine-specific commands and better organised environment

pull/413/head
Webber 2022-08-13 02:51:26 +02:00
parent 18af761e9f
commit cae5ffaf6c
10 changed files with 125 additions and 50 deletions

View File

@ -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);

View File

@ -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;

View File

@ -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 {

View File

@ -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;
}
}

View File

@ -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,
};
}
}

View File

@ -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<string, string | number | boolean>();
const subCommands: string[] = [];
const args = new Map<string, string | number | boolean>();
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 };
};

View File

@ -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',
};
}
}

29
src/core/env/environment.ts vendored 100644
View File

@ -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;
}
}

View File

@ -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();

View File

@ -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;
}