unity-hub-cli 0.19.0 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/dist/index.js +224 -13
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -46,7 +46,7 @@ On standalone Git Bash (MinTTY), raw mode is not supported; use PowerShell/CMD/W
|
|
|
46
46
|
| `i` | Launch external editor only |
|
|
47
47
|
| `q` | Quit Unity for selected project |
|
|
48
48
|
| `r` | Refresh project list |
|
|
49
|
-
| `c` | Copy project path to clipboard |
|
|
49
|
+
| `c` | Copy project path to clipboard (when launched via the `unity-hub` shell function, it will `cd` into the project directory and exit) |
|
|
50
50
|
| `s` | Open sort settings panel |
|
|
51
51
|
| `v` | Open visibility settings panel |
|
|
52
52
|
| `Ctrl + C` | Exit |
|
|
@@ -60,6 +60,7 @@ The display includes Git branch (if present), Unity version, project path, and l
|
|
|
60
60
|
- `--no-git-root-name`: Display Unity project titles instead of Git repository root folder names.
|
|
61
61
|
- `--shell-init`: Install shell function for automatic `cd` integration (with confirmation prompt).
|
|
62
62
|
- `--shell-init --dry-run`: Preview the shell function without installing.
|
|
63
|
+
- `update`: Update the globally installed `unity-hub-cli` to the latest version (with a confirmation prompt). It runs: `npm install -g unity-hub-cli@latest --ignore-scripts --no-fund`.
|
|
63
64
|
|
|
64
65
|
## Shell Integration
|
|
65
66
|
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.tsx
|
|
4
|
-
import { execSync } from "child_process";
|
|
4
|
+
import { execSync, spawnSync } from "child_process";
|
|
5
5
|
import { existsSync as existsSync3, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
6
6
|
import { homedir } from "os";
|
|
7
7
|
import { dirname as dirname2, join as join9 } from "path";
|
|
@@ -2059,11 +2059,11 @@ var extractRootFolder2 = (repository) => {
|
|
|
2059
2059
|
return base || void 0;
|
|
2060
2060
|
};
|
|
2061
2061
|
var minimumVisibleProjectCount = 4;
|
|
2062
|
-
var
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
return root && root.length > 0 ? root : view.project.path;
|
|
2062
|
+
var buildDefaultHintMessage = (outputPathOnExit) => {
|
|
2063
|
+
const cLabel = outputPathOnExit ? "cd" : "copy";
|
|
2064
|
+
return `j/k Select \xB7 [o]pen [O]+Editor [i]de [q]uit [r]efresh [c]${cLabel} [f]av [s]ort [v]isibility \xB7 ^C Exit`;
|
|
2066
2065
|
};
|
|
2066
|
+
var getCopyTargetPath = (view) => view.project.path;
|
|
2067
2067
|
var App = ({
|
|
2068
2068
|
projects,
|
|
2069
2069
|
onLaunch,
|
|
@@ -2079,6 +2079,10 @@ var App = ({
|
|
|
2079
2079
|
const { exit } = useApp();
|
|
2080
2080
|
const { stdout } = useStdout2();
|
|
2081
2081
|
const colors = useThemeColors();
|
|
2082
|
+
const defaultHintMessage = useMemo2(
|
|
2083
|
+
() => buildDefaultHintMessage(outputPathOnExit),
|
|
2084
|
+
[outputPathOnExit]
|
|
2085
|
+
);
|
|
2082
2086
|
const [projectViews, setProjectViews] = useState4(projects);
|
|
2083
2087
|
const [isSortMenuOpen, setIsSortMenuOpen] = useState4(false);
|
|
2084
2088
|
const [isVisibilityMenuOpen, setIsVisibilityMenuOpen] = useState4(false);
|
|
@@ -2239,15 +2243,21 @@ var App = ({
|
|
|
2239
2243
|
});
|
|
2240
2244
|
}, [index, limit, sortedProjects.length]);
|
|
2241
2245
|
const copyProjectPath = useCallback(() => {
|
|
2246
|
+
const actionLabel = outputPathOnExit ? "cd" : "copy";
|
|
2242
2247
|
const projectView = sortedProjects[index];
|
|
2243
2248
|
const projectPath = projectView ? getCopyTargetPath(projectView) : void 0;
|
|
2244
2249
|
if (!projectPath) {
|
|
2245
|
-
setHint(
|
|
2250
|
+
setHint(`No project to ${actionLabel}`);
|
|
2246
2251
|
setTimeout(() => {
|
|
2247
2252
|
setHint(defaultHintMessage);
|
|
2248
2253
|
}, 2e3);
|
|
2249
2254
|
return;
|
|
2250
2255
|
}
|
|
2256
|
+
if (outputPathOnExit) {
|
|
2257
|
+
onSetExitPath?.(projectPath);
|
|
2258
|
+
exit();
|
|
2259
|
+
return;
|
|
2260
|
+
}
|
|
2251
2261
|
try {
|
|
2252
2262
|
const command = buildCdCommand(projectPath);
|
|
2253
2263
|
clipboard.writeSync(command);
|
|
@@ -2260,7 +2270,7 @@ var App = ({
|
|
|
2260
2270
|
setTimeout(() => {
|
|
2261
2271
|
setHint(defaultHintMessage);
|
|
2262
2272
|
}, 2e3);
|
|
2263
|
-
}, [index, sortedProjects]);
|
|
2273
|
+
}, [defaultHintMessage, exit, index, onSetExitPath, outputPathOnExit, sortedProjects]);
|
|
2264
2274
|
const launchSelected = useCallback(async () => {
|
|
2265
2275
|
const projectView = sortedProjects[index];
|
|
2266
2276
|
if (!projectView) {
|
|
@@ -2315,7 +2325,7 @@ var App = ({
|
|
|
2315
2325
|
setHint(defaultHintMessage);
|
|
2316
2326
|
}, 3e3);
|
|
2317
2327
|
}
|
|
2318
|
-
}, [exit, index, onLaunch, onSetExitPath, outputPathOnExit, sortedProjects]);
|
|
2328
|
+
}, [defaultHintMessage, exit, index, onLaunch, onSetExitPath, outputPathOnExit, sortedProjects]);
|
|
2319
2329
|
const launchSelectedWithEditor = useCallback(async () => {
|
|
2320
2330
|
if (!onLaunchWithEditor) {
|
|
2321
2331
|
setHint("Launch with editor not available");
|
|
@@ -2377,7 +2387,7 @@ var App = ({
|
|
|
2377
2387
|
setHint(defaultHintMessage);
|
|
2378
2388
|
}, 3e3);
|
|
2379
2389
|
}
|
|
2380
|
-
}, [exit, index, onLaunchWithEditor, onSetExitPath, outputPathOnExit, sortedProjects]);
|
|
2390
|
+
}, [defaultHintMessage, exit, index, onLaunchWithEditor, onSetExitPath, outputPathOnExit, sortedProjects]);
|
|
2381
2391
|
const launchEditorOnly = useCallback(async () => {
|
|
2382
2392
|
if (!onLaunchEditorOnly) {
|
|
2383
2393
|
setHint("Launch editor only not available");
|
|
@@ -2408,7 +2418,7 @@ var App = ({
|
|
|
2408
2418
|
setHint(defaultHintMessage);
|
|
2409
2419
|
}, 3e3);
|
|
2410
2420
|
}
|
|
2411
|
-
}, [index, onLaunchEditorOnly, sortedProjects]);
|
|
2421
|
+
}, [defaultHintMessage, index, onLaunchEditorOnly, sortedProjects]);
|
|
2412
2422
|
const terminateSelected = useCallback(async () => {
|
|
2413
2423
|
const projectView = sortedProjects[index];
|
|
2414
2424
|
if (!projectView) {
|
|
@@ -2451,7 +2461,7 @@ var App = ({
|
|
|
2451
2461
|
setHint(defaultHintMessage);
|
|
2452
2462
|
}, 3e3);
|
|
2453
2463
|
}
|
|
2454
|
-
}, [index, onTerminate, sortedProjects]);
|
|
2464
|
+
}, [defaultHintMessage, index, onTerminate, sortedProjects]);
|
|
2455
2465
|
useEffect4(() => {
|
|
2456
2466
|
setProjectViews(projects);
|
|
2457
2467
|
setReleasedProjects(/* @__PURE__ */ new Set());
|
|
@@ -2509,7 +2519,7 @@ var App = ({
|
|
|
2509
2519
|
} finally {
|
|
2510
2520
|
setIsRefreshing(false);
|
|
2511
2521
|
}
|
|
2512
|
-
}, [isRefreshing, onRefresh, sortedProjects]);
|
|
2522
|
+
}, [defaultHintMessage, isRefreshing, onRefresh, sortedProjects]);
|
|
2513
2523
|
const toggleFavoriteSelected = useCallback(async () => {
|
|
2514
2524
|
if (!onToggleFavorite) {
|
|
2515
2525
|
setHint("Toggle favorite not available");
|
|
@@ -2546,7 +2556,7 @@ var App = ({
|
|
|
2546
2556
|
setHint(defaultHintMessage);
|
|
2547
2557
|
}, 3e3);
|
|
2548
2558
|
}
|
|
2549
|
-
}, [index, onToggleFavorite, sortedProjects]);
|
|
2559
|
+
}, [defaultHintMessage, index, onToggleFavorite, sortedProjects]);
|
|
2550
2560
|
useInput((input, key) => {
|
|
2551
2561
|
if (isSortMenuOpen) {
|
|
2552
2562
|
if (key.escape || input === "\x1B") {
|
|
@@ -2857,6 +2867,139 @@ var getUnityHubCliPath = () => {
|
|
|
2857
2867
|
}
|
|
2858
2868
|
return "unity-hub-cli";
|
|
2859
2869
|
};
|
|
2870
|
+
var getNpmCommand = () => {
|
|
2871
|
+
return process3.platform === "win32" ? "npm.cmd" : "npm";
|
|
2872
|
+
};
|
|
2873
|
+
var NPM_VIEW_TIMEOUT_MS = 3e4;
|
|
2874
|
+
var NPM_INSTALL_TIMEOUT_MS = 10 * 6e4;
|
|
2875
|
+
var parseSemver = (version) => {
|
|
2876
|
+
const trimmed = version.trim().replace(/^v/i, "");
|
|
2877
|
+
const match = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+([0-9A-Za-z.-]+))?$/.exec(trimmed);
|
|
2878
|
+
if (!match) {
|
|
2879
|
+
return void 0;
|
|
2880
|
+
}
|
|
2881
|
+
const major = Number(match[1]);
|
|
2882
|
+
const minor = Number(match[2]);
|
|
2883
|
+
const patch = Number(match[3]);
|
|
2884
|
+
if (!Number.isFinite(major) || !Number.isFinite(minor) || !Number.isFinite(patch)) {
|
|
2885
|
+
return void 0;
|
|
2886
|
+
}
|
|
2887
|
+
const prereleaseRaw = match[4];
|
|
2888
|
+
const prerelease = prereleaseRaw ? prereleaseRaw.split(".").map((identifier) => {
|
|
2889
|
+
const numeric = /^[0-9]+$/.test(identifier) ? Number(identifier) : void 0;
|
|
2890
|
+
return typeof numeric === "number" && Number.isFinite(numeric) ? numeric : identifier;
|
|
2891
|
+
}) : [];
|
|
2892
|
+
return { major, minor, patch, prerelease };
|
|
2893
|
+
};
|
|
2894
|
+
var compareSemverIdentifiers = (a, b) => {
|
|
2895
|
+
if (typeof a === "number" && typeof b === "number") {
|
|
2896
|
+
return a === b ? 0 : a < b ? -1 : 1;
|
|
2897
|
+
}
|
|
2898
|
+
if (typeof a === "number" && typeof b === "string") {
|
|
2899
|
+
return -1;
|
|
2900
|
+
}
|
|
2901
|
+
if (typeof a === "string" && typeof b === "number") {
|
|
2902
|
+
return 1;
|
|
2903
|
+
}
|
|
2904
|
+
const aStr = String(a);
|
|
2905
|
+
const bStr = String(b);
|
|
2906
|
+
if (aStr === bStr) {
|
|
2907
|
+
return 0;
|
|
2908
|
+
}
|
|
2909
|
+
return aStr < bStr ? -1 : 1;
|
|
2910
|
+
};
|
|
2911
|
+
var compareSemver = (a, b) => {
|
|
2912
|
+
if (a.major !== b.major) {
|
|
2913
|
+
return a.major < b.major ? -1 : 1;
|
|
2914
|
+
}
|
|
2915
|
+
if (a.minor !== b.minor) {
|
|
2916
|
+
return a.minor < b.minor ? -1 : 1;
|
|
2917
|
+
}
|
|
2918
|
+
if (a.patch !== b.patch) {
|
|
2919
|
+
return a.patch < b.patch ? -1 : 1;
|
|
2920
|
+
}
|
|
2921
|
+
const aHasPre = a.prerelease.length > 0;
|
|
2922
|
+
const bHasPre = b.prerelease.length > 0;
|
|
2923
|
+
if (!aHasPre && !bHasPre) {
|
|
2924
|
+
return 0;
|
|
2925
|
+
}
|
|
2926
|
+
if (!aHasPre && bHasPre) {
|
|
2927
|
+
return 1;
|
|
2928
|
+
}
|
|
2929
|
+
if (aHasPre && !bHasPre) {
|
|
2930
|
+
return -1;
|
|
2931
|
+
}
|
|
2932
|
+
const length = Math.max(a.prerelease.length, b.prerelease.length);
|
|
2933
|
+
for (let i = 0; i < length; i += 1) {
|
|
2934
|
+
const aId = a.prerelease[i];
|
|
2935
|
+
const bId = b.prerelease[i];
|
|
2936
|
+
if (aId === void 0 && bId === void 0) {
|
|
2937
|
+
return 0;
|
|
2938
|
+
}
|
|
2939
|
+
if (aId === void 0) {
|
|
2940
|
+
return -1;
|
|
2941
|
+
}
|
|
2942
|
+
if (bId === void 0) {
|
|
2943
|
+
return 1;
|
|
2944
|
+
}
|
|
2945
|
+
const cmp = compareSemverIdentifiers(aId, bId);
|
|
2946
|
+
if (cmp !== 0) {
|
|
2947
|
+
return cmp;
|
|
2948
|
+
}
|
|
2949
|
+
}
|
|
2950
|
+
return 0;
|
|
2951
|
+
};
|
|
2952
|
+
var readLatestVersionFromNpm = () => {
|
|
2953
|
+
const npmCommand = getNpmCommand();
|
|
2954
|
+
const result = spawnSync(npmCommand, ["view", "unity-hub-cli", "version"], {
|
|
2955
|
+
encoding: "utf-8",
|
|
2956
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
2957
|
+
timeout: NPM_VIEW_TIMEOUT_MS
|
|
2958
|
+
});
|
|
2959
|
+
if (result.signal) {
|
|
2960
|
+
return { ok: false, reason: "timeout" };
|
|
2961
|
+
}
|
|
2962
|
+
if (result.error) {
|
|
2963
|
+
const errorCode = result.error.code;
|
|
2964
|
+
if (errorCode === "ETIMEDOUT") {
|
|
2965
|
+
return { ok: false, reason: "timeout" };
|
|
2966
|
+
}
|
|
2967
|
+
return { ok: false, reason: "spawn_failed" };
|
|
2968
|
+
}
|
|
2969
|
+
if (typeof result.status === "number" && result.status !== 0) {
|
|
2970
|
+
return { ok: false, reason: "nonzero_exit" };
|
|
2971
|
+
}
|
|
2972
|
+
const version = result.stdout.trim();
|
|
2973
|
+
if (!version) {
|
|
2974
|
+
return { ok: false, reason: "empty_output" };
|
|
2975
|
+
}
|
|
2976
|
+
return { ok: true, version };
|
|
2977
|
+
};
|
|
2978
|
+
var installLatestVersionGlobally = () => {
|
|
2979
|
+
const npmCommand = getNpmCommand();
|
|
2980
|
+
const result = spawnSync(
|
|
2981
|
+
npmCommand,
|
|
2982
|
+
["install", "-g", "unity-hub-cli@latest", "--ignore-scripts", "--no-fund"],
|
|
2983
|
+
{
|
|
2984
|
+
stdio: "inherit",
|
|
2985
|
+
timeout: NPM_INSTALL_TIMEOUT_MS
|
|
2986
|
+
}
|
|
2987
|
+
);
|
|
2988
|
+
if (result.signal) {
|
|
2989
|
+
return { ok: false, reason: "timeout" };
|
|
2990
|
+
}
|
|
2991
|
+
if (result.error) {
|
|
2992
|
+
const errorCode = result.error.code;
|
|
2993
|
+
if (errorCode === "ETIMEDOUT") {
|
|
2994
|
+
return { ok: false, reason: "timeout" };
|
|
2995
|
+
}
|
|
2996
|
+
return { ok: false, reason: "spawn_failed" };
|
|
2997
|
+
}
|
|
2998
|
+
if (typeof result.status === "number" && result.status !== 0) {
|
|
2999
|
+
return { ok: false, reason: "nonzero_exit" };
|
|
3000
|
+
}
|
|
3001
|
+
return { ok: true };
|
|
3002
|
+
};
|
|
2860
3003
|
var getShellInitScript = () => {
|
|
2861
3004
|
const shell = process3.env["SHELL"] ?? "";
|
|
2862
3005
|
const isWindows = process3.platform === "win32";
|
|
@@ -2911,6 +3054,69 @@ var bootstrap = async () => {
|
|
|
2911
3054
|
console.log(getVersion());
|
|
2912
3055
|
return;
|
|
2913
3056
|
}
|
|
3057
|
+
if (args[0] === "update") {
|
|
3058
|
+
const currentVersion = getVersion();
|
|
3059
|
+
const latestVersionResult = readLatestVersionFromNpm();
|
|
3060
|
+
if (!latestVersionResult.ok) {
|
|
3061
|
+
if (latestVersionResult.reason === "timeout") {
|
|
3062
|
+
console.error("Failed to read the latest version from npm (timeout). Please check your network/proxy settings and try again.");
|
|
3063
|
+
} else if (latestVersionResult.reason === "spawn_failed") {
|
|
3064
|
+
console.error("Failed to run npm. Please ensure npm is installed and available in your PATH.");
|
|
3065
|
+
} else {
|
|
3066
|
+
console.error("Failed to read the latest version from npm. Please ensure you can access the npm registry.");
|
|
3067
|
+
}
|
|
3068
|
+
process3.exitCode = 1;
|
|
3069
|
+
return;
|
|
3070
|
+
}
|
|
3071
|
+
const latestVersion = latestVersionResult.version;
|
|
3072
|
+
const parsedCurrent = parseSemver(currentVersion);
|
|
3073
|
+
const parsedLatest = parseSemver(latestVersion);
|
|
3074
|
+
if (parsedCurrent && parsedLatest) {
|
|
3075
|
+
const cmp = compareSemver(parsedLatest, parsedCurrent);
|
|
3076
|
+
if (cmp <= 0) {
|
|
3077
|
+
console.log(`Already up to date: ${currentVersion}`);
|
|
3078
|
+
if (cmp < 0) {
|
|
3079
|
+
console.log(`Current version is newer than npm latest: ${latestVersion}`);
|
|
3080
|
+
}
|
|
3081
|
+
return;
|
|
3082
|
+
}
|
|
3083
|
+
} else if (latestVersion === currentVersion) {
|
|
3084
|
+
console.log(`Already up to date: ${currentVersion}`);
|
|
3085
|
+
return;
|
|
3086
|
+
}
|
|
3087
|
+
console.log(`Current version: ${currentVersion}`);
|
|
3088
|
+
console.log(`Latest version: ${latestVersion}`);
|
|
3089
|
+
console.log("This will update the global installation via npm: npm install -g unity-hub-cli@latest --ignore-scripts --no-fund");
|
|
3090
|
+
console.log("");
|
|
3091
|
+
if (!process3.stdin.isTTY) {
|
|
3092
|
+
console.error("Interactive confirmation is not available in this environment (stdin is not a TTY).");
|
|
3093
|
+
process3.exitCode = 1;
|
|
3094
|
+
return;
|
|
3095
|
+
}
|
|
3096
|
+
if (!parsedCurrent || !parsedLatest) {
|
|
3097
|
+
console.log("Warning: could not compare versions reliably; proceeding may downgrade your installation.");
|
|
3098
|
+
console.log("");
|
|
3099
|
+
}
|
|
3100
|
+
const confirmed = await askConfirmation("Proceed with update? (y/n): ");
|
|
3101
|
+
if (!confirmed) {
|
|
3102
|
+
console.log("Update cancelled.");
|
|
3103
|
+
return;
|
|
3104
|
+
}
|
|
3105
|
+
const installResult = installLatestVersionGlobally();
|
|
3106
|
+
if (!installResult.ok) {
|
|
3107
|
+
if (installResult.reason === "timeout") {
|
|
3108
|
+
console.error("Update failed (timeout). Please check your network/proxy settings and try again.");
|
|
3109
|
+
} else if (installResult.reason === "spawn_failed") {
|
|
3110
|
+
console.error("Failed to run npm. Please ensure npm is installed and available in your PATH.");
|
|
3111
|
+
} else {
|
|
3112
|
+
console.error("Update failed. Please check the npm output above.");
|
|
3113
|
+
}
|
|
3114
|
+
process3.exitCode = 1;
|
|
3115
|
+
return;
|
|
3116
|
+
}
|
|
3117
|
+
console.log(`Updated to: ${latestVersion}`);
|
|
3118
|
+
return;
|
|
3119
|
+
}
|
|
2914
3120
|
if (args.includes("--shell-init")) {
|
|
2915
3121
|
const isDryRun = args.includes("--dry-run");
|
|
2916
3122
|
if (isDryRun) {
|
|
@@ -2927,6 +3133,11 @@ var bootstrap = async () => {
|
|
|
2927
3133
|
`);
|
|
2928
3134
|
previewShellInit();
|
|
2929
3135
|
console.log("");
|
|
3136
|
+
if (!process3.stdin.isTTY) {
|
|
3137
|
+
console.error("Interactive confirmation is not available in this environment (stdin is not a TTY).");
|
|
3138
|
+
process3.exitCode = 1;
|
|
3139
|
+
return;
|
|
3140
|
+
}
|
|
2930
3141
|
const confirmed = await askConfirmation("Proceed with installation? (y/n): ");
|
|
2931
3142
|
if (!confirmed) {
|
|
2932
3143
|
console.log("Installation cancelled.");
|