pier-setup 0.2.1 → 0.2.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 (3) hide show
  1. package/README.md +15 -5
  2. package/install.mjs +171 -8
  3. package/package.json +3 -3
package/README.md CHANGED
@@ -46,24 +46,34 @@ pier ships as **two halves**, installed separately:
46
46
  No clone needed — run the npm package directly:
47
47
 
48
48
  ```sh
49
- npx pier-setup # user mode: pi install npm:pi-pier + herdr plugin install + auto-generated bootstrap config
50
- npm i -g pier-setup # or install globally, then just run pier-setup
49
+ npx pier-setup@latest # user mode: pi install npm:pi-pier + herdr plugin install
50
+ npx pier-setup@latest version # local vs npm latest (installer / pi-pier / herdr plugin)
51
+ npx pier-setup@latest update # refresh both halves in place (does not uninstall first)
52
+ npx pier-setup@latest uninstall # --purge also drops boot-config.json
53
+
54
+ npm i -g pier-setup # or install globally, then: pier-setup / version / update
51
55
  ```
52
56
 
57
+ Pin `@latest` so npx does not reuse a cached installer. In a clone, `npm install`
58
+ links `node_modules/.bin/pier-setup`, so in-repo `npx pier-setup` runs `./install.mjs`.
59
+
53
60
  Dev mode still requires cloning the repo:
54
61
 
55
62
  ```sh
56
63
  git clone https://github.com/July24/pier && cd pier
57
64
  node install.mjs install --dev # local-path pi install + herdr plugin link; code changes are live
65
+ node install.mjs version --dev
66
+ node install.mjs update --dev # rewrite boot-config only; pull the repo yourself
58
67
  ```
59
68
 
60
69
  The script verifies the environment (node / pi / herdr versions), probes pi's node
61
70
  and cli.js absolute paths, generates boot-config.json (user mode stores it in the
62
71
  herdr plugin config dir, so reinstalls don't lose it), and registers both halves.
63
72
 
64
- Other commands: `pier-setup update` (uninstall + reinstall, re-probes paths),
65
- `pier-setup uninstall --purge`. Override the distribution specs with
66
- `--pi-spec=` / `--herdr-spec=` (npm publishing or forks).
73
+ `update` re-runs `pi update npm:pi-pier` (falls back to `pi install`) and
74
+ `herdr plugin install … --yes`, then rewrites boot-config. Override sources with
75
+ `--pi-spec=` / `--herdr-spec=` (npm publishing or forks). `pier-setup --help`
76
+ lists every command.
67
77
 
68
78
  ### Manual install (equivalent steps)
69
79
 
package/install.mjs CHANGED
@@ -1,11 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * pier 一键安装/卸载/更新(跨平台:macOS / Linux / Windows)。
3
+ * pier 一键安装/卸载/更新/查版本(跨平台:macOS / Linux / Windows)。
4
4
  *
5
5
  * 用法:
6
6
  * node install.mjs install [--dev] [--hmr-dev] [--force] # 安装(默认命令,可省略 install)
7
+ * node install.mjs update [--dev] [--hmr-dev] # 原地刷新两半区到最新发行(不先卸载)
8
+ * node install.mjs version [--json] # 本地 vs npm latest
7
9
  * node install.mjs uninstall [--dev] [--purge] # 卸载;--purge 连 boot-config.json 一起删
8
- * node install.mjs update [--dev] [--hmr-dev] [--force] # 更新 = 卸载注册 + 重新安装(重探测路径)
10
+ * node install.mjs --prepare # npm prepare:hooksPath + 仓库根 bin 链接
11
+ * node install.mjs --help
9
12
  *
10
13
  * 模式:
11
14
  * 用户模式(默认):pi install npm:pi-pier
@@ -18,7 +21,7 @@
18
21
  * 失败语义:每步给出手动等价命令;步骤失败不阻断报告(exitCode=1)。
19
22
  */
20
23
  import { execFileSync } from 'node:child_process';
21
- import { existsSync, mkdirSync, readFileSync, realpathSync, rmSync, writeFileSync } from 'node:fs';
24
+ import { chmodSync, existsSync, mkdirSync, readFileSync, realpathSync, rmSync, symlinkSync, writeFileSync } from 'node:fs';
22
25
  import { homedir } from 'node:os';
23
26
  import { dirname, join, resolve } from 'node:path';
24
27
  import { fileURLToPath } from 'node:url';
@@ -29,10 +32,38 @@ const WB_DIR = join(ROOT, 'packages', 'pier-workbench');
29
32
  const DEV_BOOT_CONFIG = join(WB_DIR, 'scripts', 'boot-config.json');
30
33
  const EXT_PATH = join(EXT_DIR, 'src', 'index.ts');
31
34
  const IS_WIN = process.platform === 'win32';
35
+ const COMMANDS = ['install', 'uninstall', 'update', 'version', 'help'];
36
+
37
+ /** Workspace root is named pier-setup but npm does not bin-link it, so
38
+ * `npx pier-setup@version` from the clone runs `sh -c pier-setup` against a
39
+ * missing node_modules/.bin/pier-setup. Published tarball has no packages/. */
40
+ function prepareRepo() {
41
+ if (existsSync(join(ROOT, '.githooks'))) {
42
+ try { execFileSync('git', ['config', 'core.hooksPath', '.githooks'], { stdio: 'ignore' }); } catch { /* not a git checkout */ }
43
+ }
44
+ if (!existsSync(EXT_PATH)) return;
45
+ const binDir = join(ROOT, 'node_modules', '.bin');
46
+ mkdirSync(binDir, { recursive: true });
47
+ const posix = join(binDir, 'pier-setup');
48
+ const cmd = join(binDir, 'pier-setup.cmd');
49
+ rmSync(posix, { force: true });
50
+ rmSync(cmd, { force: true });
51
+ if (IS_WIN) {
52
+ writeFileSync(cmd, '@echo off\r\nnode "%~dp0\\..\\..\\install.mjs" %*\r\n');
53
+ writeFileSync(posix, '#!/bin/sh\nexec node "$(dirname "$0")/../../install.mjs" "$@"\n');
54
+ try { chmodSync(posix, 0o755); } catch { /* git-bash optional */ }
55
+ } else {
56
+ symlinkSync('../../install.mjs', posix);
57
+ }
58
+ }
32
59
 
33
60
  const argv = process.argv.slice(2);
34
- const command = ['install', 'uninstall', 'update'].includes(argv[0]) ? argv.shift() : 'install';
35
- const flags = new Set(argv.filter((a) => a.startsWith('--') && !a.includes('=')));
61
+ if (argv[0] === '--prepare') {
62
+ prepareRepo();
63
+ process.exit(0);
64
+ }
65
+ const command = COMMANDS.includes(argv[0]) ? argv.shift() : null;
66
+ const flags = new Set(argv.filter((a) => (a.startsWith('--') && !a.includes('=')) || a === '-h' || a === '-v'));
36
67
  const optValue = (name) => argv.find((a) => a.startsWith(`--${name}=`))?.split('=').slice(1).join('=') ?? null;
37
68
  const dev = flags.has('--dev');
38
69
  const PI_SPEC = optValue('pi-spec') ?? 'npm:pi-pier'; // 用户模式默认 npm 发行版;--pi-spec=git:github.com/July24/pier 可跟随仓库 main
@@ -75,6 +106,34 @@ function geVersion(v, min) {
75
106
  return true;
76
107
  }
77
108
 
109
+ function cmpSemver(a, b) {
110
+ const pa = parseVersion(a);
111
+ const pb = parseVersion(b);
112
+ if (!pa || !pb) return 0;
113
+ for (let i = 0; i < 3; i++) {
114
+ if (pa[i] !== pb[i]) return pa[i] < pb[i] ? -1 : 1;
115
+ }
116
+ return 0;
117
+ }
118
+
119
+ function readPkgVersion(dir) {
120
+ try {
121
+ return JSON.parse(readFileSync(join(dir, 'package.json'), 'utf8')).version ?? null;
122
+ } catch {
123
+ return null;
124
+ }
125
+ }
126
+
127
+ function npmLatest(name) {
128
+ try {
129
+ return execFileSync('npm', ['view', name, 'version'], {
130
+ encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'], timeout: 15000,
131
+ }).trim();
132
+ } catch {
133
+ return null;
134
+ }
135
+ }
136
+
78
137
  /** herdr 插件配置目录(用户模式 boot-config 落点;插件已注册才存在路径语义)。 */
79
138
  function herdrConfigDir() {
80
139
  try { return run('herdr', ['plugin', 'config-dir', 'pier.workbench']); } catch { return null; }
@@ -217,7 +276,109 @@ function writeBootConfig(hmrDev, force) {
217
276
  log(` extPath = ${config.extPath}`);
218
277
  }
219
278
 
220
- /* ── 三命令 ──────────────────────────────────────────────────────── */
279
+ function pkgDirFromExtPath(extPath) {
280
+ let d = dirname(extPath);
281
+ for (let i = 0; i < 5; i++) {
282
+ if (existsSync(join(d, 'package.json'))) return d;
283
+ const parent = dirname(d);
284
+ if (parent === d) break;
285
+ d = parent;
286
+ }
287
+ return null;
288
+ }
289
+
290
+ function herdrPluginInfo() {
291
+ let out;
292
+ try { out = run('herdr', ['plugin', 'list']); } catch { return { installed: false, spec: null, sha: null }; }
293
+ const line = out.split(/\r?\n/).find((l) => l.includes('pier.workbench'));
294
+ if (!line) return { installed: false, spec: null, sha: null };
295
+ const bracket = line.match(/\[([^\]]+)\]/);
296
+ const spec = bracket ? bracket[1] : line.trim();
297
+ const at = spec.lastIndexOf('@');
298
+ const fullSha = at >= 0 && !spec.startsWith('local:') ? spec.slice(at + 1) : null;
299
+ return { installed: true, spec, sha: fullSha ? fullSha.slice(0, 7) : null };
300
+ }
301
+
302
+ function collectStatus() {
303
+ const installer = readPkgVersion(ROOT);
304
+ const extPath = dev ? (existsSync(EXT_PATH) ? EXT_PATH : null) : resolveUserExtPath();
305
+ const extDir = extPath ? pkgDirFromExtPath(extPath) : null;
306
+ return {
307
+ product: (extDir ? readPkgVersion(extDir) : null) || installer,
308
+ mode: dev ? 'dev' : 'user',
309
+ installer: { name: 'pier-setup', version: installer },
310
+ piExt: { spec: piSource(), version: extDir ? readPkgVersion(extDir) : null, path: extDir },
311
+ herdr: herdrPluginInfo(),
312
+ latest: { 'pier-setup': npmLatest('pier-setup'), 'pi-pier': npmLatest('pi-pier') },
313
+ };
314
+ }
315
+
316
+ /* ── 命令 ────────────────────────────────────────────────────────── */
317
+ function usage() {
318
+ log(`pier-setup — install / update / inspect pier (pi-pier + pier.workbench)
319
+
320
+ Usage:
321
+ pier-setup [install] [--dev] [--hmr-dev] [--force]
322
+ pier-setup update [--dev] [--hmr-dev]
323
+ pier-setup version [--json]
324
+ pier-setup uninstall [--dev] [--purge]
325
+
326
+ npx pier-setup@latest # recommended user install
327
+ npx pier-setup@latest update # refresh both halves to newest release
328
+ npx pier-setup@latest version # local vs npm latest
329
+
330
+ --pi-spec= / --herdr-spec= override distribution sources
331
+ --dev local link (clone); update only rewrites boot-config`);
332
+ }
333
+
334
+ function version() {
335
+ const s = collectStatus();
336
+ if (flags.has('--json')) {
337
+ log(JSON.stringify(s, null, 2));
338
+ return;
339
+ }
340
+ log(`pier ${s.product ?? 'not installed'}${s.mode === 'dev' ? ' (dev, local link)' : ''}`);
341
+ log('');
342
+ log(` installer pier-setup ${s.installer.version ?? 'unknown'} (this CLI)`);
343
+ log(` pi ext ${s.piExt.spec} ${s.piExt.version ?? 'not installed'} ${s.piExt.path ?? ''}`);
344
+ const herdrVer = s.herdr.installed ? (s.herdr.sha ?? s.herdr.spec) : 'not installed';
345
+ log(` herdr pier.workbench ${herdrVer} ${s.herdr.spec ?? ''}`);
346
+ log('');
347
+ const ls = s.latest['pier-setup'] ?? 'unavailable (offline?)';
348
+ const lp = s.latest['pi-pier'] ?? 'unavailable (offline?)';
349
+ log(` npm latest pier-setup ${ls} pi-pier ${lp}`);
350
+ if (s.installer.version && s.piExt.version && s.installer.version !== s.piExt.version) {
351
+ log(`\n⚠ mismatch: installer ${s.installer.version} ≠ pi-pier ${s.piExt.version}`);
352
+ }
353
+ const target = s.latest['pi-pier'];
354
+ if (s.piExt.version && target && cmpSemver(s.piExt.version, target) < 0) {
355
+ log(`\nupdate available → ${target} npx pier-setup@latest update`);
356
+ }
357
+ }
358
+
359
+ function update() {
360
+ checkEnv();
361
+ const me = readPkgVersion(ROOT);
362
+ const latestSetup = npmLatest('pier-setup');
363
+ if (latestSetup && me && cmpSemver(me, latestSetup) < 0) {
364
+ log(`⚠ installer ${me} < latest ${latestSetup} — re-run: npx pier-setup@latest update`);
365
+ }
366
+ const hmrDev = flags.has('--hmr-dev');
367
+ if (dev) {
368
+ log('dev mode: local link is live; pull the repo yourself. Refreshing boot-config only.');
369
+ writeBootConfig(hmrDev, true);
370
+ } else {
371
+ if (tryRun('pi', ['update', piSource()])) log(`✓ pi extension updated (${piSource()})`);
372
+ else if (tryRun('pi', ['install', piSource()])) log(`✓ pi extension installed (${piSource()})`);
373
+ else { console.error('✗ pi update/install failed'); log(` manual: pi update ${piSource()}`); process.exitCode = 1; }
374
+ if (tryRun('herdr', ['plugin', 'install', HERDR_SPEC, '--yes'])) log(`✓ herdr plugin updated (${HERDR_SPEC})`);
375
+ else { console.error('✗ herdr plugin update failed'); log(` manual: herdr plugin install ${HERDR_SPEC} --yes`); process.exitCode = 1; }
376
+ writeBootConfig(hmrDev, true);
377
+ }
378
+ log('');
379
+ version();
380
+ }
381
+
221
382
  function install() {
222
383
  const hmrDev = flags.has('--hmr-dev');
223
384
  const force = flags.has('--force');
@@ -264,6 +425,8 @@ function uninstall() {
264
425
  log(`\npier uninstalled.${purge ? '' : ' (boot-config.json kept; use --purge to remove)'}`);
265
426
  }
266
427
 
267
- if (command === 'uninstall') uninstall();
268
- else if (command === 'update') { log('== uninstall =='); uninstall(); log('\n== install =='); install(); }
428
+ if (command === 'help' || flags.has('--help') || flags.has('-h')) usage();
429
+ else if (command === 'version' || flags.has('--version') || flags.has('-v')) version();
430
+ else if (command === 'uninstall') uninstall();
431
+ else if (command === 'update') update();
269
432
  else install();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pier-setup",
3
- "version": "0.2.1",
4
- "description": "One-shot installer for pier: registers the pi-pier extension with pi and the pier.workbench plugin with herdr (install / uninstall / update)",
3
+ "version": "0.2.2",
4
+ "description": "One-shot installer for pier: install / update / version / uninstall for pi-pier + pier.workbench",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "bin": {
@@ -33,7 +33,7 @@
33
33
  "scripts": {
34
34
  "test": "node --test \"packages/*/test/*.test.ts\"",
35
35
  "test:mutation": "stryker run",
36
- "prepare": "git config core.hooksPath .githooks"
36
+ "prepare": "node ./install.mjs --prepare"
37
37
  },
38
38
  "devDependencies": {
39
39
  "@deepseek-ai/cordis-plugin-group": "^1.0.1",