c8ctl-plugin-nano 1.7.1 → 1.7.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.
Files changed (2) hide show
  1. package/c8ctl-plugin.js +76 -12
  2. package/package.json +8 -8
package/c8ctl-plugin.js CHANGED
@@ -37,9 +37,10 @@ import {
37
37
  readdirSync,
38
38
  chmodSync,
39
39
  renameSync,
40
+ realpathSync,
40
41
  } from 'node:fs';
41
42
  import { homedir, platform as osPlatform } from 'node:os';
42
- import { join, isAbsolute, resolve as resolvePath, dirname } from 'node:path';
43
+ import { join, isAbsolute, resolve as resolvePath, dirname, sep } from 'node:path';
43
44
  import { createRequire } from 'node:module';
44
45
  import { fileURLToPath } from 'node:url';
45
46
  import { platformForHost } from './platforms.mjs';
@@ -1308,6 +1309,51 @@ function isGlobalInstall() {
1308
1309
  return Boolean(root) && pluginDir.startsWith(root);
1309
1310
  }
1310
1311
 
1312
+ /**
1313
+ * How this plugin is installed, which decides how `nano update` self-updates:
1314
+ * - 'managed': under c8ctl's own plugin store (…/c8ctl/plugins/node_modules),
1315
+ * where `c8ctl load plugin` installed it. Self-update in place by
1316
+ * reinstalling into that same npm --prefix. This is the norm for the
1317
+ * integrated c8ctl plugin architecture, so it takes precedence over a
1318
+ * coincidental global install of the same name.
1319
+ * - 'global': under `npm root -g` (a plain `npm install -g`).
1320
+ * - 'local': a checkout / `npm link` — self-update isn't safe; tell the user.
1321
+ */
1322
+ function pluginInstallInfo() {
1323
+ const rt = globalThis.c8ctl;
1324
+ if (rt && typeof rt.getUserDataDir === 'function') {
1325
+ try {
1326
+ // Node resolves symlinks when computing this module's path, so realpath
1327
+ // both sides before comparing (e.g. macOS /var → /private/var, or a
1328
+ // C8CTL_DATA_DIR that isn't canonicalized) to avoid a false 'local'.
1329
+ const real = (p) => {
1330
+ try {
1331
+ return realpathSync(p);
1332
+ } catch {
1333
+ return p;
1334
+ }
1335
+ };
1336
+ const pluginsDir = join(rt.getUserDataDir(), 'plugins');
1337
+ const nm = real(join(pluginsDir, 'node_modules'));
1338
+ const self = real(pluginDir);
1339
+ if (self === nm || self.startsWith(nm + sep)) {
1340
+ return { mode: 'managed', prefix: real(pluginsDir) };
1341
+ }
1342
+ } catch {
1343
+ /* fall through to the global/local probes */
1344
+ }
1345
+ }
1346
+ if (isGlobalInstall()) return { mode: 'global' };
1347
+ return { mode: 'local' };
1348
+ }
1349
+
1350
+ /** The copy-pasteable command that matches how this plugin is installed. */
1351
+ function manualUpdateCommand(name, info) {
1352
+ if (info.mode === 'managed') return ` c8ctl load plugin ${name}@latest`;
1353
+ if (info.mode === 'local') return ' git pull # in your checkout, then reload the plugin';
1354
+ return ` npm install -g ${name}@latest`;
1355
+ }
1356
+
1311
1357
  function updatePlugin(req) {
1312
1358
  const { name, version: current } = pluginPackage();
1313
1359
 
@@ -1326,7 +1372,8 @@ function updatePlugin(req) {
1326
1372
  const nanoNote = nanoBin
1327
1373
  ? ` (nano server ${nanoVer ?? bundled?.version ?? 'present'})`
1328
1374
  : ' (nano server: not installed for this platform)';
1329
- const manual = ` npm install -g ${name}@latest`;
1375
+ const info = pluginInstallInfo();
1376
+ const manual = manualUpdateCommand(name, info);
1330
1377
 
1331
1378
  console.log(`Installed: ${name} v${current ?? '?'}${nanoNote}`);
1332
1379
 
@@ -1362,26 +1409,42 @@ function updatePlugin(req) {
1362
1409
  return;
1363
1410
  }
1364
1411
 
1365
- if (!isGlobalInstall()) {
1366
- console.log('This plugin is not a global npm install, so it cannot self-update in place.');
1367
- console.log('Pull the latest release with:');
1412
+ if (info.mode === 'local') {
1413
+ console.log('This plugin runs from a local checkout, so it cannot self-update in place.');
1414
+ console.log('Update it with:');
1368
1415
  console.log(manual);
1369
- console.log('(or, for a local checkout, `git pull` then reload the plugin).');
1370
1416
  return;
1371
1417
  }
1372
1418
 
1373
- console.log(`Pulling ${name}@${latest} via npm...`);
1419
+ const installArgs =
1420
+ info.mode === 'managed'
1421
+ ? ['install', `${name}@${latest}`, '--prefix', info.prefix]
1422
+ : ['install', '-g', `${name}@${latest}`];
1423
+ const where = info.mode === 'managed' ? 'the c8ctl plugin store' : "npm's global prefix";
1424
+ console.log(`Pulling ${name}@${latest} into ${where}...`);
1374
1425
  console.log('');
1375
- const res = spawnSync('npm', ['install', '-g', `${name}@${latest}`], { stdio: 'inherit' });
1426
+ const res = spawnSync('npm', installArgs, { stdio: 'inherit' });
1376
1427
  if (res.error) throw new Error(res.error.message);
1377
1428
  if (res.status !== 0) {
1429
+ let hint;
1430
+ if (info.mode === 'managed') {
1431
+ hint = `You can also run:\n${manual}`;
1432
+ } else if (osPlatform() === 'win32') {
1433
+ hint = `You may need to run this command in an elevated terminal (Administrator): ${manual.trim()}`;
1434
+ } else {
1435
+ hint = `You may need elevated permissions: sudo ${manual.trim()}`;
1436
+ }
1378
1437
  throw new Error(
1379
- `npm install -g ${name}@${latest} failed (exit ${res.status}). ` +
1380
- `You may need elevated permissions: sudo ${manual.trim()}`,
1438
+ `npm ${installArgs.join(' ')} failed (exit ${res.status}). ${hint}`,
1381
1439
  );
1382
1440
  }
1383
1441
  console.log('');
1384
- console.log(`Updated to v${latest}. Restart any running cluster to use the new binary:`);
1442
+ if (info.mode === 'managed') {
1443
+ console.log(`Updated to v${latest}. The new plugin and bundled nano server load on your next c8ctl command.`);
1444
+ } else {
1445
+ console.log(`Updated to v${latest}.`);
1446
+ }
1447
+ console.log('Restart any running cluster to use the new server binary:');
1385
1448
  console.log(' c8ctl nano restart');
1386
1449
  }
1387
1450
 
@@ -1449,12 +1512,13 @@ function spawnUpdateRefresh(name, cacheFile) {
1449
1512
  }
1450
1513
 
1451
1514
  function printUpdateNotice(name, current, latest) {
1515
+ const manual = manualUpdateCommand(name, pluginInstallInfo()).trim();
1452
1516
  const lines = [
1453
1517
  '',
1454
1518
  `╭─ Update available: ${name} v${current} → v${latest}`,
1455
1519
  '│ A newer nano release (plugin + bundled server) is published on npm.',
1456
1520
  '│ Install it: c8ctl nano update',
1457
- `│ Or manually: npm install -g ${name}@latest`,
1521
+ `│ Or manually: ${manual}`,
1458
1522
  '╰─ Then restart any running cluster: c8ctl nano restart',
1459
1523
  '',
1460
1524
  ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "c8ctl-plugin-nano",
3
- "version": "1.7.1",
3
+ "version": "1.7.2",
4
4
  "type": "module",
5
5
  "description": "c8ctl plugin to start, inspect, and stop a local Nano BPM (nanobpmn) cluster",
6
6
  "main": "c8ctl-plugin.js",
@@ -49,12 +49,12 @@
49
49
  "semantic-release": "^25.0.3"
50
50
  },
51
51
  "optionalDependencies": {
52
- "@nanobpm/c8ctl-plugin-nano-darwin-arm64": "1.7.1",
53
- "@nanobpm/c8ctl-plugin-nano-darwin-x64": "1.7.1",
54
- "@nanobpm/c8ctl-plugin-nano-linux-x64": "1.7.1",
55
- "@nanobpm/c8ctl-plugin-nano-linux-arm64": "1.7.1",
56
- "@nanobpm/c8ctl-plugin-nano-linux-armv7": "1.7.1",
57
- "@nanobpm/c8ctl-plugin-nano-linux-armv6": "1.7.1",
58
- "@nanobpm/c8ctl-plugin-nano-win32-x64": "1.7.1"
52
+ "@nanobpm/c8ctl-plugin-nano-darwin-arm64": "1.7.2",
53
+ "@nanobpm/c8ctl-plugin-nano-darwin-x64": "1.7.2",
54
+ "@nanobpm/c8ctl-plugin-nano-linux-x64": "1.7.2",
55
+ "@nanobpm/c8ctl-plugin-nano-linux-arm64": "1.7.2",
56
+ "@nanobpm/c8ctl-plugin-nano-linux-armv7": "1.7.2",
57
+ "@nanobpm/c8ctl-plugin-nano-linux-armv6": "1.7.2",
58
+ "@nanobpm/c8ctl-plugin-nano-win32-x64": "1.7.2"
59
59
  }
60
60
  }