Skip to content

Commit b14d7b2

Browse files
unity-cli@v1.1.1 (#17)
- update unity-hub task complete impl to try to resolve macOS editor install hanging
1 parent 9a8a6ac commit b14d7b2

4 files changed

Lines changed: 73 additions & 42 deletions

File tree

package-lock.json

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

package.json

Lines changed: 2 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.1.0",
3+
"version": "1.1.1",
44
"description": "A command line utility for the Unity Game Engine.",
55
"author": "RageAgainstThePixel",
66
"license": "MIT",
@@ -57,7 +57,7 @@
5757
"yaml": "2.8.1"
5858
},
5959
"devDependencies": {
60-
"@types/commander": "2.12.0",
60+
"@types/commander": "2.12.5",
6161
"@types/jest": "30.0.0",
6262
"@types/node": "24.6.2",
6363
"@types/semver": "7.7.1",

src/unity-editor.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,10 @@ export class UnityEditor {
132132
*/
133133
public async Run(command: EditorCommand): Promise<void> {
134134
let isCancelled = false;
135+
let exitCode: number = 1;
135136
let procInfo: ProcInfo | null = null;
136137
let logTail: LogTailResult | null = null;
138+
let unityProcess: ChildProcessByStdio<null, null, null>;
137139

138140
async function tryKillEditorProcesses(): Promise<void> {
139141
try {
@@ -151,8 +153,6 @@ export class UnityEditor {
151153
void tryKillEditorProcesses();
152154
}
153155

154-
let exitCode: number = 1;
155-
156156
try {
157157
if (!command.args || command.args.length === 0) {
158158
throw Error('No command arguments provided for Unity execution');
@@ -179,7 +179,6 @@ export class UnityEditor {
179179
const logPath: string = GetArgumentValueAsString('-logFile', command.args);
180180
const commandStr = `\x1b[34m${this.editorPath} ${command.args.join(' ')}\x1b[0m`;
181181
this.logger.startGroup(commandStr);
182-
let unityProcess: ChildProcessByStdio<null, null, null>;
183182

184183
if (process.platform === 'linux' && !command.args.includes('-nographics')) {
185184
unityProcess = spawn(

src/unity-hub.ts

Lines changed: 62 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import {
1919
ExecOptions,
2020
ReadFileContents,
2121
GetTempDir,
22-
KillProcess,
2322
} from './utilities';
2423
import {
2524
UnityReleasesClient,
@@ -100,27 +99,27 @@ export class UnityHub {
10099

101100
try {
102101
exitCode = await new Promise<number>((resolve, reject) => {
103-
let tasksComplete: boolean = false;
102+
let isSettled: boolean = false; // Has the promise been settled (resolved or rejected)?
103+
let isHubTaskComplete: boolean = false; // Has the Unity Hub tasks completed successfully?
104+
let lineBuffer = ''; // Buffer for incomplete lines
104105
const tasksCompleteMessage = 'All Tasks Completed Successfully.';
105106
const child = spawn(executable, execArgs, {
106-
stdio: ['ignore', 'pipe', 'pipe'],
107-
detached: process.platform !== 'win32'
107+
stdio: ['ignore', 'pipe', 'pipe']
108108
});
109109
const sigintHandler = () => child.kill('SIGINT');
110110
const sigtermHandler = () => child.kill('SIGTERM');
111111
process.once('SIGINT', sigintHandler);
112112
process.once('SIGTERM', sigtermHandler);
113113

114114
let hasCleanedUpListeners = false;
115-
function removeListeners() {
115+
function removeListeners(): void {
116116
if (hasCleanedUpListeners) { return; }
117117
hasCleanedUpListeners = true;
118118
process.removeListener('SIGINT', sigintHandler);
119119
process.removeListener('SIGTERM', sigtermHandler);
120120
}
121121

122-
let lineBuffer = ''; // Buffer for incomplete lines
123-
function processOutput(data: Buffer) {
122+
function processOutput(data: Buffer): void {
124123
try {
125124
const chunk = data.toString();
126125
const fullChunk = lineBuffer + chunk;
@@ -137,10 +136,26 @@ export class UnityHub {
137136
const outputLines = lines.filter(line => !ignoredLines.some(ignored => line.includes(ignored)));
138137

139138
if (outputLines.includes(tasksCompleteMessage)) {
140-
tasksComplete = true;
139+
isHubTaskComplete = true;
141140

142141
if (child?.pid) {
143-
void KillProcess({ pid: child.pid, name: child.spawnfile, ppid: process.pid });
142+
try {
143+
child.kill('SIGTERM');
144+
145+
setTimeout(() => {
146+
try {
147+
if (child?.pid && !child.killed) {
148+
child.kill('SIGKILL');
149+
}
150+
} catch {
151+
// Ignore, process may have already exited
152+
}
153+
}, 1000);
154+
} catch {
155+
// Ignore, process may have already exited
156+
} finally {
157+
settle(0);
158+
}
144159
}
145160
}
146161

@@ -157,41 +172,57 @@ export class UnityHub {
157172
}
158173
}
159174
}
160-
child.stdout.on('data', processOutput);
161-
child.stderr.on('data', processOutput);
162-
child.on('error', (error) => {
163-
removeListeners();
164-
reject(error);
165-
});
166-
child.on('close', (code) => {
167-
removeListeners();
168175

169-
// Flush any remaining buffered content
170-
if (lineBuffer.length > 0) {
171-
const lines = lineBuffer.split('\n') // split by newline
172-
.map(line => line.replace(/\r$/, '')) // remove trailing carriage return
173-
.filter(line => line.length > 0); // filter out empty lines
174-
const outputLines = lines.filter(line => !ignoredLines.some(ignored => line.includes(ignored)));
176+
function flushOutput(): void {
177+
try {
178+
if (lineBuffer.length > 0) {
179+
const lines = lineBuffer.split('\n') // split by newline
180+
.map(line => line.replace(/\r$/, '')) // remove trailing carriage return
181+
.filter(line => line.length > 0); // filter out empty lines
182+
lineBuffer = '';
183+
const outputLines = lines.filter(line => !ignoredLines.some(ignored => line.includes(ignored)));
175184

176-
if (outputLines.includes(tasksCompleteMessage)) {
177-
tasksComplete = true;
178-
}
185+
if (outputLines.includes(tasksCompleteMessage)) {
186+
isHubTaskComplete = true;
187+
}
179188

180-
for (const line of outputLines) {
181-
output += `${line}\n`;
189+
for (const line of outputLines) {
190+
output += `${line}\n`;
182191

183-
if (!options.silent) {
184-
process.stdout.write(`${line}\n`);
192+
if (!options.silent) {
193+
process.stdout.write(`${line}\n`);
194+
}
185195
}
186196
}
197+
} catch (error: any) {
198+
if (error.code !== 'EPIPE') {
199+
Logger.instance.error(`Failed to process buffered output: ${error}`);
200+
}
187201
}
202+
}
188203

189-
if (tasksComplete) {
204+
function settle(code: number | null): void {
205+
if (isSettled) { return; }
206+
isSettled = true;
207+
removeListeners();
208+
flushOutput();
209+
210+
if (isHubTaskComplete) {
190211
resolve(0);
191212
} else {
192213
resolve(code === null ? 0 : code);
193214
}
215+
}
216+
217+
child.stdout.on('data', processOutput);
218+
child.stderr.on('data', processOutput);
219+
child.on('error', (error) => {
220+
isSettled = true;
221+
removeListeners();
222+
flushOutput();
223+
reject(error);
194224
});
225+
child.on('close', settle);
195226
});
196227
} finally {
197228
this.logger.endGroup();

0 commit comments

Comments
 (0)