mobygate 0.6.1 → 0.6.2
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/CHANGELOG.md +20 -0
- package/bin/mobygate.js +27 -8
- package/lib/updater.js +28 -8
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,26 @@ All notable changes to mobygate are documented here. Format loosely follows
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/); version numbers are
|
|
5
5
|
[Semantic Versioning](https://semver.org/).
|
|
6
6
|
|
|
7
|
+
## [0.6.2] — 2026-04-24
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **Update on Windows failed with `EBUSY` after the npm-spawn fix**
|
|
12
|
+
in v0.6.1 because the running mobygate Node process holds open
|
|
13
|
+
file handles inside its own install dir
|
|
14
|
+
(`...\AppData\Roaming\npm\node_modules\mobygate`). When `npm install -g`
|
|
15
|
+
tries to atomically rename the directory, Windows refuses — POSIX
|
|
16
|
+
systems can replace open files, Windows can't.
|
|
17
|
+
- **Fix:** stop the service *before* running npm install, then start
|
|
18
|
+
on the new build. Affects both `mobygate update` (CLI) and the
|
|
19
|
+
dashboard `/update/apply` endpoint. The detached update child
|
|
20
|
+
survives because we spawn it with `detached: true` +
|
|
21
|
+
`windowsHide: true`, putting it in its own console group
|
|
22
|
+
independent of the parent that gets killed.
|
|
23
|
+
- POSIX systems now also stop-then-start (instead of unload-load /
|
|
24
|
+
restart), purely for symmetry and a cleaner log progression. No
|
|
25
|
+
behavior change there.
|
|
26
|
+
|
|
7
27
|
## [0.6.1] — 2026-04-24
|
|
8
28
|
|
|
9
29
|
### Fixed
|
package/bin/mobygate.js
CHANGED
|
@@ -581,6 +581,25 @@ async function cmdUpdate() {
|
|
|
581
581
|
}
|
|
582
582
|
print('');
|
|
583
583
|
|
|
584
|
+
// ---- Stop the service FIRST on Windows, otherwise running Node holds
|
|
585
|
+
// open file handles inside the install dir and `npm install -g` fails
|
|
586
|
+
// with EBUSY when it tries to rename the directory. On macOS/Linux we
|
|
587
|
+
// can replace open files freely, but stopping early there too is harmless
|
|
588
|
+
// and gives a cleaner restart sequence — so we do it everywhere.
|
|
589
|
+
let stoppedForUpdate = false;
|
|
590
|
+
if (IS_WIN) {
|
|
591
|
+
info('Stopping service so npm install can replace files...');
|
|
592
|
+
stopWindowsTask(WIN_LABELS.server);
|
|
593
|
+
stoppedForUpdate = true;
|
|
594
|
+
} else if (IS_MAC) {
|
|
595
|
+
const p = plistPathForLabel(SERVER_LABEL);
|
|
596
|
+
launchctlUnload(p);
|
|
597
|
+
stoppedForUpdate = true;
|
|
598
|
+
} else if (IS_LINUX) {
|
|
599
|
+
stopLinuxUnit(LINUX_UNITS.server);
|
|
600
|
+
stoppedForUpdate = true;
|
|
601
|
+
}
|
|
602
|
+
|
|
584
603
|
// ---- Perform the upgrade
|
|
585
604
|
if (mode === 'npm') {
|
|
586
605
|
info(`Running \`npm install -g mobygate@latest\`...`);
|
|
@@ -599,19 +618,19 @@ async function cmdUpdate() {
|
|
|
599
618
|
return die(`Install mode is "${mode}" — can't auto-update. Reinstall via npm or git.`);
|
|
600
619
|
}
|
|
601
620
|
|
|
602
|
-
// ----
|
|
621
|
+
// ---- Bring the service back up on the new code
|
|
603
622
|
section('Restart');
|
|
604
|
-
info('
|
|
623
|
+
info('Starting service on the new build...');
|
|
605
624
|
if (IS_MAC) {
|
|
606
625
|
const p = plistPathForLabel(SERVER_LABEL);
|
|
607
|
-
|
|
608
|
-
ok(`
|
|
626
|
+
launchctlLoad(p);
|
|
627
|
+
ok(`Loaded ${SERVER_LABEL}`);
|
|
609
628
|
} else if (IS_WIN) {
|
|
610
|
-
|
|
611
|
-
ok(`
|
|
629
|
+
startWindowsTask(WIN_LABELS.server);
|
|
630
|
+
ok(`Started ${WIN_LABELS.server}`);
|
|
612
631
|
} else if (IS_LINUX) {
|
|
613
|
-
|
|
614
|
-
ok(`
|
|
632
|
+
startLinuxUnit(LINUX_UNITS.server);
|
|
633
|
+
ok(`Started ${LINUX_UNITS.server}`);
|
|
615
634
|
}
|
|
616
635
|
print('');
|
|
617
636
|
info(`Tip: if the install-layout changed (new service file, new paths), run \`mobygate init\` to re-install the service definitions.`);
|
package/lib/updater.js
CHANGED
|
@@ -160,6 +160,16 @@ function writeUpdateState(patch) {
|
|
|
160
160
|
* Build the shell command that performs update + restart. Returned as a
|
|
161
161
|
* single string we can hand to `sh -c` / `cmd /c`. Written as a string
|
|
162
162
|
* (not an array) because we want shell redirection for log capture.
|
|
163
|
+
*
|
|
164
|
+
* Order of operations matters on Windows: the running mobygate process
|
|
165
|
+
* holds open file handles inside its own install dir (`...\node_modules\mobygate`),
|
|
166
|
+
* so `npm install -g` fails with EBUSY when it tries to rename the dir.
|
|
167
|
+
* Fix: stop the service FIRST (which kills our parent), then install,
|
|
168
|
+
* then start. The detached child survives because we spawn with
|
|
169
|
+
* `detached: true` + `windowsHide: true`, putting it in its own console
|
|
170
|
+
* group independent of the parent. POSIX systems can replace open files
|
|
171
|
+
* freely, but stopping early there too is harmless and gives a cleaner
|
|
172
|
+
* "off → install → on" sequence in the log.
|
|
163
173
|
*/
|
|
164
174
|
function buildUpdateCommand({ mode, repoRoot, logPath }) {
|
|
165
175
|
if (IS_WIN) {
|
|
@@ -167,6 +177,11 @@ function buildUpdateCommand({ mode, repoRoot, logPath }) {
|
|
|
167
177
|
// line so failures short-circuit via `||`.
|
|
168
178
|
const steps = [];
|
|
169
179
|
steps.push(`echo [mobygate-update] start at %DATE% %TIME%`);
|
|
180
|
+
// Stop FIRST so npm can replace files without EBUSY. /F forces close
|
|
181
|
+
// even if the process is mid-request; the SDK session map writes are
|
|
182
|
+
// synchronous and the SIGTERM handler flushes before exit.
|
|
183
|
+
steps.push(`echo [mobygate-update] stopping service`);
|
|
184
|
+
steps.push(`schtasks /End /TN "${WIN_SERVER_TASK}"`);
|
|
170
185
|
if (mode === 'npm') {
|
|
171
186
|
steps.push(`npm install -g mobygate@latest`);
|
|
172
187
|
} else if (mode === 'git') {
|
|
@@ -175,15 +190,22 @@ function buildUpdateCommand({ mode, repoRoot, logPath }) {
|
|
|
175
190
|
steps.push(`npm install`);
|
|
176
191
|
}
|
|
177
192
|
steps.push(`echo [mobygate-update] restarting service`);
|
|
178
|
-
steps.push(`schtasks /
|
|
179
|
-
steps.push(`schtasks /Run /TN "${WIN_SERVER_TASK}"`);
|
|
193
|
+
steps.push(`schtasks /Run /TN "${WIN_SERVER_TASK}"`);
|
|
180
194
|
steps.push(`echo [mobygate-update] done`);
|
|
181
195
|
// Join with && so any failure stops the chain. Final redirect to log.
|
|
182
196
|
const inner = steps.map((s) => `(${s})`).join(' && ');
|
|
183
197
|
return { shell: 'cmd', cmd: `${inner} >> "${logPath}" 2>&1` };
|
|
184
198
|
}
|
|
185
|
-
// POSIX: sh -c, bail-on-first-failure via set -e
|
|
199
|
+
// POSIX: sh -c, bail-on-first-failure via set -e. Stop service first
|
|
200
|
+
// for the same reason — symmetry, cleaner restart, no harm.
|
|
186
201
|
const parts = [`set -e`, `echo "[mobygate-update] start $(date)"`];
|
|
202
|
+
parts.push(`echo "[mobygate-update] stopping service"`);
|
|
203
|
+
if (IS_MAC) {
|
|
204
|
+
const plist = join(process.env.HOME || '~', 'Library', 'LaunchAgents', `${SERVER_LABEL}.plist`);
|
|
205
|
+
parts.push(`launchctl unload "${plist}" 2>/dev/null || true`);
|
|
206
|
+
} else if (IS_LINUX) {
|
|
207
|
+
parts.push(`systemctl --user stop ${LINUX_SERVER_UNIT} 2>/dev/null || true`);
|
|
208
|
+
}
|
|
187
209
|
if (mode === 'npm') {
|
|
188
210
|
parts.push(`npm install -g mobygate@latest`);
|
|
189
211
|
} else if (mode === 'git') {
|
|
@@ -191,14 +213,12 @@ function buildUpdateCommand({ mode, repoRoot, logPath }) {
|
|
|
191
213
|
parts.push(`git pull --ff-only`);
|
|
192
214
|
parts.push(`npm install`);
|
|
193
215
|
}
|
|
194
|
-
parts.push(`echo "[mobygate-update]
|
|
216
|
+
parts.push(`echo "[mobygate-update] starting service on new build"`);
|
|
195
217
|
if (IS_MAC) {
|
|
196
218
|
const plist = join(process.env.HOME || '~', 'Library', 'LaunchAgents', `${SERVER_LABEL}.plist`);
|
|
197
|
-
|
|
198
|
-
parts.push(`launchctl unload "${plist}" 2>/dev/null || true`);
|
|
199
|
-
parts.push(`launchctl load "${plist}"`);
|
|
219
|
+
parts.push(`launchctl load "${plist}"`);
|
|
200
220
|
} else if (IS_LINUX) {
|
|
201
|
-
parts.push(`systemctl --user
|
|
221
|
+
parts.push(`systemctl --user start ${LINUX_SERVER_UNIT}`);
|
|
202
222
|
}
|
|
203
223
|
parts.push(`echo "[mobygate-update] done"`);
|
|
204
224
|
const script = parts.join('\n');
|