feat: add "config open" command

pull/413/head
Webber 2022-09-01 22:14:18 +02:00
parent 0ac9b8034b
commit f188ecfd62
10 changed files with 120 additions and 96 deletions

View File

@ -1,39 +1,16 @@
# Unity - Builder # GameCI CLI
(Not affiliated with Unity Technologies) The CLI is currently a work in progress.
GitHub Action to We expect it to be working by the end of 2022.
[build Unity projects](https://github.com/marketplace/actions/unity-builder)
for different platforms.
Part of the <a href="https://game.ci">GameCI</a> open source project. See [our roadmap](https://github.com/orgs/game-ci/projects/4/views/1) to follow our progress.
<br />
<br />
[![Actions status](https://github.com/game-ci/unity-builder/workflows/Builds/badge.svg?event=push&branch=main)](https://github.com/game-ci/unity-builder/actions?query=branch%3Amain+event%3Apush+workflow%3A%22Builds)
[![lgtm - code quality](https://img.shields.io/lgtm/grade/javascript/g/webbertakken/unity-builder.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/webbertakken/unity-builder/context:javascript)
[![codecov - test coverage](https://codecov.io/gh/game-ci/unity-builder/branch/master/graph/badge.svg)](https://codecov.io/gh/game-ci/unity-builder)
<br />
<br />
## How to use
Find the
[docs](https://game.ci/docs/github/builder)
on the GameCI
[documentation website](https://game.ci/docs).
## Related actions
Visit the
GameCI <a href="https://github.com/game-ci/unity-actions">Unity Actions</a>
status repository for related Actions.
## Community ## Community
Feel free to join us on Feel free to join us on
<a href="http://game.ci/discord"><img height="30" src="media/Discord-Logo.svg" alt="Discord" /></a> <a href="http://game.ci/discord"><img height="30" src="media/Discord-Logo.svg" alt="Discord" /></a> and engage with the
and engage with the community. community.
## Contributing ## Contributing

View File

@ -1,27 +1,40 @@
{ {
"tasks": { "tasks": {
"config": "deno run -A .tools/open-config-folder.ts",
"test": "deno test -A ./src/integrity.test.ts", "test": "deno test -A ./src/integrity.test.ts",
"coverage": "rm -rf .coverage ; deno test -A ./src/integrity.test.ts --coverage=.coverage/raw ; deno coverage .coverage/raw --lcov --output=.coverage/raw/.lcov ; perl .tools/lcov/genhtml.perl -q -o .coverage/report .coverage/raw/.lcov && deno run -A .tools/open-coverage-report.ts" "coverage": "rm -rf .coverage ; deno test -A ./src/integrity.test.ts --coverage=.coverage/raw ; deno coverage .coverage/raw --lcov --output=.coverage/raw/.lcov ; perl .tools/lcov/genhtml.perl -q -o .coverage/report .coverage/raw/.lcov && deno run -A .tools/open-coverage-report.ts"
}, },
"compilerOptions": { "compilerOptions": {
"allowJs": true, "allowJs": true,
"lib": ["deno.window"], "lib": [
"deno.window"
],
"strict": true "strict": true
}, },
"lint": { "lint": {
"files": { "files": {
"include": ["src/"], "include": [
"src/"
],
"exclude": [] "exclude": []
}, },
"rules": { "rules": {
"tags": ["recommended"], "tags": [
"include": ["ban-untagged-todo"], "recommended"
"exclude": ["no-unused-vars"] ],
"include": [
"ban-untagged-todo"
],
"exclude": [
"no-unused-vars"
]
} }
}, },
"fmt": { "fmt": {
"files": { "files": {
"include": ["src/"], "include": [
"src/"
],
"exclude": [] "exclude": []
}, },
"options": { "options": {

View File

@ -25,6 +25,7 @@ export class Cli {
this.configureLogger(); this.configureLogger();
this.globalOptions(); this.globalOptions();
await this.registerConfigCommand();
await this.registerBuildCommand(); await this.registerBuildCommand();
await this.parse(); await this.parse();
@ -45,7 +46,7 @@ export class Cli {
const defaultAbsolutePath = `${this.cliStorageAbsolutePath}/${this.configFileName}`; const defaultAbsolutePath = `${this.cliStorageAbsolutePath}/${this.configFileName}`;
this.yargs this.yargs
.config('config', `default: ${defaultCanonicalPath}`, async (override) => { .config('config', `default: ${defaultCanonicalPath}`, async (override: string) => {
const configPath = override || defaultAbsolutePath; const configPath = override || defaultAbsolutePath;
return JSON.parse(await Deno.readTextFile(configPath)); return JSON.parse(await Deno.readTextFile(configPath));
@ -150,6 +151,14 @@ export class Cli {
}); });
} }
private async registerConfigCommand() {
this.yargs.command('config', 'GameCI CLI configuration', async (yargs) => {
yargs
.command('open', 'Opens the CLI configuration folder', async (yargs) => {})
.middleware([async (args) => this.registerCommand(args, yargs)]);
});
}
private async registerCommand(args: YargsArguments, yargs: YargsInstance) { private async registerCommand(args: YargsArguments, yargs: YargsInstance) {
const { engine, engineVersion, _: command } = args; const { engine, engineVersion, _: command } = args;

View File

@ -4,36 +4,33 @@ import PlatformSetup from '../../model/platform-setup.ts';
import MacBuilder from '../../model/mac-builder.ts'; import MacBuilder from '../../model/mac-builder.ts';
import { CommandBase } from '../command-base.ts'; import { CommandBase } from '../command-base.ts';
import { UnityOptions } from '../../command-options/unity-options.ts'; import { UnityOptions } from '../../command-options/unity-options.ts';
import { YargsInstance } from '../../dependencies.ts'; import { YargsInstance, YargsArguments } from '../../dependencies.ts';
import { VersioningOptions } from '../../command-options/versioning-options.ts'; import { VersioningOptions } from '../../command-options/versioning-options.ts';
import { BuildOptions } from '../../command-options/build-options.ts'; import { BuildOptions } from '../../command-options/build-options.ts';
export class UnityBuildCommand extends CommandBase implements CommandInterface { export class UnityBuildCommand extends CommandBase implements CommandInterface {
public async execute(options): Promise<boolean> { public async execute(options: YargsArguments): Promise<boolean> {
try { // Todo - rework this without needing this.options, use parameters from cli instead.
// Todo - rework this without needing this.options, use parameters from cli instead. // const { workspace, actionFolder } = Action;
// const { workspace, actionFolder } = Action; // const { parameters, env } = this.options;
// const { parameters, env } = this.options; //
// // Action.checkCompatibility();
// Action.checkCompatibility(); // Cache.verify();
// Cache.verify(); //
// // const baseImage = new ImageTag(options);
// const baseImage = new ImageTag(options); // log.debug('baseImage', baseImage);
// log.debug('baseImage', baseImage); //
// // await PlatformSetup.setup(parameters, actionFolder);
// await PlatformSetup.setup(parameters, actionFolder); // if (env.getOS() === 'darwin') {
// if (env.getOS() === 'darwin') { // MacBuilder.run(actionFolder, workspace, parameters);
// MacBuilder.run(actionFolder, workspace, parameters); // } else {
// } else { // await Docker.run(baseImage, { workspace, actionFolder, ...parameters });
// await Docker.run(baseImage, { workspace, actionFolder, ...parameters }); // }
// } //
// // // Set output
// // Set output // await Output.setBuildVersion(parameters.buildVersion);
// await Output.setBuildVersion(parameters.buildVersion);
} catch (error) { return true;
log.error(error);
Deno.exit(1);
}
} }
public async configureOptions(yargs: YargsInstance): Promise<void> { public async configureOptions(yargs: YargsInstance): Promise<void> {

View File

@ -1,17 +1,18 @@
import { Options } from '../config/options.ts';
import { Input } from '../model/index.ts';
import Parameters from '../model/parameters.ts';
import { CommandInterface } from './command-interface.ts'; import { CommandInterface } from './command-interface.ts';
import { YargsArguments, YargsInstance } from '../dependencies.ts';
export class CommandBase implements CommandInterface { export class CommandBase implements CommandInterface {
public readonly name: string; public readonly name: string;
private options: Options;
constructor(name: string) { constructor(name: string) {
this.name = name; this.name = name.charAt(0).toUpperCase() + name.slice(1);
} }
public async execute(): Promise<boolean> { public execute(options: YargsArguments): Promise<boolean> {
throw new Error('Method not implemented.');
}
public configureOptions(yargs: YargsInstance): Promise<void> {
throw new Error('Method not implemented.'); throw new Error('Method not implemented.');
} }
} }

View File

@ -1,8 +1,8 @@
import { NonExistentCommand } from './null/non-existent-command.ts'; import { NonExistentCommand } from './null/non-existent-command.ts';
import { UnityBuildCommand } from './build/unity-build-command.ts'; import { UnityBuildCommand } from './build/unity-build-command.ts';
import { CommandInterface } from './command-interface.ts'; import { CommandInterface } from './command-interface.ts';
import { UnityRemoteBuildCommand } from './remote/unity-remote-build-command.ts';
import { Engine } from '../model/engine/engine.ts'; import { Engine } from '../model/engine/engine.ts';
import { OpenConfigFolderCommand } from './config/open-config-folder-command.ts';
export class CommandFactory { export class CommandFactory {
constructor() {} constructor() {}
@ -14,27 +14,40 @@ export class CommandFactory {
return this; return this;
} }
public createCommand(command: string[]): CommandInterface { public createCommand(commandArray: string[]): CommandInterface {
// Structure looks like: _: [ "build" ], // Structure looks like: _: [ "build" ], or _: [ "config", "open" ]
const commandName = command[0]; const [command, ...subCommands] = commandArray;
if (command === 'config') {
return this.createConfigCommand(command, subCommands);
}
switch (this.engine) { switch (this.engine) {
case Engine.unity: case Engine.unity:
return this.createUnityCommand(commandName); return this.createUnityCommand(command, subCommands);
default: default:
throw new Error(`Engine ${this.engine} is not yet supported.`); throw new Error(`Engine ${this.engine} is not yet supported.`);
} }
} }
private createUnityCommand(commandName: string) { private createConfigCommand(command: string, subCommands: string[]) {
switch (commandName) { switch (subCommands[0]) {
case 'open':
return new OpenConfigFolderCommand(command);
default:
return new NonExistentCommand([command, ...subCommands].join(' '));
}
}
private createUnityCommand(command: string, subCommands: string[]) {
switch (command) {
case 'build': case 'build':
return new UnityBuildCommand(commandName); return new UnityBuildCommand(command);
// case 'remote-build': // case 'remote-build':
// return new UnityRemoteBuildCommand(commandName); // return new UnityRemoteBuildCommand(commandName);
// default: default:
// return new NonExistentCommand(commandName); return new NonExistentCommand([command, ...subCommands].join(' '));
} }
} }
} }

View File

@ -1,8 +1,7 @@
import { Options } from '../config/options.ts'; import { YargsInstance, YargsArguments } from '../dependencies.ts';
import { yargs } from '../dependencies.ts';
export interface CommandInterface { export interface CommandInterface {
name: string; name: string;
execute: (options: Options) => Promise<boolean>; execute: (options: YargsArguments) => Promise<boolean>;
configureOptions: (instance: yargs.Argv) => Promise<void>; configureOptions: (instance: YargsInstance) => Promise<void>;
} }

View File

@ -0,0 +1,17 @@
import { CommandInterface } from '../command-interface.ts';
import { CommandBase } from '../command-base.ts';
import { YargsInstance, YargsArguments } from '../../dependencies.ts';
import { default as getHomeDir } from 'https://deno.land/x/dir@1.5.1/home_dir/mod.ts';
import { open } from 'https://deno.land/x/opener@v1.0.1/mod.ts';
export class OpenConfigFolderCommand extends CommandBase implements CommandInterface {
public async execute(options: YargsArguments): Promise<boolean> {
const cliStorageAbsolutePath = `${getHomeDir()}/.game-ci`;
await open(`file://${cliStorageAbsolutePath}/`);
return true;
}
public async configureOptions(yargs: YargsInstance): Promise<void> {}
}

View File

@ -1,16 +1,11 @@
import { CommandInterface } from '../command-interface.ts'; import { CommandInterface } from '../command-interface.ts';
import { Options } from '../../config/options.ts'; import { YargsInstance, YargsArguments } from '../../dependencies.ts';
import { CommandBase } from '../command-base.ts';
export class NonExistentCommand implements CommandInterface { export class NonExistentCommand extends CommandBase implements CommandInterface {
public name: string; public execute(options: YargsArguments): Promise<boolean> {
throw new Error(`Command "${this.name}" does not exist`);
constructor(name: string) {
this.name = name;
} }
public async execute(options: Options): Promise<boolean> { public async configureOptions(yargs: YargsInstance): Promise<void> {}
throw new Error(`Command ${this.name} does not exist`);
}
public async parseParameters() {}
} }

View File

@ -7,10 +7,13 @@ class GameCI {
const success = await command.execute(options); const success = await command.execute(options);
if (!success) log.warning(`${command.constructor.name} failed.`); if (success) {
log.info(`${command.name} done.`);
} else {
log.warning(`${command.constructor.name} failed.`);
}
} catch (error) { } catch (error) {
// eslint-disable-next-line no-console log.error(error);
console.error(error);
Deno.exit(1); Deno.exit(1);
} }
} }