@happytoolin/alur 0.0.1
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/LICENSE +674 -0
- package/README.md +440 -0
- package/binary-install.js +212 -0
- package/binary.js +128 -0
- package/install.js +4 -0
- package/npm-shrinkwrap.json +553 -0
- package/package.json +145 -0
- package/run-alur.js +4 -0
- package/run-nci.js +5 -0
- package/run-ni.js +5 -0
- package/run-nlx.js +5 -0
- package/run-np.js +5 -0
- package/run-nr.js +5 -0
- package/run-ns.js +5 -0
- package/run-nun.js +5 -0
package/README.md
ADDED
|
@@ -0,0 +1,440 @@
|
|
|
1
|
+
# alur
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
[](https://github.com/happytoolin/alur/actions/workflows/ci.yml)
|
|
6
|
+
[](https://www.gnu.org/licenses/gpl-3.0)
|
|
7
|
+
[](https://www.npmjs.com/package/alur)
|
|
8
|
+

|
|
9
|
+

|
|
10
|
+

|
|
11
|
+

|
|
12
|
+

|
|
13
|
+
|
|
14
|
+
Fast package manager routing for `npm`, `yarn`, `pnpm`, `bun`, and `deno`.
|
|
15
|
+
|
|
16
|
+
`alur` is inspired by Antfu's [`ni`](https://github.com/antfu-collective/ni#readme), but packaged as a single multicall binary with extra shell setup for a `node` shim.
|
|
17
|
+
|
|
18
|
+
`alur` is still beta software and may have bugs.
|
|
19
|
+
The supported interface is the CLI; the Rust crate modules are internal and do not carry a stable API guarantee.
|
|
20
|
+
|
|
21
|
+
One install gives you:
|
|
22
|
+
|
|
23
|
+
- `alur`
|
|
24
|
+
- `ni`, `nr`, `nlx`, `nun`, `nci`, `np`, `ns`
|
|
25
|
+
- `node` shim via `alur init <shell>` (managed launcher)
|
|
26
|
+
|
|
27
|
+
## Install
|
|
28
|
+
|
|
29
|
+
### npm (global)
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npm install -g alur
|
|
33
|
+
alur --version
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
This installs `alur` and the `ni`-family aliases (`ni`, `nr`, `nlx`, `nun`, `nci`, `np`, `ns`) onto your global npm bin path.
|
|
37
|
+
The `node` shim is only enabled through `alur init <shell>`.
|
|
38
|
+
Under the hood, the npm postinstall downloads the matching native `alur` binary from the GitHub release.
|
|
39
|
+
|
|
40
|
+
### Homebrew
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
brew tap happytoolin/happytap
|
|
44
|
+
brew install alur
|
|
45
|
+
alur --version
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Script install (macOS / Linux)
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/happytoolin/alur/releases/latest/download/alur-installer.sh | sh
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
To pin a specific version:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/happytoolin/alur/releases/download/v0.0.1/alur-installer.sh | sh
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Script install (PowerShell)
|
|
61
|
+
|
|
62
|
+
```powershell
|
|
63
|
+
powershell -ExecutionPolicy Bypass -c "irm https://github.com/happytoolin/alur/releases/latest/download/alur-installer.ps1 | iex"
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### CI / automation
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/happytoolin/alur/releases/download/v0.0.1/alur-installer.sh | sh
|
|
70
|
+
echo 'export PATH="$HOME/.cargo/bin:$PATH"' >> "$GITHUB_ENV"
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Use the versioned release URL to pin. Use `releases/latest/download` to track the latest release.
|
|
74
|
+
|
|
75
|
+
## Enable the `node` shim
|
|
76
|
+
|
|
77
|
+
Once `alur` is installed, run `alur init` for your shell to enable the `node` shim.
|
|
78
|
+
This creates a managed `node` launcher (a symlink on Unix, copied executable on Windows) and outputs a PATH setup line for your shell config.
|
|
79
|
+
|
|
80
|
+
Add the output to the **end** of your shell rc file (after nvm / mise / asdf / fnm / volta init):
|
|
81
|
+
|
|
82
|
+
**zsh** (`~/.zshrc`):
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
eval "$(alur init zsh)"
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
**bash** (`~/.bashrc`):
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
eval "$(alur init bash)"
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**fish** (`~/.config/fish/config.fish`):
|
|
95
|
+
|
|
96
|
+
```fish
|
|
97
|
+
alur init fish | source
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
**PowerShell** (`$PROFILE`):
|
|
101
|
+
|
|
102
|
+
```powershell
|
|
103
|
+
Invoke-Expression (& alur init powershell)
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
**Nushell** (`~/.config/nushell/config.nu`):
|
|
107
|
+
|
|
108
|
+
```nu
|
|
109
|
+
alur init nushell | save --force ~/.config/nushell/alur.nu
|
|
110
|
+
source ~/.config/nushell/alur.nu
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Once added, restart your shell. `node` will route known npm verbs through alur
|
|
114
|
+
(`node install vite` → `ni vite`) and pass everything else through to the real Node.js.
|
|
115
|
+
|
|
116
|
+
## Commands
|
|
117
|
+
|
|
118
|
+
### Canonical `alur` commands
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
alur install vite
|
|
122
|
+
alur uninstall lodash
|
|
123
|
+
alur run dev
|
|
124
|
+
alur exec vitest
|
|
125
|
+
alur ci
|
|
126
|
+
alur parallel "pnpm dev" "pnpm test"
|
|
127
|
+
alur sequential "pnpm lint" "pnpm test"
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### `ni`
|
|
131
|
+
|
|
132
|
+
Install dependencies or add new ones.
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
ni
|
|
136
|
+
ni vite
|
|
137
|
+
ni -D vitest
|
|
138
|
+
ni -g eslint
|
|
139
|
+
ni --frozen
|
|
140
|
+
ni --frozen-if-present
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### `nr`
|
|
144
|
+
|
|
145
|
+
Run package scripts.
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
nr
|
|
149
|
+
nr dev
|
|
150
|
+
nr build
|
|
151
|
+
nr test -- --watch
|
|
152
|
+
nr --if-present lint
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### `nlx`
|
|
156
|
+
|
|
157
|
+
Execute binaries without adding them permanently to your project.
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
nlx vitest
|
|
161
|
+
nlx eslint .
|
|
162
|
+
nlx create-vite@latest
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### `nun`
|
|
166
|
+
|
|
167
|
+
Uninstall dependencies.
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
nun lodash
|
|
171
|
+
nun react react-dom
|
|
172
|
+
nun -g typescript
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### `nci`
|
|
176
|
+
|
|
177
|
+
Run a clean install. If a lockfile exists, `alur` uses the package-manager-specific frozen install command.
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
nci
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### `np` / `ns`
|
|
184
|
+
|
|
185
|
+
Run shell commands in parallel or sequentially.
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
np "pnpm dev" "pnpm test"
|
|
189
|
+
ns "pnpm lint" "pnpm test"
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### `node`
|
|
193
|
+
|
|
194
|
+
`alur` can also act as a package-manager-aware `node` shim.
|
|
195
|
+
Enable it by adding `alur init <shell>` to your shell config first.
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
node install vite
|
|
199
|
+
node uninstall lodash
|
|
200
|
+
node run dev
|
|
201
|
+
node exec vitest
|
|
202
|
+
node ci
|
|
203
|
+
node p "echo one" "echo two"
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Regular Node.js usage still passes through:
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
node script.js
|
|
210
|
+
node -v
|
|
211
|
+
node -- --trace-warnings
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### Utilities
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
alur help ni
|
|
218
|
+
alur completion zsh
|
|
219
|
+
alur init bash
|
|
220
|
+
alur doctor
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
## Global Flags
|
|
224
|
+
|
|
225
|
+
These work across `alur` and the multicall aliases:
|
|
226
|
+
|
|
227
|
+
```bash
|
|
228
|
+
--print-command
|
|
229
|
+
--explain
|
|
230
|
+
-C <dir>
|
|
231
|
+
-v --version
|
|
232
|
+
-h --help
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Use `--` to forward flags to the underlying package manager or script:
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
alur install -- --help
|
|
239
|
+
nr test -- --watch
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## Configuration
|
|
243
|
+
|
|
244
|
+
Config file:
|
|
245
|
+
|
|
246
|
+
- `$XDG_CONFIG_HOME/alur/config.toml`
|
|
247
|
+
- macOS default: `~/Library/Application Support/alur/config.toml`
|
|
248
|
+
- Windows default: `%APPDATA%\alur\config.toml`
|
|
249
|
+
|
|
250
|
+
Supported keys:
|
|
251
|
+
|
|
252
|
+
```toml
|
|
253
|
+
default_package_manager = "pnpm"
|
|
254
|
+
global_package_manager = "npm"
|
|
255
|
+
fast_mode = true
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
Environment overrides:
|
|
259
|
+
|
|
260
|
+
- `ALUR_CONFIG_FILE`
|
|
261
|
+
- `ALUR_DEFAULT_PACKAGE_MANAGER`
|
|
262
|
+
- `ALUR_GLOBAL_PACKAGE_MANAGER`
|
|
263
|
+
- `ALUR_FAST_MODE`
|
|
264
|
+
|
|
265
|
+
## How It Works
|
|
266
|
+
|
|
267
|
+
`alur` detects the package manager from:
|
|
268
|
+
|
|
269
|
+
1. `packageManager` in `package.json`
|
|
270
|
+
2. lockfiles such as `pnpm-lock.yaml`, `pnpm-workspace.yaml`, `yarn.lock`, `package-lock.json`, `bun.lockb`, or `deno.lock`
|
|
271
|
+
3. `devEngines.packageManager` in `package.json`
|
|
272
|
+
4. install metadata such as `.pnp.cjs`, `node_modules/.pnpm`, or `node_modules/.package-lock.json`
|
|
273
|
+
5. config defaults if detection is unavailable
|
|
274
|
+
|
|
275
|
+
Then it maps the command family to the right underlying command:
|
|
276
|
+
|
|
277
|
+
- `ni` -> install or add
|
|
278
|
+
- `nr` -> run or task
|
|
279
|
+
- `nlx` -> `npx` / `pnpm dlx` / `yarn dlx` / `bun x`
|
|
280
|
+
- `nun` -> uninstall or remove
|
|
281
|
+
- `nci` -> frozen install when lockfiles exist
|
|
282
|
+
- `np` / `ns` -> parallel or sequential shell commands
|
|
283
|
+
|
|
284
|
+
## Troubleshooting
|
|
285
|
+
|
|
286
|
+
### PowerShell `ni` alias conflict
|
|
287
|
+
|
|
288
|
+
PowerShell ships with a built-in `ni` alias for `New-Item`.
|
|
289
|
+
|
|
290
|
+
If that conflicts with `alur`, remove or override it in your profile before loading `alur`:
|
|
291
|
+
|
|
292
|
+
```powershell
|
|
293
|
+
Remove-Item Alias:ni -ErrorAction SilentlyContinue
|
|
294
|
+
Invoke-Expression (& alur init powershell)
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### Check what `alur` resolved
|
|
298
|
+
|
|
299
|
+
```bash
|
|
300
|
+
ni vite --print-command
|
|
301
|
+
nr dev --explain
|
|
302
|
+
alur doctor
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
## Benchmarking
|
|
306
|
+
|
|
307
|
+
The active benchmark suite lives in [`benchmark/`](benchmark/).
|
|
308
|
+
|
|
309
|
+
If you use [`just`](https://github.com/casey/just), the common local commands are wrapped in [`justfile`](justfile):
|
|
310
|
+
|
|
311
|
+
```bash
|
|
312
|
+
just build-release
|
|
313
|
+
just test
|
|
314
|
+
just test-fast
|
|
315
|
+
just ci
|
|
316
|
+
just bench
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
Run the default local benchmark with:
|
|
320
|
+
|
|
321
|
+
```bash
|
|
322
|
+
npm ci
|
|
323
|
+
npm run bench
|
|
324
|
+
just bench
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
Pass options through either entrypoint:
|
|
328
|
+
|
|
329
|
+
```bash
|
|
330
|
+
npm run bench -- --track=compare
|
|
331
|
+
npm run bench -- --track=fast
|
|
332
|
+
npm run bench -- --track=runtime
|
|
333
|
+
npm run bench -- --track=direct
|
|
334
|
+
just bench --track=direct --runs=3 --warmups=1 --no-build
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
Run the full release-style matrix with:
|
|
338
|
+
|
|
339
|
+
```bash
|
|
340
|
+
npm run bench -- --track=all --runs=500 --warmups=50
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
Generate flamegraphs with:
|
|
344
|
+
|
|
345
|
+
```bash
|
|
346
|
+
./benchmark/profile.sh
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
Tracked benchmark docs:
|
|
350
|
+
|
|
351
|
+
- current snapshot: [`benchmark/LATEST.md`](benchmark/LATEST.md)
|
|
352
|
+
- lightweight history: [`benchmark/HISTORY.md`](benchmark/HISTORY.md)
|
|
353
|
+
- fast-mode compatibility: [`docs/fast-compat.md`](docs/fast-compat.md)
|
|
354
|
+
|
|
355
|
+
### Representative Results
|
|
356
|
+
|
|
357
|
+
All numbers below were measured on macOS (Apple Silicon) with the release binary, using `hyperfine` with 10 warmups and 100 measured runs per case. See [`benchmark/LATEST.md`](benchmark/LATEST.md) for the raw tracked snapshot.
|
|
358
|
+
|
|
359
|
+
**Headline:** `alur --fast` is **7.4x faster** than running package managers directly, and **4.6x faster** than `alur` in its own PM fallback mode.
|
|
360
|
+
|
|
361
|
+
#### 1. Fast mode vs PM mode (inside alur)
|
|
362
|
+
|
|
363
|
+
Fast mode bypasses the package manager CLI entirely and runs scripts / local bins natively.
|
|
364
|
+
|
|
365
|
+
| Case | PM mode | Fast mode | Speedup |
|
|
366
|
+
| ------------------------ | ------: | --------: | --------: |
|
|
367
|
+
| `nr noop (npm)` | 246 ms | 37 ms | **6.6x** |
|
|
368
|
+
| `nr noop (pnpm)` | 799 ms | 49 ms | **16.4x** |
|
|
369
|
+
| `nr noop (yarn)` | 348 ms | 38 ms | **9.3x** |
|
|
370
|
+
| `node run noop (pnpm)` | 956 ms | 34 ms | **28.4x** |
|
|
371
|
+
| `nlx hello --flag (npm)` | 288 ms | 17 ms | **17.0x** |
|
|
372
|
+
| `nr noop (bun)` | 70 ms | 37 ms | **1.9x** |
|
|
373
|
+
| `nr noop (deno)` | 80 ms | 35 ms | **2.2x** |
|
|
374
|
+
|
|
375
|
+
_Geometric mean across all package managers: **4.6x**._
|
|
376
|
+
|
|
377
|
+
pnpm and yarn see the biggest wins because their CLIs carry the most startup overhead. Bun and Deno are already fast, so the margin is smaller (but still consistently ahead).
|
|
378
|
+
|
|
379
|
+
#### 2. alur fast vs direct package-manager usage
|
|
380
|
+
|
|
381
|
+
This is the real-world comparison: what users actually type today versus using `alur`.
|
|
382
|
+
|
|
383
|
+
| Case | Direct PM | alur --fast | Speedup |
|
|
384
|
+
| ------------------------ | --------: | ---------: | --------: |
|
|
385
|
+
| `npm run noop` | 320 ms | 53 ms | **6.1x** |
|
|
386
|
+
| `pnpm run noop` | 749 ms | 41 ms | **18.2x** |
|
|
387
|
+
| `yarn run noop` | 443 ms | 34 ms | **13.0x** |
|
|
388
|
+
| `npx hello --flag` | 300 ms | 4.8 ms | **62.0x** |
|
|
389
|
+
| `pnpm exec hello --flag` | 733 ms | 8.9 ms | **82.8x** |
|
|
390
|
+
| `bun run noop` | 79 ms | 34 ms | **2.4x** |
|
|
391
|
+
| `deno task noop` | 50 ms | 34 ms | **1.5x** |
|
|
392
|
+
|
|
393
|
+
_Geometric mean: **7.4x**._
|
|
394
|
+
|
|
395
|
+
Local bin execution is the standout feature: `npx` and `pnpm exec` spend hundreds of milliseconds resolving, validating, and bootstrapping before they even start your binary. `alur` resolves the bin once and runs it directly.
|
|
396
|
+
|
|
397
|
+
#### 3. alur vs Antfu's `ni`
|
|
398
|
+
|
|
399
|
+
For startup/version checks, `alur` is faster:
|
|
400
|
+
|
|
401
|
+
| Case | antfu/ni | alur | Speedup |
|
|
402
|
+
| -------------- | -------: | ----: | -------: |
|
|
403
|
+
| `ni --version` | 149 ms | 92 ms | **1.6x** |
|
|
404
|
+
|
|
405
|
+
_Current compare track keeps only version startup because `alur` no longer carries legacy `?` command-printing compatibility._
|
|
406
|
+
|
|
407
|
+
#### 4. Runtime comparison vs Bun and Deno
|
|
408
|
+
|
|
409
|
+
Even against native runtime task execution, `alur` holds its own:
|
|
410
|
+
|
|
411
|
+
| Case | alur | bun | deno |
|
|
412
|
+
| ------------ | ----: | -----: | ----: |
|
|
413
|
+
| `task noop` | 33 ms | 78 ms | 49 ms |
|
|
414
|
+
| `task hooks` | 90 ms | 210 ms | 77 ms |
|
|
415
|
+
|
|
416
|
+
`alur` is **2.3x faster than bun** for task execution and slightly faster than Deno for simple scripts.
|
|
417
|
+
|
|
418
|
+
### Methodology
|
|
419
|
+
|
|
420
|
+
The benchmark suite lives in [`benchmark/`](benchmark/) and uses `hyperfine` to time the release binary. It covers five angles:
|
|
421
|
+
|
|
422
|
+
- **`direct`** — normal package-manager commands (`npm run`, `pnpm exec`, etc.) vs `alur --fast`
|
|
423
|
+
- **`fast`** — `alur` PM mode vs `alur` fast mode (isolates the native-execution win)
|
|
424
|
+
- **`compare`** — `alur` vs `@antfu/ni` on startup/version overhead
|
|
425
|
+
- **`runtime`** — `alur` vs `bun` vs `deno` on actual task execution time
|
|
426
|
+
- **`fixtures`** — real project fixtures from `tests/fixtures/` across all detection categories
|
|
427
|
+
|
|
428
|
+
Run the full matrix locally:
|
|
429
|
+
|
|
430
|
+
```bash
|
|
431
|
+
npm run bench -- --track=all --runs=100 --warmups=10
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
Or generate flamegraphs:
|
|
435
|
+
|
|
436
|
+
```bash
|
|
437
|
+
./benchmark/profile.sh
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
Tracked snapshots are kept in [`benchmark/LATEST.md`](benchmark/LATEST.md).
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
const { createWriteStream, existsSync, mkdirSync, mkdtemp } = require("fs");
|
|
2
|
+
const { join, sep } = require("path");
|
|
3
|
+
const { spawnSync } = require("child_process");
|
|
4
|
+
const { tmpdir } = require("os");
|
|
5
|
+
|
|
6
|
+
const axios = require("axios");
|
|
7
|
+
const rimraf = require("rimraf");
|
|
8
|
+
const tmpDir = tmpdir();
|
|
9
|
+
|
|
10
|
+
const error = (msg) => {
|
|
11
|
+
console.error(msg);
|
|
12
|
+
process.exit(1);
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
class Package {
|
|
16
|
+
constructor(platform, name, url, filename, zipExt, binaries) {
|
|
17
|
+
let errors = [];
|
|
18
|
+
if (typeof url !== "string") {
|
|
19
|
+
errors.push("url must be a string");
|
|
20
|
+
} else {
|
|
21
|
+
try {
|
|
22
|
+
new URL(url);
|
|
23
|
+
} catch (e) {
|
|
24
|
+
errors.push(e);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
if (name && typeof name !== "string") {
|
|
28
|
+
errors.push("package name must be a string");
|
|
29
|
+
}
|
|
30
|
+
if (!name) {
|
|
31
|
+
errors.push("You must specify the name of your package");
|
|
32
|
+
}
|
|
33
|
+
if (binaries && typeof binaries !== "object") {
|
|
34
|
+
errors.push("binaries must be a string => string map");
|
|
35
|
+
}
|
|
36
|
+
if (!binaries) {
|
|
37
|
+
errors.push("You must specify the binaries in the package");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (errors.length > 0) {
|
|
41
|
+
let errorMsg =
|
|
42
|
+
"One or more of the parameters you passed to the Binary constructor are invalid:\n";
|
|
43
|
+
errors.forEach((error) => {
|
|
44
|
+
errorMsg += error;
|
|
45
|
+
});
|
|
46
|
+
errorMsg +=
|
|
47
|
+
'\n\nCorrect usage: new Package("my-binary", "https://example.com/binary/download.tar.gz", {"my-binary": "my-binary"})';
|
|
48
|
+
error(errorMsg);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
this.platform = platform;
|
|
52
|
+
this.url = url;
|
|
53
|
+
this.name = name;
|
|
54
|
+
this.filename = filename;
|
|
55
|
+
this.zipExt = zipExt;
|
|
56
|
+
this.installDirectory = join(__dirname, "node_modules", ".bin_real");
|
|
57
|
+
this.binaries = binaries;
|
|
58
|
+
|
|
59
|
+
if (!existsSync(this.installDirectory)) {
|
|
60
|
+
mkdirSync(this.installDirectory, { recursive: true });
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
exists() {
|
|
65
|
+
for (const binaryName in this.binaries) {
|
|
66
|
+
const binRelPath = this.binaries[binaryName];
|
|
67
|
+
const binPath = join(this.installDirectory, binRelPath);
|
|
68
|
+
if (!existsSync(binPath)) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
install(fetchOptions, suppressLogs = false) {
|
|
76
|
+
if (this.exists()) {
|
|
77
|
+
if (!suppressLogs) {
|
|
78
|
+
console.error(
|
|
79
|
+
`${this.name} is already installed, skipping installation.`,
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
return Promise.resolve();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (existsSync(this.installDirectory)) {
|
|
86
|
+
rimraf.sync(this.installDirectory);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
mkdirSync(this.installDirectory, { recursive: true });
|
|
90
|
+
|
|
91
|
+
if (!suppressLogs) {
|
|
92
|
+
console.error(`Downloading release from ${this.url}`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return axios({ ...fetchOptions, url: this.url, responseType: "stream" })
|
|
96
|
+
.then((res) => {
|
|
97
|
+
return new Promise((resolve, reject) => {
|
|
98
|
+
mkdtemp(`${tmpDir}${sep}`, (err, directory) => {
|
|
99
|
+
let tempFile = join(directory, this.filename);
|
|
100
|
+
const sink = res.data.pipe(createWriteStream(tempFile));
|
|
101
|
+
sink.on("error", (err) => reject(err));
|
|
102
|
+
sink.on("close", () => {
|
|
103
|
+
if (/\.tar\.*/.test(this.zipExt)) {
|
|
104
|
+
const result = spawnSync("tar", [
|
|
105
|
+
"xf",
|
|
106
|
+
tempFile,
|
|
107
|
+
// The tarballs are stored with a leading directory
|
|
108
|
+
// component; we strip one component in the
|
|
109
|
+
// shell installers too.
|
|
110
|
+
"--strip-components",
|
|
111
|
+
"1",
|
|
112
|
+
"-C",
|
|
113
|
+
this.installDirectory,
|
|
114
|
+
]);
|
|
115
|
+
if (result.status == 0) {
|
|
116
|
+
resolve();
|
|
117
|
+
} else if (result.error) {
|
|
118
|
+
reject(result.error);
|
|
119
|
+
} else {
|
|
120
|
+
reject(
|
|
121
|
+
new Error(
|
|
122
|
+
`An error occurred untarring the artifact: stdout: ${result.stdout}; stderr: ${result.stderr}`,
|
|
123
|
+
),
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
} else if (this.zipExt == ".zip") {
|
|
127
|
+
let result;
|
|
128
|
+
if (this.platform.artifactName.includes("windows")) {
|
|
129
|
+
// Windows does not have "unzip" by default on many installations, instead
|
|
130
|
+
// we use Expand-Archive from powershell
|
|
131
|
+
result = spawnSync("powershell.exe", [
|
|
132
|
+
"-NoProfile",
|
|
133
|
+
"-NonInteractive",
|
|
134
|
+
"-Command",
|
|
135
|
+
`& {
|
|
136
|
+
param([string]$LiteralPath, [string]$DestinationPath)
|
|
137
|
+
Expand-Archive -LiteralPath $LiteralPath -DestinationPath $DestinationPath -Force
|
|
138
|
+
}`,
|
|
139
|
+
tempFile,
|
|
140
|
+
this.installDirectory,
|
|
141
|
+
]);
|
|
142
|
+
} else {
|
|
143
|
+
result = spawnSync("unzip", [
|
|
144
|
+
"-q",
|
|
145
|
+
tempFile,
|
|
146
|
+
"-d",
|
|
147
|
+
this.installDirectory,
|
|
148
|
+
]);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (result.status == 0) {
|
|
152
|
+
resolve();
|
|
153
|
+
} else if (result.error) {
|
|
154
|
+
reject(result.error);
|
|
155
|
+
} else {
|
|
156
|
+
reject(
|
|
157
|
+
new Error(
|
|
158
|
+
`An error occurred unzipping the artifact: stdout: ${result.stdout}; stderr: ${result.stderr}`,
|
|
159
|
+
),
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
} else {
|
|
163
|
+
reject(
|
|
164
|
+
new Error(`Unrecognized file extension: ${this.zipExt}`),
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
})
|
|
171
|
+
.then(() => {
|
|
172
|
+
if (!suppressLogs) {
|
|
173
|
+
console.error(`${this.name} has been installed!`);
|
|
174
|
+
}
|
|
175
|
+
})
|
|
176
|
+
.catch((e) => {
|
|
177
|
+
error(`Error fetching release: ${e.message}`);
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
run(binaryName, fetchOptions) {
|
|
182
|
+
const promise = !this.exists()
|
|
183
|
+
? this.install(fetchOptions, true)
|
|
184
|
+
: Promise.resolve();
|
|
185
|
+
|
|
186
|
+
promise
|
|
187
|
+
.then(() => {
|
|
188
|
+
const [, , ...args] = process.argv;
|
|
189
|
+
|
|
190
|
+
const options = { cwd: process.cwd(), stdio: "inherit" };
|
|
191
|
+
|
|
192
|
+
const binRelPath = this.binaries[binaryName];
|
|
193
|
+
if (!binRelPath) {
|
|
194
|
+
error(`${binaryName} is not a known binary in ${this.name}`);
|
|
195
|
+
}
|
|
196
|
+
const binPath = join(this.installDirectory, binRelPath);
|
|
197
|
+
const result = spawnSync(binPath, args, options);
|
|
198
|
+
|
|
199
|
+
if (result.error) {
|
|
200
|
+
error(result.error);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
process.exit(result.status);
|
|
204
|
+
})
|
|
205
|
+
.catch((e) => {
|
|
206
|
+
error(e.message);
|
|
207
|
+
process.exit(1);
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
module.exports.Package = Package;
|