Skip to content

Commit 00579cd

Browse files
unity-cli@v1.1.2 (#18)
- try to fix hanging on windows during editor execution
1 parent b14d7b2 commit 00579cd

7 files changed

Lines changed: 74 additions & 97 deletions

File tree

.github/workflows/publish.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ jobs:
1212
- uses: actions/checkout@v4
1313
- uses: actions/setup-node@v4
1414
with:
15-
node-version: 22.x
15+
node-version: 24.x
1616
registry-url: "https://registry.npmjs.org"
1717
- run: |
1818
npm i

.github/workflows/unity-build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ jobs:
2626
- uses: actions/checkout@v4
2727
- uses: actions/setup-node@v4
2828
with:
29-
node-version: 22.x
29+
node-version: 24.x
3030
- name: setup unity-cli
3131
shell: bash
3232
run: |

package-lock.json

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

package.json

Lines changed: 15 additions & 16 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.1",
3+
"version": "1.1.2",
44
"description": "A command line utility for the Unity Game Engine.",
55
"author": "RageAgainstThePixel",
66
"license": "MIT",
@@ -48,22 +48,21 @@
4848
"tests": "jest --roots tests"
4949
},
5050
"dependencies": {
51-
"@electron/asar": "4.0.1",
52-
"@rage-against-the-pixel/unity-releases-api": "1.0.3",
53-
"commander": "14.0.1",
51+
"@electron/asar": "^4.0.1",
52+
"@rage-against-the-pixel/unity-releases-api": "^1.0.3",
53+
"commander": "^14.0.1",
5454
"glob": "11.0.3",
55-
"semver": "7.7.2",
56-
"source-map-support": "0.5.21",
57-
"yaml": "2.8.1"
55+
"semver": "^7.7.2",
56+
"source-map-support": "^0.5.21",
57+
"yaml": "^2.8.1"
5858
},
5959
"devDependencies": {
60-
"@types/commander": "2.12.5",
61-
"@types/jest": "30.0.0",
62-
"@types/node": "24.6.2",
63-
"@types/semver": "7.7.1",
64-
"jest": "30.2.0",
65-
"ts-jest": "29.4.4",
66-
"ts-node": "10.9.2",
67-
"typescript": "5.9.3"
60+
"@types/jest": "^30.0.0",
61+
"@types/node": "^24.6.2",
62+
"@types/semver": "^7.7.1",
63+
"jest": "^30.2.0",
64+
"ts-jest": "^29.4.4",
65+
"ts-node": "^10.9.2",
66+
"typescript": "^5.9.3"
6867
}
69-
}
68+
}

src/unity-editor.ts

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -211,29 +211,24 @@ export class UnityEditor {
211211
process.once('SIGTERM', onCancel);
212212
procInfo = { pid: unityProcess.pid, ppid: process.pid, name: this.editorPath };
213213
this.logger.debug(`Unity process started with pid: ${procInfo.pid}`);
214-
const timeout = 10000; // 10 seconds
215-
await WaitForFileToBeCreatedAndReadable(logPath, timeout);
214+
await WaitForFileToBeCreatedAndReadable(logPath, 10_000);
216215
logTail = TailLogFile(logPath);
217216
exitCode = await new Promise((resolve, reject) => {
218217
unityProcess.on('close', (code) => {
219-
setTimeout(() => {
220-
logTail?.stopLogTail();
221-
resolve(code === null ? 1 : code);
222-
}, timeout);
218+
logTail?.stopLogTail();
219+
resolve(code === null ? 1 : code);
223220
});
224221
unityProcess.on('error', (error) => {
225-
setTimeout(() => {
226-
logTail?.stopLogTail();
227-
reject(error);
228-
}, timeout);
222+
logTail?.stopLogTail();
223+
reject(error);
229224
});
230225
});
231226
// Wait for log tailing to finish writing remaining content
232227
if (logTail && logTail.tailPromise) {
233228
try {
234229
await logTail.tailPromise;
235230
} catch (error) {
236-
this.logger.error(`Error occurred while tailing log file: ${error}`);
231+
this.logger.error(`Error occurred while tailing log: ${error}`);
237232
}
238233
}
239234
} finally {

src/unity-hub.ts

Lines changed: 11 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import * as fs from 'fs';
22
import * as os from 'os';
33
import * as path from 'path';
44
import * as yaml from 'yaml';
5+
import * as asar from '@electron/asar';
56
import { spawn } from 'child_process';
67
import { Logger, LogLevel } from './logging';
78
import { UnityEditor } from './unity-editor';
@@ -94,7 +95,8 @@ export class UnityHub {
9495
'You have to request `id` or `_id` fields for all selection sets or create a custom `keys` config for `UnityReleaseLabel`.',
9596
'Entities without keys will be embedded directly on the parent entity. If this is intentional, create a `keys` config for `UnityReleaseLabel` that always returns null.',
9697
'https://bit.ly/2XbVrpR#15',
97-
'Interaction is not allowed with the Security Server." (-25308)'
98+
'Interaction is not allowed with the Security Server." (-25308)',
99+
'Network service crashed, restarting service.',
98100
];
99101

100102
try {
@@ -245,6 +247,7 @@ export class UnityHub {
245247
throw new Error(`Failed to execute Unity Hub: [${exitCode}] ${errorMessage}`);
246248
}
247249
}
250+
248251
output = output.split('\n')
249252
.filter(line => line.trim().length > 0)
250253
.filter(line => !ignoredLines.some(ignored => line.includes(ignored)))
@@ -426,7 +429,6 @@ chmod -R 777 "$hubPath"`]);
426429
}
427430

428431
await fs.promises.access(asarPath, fs.constants.R_OK);
429-
const asar = await import('@electron/asar');
430432
const fileBuffer = asar.extractFile(asarPath, 'package.json');
431433
const packageJson = JSON.parse(fileBuffer.toString());
432434
const version = coerce(packageJson.version);
@@ -517,7 +519,7 @@ chmod -R 777 "$hubPath"`]);
517519
if (!resolvedVersion.isLegacy()) {
518520
try {
519521
if (!resolvedVersion.isFullyQualified()) {
520-
const releases = await this.getLatestHubReleases();
522+
const releases = await this.ListAvailableReleases();
521523
resolvedVersion = resolvedVersion.findMatch(releases);
522524
}
523525

@@ -609,7 +611,7 @@ chmod -R 777 "$hubPath"`]);
609611
public async ListInstalledEditors(): Promise<string[]> {
610612
const output = await this.Exec(['editors', '-i']);
611613
return output.split('\n')
612-
.filter(line => line.trim().length > 0)
614+
.filter(line => /installed at/.test(line))
613615
.map(line => line.trim());
614616
}
615617

@@ -619,8 +621,9 @@ chmod -R 777 "$hubPath"`]);
619621
*/
620622
public async ListAvailableReleases(): Promise<string[]> {
621623
const output = await this.Exec(['editors', '--releases']);
624+
// filter out version lines only 2021.3.45f2 (may include installed path following version)
622625
return output.split('\n')
623-
.filter(line => line.trim().length > 0)
626+
.filter(line => /^\d{1,4}\.\d+\.\d+[abcfpx]?\d*/.test(line.trim()))
624627
.map(line => line.trim());
625628
}
626629

@@ -722,25 +725,6 @@ chmod -R 777 "$hubPath"`]);
722725
return editorPath;
723726
}
724727

725-
private async getLatestHubReleases(): Promise<string[]> {
726-
// Normalize output to bare version strings (e.g., 2022.3.62f1)
727-
// Unity Hub can return lines like:
728-
// - "6000.0.56f1 (Apple silicon)"
729-
// - "2022.3.62f1 installed at C:\\..."
730-
// - "2022.3.62f1, installed at ..." (older format)
731-
// We extract the first version token and discard the rest.
732-
const versionRegex = /(\d{1,4})\.(\d+)\.(\d+)([abcfpx])(\d+)/;
733-
return (await this.Exec([`editors`, `--releases`]))
734-
.split('\n')
735-
.map(line => line.trim())
736-
.filter(line => line.length > 0)
737-
.map(line => {
738-
const match = line.match(versionRegex);
739-
return match ? match[0] : '';
740-
})
741-
.filter(v => v.length > 0);
742-
}
743-
744728
/**
745729
* Patches the Bee Backend for Unity Linux Editor.
746730
* https://discussions.unity.com/t/linux-editor-stuck-on-loading-because-of-bee-backend-w-workaround/854480
@@ -782,9 +766,9 @@ done
782766
if (fullUnityVersionPattern.test(unityVersion.version)) {
783767
version = unityVersion.version;
784768
} else {
785-
const mm = unityVersion.version.match(/^(\d{1,4})(?:\.(\d+))?/);
786-
if (mm) {
787-
version = mm[2] ? `${mm[1]}.${mm[2]}` : mm[1]!;
769+
const match = unityVersion.version.match(/^(\d{1,4})(?:\.(\d+))?/);
770+
if (match) {
771+
version = match[2] ? `${match[1]}.${match[2]}` : match[1]!;
788772
} else {
789773
version = unityVersion.version.split('.')[0]!;
790774
}

src/utilities.ts

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -144,19 +144,25 @@ export async function Exec(command: string, args: string[], options: ExecOptions
144144
child.on('close', (code) => {
145145
removeListeners();
146146

147-
// Flush any remaining buffered content
148-
if (lineBuffer.length > 0) {
149-
const lines = lineBuffer.split('\n') // split by newline
150-
.map(line => line.replace(/\r$/, '')) // remove trailing carriage return
151-
.filter(line => line.length > 0); // filter out empty lines
152-
153-
for (const line of lines) {
154-
output += `${line}\n`;
155-
156-
if (!isSilent) {
157-
process.stdout.write(`${line}\n`);
147+
try {
148+
// Flush any remaining buffered content
149+
if (lineBuffer.length > 0) {
150+
const lines = lineBuffer.split('\n') // split by newline
151+
.map(line => line.replace(/\r$/, '')) // remove trailing carriage return
152+
.filter(line => line.length > 0); // filter out empty lines
153+
154+
for (const line of lines) {
155+
output += `${line}\n`;
156+
157+
if (!isSilent) {
158+
process.stdout.write(`${line}\n`);
159+
}
158160
}
159161
}
162+
} catch (error: any) {
163+
if (error.code !== 'EPIPE') {
164+
logger.error(`Error while flushing output: ${error}`);
165+
}
160166
}
161167

162168
resolve(code === null ? 0 : code);
@@ -524,8 +530,7 @@ export async function KillProcess(procInfo: ProcInfo, signal: NodeJS.Signals = '
524530

525531
try {
526532
if (process.platform === 'win32') {
527-
const command = `taskkill /PID ${procInfo.pid} /F /T`;
528-
await Exec('powershell', ['-Command', command], { silent: true, showCommand: false });
533+
await Exec('taskkill', ['/PID', procInfo.pid.toString(), '/F', '/T'], { silent: true, showCommand: false });
529534
} else { // linux and macos
530535
process.kill(procInfo.pid, 'SIGKILL');
531536
}
@@ -552,7 +557,7 @@ export async function KillChildProcesses(procInfo: ProcInfo): Promise<void> {
552557
try {
553558
if (process.platform === 'win32') {
554559
const command = `Get-CimInstance Win32_Process -Filter "ParentProcessId=${procInfo.pid}" | ForEach-Object { Stop-Process -Id $_.ProcessId -Force }`;
555-
await Exec('powershell', ['-Command', command], { silent: true, showCommand: true });
560+
await Exec('powershell', ['-Command', command], { silent: true, showCommand: false });
556561
} else { // linux and macos
557562
const psOutput = await Exec('ps', ['-eo', 'pid,ppid,comm'], { silent: true, showCommand: false });
558563
const lines = psOutput.split('\n').slice(1); // Skip header line

0 commit comments

Comments
 (0)