Skip to content

Commit ed3f07a

Browse files
unity-cli@v1.0.1 (#2)
- export public api for use in other typescript projects - document all public members - cleanup process SIGINT and SIGTERM handlers
1 parent 3573642 commit ed3f07a

11 files changed

Lines changed: 210 additions & 106 deletions

package-lock.json

Lines changed: 8 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@rage-against-the-pixel/unity-cli",
3-
"version": "1.0.0",
3+
"version": "1.0.1",
44
"description": "A command line utility for the Unity Game Engine.",
55
"author": "RageAgainstThePixel",
66
"license": "MIT",
@@ -23,6 +23,7 @@
2323
"build": "tsc",
2424
"dev": "tsc --watch",
2525
"link": "npm link",
26+
"unlink": "npm unlink @rage-against-the-pixel/unity-cli",
2627
"tests": "jest --roots tests"
2728
},
2829
"dependencies": {
@@ -44,4 +45,4 @@
4445
"ts-node": "^10.9.2",
4546
"typescript": "^5.9.2"
4647
}
47-
}
48+
}

src/android-sdk.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,12 @@ import fs from 'fs';
22
import os from 'os';
33
import path from 'path';
44
import { spawn } from 'child_process';
5+
import { Logger } from './logging';
56
import { UnityEditor } from './unity-editor';
67
import {
78
ReadFileContents,
89
ResolveGlobToPath
910
} from './utilities';
10-
import {
11-
Logger,
12-
LogLevel
13-
} from './logging';
1411

1512
const logger = Logger.instance;
1613

@@ -132,8 +129,10 @@ async function execSdkManager(sdkManagerPath: string, javaPath: string, args: st
132129
env: { ...process.env, JAVA_HOME: javaPath }
133130
});
134131

135-
process.once('SIGINT', () => child.kill('SIGINT'));
136-
process.once('SIGTERM', () => child.kill('SIGTERM'));
132+
const sigintHandler = () => child.kill('SIGINT');
133+
const sigtermHandler = () => child.kill('SIGTERM');
134+
process.once('SIGINT', sigintHandler);
135+
process.once('SIGTERM', sigtermHandler);
137136
child.stdout.on('data', (data: Buffer) => {
138137
const chunk = data.toString();
139138
output += chunk;
@@ -150,9 +149,16 @@ async function execSdkManager(sdkManagerPath: string, javaPath: string, args: st
150149
output += chunk;
151150
process.stderr.write(chunk);
152151
});
153-
child.on('error', (error: Error) => reject(error));
152+
child.on('error', (error: Error) => {
153+
process.stdout.write('\n');
154+
process.removeListener('SIGINT', sigintHandler);
155+
process.removeListener('SIGTERM', sigtermHandler);
156+
reject(error);
157+
});
154158
child.on('close', (code: number | null) => {
155159
process.stdout.write('\n');
160+
process.removeListener('SIGINT', sigintHandler);
161+
process.removeListener('SIGTERM', sigtermHandler);
156162
resolve(code === null ? 0 : code);
157163
});
158164
});

src/index.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
#!/usr/bin/env node
22

33
import 'source-map-support/register';
4+
import * as fs from 'fs';
45
import * as os from 'os';
56
import { Command } from 'commander';
6-
import { readFileSync } from 'fs';
77
import path, { join } from 'path';
88
import { LicenseType, LicensingClient } from './license-client';
99
import { PromptForSecretInput } from './utilities';
@@ -14,8 +14,18 @@ import { UnityProject } from './unity-project';
1414
import { CheckAndroidSdkInstalled } from './android-sdk';
1515
import { UnityEditor } from './unity-editor';
1616

17+
// export public API
18+
export * from './license-client';
19+
export * from './utilities';
20+
export * from './unity-hub';
21+
export * from './logging';
22+
export * from './unity-version';
23+
export * from './unity-project';
24+
export * from './android-sdk';
25+
export * from './unity-editor';
26+
1727
const pkgPath = join(__dirname, '..', 'package.json');
18-
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
28+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
1929
const program = new Command();
2030

2131
program.name('unity-cli')

src/license-client.ts

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,16 @@ export enum LicenseType {
1313
}
1414

1515
export class LicensingClient {
16-
private unityHub: UnityHub = new UnityHub();
16+
private readonly unityHub: UnityHub = new UnityHub();
17+
private readonly logger: Logger = Logger.instance;
18+
1719
private licenseClientPath: string | undefined;
1820
private licenseVersion: string | undefined;
19-
private logger: Logger = Logger.instance;
2021

22+
/**
23+
* Creates an instance of LicensingClient.
24+
* @param licenseVersion The license version to use (e.g., '4.x', '5.x', '6.x'). If undefined, defaults to '6.x'.
25+
*/
2126
constructor(licenseVersion: string | undefined = undefined) {
2227
this.licenseVersion = licenseVersion;
2328
}
@@ -245,13 +250,22 @@ export class LicensingClient {
245250
stdio: ['ignore', 'pipe', 'pipe']
246251
});
247252

248-
process.once('SIGINT', () => child.kill('SIGINT'));
249-
process.once('SIGTERM', () => child.kill('SIGTERM'));
253+
const sigintHandler = () => child.kill('SIGINT');
254+
const sigtermHandler = () => child.kill('SIGTERM');
255+
process.once('SIGINT', sigintHandler);
256+
process.once('SIGTERM', sigtermHandler);
250257
child.stdout.on('data', processOutput);
251258
child.stderr.on('data', processOutput);
252-
child.on('error', (error) => reject(error));
259+
child.on('error', (error) => {
260+
process.stdout.write('\n');
261+
process.removeListener('SIGINT', sigintHandler);
262+
process.removeListener('SIGTERM', sigtermHandler);
263+
reject(error);
264+
});
253265
child.on('close', (code) => {
254266
process.stdout.write('\n');
267+
process.removeListener('SIGINT', sigintHandler);
268+
process.removeListener('SIGTERM', sigtermHandler);
255269
resolve(code === null ? 0 : code);
256270
});
257271
});
@@ -283,10 +297,22 @@ export class LicensingClient {
283297
});
284298
}
285299

300+
/**
301+
* Displays the version of the licensing client to the console.
302+
*/
286303
public async Version(): Promise<void> {
287304
await this.exec(['--version']);
288305
}
289306

307+
/**
308+
* Activates a Unity license.
309+
* @param licenseType The type of license to activate.
310+
* @param servicesConfig The services config path for floating licenses.
311+
* @param serial The license serial number.
312+
* @param username The Unity ID username.
313+
* @param password The Unity ID password.
314+
* @throws Error if activation fails or required parameters are missing.
315+
*/
290316
public async Activate(licenseType: LicenseType, servicesConfig: string | undefined = undefined, serial: string | undefined = undefined, username: string | undefined = undefined, password: string | undefined = undefined): Promise<void> {
291317
let activeLicenses = await this.showEntitlements();
292318

@@ -356,6 +382,12 @@ export class LicensingClient {
356382
}
357383
}
358384

385+
/**
386+
* Deactivates a Unity license.
387+
* @param licenseType The type of license to deactivate.
388+
* @returns A promise that resolves when the license is deactivated.
389+
* @throws Error if deactivation fails.
390+
*/
359391
public async Deactivate(licenseType: LicenseType): Promise<void> {
360392
if (licenseType === LicenseType.floating) {
361393
return;

src/logging.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,14 @@ export enum LogLevel {
88

99
export class Logger {
1010
public logLevel: LogLevel = LogLevel.INFO;
11-
private _ci: string | undefined;
12-
static instance: Logger = new Logger();
11+
private readonly _ci: string | undefined;
12+
static readonly instance: Logger = new Logger();
1313

1414
private constructor() {
1515
if (process.env.GITHUB_ACTIONS) {
1616
this._ci = 'GITHUB_ACTIONS';
1717
this.logLevel = process.env.ACTIONS_STEP_DEBUG === 'true' ? LogLevel.DEBUG : LogLevel.CI;
1818
}
19-
20-
Logger.instance = this;
2119
}
2220

2321
/**

src/unity-editor.ts

Lines changed: 21 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ import * as fs from 'fs';
22
import * as path from 'path';
33
import { Logger } from './logging';
44
import {
5-
getArgumentValueAsString,
6-
killChildProcesses,
5+
GetArgumentValueAsString,
6+
KillChildProcesses,
77
ProcInfo,
8-
readPidFile,
9-
tryKillProcess
8+
ReadPidFile,
9+
TryKillProcess
1010
} from './utilities';
1111
import {
1212
spawn,
@@ -19,21 +19,25 @@ export interface EditorCommand {
1919
}
2020

2121
export class UnityEditor {
22-
public editorRootPath: string;
22+
public readonly editorRootPath: string;
23+
24+
private readonly logger: Logger = Logger.instance;
25+
private readonly autoAddNoGraphics: boolean;
2326

2427
private procInfo: ProcInfo | undefined;
25-
private pidFile: string;
26-
private logger: Logger = Logger.instance;
27-
private autoAddNoGraphics: boolean;
2828

29-
constructor(public editorPath: string) {
29+
/**
30+
* Initializes a new instance of the UnityEditor class.
31+
* @param editorPath The path to the Unity Editor installation.
32+
* @throws Will throw an error if the editor path is invalid or not executable.
33+
*/
34+
constructor(public readonly editorPath: string) {
3035
if (!fs.existsSync(editorPath)) {
3136
throw new Error(`The Unity Editor path does not exist: ${editorPath}`);
3237
}
3338

3439
fs.accessSync(editorPath, fs.constants.X_OK);
3540
this.editorRootPath = UnityEditor.GetEditorRootPath(editorPath);
36-
this.pidFile = path.join(process.env.RUNNER_TEMP || process.env.USERPROFILE || '.', '.unity', 'unity-editor-process-id.txt');
3741

3842
const match = editorPath.match(/(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)/);
3943

@@ -117,6 +121,7 @@ export class UnityEditor {
117121
isCancelled = true;
118122
await this.tryKillEditorProcess();
119123
};
124+
120125
process.once('SIGINT', onCancel);
121126
process.once('SIGTERM', onCancel);
122127
let exitCode: number | undefined;
@@ -133,7 +138,10 @@ export class UnityEditor {
133138
exitCode = 1;
134139
}
135140
} finally {
141+
process.removeListener('SIGINT', onCancel);
142+
process.removeListener('SIGTERM', onCancel);
136143
this.logger.endGroup();
144+
137145
if (!isCancelled) {
138146
await this.tryKillEditorProcess();
139147

@@ -188,7 +196,7 @@ export class UnityEditor {
188196
command.args.push('-logFile', this.GenerateLogFilePath(command.projectPath));
189197
}
190198

191-
const logPath: string = getArgumentValueAsString('-logFile', command.args);
199+
const logPath: string = GetArgumentValueAsString('-logFile', command.args);
192200

193201
let unityProcess: ChildProcessByStdio<null, null, null>;
194202

@@ -225,26 +233,6 @@ export class UnityEditor {
225233

226234
onPid({ pid: processId, ppid: process.pid, name: this.editorPath });
227235
this.logger.debug(`Unity process started with pid: ${processId}`);
228-
// make sure the directory for the PID file exists
229-
const pidDir = path.dirname(this.pidFile);
230-
231-
if (!fs.existsSync(pidDir)) {
232-
fs.mkdirSync(pidDir, { recursive: true });
233-
} else {
234-
try {
235-
var existingProcInfo = await readPidFile(this.pidFile);
236-
if (existingProcInfo) {
237-
const killedPid = await tryKillProcess(existingProcInfo);
238-
if (killedPid) {
239-
this.logger.warn(`Killed existing Unity process with pid: ${killedPid}`);
240-
}
241-
}
242-
} catch {
243-
// PID file does not exist, continue
244-
}
245-
}
246-
// Write the PID to the PID file
247-
fs.writeFileSync(this.pidFile, String(processId));
248236
const logPollingInterval = 100; // milliseconds
249237
// Wait for log file to appear
250238
while (!fs.existsSync(logPath)) {
@@ -318,8 +306,8 @@ export class UnityEditor {
318306

319307
private async tryKillEditorProcess(): Promise<void> {
320308
if (this.procInfo) {
321-
await tryKillProcess(this.procInfo);
322-
await killChildProcesses(this.procInfo);
309+
await TryKillProcess(this.procInfo);
310+
await KillChildProcesses(this.procInfo);
323311
} else {
324312
this.logger.debug('No Unity process info available to kill.');
325313
}

0 commit comments

Comments
 (0)