Skip to content

Commit 3a81c8f

Browse files
unity-cli@v1.2.2 (#24)
- added additional version checks - add Rosetta 2 exec on mac for x86_64 editors - add support for floating license config as path AND base64 string - fix occasional race condition when starting editor process exits faster than logs can be seen
1 parent b8339c3 commit 3a81c8f

File tree

11 files changed

+150
-49
lines changed

11 files changed

+150
-49
lines changed

.github/workflows/build-options.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@
66
],
77
"unity-version": [
88
"4.7.2",
9+
"5.6.7f1 (e80cc3114ac1)",
10+
"2017.x",
11+
"2018.x",
12+
"2019.x",
13+
"2020.x",
914
"2021.x",
1015
"2022.x",
1116
"6000.0.x",
@@ -34,6 +39,10 @@
3439
{
3540
"os": "macos-latest",
3641
"unity-version": "4.7.2"
42+
},
43+
{
44+
"os": "ubuntu-latest",
45+
"unity-version": "5.6.7f1 (e80cc3114ac1)"
3746
}
3847
]
3948
}

.github/workflows/integration-tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616
permissions:
1717
contents: read
1818
steps:
19-
- uses: actions/checkout@v4
19+
- uses: actions/checkout@v5
2020
with:
2121
sparse-checkout: .github/
2222
- uses: RageAgainstThePixel/job-builder@v1

.github/workflows/publish.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ jobs:
99
permissions:
1010
contents: read
1111
steps:
12-
- uses: actions/checkout@v4
12+
- uses: actions/checkout@v5
1313
- uses: actions/setup-node@v4
1414
with:
1515
node-version: 24.x

.github/workflows/unity-build.yml

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,9 @@ jobs:
2424
timeout-minutes: 30
2525
env:
2626
UNITY_PROJECT_PATH: '' # Set from create-project step
27+
RUN_BUILD: '' # Set to true if the build pipeline package can be installed and used
2728
steps:
28-
- uses: actions/checkout@v4
29+
- uses: actions/checkout@v5
2930
- uses: actions/setup-node@v4
3031
with:
3132
node-version: 24.x
@@ -73,15 +74,30 @@ jobs:
7374
echo "Error: UNITY_PROJECT_PATH is not set"
7475
exit 1
7576
fi
77+
# check if the project can be built. Only Unity 2019.4+ and newer majors support the build pipeline package
78+
version="${{ matrix.unity-version }}"
79+
# extract major and minor (minor may be empty if version is just '2019' etc.)
80+
major=$(echo "$version" | cut -d'.' -f1)
81+
minor=$(echo "$version" | cut -d'.' -f2)
82+
if [ -z "$minor" ]; then
83+
minor=0
84+
fi
85+
# numeric comparison: enable build for major > 2019 or major == 2019 and minor >= 4
86+
if [ "$major" -gt 2019 ] || { [ "$major" -eq 2019 ] && [ "$minor" -ge 4 ]; }; then
87+
echo "Proceeding with build for Unity version $version"
88+
echo "RUN_BUILD=true" >> $GITHUB_ENV
89+
else
90+
echo "Skipping build: Unity version $version does not support the build pipeline package (requires 2019.4+)"
91+
fi
7692
- name: Install OpenUPM and build pipeline package
7793
shell: bash
78-
if: ${{ matrix.unity-version != '4.7.2' && matrix.unity-version != '5.6.7' }}
94+
if: ${{ env.RUN_BUILD == 'true' }}
7995
run: |
8096
npm install -g openupm-cli
8197
cd "${UNITY_PROJECT_PATH}"
8298
openupm add com.utilities.buildpipeline
8399
- name: Build project
84-
if: ${{ matrix.unity-version != '4.7.2' && matrix.unity-version != '5.6.7' }}
100+
if: ${{ env.RUN_BUILD == 'true' }}
85101
shell: bash
86102
run: |
87103
unity-cli run --log-name Validate -quit -nographics -batchmode -executeMethod Utilities.Editor.BuildPipeline.UnityPlayerBuildTools.ValidateProject -importTMProEssentialsAsset

README.md

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,14 @@ npm install -g @rage-against-the-pixel/unity-cli
2222

2323
## Usage
2424

25+
In general, the command structure is:
26+
2527
```bash
26-
unity-cli [command] [options]
28+
unity-cli [command] [options] <args...>
2729
```
2830

31+
With options always using double dashes (`--option`) and arguments passed directly to Unity or Unity Hub commands as they normally would with single dashes (`-arg`). Each option typically has a short alias using a single dash (`-o`), except for commands where we pass through arguments, as those get confused by the command parser.
32+
2933
### Common Commands
3034

3135
#### Auth
@@ -61,21 +65,25 @@ unity-cli setup-unity --unity-version 2022.3.x --modules android,ios
6165

6266
#### Activate a Unity License
6367

68+
Supports personal, professional, and floating licenses (using a license server configuration).
69+
6470
```bash
65-
unity-cli activate-license --email <your-email> --password <your-password> --serial <your-serial>
71+
unity-cli activate-license --license personal --email <your-email> --password <your-password>
6672
```
6773

6874
#### Create a New Project from a Template
6975

70-
> [!NOTE] Regex patterns are supported for the `--template` option. For example, to create a 3D project with either the standard or cross-platform template, you can use `com.unity.template.3d(-cross-platform)?`.
76+
> [!NOTE]
77+
> Regex patterns are supported for the `--template` option. For example, to create a 3D project with either the standard or cross-platform template, you can use `com.unity.template.3d(-cross-platform)?`.
7178
7279
```bash
7380
unity-cli create-project --name "MyGame" --template com.unity.template.3d(-cross-platform)? --unity-editor <path-to-editor>
7481
```
7582

7683
#### Open a project from the command line
7784

78-
> [!NOTE] If you run this command in the same directory as your Unity project, you can omit the `--unity-project`, `--unity-version`, and `--unity-editor` options.
85+
> [!TIP]
86+
> If you run this command in the same directory as your Unity project, you can omit the `--unity-project`, `--unity-version`, and `--unity-editor` options.
7987
8088
```bash
8189
unity-cli open-project

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@rage-against-the-pixel/unity-cli",
3-
"version": "1.2.1",
3+
"version": "1.2.2",
44
"description": "A command line utility for the Unity Game Engine.",
55
"author": "RageAgainstThePixel",
66
"license": "MIT",

src/cli.ts

Lines changed: 51 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,16 @@ program.command('license-version')
3232
.action(async () => {
3333
const client = new LicensingClient();
3434
await client.Version();
35+
process.exit(0);
3536
});
3637

3738
program.command('activate-license')
3839
.description('Activate a Unity license.')
40+
.option('-l, --license <license>', 'License type (personal, professional, floating). Required.')
3941
.option('-e, --email <email>', 'Email associated with the Unity account. Required when activating a personal or professional license.')
4042
.option('-p, --password <password>', 'Password for the Unity account. Required when activating a personal or professional license.')
4143
.option('-s, --serial <serial>', 'License serial number. Required when activating a professional license.')
42-
.option('-l, --license <license>', 'License type (personal, professional, floating).')
43-
.option('-c, --config <config>', 'Path to the configuration file. Required when activating a floating license.')
44+
.option('-c, --config <config>', 'Path to the configuration file, or base64 encoded JSON string. Required when activating a floating license.')
4445
.option('--verbose', 'Enable verbose logging.')
4546
.action(async (options) => {
4647
if (options.verbose) {
@@ -53,13 +54,15 @@ program.command('activate-license')
5354
const licenseStr: string = options.license?.toString()?.trim();
5455

5556
if (!licenseStr || licenseStr.length === 0) {
56-
throw new Error('License type is required. Use -l or --license to specify it.');
57+
Logger.instance.error('License type is required. Use -l or --license to specify it.');
58+
process.exit(1);
5759
}
5860

5961
const licenseType: LicenseType = options.license.toLowerCase() as LicenseType;
6062

6163
if (![LicenseType.personal, LicenseType.professional, LicenseType.floating].includes(licenseType)) {
62-
throw new Error(`Invalid license type: ${licenseType}`);
64+
Logger.instance.error(`Invalid license type: ${licenseType}`);
65+
process.exit(1);
6366
}
6467

6568
if (licenseType !== LicenseType.floating) {
@@ -83,6 +86,7 @@ program.command('activate-license')
8386
username: options.email,
8487
password: options.password
8588
});
89+
process.exit(0);
8690
});
8791

8892
program.command('return-license')
@@ -100,16 +104,19 @@ program.command('return-license')
100104
const licenseStr: string = options.license?.toString()?.trim();
101105

102106
if (!licenseStr || licenseStr.length === 0) {
103-
throw new Error('License type is required. Use -l or --license to specify it.');
107+
Logger.instance.error('License type is required. Use -l or --license to specify it.');
108+
process.exit(1);
104109
}
105110

106111
const licenseType: LicenseType = licenseStr.toLowerCase() as LicenseType;
107112

108113
if (![LicenseType.personal, LicenseType.professional, LicenseType.floating].includes(licenseType)) {
109-
throw new Error(`Invalid license type: ${licenseType}`);
114+
Logger.instance.error(`Invalid license type: ${licenseType}`);
115+
process.exit(1);
110116
}
111117

112118
await client.Deactivate(licenseType);
119+
process.exit(0);
113120
});
114121

115122
program.commandsGroup('Unity Hub:');
@@ -123,6 +130,8 @@ program.command('hub-version')
123130
process.stdout.write(`${version}\n`);
124131
} catch (error) {
125132
process.stdout.write(`${error}\n`);
133+
} finally {
134+
process.exit(0);
126135
}
127136
});
128137

@@ -148,6 +157,8 @@ program.command('hub-install')
148157
} else {
149158
process.stdout.write(`${hubPath}\n`);
150159
}
160+
161+
process.exit(0);
151162
});
152163

153164
program.command('hub-path')
@@ -163,13 +174,15 @@ program.command('hub-path')
163174
} else {
164175
process.stdout.write(`${hub.executable}\n`);
165176
}
177+
178+
process.exit(0);
166179
});
167180

168181
program.command('hub')
169182
.description('Run commands directly to the Unity Hub. (You need not to pass --headless or -- to this command).')
183+
.allowUnknownOption(true)
170184
.argument('<args...>', 'Arguments to pass to the Unity Hub executable.')
171185
.option('--verbose', 'Enable verbose logging.')
172-
.allowUnknownOption(true)
173186
.action(async (args: string[], options) => {
174187
if (options.verbose) {
175188
Logger.instance.logLevel = LogLevel.DEBUG;
@@ -179,6 +192,7 @@ program.command('hub')
179192

180193
const unityHub = new UnityHub();
181194
await unityHub.Exec(args, { silent: false, showCommand: Logger.instance.logLevel === LogLevel.DEBUG });
195+
process.exit(0);
182196
});
183197

184198
program.command('setup-unity')
@@ -206,7 +220,8 @@ program.command('setup-unity')
206220
}
207221

208222
if (!options.unityVersion && !unityProject) {
209-
throw new Error('You must specify a Unity version or project path with -u, --unity-version, -p, --unity-project.');
223+
Logger.instance.error('You must specify a Unity version or project path with -u, --unity-version, -p, --unity-project.');
224+
process.exit(1);
210225
}
211226

212227
const unityVersion = unityProject?.version ?? new UnityVersion(options.unityVersion, options.changeset);
@@ -266,6 +281,8 @@ program.command('setup-unity')
266281
}
267282
}
268283
}
284+
285+
process.exit(0);
269286
});
270287

271288
program.command('uninstall-unity')
@@ -289,6 +306,7 @@ program.command('uninstall-unity')
289306
const unityVersion = new UnityVersion(unityVersionStr, options.changeset, options.arch);
290307
const unityHub = new UnityHub();
291308
const installedEditors = await unityHub.ListInstalledEditors();
309+
292310
if (unityVersion.isLegacy()) {
293311
const installPath = await unityHub.GetInstallPath();
294312
unityEditor = new UnityEditor(path.join(installPath, `Unity ${unityVersion.toString()}`, 'Unity.exe'));
@@ -299,7 +317,8 @@ program.command('uninstall-unity')
299317
const editorPath = options.unityEditor?.toString()?.trim() || process.env.UNITY_EDITOR_PATH || undefined;
300318

301319
if (!editorPath || editorPath.length === 0) {
302-
throw new Error('You must specify a Unity version or editor path with -u, --unity-version, -e, --unity-editor.');
320+
Logger.instance.error('You must specify a Unity version or editor path with -u, --unity-version, -e, --unity-editor.');
321+
process.exit(1);
303322
}
304323

305324
try {
@@ -332,11 +351,12 @@ program.command('open-project')
332351
}
333352

334353
Logger.instance.debug(JSON.stringify(options));
335-
const projectPath = options.unityProject?.toString()?.trim() || process.env.UNITY_PROJECT_PATH || process.cwd();
354+
const projectPath = options.unityProject?.toString()?.trim() || process.env.UNITY_PROJECT_PATH || undefined;
336355
const unityProject = await UnityProject.GetProject(projectPath);
337356

338357
if (!unityProject) {
339-
throw new Error(`The specified path is not a valid Unity project: ${projectPath}`);
358+
Logger.instance.error(`The specified path is not a valid Unity project: ${projectPath}`);
359+
process.exit(1);
340360
}
341361

342362
const unityVersion = unityProject?.version ?? new UnityVersion(options.unityVersion, options.changeset);
@@ -380,7 +400,8 @@ program.command('run')
380400
const unityProject = await UnityProject.GetProject(projectPath);
381401

382402
if (!unityProject) {
383-
throw new Error(`The specified path is not a valid Unity project: ${projectPath}`);
403+
Logger.instance.error(`The specified path is not a valid Unity project: ${projectPath}`);
404+
process.exit(1);
384405
}
385406

386407
if (!unityEditor) {
@@ -389,7 +410,8 @@ program.command('run')
389410
}
390411

391412
if (!unityEditor) {
392-
throw new Error('The Unity Editor path was not specified. Use --unity-editor to specify it or set the UNITY_EDITOR_PATH environment variable.');
413+
Logger.instance.error('The Unity Editor path was not specified. Use --unity-editor to specify it or set the UNITY_EDITOR_PATH environment variable.');
414+
process.exit(1);
393415
}
394416

395417
if (!args.includes('-logFile')) {
@@ -400,6 +422,7 @@ program.command('run')
400422
await unityEditor.Run({
401423
args: [...args]
402424
});
425+
process.exit(0);
403426
});
404427

405428
program.command('list-project-templates')
@@ -418,7 +441,8 @@ program.command('list-project-templates')
418441
const unityVersionStr = options.unityVersion?.toString()?.trim();
419442

420443
if (!unityVersionStr && !options.unityEditor) {
421-
throw new Error('You must specify a Unity version or editor path with -u, --unity-version, -e, --unity-editor.');
444+
Logger.instance.error('You must specify a Unity version or editor path with -u, --unity-version, -e, --unity-editor.');
445+
process.exit(1);
422446
}
423447

424448
let unityEditor: UnityEditor;
@@ -442,14 +466,13 @@ program.command('list-project-templates')
442466
if (options.json) {
443467
process.stdout.write(`\n${JSON.stringify({ templates })}\n`);
444468
} else {
445-
process.stdout.write(`Available project templates:\n`);
446-
for (const template of templates) {
447-
process.stdout.write(` - ${path.basename(template)}\n`);
448-
}
469+
process.stdout.write(`Available project templates:\n${templates.map(t => ` - ${path.basename(t)}`).join('\n')}\n`);
449470
}
450471
} else {
451472
process.stdout.write('No project templates found for this Unity Editor.\n');
452473
}
474+
475+
process.exit(0);
453476
});
454477

455478
program.command('create-project')
@@ -471,7 +494,8 @@ program.command('create-project')
471494
const unityVersionStr = options.unityVersion?.toString()?.trim();
472495

473496
if (!unityVersionStr && !options.unityEditor) {
474-
throw new Error('You must specify a Unity version or editor path with -u, --unity-version, -e, --unity-editor.');
497+
Logger.instance.error('You must specify a Unity version or editor path with -u, --unity-version, -e, --unity-editor.');
498+
process.exit(1);
475499
}
476500

477501
let unityEditor: UnityEditor;
@@ -483,7 +507,8 @@ program.command('create-project')
483507
const editorPath = options.unityEditor?.toString()?.trim() || process.env.UNITY_EDITOR_PATH;
484508

485509
if (!editorPath || editorPath.length === 0) {
486-
throw new Error('The Unity Editor path was not specified. Use -e or --unity-editor to specify it, or set the UNITY_EDITOR_PATH environment variable.');
510+
Logger.instance.error('The Unity Editor path was not specified. Use -e or --unity-editor to specify it, or set the UNITY_EDITOR_PATH environment variable.');
511+
process.exit(1);
487512
}
488513

489514
unityEditor = new UnityEditor(editorPath);
@@ -506,7 +531,10 @@ program.command('create-project')
506531

507532
if (!unityEditor.version.isLegacy() && options.template && options.template.length > 0) {
508533
const templatePath = unityEditor.GetTemplatePath(options.template);
509-
args.push('-cloneFromTemplate', templatePath);
534+
535+
if (templatePath) {
536+
args.push('-cloneFromTemplate', templatePath);
537+
}
510538
}
511539

512540
await unityEditor.Run({ projectPath, args });
@@ -518,6 +546,8 @@ program.command('create-project')
518546
} else {
519547
process.stdout.write(`Unity project created at: ${projectPath}\n`);
520548
}
549+
550+
process.exit(0);
521551
});
522552

523553
program.parse(process.argv);

0 commit comments

Comments
 (0)