szpont 0.90.0 → 0.92.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 +3 -3
- package/package.json +1 -1
- package/src/launcher.js +55 -17
package/README.md
CHANGED
|
@@ -26,7 +26,7 @@ is already in the repository:
|
|
|
26
26
|
| get it | `git clone` into `~/.diplomat/checkout`, or fast-forward it | same |
|
|
27
27
|
| build it | `install/build-app.sh` → `Diplomat.app` | `install/build-core.sh` when the prompt binary is missing |
|
|
28
28
|
| set it up | - | a venv at `~/.diplomat/venv` with `requirements.txt` in it |
|
|
29
|
-
| start it | `open Diplomat.app` | the package's own `./diplomat`, on the venv's interpreter |
|
|
29
|
+
| start it | `open -n Diplomat.app` | the package's own `./diplomat`, on the venv's interpreter |
|
|
30
30
|
|
|
31
31
|
Nothing is rebuilt that does not need to be: a venv is left alone until
|
|
32
32
|
`requirements.txt` changes, and the Swift prompt binary is only built when the
|
|
@@ -49,8 +49,8 @@ may have work in it. `DIPLOMAT_REPO_URL` clones from a fork.
|
|
|
49
49
|
[`pip install szpont`](https://github.com/latekvo/Diplomat/tree/main/packages/szpont)
|
|
50
50
|
installs the same launcher and nothing else. The two implementations are separate
|
|
51
51
|
files in separate languages, so what keeps them one command is
|
|
52
|
-
`test/parity-with-python.mjs`: every machine
|
|
53
|
-
|
|
52
|
+
`test/parity-with-python.mjs`: every machine in `test/scenarios.mjs` is probed by
|
|
53
|
+
both and every fact set planned by both, and the two answers have to be identical.
|
|
54
54
|
|
|
55
55
|
## Its own tests
|
|
56
56
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "szpont",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.92.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Install-and-run for Diplomat (Szpont Yon): fetches the checkout, builds it, and starts the applet.",
|
|
6
6
|
"keywords": ["diplomat", "szpont", "szpontnet", "launcher", "menu-bar", "tray"],
|
package/src/launcher.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
// The npm twin of `packages/szpont/szpont_launcher.py`, line for line where it
|
|
2
2
|
// matters: `probe()` reads the machine, `plan()` turns those facts into the steps
|
|
3
3
|
// that will run, and `run()` runs them. Splitting it that way is what lets
|
|
4
|
-
// `test/parity-with-python.mjs` hand both implementations one
|
|
5
|
-
//
|
|
6
|
-
// to the two indexes, and a `szpont`
|
|
7
|
-
// which one you installed from would be
|
|
4
|
+
// `test/parity-with-python.mjs` hand both implementations one machine on disk and
|
|
5
|
+
// demand the same facts back, then one set of facts and demand the same plan —
|
|
6
|
+
// the two are published under the same name to the two indexes, and a `szpont`
|
|
7
|
+
// that meant different things depending on which one you installed from would be
|
|
8
|
+
// the whole failure mode.
|
|
8
9
|
//
|
|
9
10
|
// Why a bootstrapper at all, rather than a package holding Diplomat: see the
|
|
10
11
|
// Python module's header. Short version — Diplomat is built out of the checkout it
|
|
@@ -16,7 +17,7 @@ import path from 'node:path';
|
|
|
16
17
|
import { createHash } from 'node:crypto';
|
|
17
18
|
import { spawnSync } from 'node:child_process';
|
|
18
19
|
|
|
19
|
-
export const VERSION = '0.
|
|
20
|
+
export const VERSION = '0.92.0';
|
|
20
21
|
export const DEFAULT_REPO_URL = 'https://github.com/latekvo/Diplomat.git';
|
|
21
22
|
export const SUPPORTED = ['darwin', 'linux'];
|
|
22
23
|
export const MIN_PYTHON = [3, 10];
|
|
@@ -25,16 +26,48 @@ export const MIN_PYTHON = [3, 10];
|
|
|
25
26
|
// already owns, so uninstalling is one `rm -rf ~/.diplomat` rather than a hunt.
|
|
26
27
|
const STATE_DIR = '.diplomat';
|
|
27
28
|
|
|
29
|
+
// `getconf PATH`, where shutil.which looks when neither the machine nor the
|
|
30
|
+
// process has a PATH (glibc and musl agree on the Linux value). Bound once: the
|
|
31
|
+
// libc's answer does not move with the platform probe() is told it is on.
|
|
32
|
+
const DEFAULT_PATH = process.platform === 'darwin' ? '/usr/bin:/bin:/usr/sbin:/sbin' : '/bin:/usr/bin';
|
|
33
|
+
|
|
34
|
+
// The executable's path, or null - `shutil.which` in the Python twin: anything
|
|
35
|
+
// executable that is not a directory, and an empty PATH entry is the working
|
|
36
|
+
// directory, as `sh` reads it.
|
|
28
37
|
function which(name, env) {
|
|
29
|
-
const
|
|
30
|
-
|
|
38
|
+
const searchPath = env.PATH ?? process.env.PATH ?? DEFAULT_PATH;
|
|
39
|
+
if (!searchPath) return null;
|
|
40
|
+
for (const dir of searchPath.split(path.delimiter)) {
|
|
41
|
+
const candidate = path.join(dir, name);
|
|
31
42
|
try {
|
|
32
|
-
fs.accessSync(
|
|
33
|
-
return
|
|
43
|
+
fs.accessSync(candidate, fs.constants.X_OK);
|
|
44
|
+
if (!fs.statSync(candidate).isDirectory()) return candidate;
|
|
34
45
|
} catch {
|
|
35
|
-
|
|
46
|
+
// not here
|
|
36
47
|
}
|
|
37
|
-
}
|
|
48
|
+
}
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Whether `name` is on PATH and would run. Every Mac has a /usr/bin/git and a
|
|
53
|
+
// /usr/bin/swift: xcrun shims that run the Command Line Tools when those are
|
|
54
|
+
// installed and pop the graphical installer when they are not. Found there, a tool
|
|
55
|
+
// is only as present as the toolchain, which `xcode-select -p` reports without
|
|
56
|
+
// going through a shim.
|
|
57
|
+
function tool(name, env, platform) {
|
|
58
|
+
const found = which(name, env);
|
|
59
|
+
if (!found) return false;
|
|
60
|
+
// realpath: a symlink to it, or a directory symlinked onto /usr/bin - what runs
|
|
61
|
+
// is still the shim.
|
|
62
|
+
if (platform === 'darwin' && fs.realpathSync(found) === `/usr/bin/${name}`) return toolchainPresent(env);
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function toolchainPresent(env) {
|
|
67
|
+
const xcodeSelect = which('xcode-select', env);
|
|
68
|
+
if (!xcodeSelect) return false;
|
|
69
|
+
const r = spawnSync(xcodeSelect, ['-p'], { encoding: 'utf8', timeout: 30_000 });
|
|
70
|
+
return r.status === 0 && isDir((r.stdout || '').trim());
|
|
38
71
|
}
|
|
39
72
|
|
|
40
73
|
// `python3 --version`, or null when there is no python3 to ask. Only ever called
|
|
@@ -113,13 +146,16 @@ export function probe(appArgs = [], { update = true, env = process.env } = {}) {
|
|
|
113
146
|
managed: !explicit,
|
|
114
147
|
repo_url: env.DIPLOMAT_REPO_URL || DEFAULT_REPO_URL,
|
|
115
148
|
update: Boolean(update),
|
|
116
|
-
git:
|
|
117
|
-
swift:
|
|
149
|
+
git: tool('git', env, platform),
|
|
150
|
+
swift: tool('swift', env, platform),
|
|
118
151
|
// Linux-only questions, and asked only there — see python3Version.
|
|
119
152
|
python3: platform === 'linux' ? python3Version(env) : null,
|
|
120
153
|
core_bin: platform === 'linux' ? coreBinPresent(home, env) : null,
|
|
121
154
|
venv,
|
|
122
|
-
|
|
155
|
+
// Both, because a venv is only useful with something to install into it:
|
|
156
|
+
// Debian without python3-venv leaves bin/python behind and stops before pip,
|
|
157
|
+
// and running `venv` again over that directory is what completes it.
|
|
158
|
+
venv_python: fs.existsSync(path.join(venv, 'bin', 'python')) && fs.existsSync(path.join(venv, 'bin', 'pip')),
|
|
123
159
|
venv_current: digest !== null && readText(path.join(venv, '.szpont-requirements')) === digest,
|
|
124
160
|
args: [...appArgs],
|
|
125
161
|
};
|
|
@@ -225,8 +261,10 @@ export function plan(facts) {
|
|
|
225
261
|
steps.push(step('build', [path.join(macos, 'install', 'build-app.sh')], { cwd: macos, needs: 'swift' }));
|
|
226
262
|
// build-app.sh rebuilds unconditionally, so the bundle always matches the
|
|
227
263
|
// source it was just cloned or pulled from; `open` then detaches it from this
|
|
228
|
-
// terminal, which is where a menu-bar app belongs.
|
|
229
|
-
|
|
264
|
+
// terminal, which is where a menu-bar app belongs. `-n` starts that bundle
|
|
265
|
+
// even while an instance is up, and the app's newest-wins singleton retires
|
|
266
|
+
// the old one - the swap its own updater relies on.
|
|
267
|
+
const launch = ['open', '-n', path.join(macos, 'Diplomat.app')];
|
|
230
268
|
if (args.length) launch.push('--args', ...args);
|
|
231
269
|
steps.push(step('launch', launch, { cwd: macos }));
|
|
232
270
|
} else if (facts.platform === 'linux') {
|
|
@@ -290,7 +328,7 @@ export function run(steps, { checkout, venv }) {
|
|
|
290
328
|
try {
|
|
291
329
|
fs.writeFileSync(path.join(venv, '.szpont-requirements'), digest || '');
|
|
292
330
|
} catch {
|
|
293
|
-
//
|
|
331
|
+
// re-installed next time; linux/diplomat passes the venv over until then
|
|
294
332
|
}
|
|
295
333
|
}
|
|
296
334
|
}
|