launch-unity 0.1.0 → 0.2.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.ja.md CHANGED
@@ -21,6 +21,11 @@ npx launch-unity /path Android -- -executeMethod My.Build.Entry
21
21
  launch-unity
22
22
  launch-unity /path/to/MyUnityProject Android
23
23
  launch-unity . -- -buildTarget iOS -projectPath . # 上書きも可能
24
+
25
+ # プロジェクトを開いている Unity を終了
26
+ quit-unity # カレントディレクトリのプロジェクトの Unity を終了
27
+ quit-unity /path/to/Proj # 指定プロジェクトの Unity を終了
28
+ quit-unity . --timeout 20000 --force
24
29
  ```
25
30
 
26
31
  指定した Unity プロジェクトの `ProjectSettings/ProjectVersion.txt` から必要な Unity Editor のバージョンを読み取り、
@@ -46,5 +51,16 @@ Unity Hub でインストール済みの該当バージョンを起動する mac
46
51
  - macOS / Windows: Unity Hub のデフォルトインストールパスを前提にサポート。
47
52
  - Linux: 未対応です。対応 PR を歓迎します。
48
53
 
54
+ ## quit-unity 使い方(詳細)
55
+ ```bash
56
+ # 基本構文
57
+ quit-unity [PROJECT_PATH] [--timeout <ms>] [--force]
58
+
59
+ # フラグ
60
+ # -t, --timeout <ms> 正常終了を待つ時間(既定: 15000ms)
61
+ # -f, --force タイムアウト後に強制終了
62
+ # -h, --help ヘルプ
63
+ ```
64
+
49
65
  ## ライセンス
50
66
  - MIT。詳細は `LICENSE` を参照してください。
package/README.md CHANGED
@@ -21,6 +21,11 @@ npx launch-unity /path Android -- -executeMethod My.Build.Entry
21
21
  launch-unity
22
22
  launch-unity /path/to/MyUnityProject Android
23
23
  launch-unity . -- -buildTarget iOS -projectPath . # You can override
24
+
25
+ # Quit the Unity instance holding a project open
26
+ quit-unity # Quit Unity for current directory project
27
+ quit-unity /path/to/Proj # Quit Unity for a specific project
28
+ quit-unity . --timeout 20000 --force
24
29
  ```
25
30
 
26
31
  A TypeScript CLI for macOS and Windows that reads the required Unity Editor version from
@@ -58,6 +63,14 @@ launch-unity [PROJECT_PATH] [PLATFORM]
58
63
 
59
64
  # Flags
60
65
  # -h, --help Show help
66
+
67
+ # Quit syntax
68
+ quit-unity [PROJECT_PATH] [--timeout <ms>] [--force]
69
+
70
+ # Flags (quit-unity)
71
+ # -t, --timeout <ms> Time to wait for graceful quit (default: 15000)
72
+ # -f, --force Force kill if not exited within timeout
73
+ # -h, --help Show help
61
74
  ```
62
75
 
63
76
 
File without changes
package/dist/quit.js ADDED
@@ -0,0 +1,166 @@
1
+ #!/usr/bin/env node
2
+ /*
3
+ quit-unity: Quit the Unity Editor instance that has the specified project open.
4
+ Platforms: macOS, Windows
5
+ */
6
+ import { existsSync, readFileSync } from "node:fs";
7
+ import { join, resolve } from "node:path";
8
+ function parseArgs(argv) {
9
+ const defaultProjectPath = process.cwd();
10
+ const args = argv.slice(2);
11
+ let projectPath = defaultProjectPath;
12
+ let timeoutMs = 15000;
13
+ let force = false;
14
+ for (let i = 0; i < args.length; i++) {
15
+ const arg = args[i] ?? "";
16
+ if (arg === "--help" || arg === "-h") {
17
+ printHelp();
18
+ process.exit(0);
19
+ }
20
+ if (arg === "--force" || arg === "-f") {
21
+ force = true;
22
+ continue;
23
+ }
24
+ if (arg === "--timeout" || arg === "-t") {
25
+ const value = args[i + 1];
26
+ if (!value || value.startsWith("-")) {
27
+ console.error("Error: --timeout requires a millisecond value");
28
+ process.exit(1);
29
+ }
30
+ const parsed = Number(value);
31
+ if (!Number.isFinite(parsed) || parsed < 0) {
32
+ console.error("Error: --timeout must be a non-negative number (milliseconds)");
33
+ process.exit(1);
34
+ }
35
+ timeoutMs = parsed;
36
+ i++;
37
+ continue;
38
+ }
39
+ if (arg.startsWith("-")) {
40
+ // Unknown flags are ignored to keep CLI permissive
41
+ continue;
42
+ }
43
+ // First positional = project path
44
+ projectPath = resolve(arg);
45
+ }
46
+ return { projectPath, timeoutMs, force };
47
+ }
48
+ function printHelp() {
49
+ const help = `
50
+ Usage: quit-unity [PROJECT_PATH] [--timeout <ms>] [--force]
51
+
52
+ Quit the Unity Editor instance that has PROJECT_PATH open.
53
+
54
+ Arguments:
55
+ PROJECT_PATH Optional. Defaults to current directory
56
+
57
+ Flags:
58
+ -t, --timeout <ms> Time to wait for graceful quit (default: 15000)
59
+ -f, --force Force kill if not exited within timeout
60
+ -h, --help Show this help message
61
+ `;
62
+ process.stdout.write(help);
63
+ }
64
+ function ensureProjectPath(projectPath) {
65
+ if (!existsSync(projectPath)) {
66
+ console.error(`Error: Project directory not found: ${projectPath}`);
67
+ process.exit(1);
68
+ }
69
+ }
70
+ function readPidFromEditorInstance(projectPath) {
71
+ const editorInstancePath = join(projectPath, "Library", "EditorInstance.json");
72
+ if (!existsSync(editorInstancePath))
73
+ return null;
74
+ try {
75
+ const content = readFileSync(editorInstancePath, "utf8");
76
+ const data = JSON.parse(content);
77
+ const candidateKeys = [
78
+ "process_id",
79
+ "processId",
80
+ "processID",
81
+ "pid",
82
+ "PID",
83
+ ];
84
+ for (const key of candidateKeys) {
85
+ const value = data[key];
86
+ if (typeof value === "number" && Number.isFinite(value))
87
+ return value;
88
+ if (typeof value === "string" && /^\d+$/.test(value))
89
+ return Number(value);
90
+ }
91
+ }
92
+ catch {
93
+ // fallthrough
94
+ }
95
+ return null;
96
+ }
97
+ function isProcessAlive(pid) {
98
+ try {
99
+ // signal 0: does not actually send a signal, just tests for existence/permissions
100
+ process.kill(pid, 0);
101
+ return true;
102
+ }
103
+ catch {
104
+ return false;
105
+ }
106
+ }
107
+ async function waitForExit(pid, timeoutMs) {
108
+ const start = Date.now();
109
+ const stepMs = 200;
110
+ while (Date.now() - start < timeoutMs) {
111
+ if (!isProcessAlive(pid))
112
+ return true;
113
+ await new Promise((r) => setTimeout(r, stepMs));
114
+ }
115
+ return !isProcessAlive(pid);
116
+ }
117
+ async function quitByPid(pid, force, timeoutMs) {
118
+ // Try graceful first
119
+ try {
120
+ process.kill(pid, "SIGTERM");
121
+ }
122
+ catch (error) {
123
+ // If process already exited, consider it success
124
+ if (!isProcessAlive(pid))
125
+ return true;
126
+ // If we cannot send the signal and the process is alive, escalate when force is true
127
+ if (!force)
128
+ return false;
129
+ }
130
+ const graceful = await waitForExit(pid, timeoutMs);
131
+ if (graceful)
132
+ return true;
133
+ if (!force)
134
+ return false;
135
+ // Force kill
136
+ try {
137
+ process.kill(pid, "SIGKILL");
138
+ }
139
+ catch {
140
+ // ignore
141
+ }
142
+ // Give a short moment after force
143
+ return await waitForExit(pid, 2000);
144
+ }
145
+ async function main() {
146
+ const options = parseArgs(process.argv);
147
+ ensureProjectPath(options.projectPath);
148
+ const pid = readPidFromEditorInstance(options.projectPath);
149
+ if (pid === null) {
150
+ console.error("Error: Could not find Unity PID. Is this project currently open in Unity? (Missing Library/EditorInstance.json)");
151
+ process.exit(1);
152
+ return;
153
+ }
154
+ console.log(`Attempting to quit Unity (pid: ${pid}) for project: ${options.projectPath}`);
155
+ const ok = await quitByPid(pid, options.force, options.timeoutMs);
156
+ if (!ok) {
157
+ console.error(`Failed to quit Unity (pid: ${pid}) within ${options.timeoutMs}ms.${options.force ? "" : " Try --force."}`);
158
+ process.exit(1);
159
+ return;
160
+ }
161
+ console.log("Unity has exited.");
162
+ }
163
+ main().catch((error) => {
164
+ console.error(error);
165
+ process.exit(1);
166
+ });
package/package.json CHANGED
@@ -1,15 +1,16 @@
1
1
  {
2
2
  "name": "launch-unity",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Open a Unity project with the matching Editor version (macOS/Windows)",
5
5
  "type": "module",
6
- "main": "dist/cli.js",
6
+ "main": "dist/launch.js",
7
7
  "bin": {
8
- "launch-unity": "dist/cli.js"
8
+ "launch-unity": "dist/launch.js",
9
+ "quit-unity": "dist/quit.js"
9
10
  },
10
11
  "scripts": {
11
12
  "build": "tsc -p tsconfig.json",
12
- "start": "node dist/cli.js",
13
+ "start": "node dist/launch.js",
13
14
  "lint": "eslint . --ext .ts,.js",
14
15
  "lint:fix": "eslint . --ext .ts,.js --fix",
15
16
  "format": "prettier --write .",