@zendero/runctl 0.1.9 → 0.1.11
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 +31 -7
- package/bin/runctl +109 -14
- package/examples/run.sh.example +6 -0
- package/lib/run-lib.sh +49 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -110,7 +110,11 @@ pnpm add -g @zendero/runctl@latest
|
|
|
110
110
|
hash -r
|
|
111
111
|
```
|
|
112
112
|
|
|
113
|
-
|
|
113
|
+
### `runctl update` and pnpm version messages
|
|
114
|
+
|
|
115
|
+
If pnpm nags about versions or **`pnpm self-update`** does nothing useful, **`runctl update --help`** explains why and lists concrete fixes (same text is summarized after a successful pnpm-based update unless **`CI`** or **`RUNCTL_UPDATE_SKIP_PNPM_HINT=1`**).
|
|
116
|
+
|
|
117
|
+
`--help` on the install script prints the same usage summary.
|
|
114
118
|
|
|
115
119
|
---
|
|
116
120
|
|
|
@@ -135,23 +139,37 @@ Add scripts to your `package.json`:
|
|
|
135
139
|
|
|
136
140
|
**`predev`:** If you define `predev` next to `dev` (e.g. a doctor step) and your script name is `dev:*` or `dev_*` without its own `pre<script>`, runctl runs `predev` once before starting. Set `RUNCTL_SKIP_PREDEV=1` to skip.
|
|
137
141
|
|
|
142
|
+
**Dashboard / API-only (split names):** Avoid `runctl start --script dev` when `dev` is the runctl wrapper — that loops. Use a dedicated script for the real server:
|
|
143
|
+
|
|
144
|
+
```json
|
|
145
|
+
{
|
|
146
|
+
"scripts": {
|
|
147
|
+
"dev": "runctl start --script dev:workbench",
|
|
148
|
+
"dev:workbench": "node --env-file=.env src/dashboard/server.js",
|
|
149
|
+
"dev:stop": "runctl stop"
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Listen on `process.env.PORT` (runctl sets it). Optional: `runctl start --script dev:workbench --open` to open the browser after start, or `runctl start … && runctl open .`.
|
|
155
|
+
|
|
138
156
|
---
|
|
139
157
|
|
|
140
158
|
## Commands
|
|
141
159
|
|
|
142
160
|
| Command | What it does |
|
|
143
161
|
|---------|-------------|
|
|
144
|
-
| `runctl start` \| `runctl dev` | Start dev server (same command; picks free port, backgrounds) |
|
|
162
|
+
| `runctl start` \| `runctl dev` | Start dev server (same command; picks free port, backgrounds). Flags: `--script`, `--open` (open browser after a successful start) |
|
|
145
163
|
| `runctl stop [dir]` | Stop daemons & release ports |
|
|
146
164
|
| `runctl status [dir]` | Show `.run` state for this package |
|
|
147
165
|
| `runctl ps` | List running programs with PID, port, service, project |
|
|
148
|
-
| `runctl logs [dir] [service]` | Tail `.run/logs/<service>.log` (default service: `web`) |
|
|
166
|
+
| `runctl logs [dir] [service]` | Tail `.run/logs/<service>.log` (default service: **`RUNCTL_SERVICE`**, else `package.json` `name` basename, else `web`) |
|
|
149
167
|
| `runctl ports` | List user-wide port registry (`~/.run`) |
|
|
150
168
|
| `runctl ports gc` | Clean up stale port claims |
|
|
151
169
|
| `runctl env expand <manifest> [--out file]` | Generate `.env.local` from manifest |
|
|
152
|
-
| `runctl doctor [dir]` | Check Node 18+, `lsof`, package manager, `package.json` |
|
|
170
|
+
| `runctl doctor [dir]` | Check Node 18+, `lsof`, package manager, `package.json`; reminds that child scripts get **`PORT`** / **`HOST`** (custom servers should `listen` on `process.env.PORT`) |
|
|
153
171
|
| `runctl update` | Refresh global CLI: default **`auto`** (npm `@latest`, then Git). **`runctl update npm`** / **`git`** / **`auto`** or flags **`--registry`** / **`--git`** / **`--auto`**; **`runctl update --help`**; env `RUNCTL_PACKAGE`, `RUNCTL_GIT_BASE`, `RUNCTL_GIT_REF` (aligned with [`install-global.sh`](scripts/install-global.sh)) |
|
|
154
|
-
| `runctl version` | Print package version and install path |
|
|
172
|
+
| `runctl version` \| `runctl --version` \| `runctl -v` | Print package version and install path (supported interchangeably) |
|
|
155
173
|
|
|
156
174
|
**Monorepo:** `runctl start ./apps/web --script dev:server`
|
|
157
175
|
|
|
@@ -168,7 +186,7 @@ Add scripts to your `package.json`:
|
|
|
168
186
|
| **`predev`** + split `dev` / `dev:server` | **Supported** — see above. |
|
|
169
187
|
| Monorepo app in a subfolder | Use `runctl start ./apps/web`. |
|
|
170
188
|
| **No `package.json`** (Python, Go, etc.) | **Not a fit** — this tool is for Node package scripts. |
|
|
171
|
-
| Custom Node entry (gateways, CLIs) | **Weak fit** — `PORT`/`HOST` are
|
|
189
|
+
| Custom Node entry (gateways, CLIs) | **Weak fit** — `PORT`/`HOST` are injected; bind with `server.listen(process.env.PORT)` (see `runctl doctor`). |
|
|
172
190
|
|
|
173
191
|
---
|
|
174
192
|
|
|
@@ -176,7 +194,13 @@ Add scripts to your `package.json`:
|
|
|
176
194
|
|
|
177
195
|
[`examples/consumer-package.json`](examples/consumer-package.json) · [`docs/vercel-and-env.md`](docs/vercel-and-env.md) · [`examples/env.manifest.example`](examples/env.manifest.example)
|
|
178
196
|
|
|
179
|
-
**
|
|
197
|
+
**CLI vs `run-lib.sh`:** Most apps only need the **`runctl`** binary and `package.json` scripts. For shell-heavy repos, [`examples/run.sh.example`](examples/run.sh.example) shows sourcing **`lib/run-lib.sh`** (same library the CLI uses). Resolve the installed path with **`runctl lib-path`**.
|
|
198
|
+
|
|
199
|
+
**CI:** Prefer **`pnpm add -D @zendero/runctl`** (or a global install) so `runctl` is on `PATH` with a stable version. **`pnpm dlx @zendero/runctl`** is fine for one-off recovery; avoid relying on it for every CI job (cold cache / latency).
|
|
200
|
+
|
|
201
|
+
**Roadmap (ideas):** `runctl exec` (one-off commands with the same port / `.run` contract as `start`); optional HTTP health gate before “ready”.
|
|
202
|
+
|
|
203
|
+
**Develop this repo:** `pnpm install` → `./run.sh` (default **doctor**, like `elata-bio-sdk/run.sh`) → `./run.sh ports` · **`pnpm test`** runs [`tests/run-all.sh`](tests/run-all.sh) (Jest-style output: suites, ✓/✗, `PASS`/`FAIL` per file, shared helpers in [`tests/lib/test-runner.sh`](tests/lib/test-runner.sh))
|
|
180
204
|
|
|
181
205
|
**Publish (maintainers)** — workflow similar to elata’s release preflight, scaled for one package:
|
|
182
206
|
|
package/bin/runctl
CHANGED
|
@@ -3,6 +3,71 @@
|
|
|
3
3
|
set -euo pipefail
|
|
4
4
|
RUNCTL_PKG_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")/.." && pwd)"
|
|
5
5
|
|
|
6
|
+
# Volta resolves pnpm from the *current* project's packageManager for `pnpm -v`, but
|
|
7
|
+
# `pnpm add -g` uses the default global pnpm (often older). Run pnpm as this package
|
|
8
|
+
# defines it: Corepack reads packageManager from $RUNCTL_PKG_ROOT/package.json (no
|
|
9
|
+
# duplicated version in shell). If Corepack is missing, fall back to npm exec with
|
|
10
|
+
# the same field, then plain pnpm on PATH.
|
|
11
|
+
_runctl_read_pnpm_version_from_package_json() {
|
|
12
|
+
node -e '
|
|
13
|
+
const fs = require("fs");
|
|
14
|
+
const p = JSON.parse(fs.readFileSync(process.argv[1], "utf8"));
|
|
15
|
+
const pm = p.packageManager || "";
|
|
16
|
+
const m = /^pnpm@([0-9]+(?:\.[0-9]+){0,2})/.exec(pm);
|
|
17
|
+
process.stdout.write(m ? m[1] : "");
|
|
18
|
+
' "$RUNCTL_PKG_ROOT/package.json" 2>/dev/null || true
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
# Shown after runctl update (pnpm) and in --help. pnpm often suggests "pnpm self-update" but that
|
|
22
|
+
# updates a different notion of "current" than the pnpm used for global -g installs (Volta/Corepack).
|
|
23
|
+
_runctl_print_pnpm_version_fixup_hint() {
|
|
24
|
+
local ver
|
|
25
|
+
ver="$(_runctl_read_pnpm_version_from_package_json)"
|
|
26
|
+
[[ -n "$ver" ]] || ver="X.Y.Z"
|
|
27
|
+
cat >&2 <<EOF
|
|
28
|
+
|
|
29
|
+
runctl update: pnpm may show "Update available" or tell you to run pnpm self-update; self-update
|
|
30
|
+
often does nothing useful here: it follows your project packageManager, while global installs use a
|
|
31
|
+
separate default pnpm. If the footer still shows an older pnpm than you expect, fix that default — do
|
|
32
|
+
not rely on pnpm self-update alone. Replace ${ver} with the version you want (match package.json).
|
|
33
|
+
|
|
34
|
+
volta install pnpm@${ver}
|
|
35
|
+
corepack enable && corepack prepare pnpm@${ver} --activate
|
|
36
|
+
RUNCTL_PNPM_VERSION=${ver} runctl update git
|
|
37
|
+
runctl update --pm npm
|
|
38
|
+
|
|
39
|
+
Hide this hint: RUNCTL_UPDATE_SKIP_PNPM_HINT=1 (also suppressed when CI is set.)
|
|
40
|
+
EOF
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
_runctl_maybe_print_pnpm_update_hint() {
|
|
44
|
+
local pm="$1"
|
|
45
|
+
[[ "$pm" == "pnpm" ]] || return 0
|
|
46
|
+
[[ -z "${RUNCTL_UPDATE_SKIP_PNPM_HINT:-}" ]] || return 0
|
|
47
|
+
[[ -z "${CI:-}" ]] || return 0
|
|
48
|
+
_runctl_print_pnpm_version_fixup_hint
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
_runctl_pnpm_exec() {
|
|
52
|
+
if command -v corepack >/dev/null 2>&1; then
|
|
53
|
+
if (cd "$RUNCTL_PKG_ROOT" && corepack pnpm "$@"); then
|
|
54
|
+
return 0
|
|
55
|
+
fi
|
|
56
|
+
echo "runctl update: warning: corepack pnpm failed; trying npm exec / PATH pnpm" >&2
|
|
57
|
+
fi
|
|
58
|
+
local ver="${RUNCTL_PNPM_VERSION:-}"
|
|
59
|
+
if [[ -z "$ver" ]]; then
|
|
60
|
+
ver="$(_runctl_read_pnpm_version_from_package_json)"
|
|
61
|
+
fi
|
|
62
|
+
if [[ -n "$ver" ]] && command -v npm >/dev/null 2>&1; then
|
|
63
|
+
if npm exec -y "pnpm@${ver}" -- "$@"; then
|
|
64
|
+
return 0
|
|
65
|
+
fi
|
|
66
|
+
echo "runctl update: warning: npm exec pnpm@${ver} failed; falling back to pnpm on PATH" >&2
|
|
67
|
+
fi
|
|
68
|
+
command pnpm "$@"
|
|
69
|
+
}
|
|
70
|
+
|
|
6
71
|
_b="" _d="" _r="" _c="" _y="" _g=""
|
|
7
72
|
if [[ -t 1 ]]; then
|
|
8
73
|
_b=$'\033[1m' _d=$'\033[2m' _r=$'\033[0m'
|
|
@@ -16,12 +81,12 @@ ${_b}runctl${_r} — dev-server orchestrator & port manager
|
|
|
16
81
|
${_y}Usage:${_r} runctl <command> [options]
|
|
17
82
|
|
|
18
83
|
${_y}Commands${_r}
|
|
19
|
-
${_c}start${_r} | ${_c}dev${_r} [dir] [--script name] Start dev server ${_d}(alias: dev = start)${_r}
|
|
84
|
+
${_c}start${_r} | ${_c}dev${_r} [dir] [--script name] [--open] Start dev server ${_d}(alias: dev = start; ${_c}--open${_r} = browser after start)${_r}
|
|
20
85
|
${_c}open${_r} [dir] Open running dev server in browser
|
|
21
86
|
${_c}stop${_r} [dir] Stop daemons & release ports
|
|
22
87
|
${_c}status${_r} [dir] Show running state
|
|
23
88
|
${_c}ps${_r} List running programs with ports/projects
|
|
24
|
-
${_c}logs${_r} [dir] [service] Tail .run/logs/<service>.log ${_d}(default
|
|
89
|
+
${_c}logs${_r} [dir] [service] Tail .run/logs/<service>.log ${_d}(default: ${_c}RUNCTL_SERVICE${_r} or package name, else web)${_r}
|
|
25
90
|
${_c}ports${_r} List user-wide port registry (~/.run)
|
|
26
91
|
${_c}ports gc${_r} Clean up stale port claims
|
|
27
92
|
${_c}env expand${_r} <manifest> [opts] Generate .env.local from manifest
|
|
@@ -30,6 +95,8 @@ ${_y}Commands${_r}
|
|
|
30
95
|
|
|
31
96
|
${_y}Options${_r}
|
|
32
97
|
${_c}--script${_r} <name> Package script to run ${_d}(default: dev)${_r}
|
|
98
|
+
${_c}--open${_r} After a successful start, open the app URL ${_d}(same as ${_c}runctl open${_r})${_r}
|
|
99
|
+
${_c}version${_r} | ${_c}--version${_r} | ${_c}-v${_r} Print version and install path
|
|
33
100
|
${_c}--help${_r}, ${_c}-h${_r} Show this help
|
|
34
101
|
|
|
35
102
|
${_d}Quick start — add to package.json:
|
|
@@ -48,12 +115,13 @@ _resolve_proj() {
|
|
|
48
115
|
}
|
|
49
116
|
|
|
50
117
|
cmd_start() {
|
|
51
|
-
local proj script="" args=()
|
|
118
|
+
local proj script="" args=() open_after=0
|
|
52
119
|
proj="$(pwd)"
|
|
53
120
|
|
|
54
121
|
while [[ $# -gt 0 ]]; do
|
|
55
122
|
case "$1" in
|
|
56
123
|
--script) script="${2:-}"; shift 2 ;;
|
|
124
|
+
--open) open_after=1; shift ;;
|
|
57
125
|
--) shift; args+=("$@"); break ;;
|
|
58
126
|
-h | --help) usage; exit 0 ;;
|
|
59
127
|
-*)
|
|
@@ -77,7 +145,12 @@ cmd_start() {
|
|
|
77
145
|
# shellcheck source=../lib/run-lib.sh
|
|
78
146
|
source "$RUNCTL_PKG_ROOT/lib/run-lib.sh"
|
|
79
147
|
run_project_init "$proj"
|
|
80
|
-
run_with_lock run_start_package_dev "${args[@]+"${args[@]}"}"
|
|
148
|
+
if ! run_with_lock run_start_package_dev "${args[@]+"${args[@]}"}"; then
|
|
149
|
+
return 1
|
|
150
|
+
fi
|
|
151
|
+
if [[ "$open_after" -eq 1 ]]; then
|
|
152
|
+
cmd_open "$proj"
|
|
153
|
+
fi
|
|
81
154
|
}
|
|
82
155
|
|
|
83
156
|
cmd_open() {
|
|
@@ -212,6 +285,22 @@ ${_y}Environment${_r} ${_d}(optional; same as install-global.sh)${_r}
|
|
|
212
285
|
RUNCTL_PACKAGE npm name ${_d}(default: @zendero/runctl)${_r}
|
|
213
286
|
RUNCTL_GIT_BASE Git URL without fragment ${_d}(default: git+https://github.com/DoctorKhan/runctl.git)${_r}
|
|
214
287
|
RUNCTL_GIT_REF default Git ref ${_d}(default: main)${_r}
|
|
288
|
+
RUNCTL_PNPM_VERSION Force pnpm version for npm-exec fallback ${_d}(e.g. 10.33.0)${_r}
|
|
289
|
+
RUNCTL_UPDATE_SKIP_PNPM_HINT Set to ${_c}1${_r} to hide the post-update pnpm version hint ${_d}(CI also hides)${_r}
|
|
290
|
+
|
|
291
|
+
${_y}pnpm version / “pnpm self-update” does nothing${_r}
|
|
292
|
+
pnpm often prints an upgrade banner or suggests ${_c}pnpm self-update${_r}. That advice targets the
|
|
293
|
+
project ${_c}packageManager${_r} pin; ${_c}pnpm add -g${_r} uses a ${_b}different${_r} default pnpm
|
|
294
|
+
(e.g. Volta’s global toolchain). So self-update may say you are already on 10.33.0 while the footer
|
|
295
|
+
still shows 10.27.0 — both can be “true.” Fix the ${_b}global${_r} default, not self-update alone:
|
|
296
|
+
|
|
297
|
+
${_c}volta install pnpm@<version>${_r} ${_d}match your repo / Volta default${_r}
|
|
298
|
+
${_c}corepack enable && corepack prepare pnpm@<version> --activate${_r}
|
|
299
|
+
${_c}RUNCTL_PNPM_VERSION=<version> runctl update git${_r}
|
|
300
|
+
${_c}runctl update --pm npm${_r} ${_d}use npm for this install instead${_r}
|
|
301
|
+
|
|
302
|
+
A short hint is printed after a successful ${_c}runctl update${_r} with pnpm unless ${_c}CI${_r} is set
|
|
303
|
+
or ${_c}RUNCTL_UPDATE_SKIP_PNPM_HINT=1${_r}.
|
|
215
304
|
EOF
|
|
216
305
|
}
|
|
217
306
|
|
|
@@ -337,7 +426,7 @@ cmd_update() {
|
|
|
337
426
|
return 0
|
|
338
427
|
fi
|
|
339
428
|
if [[ "$pm" == "pnpm" ]]; then
|
|
340
|
-
|
|
429
|
+
_runctl_pnpm_exec remove -g runctl >/dev/null 2>&1 || true
|
|
341
430
|
else
|
|
342
431
|
npm uninstall -g runctl >/dev/null 2>&1 || true
|
|
343
432
|
fi
|
|
@@ -364,7 +453,7 @@ cmd_update() {
|
|
|
364
453
|
echo "runctl update: installing latest from registry ($spec)..."
|
|
365
454
|
_remove_conflicting_global_runctl
|
|
366
455
|
if [[ "$pm" == "pnpm" ]]; then
|
|
367
|
-
|
|
456
|
+
_runctl_pnpm_exec add -g --force "$spec"
|
|
368
457
|
else
|
|
369
458
|
npm install -g --force "$spec"
|
|
370
459
|
fi
|
|
@@ -382,7 +471,7 @@ cmd_update() {
|
|
|
382
471
|
fi
|
|
383
472
|
_remove_conflicting_global_runctl
|
|
384
473
|
if [[ "$pm" == "pnpm" ]]; then
|
|
385
|
-
|
|
474
|
+
_runctl_pnpm_exec add -g --force "$spec"
|
|
386
475
|
else
|
|
387
476
|
npm install -g --force "$spec"
|
|
388
477
|
fi
|
|
@@ -396,17 +485,18 @@ cmd_update() {
|
|
|
396
485
|
_update_git
|
|
397
486
|
;;
|
|
398
487
|
auto)
|
|
399
|
-
if _update_registry; then
|
|
400
|
-
|
|
488
|
+
if ! _update_registry; then
|
|
489
|
+
echo "runctl update: registry install failed; trying Git..." >&2
|
|
490
|
+
_update_git
|
|
401
491
|
fi
|
|
402
|
-
echo "runctl update: registry install failed; trying Git..." >&2
|
|
403
|
-
_update_git
|
|
404
492
|
;;
|
|
405
493
|
*)
|
|
406
494
|
echo "runctl update: internal error: unknown mode $MODE" >&2
|
|
407
495
|
exit 1
|
|
408
496
|
;;
|
|
409
497
|
esac
|
|
498
|
+
|
|
499
|
+
_runctl_maybe_print_pnpm_update_hint "$pm"
|
|
410
500
|
}
|
|
411
501
|
|
|
412
502
|
cmd_doctor() {
|
|
@@ -442,6 +532,7 @@ cmd_doctor() {
|
|
|
442
532
|
printf ' %-12s %s\n' "package.json:" "not found in $proj" >&2
|
|
443
533
|
ec=1
|
|
444
534
|
fi
|
|
535
|
+
printf ' %-12s %s\n' "child env:" "PORT and HOST are set for package scripts; custom Node servers should listen on process.env.PORT (and bind HOST if needed)."
|
|
445
536
|
return "$ec"
|
|
446
537
|
}
|
|
447
538
|
|
|
@@ -456,7 +547,8 @@ cmd_logs() {
|
|
|
456
547
|
shift 2
|
|
457
548
|
;;
|
|
458
549
|
-h | --help)
|
|
459
|
-
echo "Usage: runctl logs [dir] [service]
|
|
550
|
+
echo "Usage: runctl logs [dir] [service]" >&2
|
|
551
|
+
echo " Default service: RUNCTL_SERVICE, else package.json name, else web." >&2
|
|
460
552
|
echo " runctl logs [--lines N]" >&2
|
|
461
553
|
exit 0
|
|
462
554
|
;;
|
|
@@ -470,10 +562,13 @@ cmd_logs() {
|
|
|
470
562
|
proj="$(cd "${pos[0]}" && pwd)"
|
|
471
563
|
pos=("${pos[@]:1}")
|
|
472
564
|
fi
|
|
473
|
-
local svc="${pos[0]:-
|
|
565
|
+
local svc="${pos[0]:-}"
|
|
474
566
|
# shellcheck source=../lib/run-lib.sh
|
|
475
567
|
source "$RUNCTL_PKG_ROOT/lib/run-lib.sh"
|
|
476
568
|
run_project_init "$proj"
|
|
569
|
+
if [[ -z "$svc" ]]; then
|
|
570
|
+
svc="$(run_default_service_name)"
|
|
571
|
+
fi
|
|
477
572
|
local logf="$RUN_LOCAL_STATE/logs/${svc}.log"
|
|
478
573
|
if [[ ! -f "$logf" ]]; then
|
|
479
574
|
echo "runctl logs: no file at $logf" >&2
|
|
@@ -515,7 +610,7 @@ main() {
|
|
|
515
610
|
|
|
516
611
|
# Plumbing
|
|
517
612
|
lib-path) printf '%s\n' "$RUNCTL_PKG_ROOT/lib/run-lib.sh" ;;
|
|
518
|
-
version | -v) cmd_version_print ;;
|
|
613
|
+
version | -v | --version) cmd_version_print ;;
|
|
519
614
|
help | -h | --help) usage ;;
|
|
520
615
|
|
|
521
616
|
# Hidden backward-compat aliases
|
package/examples/run.sh.example
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# Optional shell entrypoint — most projects only need package.json scripts.
|
|
3
3
|
# Prerequisite: pnpm add -D @zendero/runctl (Node >= 18)
|
|
4
|
+
#
|
|
5
|
+
# When to use this vs the runctl CLI:
|
|
6
|
+
# - Prefer `runctl start` / package.json scripts (see README) for normal dev.
|
|
7
|
+
# - Source lib/run-lib.sh (this file) for advanced shell integration: custom
|
|
8
|
+
# wrappers, CI, or repos that already drive everything through ./run.sh.
|
|
9
|
+
# Shipped path: package `lib/run-lib.sh` (see `runctl lib-path`).
|
|
4
10
|
|
|
5
11
|
set -euo pipefail
|
|
6
12
|
|
package/lib/run-lib.sh
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
# run-lib.sh — npm package `@zendero/runctl`: project .run/ + user ~/.run registry.
|
|
3
3
|
# Requires bash. Port/dev automation requires Node.js >= 18 (see package.json engines).
|
|
4
4
|
# Intentionally no global `set -e` — this file is usually sourced.
|
|
5
|
+
# Consumer shell example: examples/run.sh.example (most projects use the `runctl` CLI instead).
|
|
5
6
|
|
|
6
7
|
if [[ -z "${BASH_VERSION:-}" ]]; then
|
|
7
8
|
echo "run-lib.sh: must be sourced or executed with bash (not zsh)" >&2
|
|
@@ -146,6 +147,50 @@ run_require_node() {
|
|
|
146
147
|
}
|
|
147
148
|
}
|
|
148
149
|
|
|
150
|
+
# Basename for .run/logs/<name>.log and RUN_DEV_SERVICE — override with RUNCTL_SERVICE.
|
|
151
|
+
run_sanitize_service_name() {
|
|
152
|
+
local s="${1:-web}"
|
|
153
|
+
s="${s//[^a-zA-Z0-9._-]/-}"
|
|
154
|
+
s="$(printf '%s' "$s" | sed 's/^-*//;s/-*$//')"
|
|
155
|
+
[[ -z "$s" ]] && s="web"
|
|
156
|
+
printf '%s\n' "$s"
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
run_default_service_name() {
|
|
160
|
+
if [[ -n "${RUNCTL_SERVICE:-}" ]]; then
|
|
161
|
+
run_sanitize_service_name "${RUNCTL_SERVICE}"
|
|
162
|
+
return 0
|
|
163
|
+
fi
|
|
164
|
+
[[ -f "$RUN_PROJECT_ROOT/package.json" ]] || {
|
|
165
|
+
printf '%s\n' web
|
|
166
|
+
return 0
|
|
167
|
+
}
|
|
168
|
+
command -v node >/dev/null 2>&1 || {
|
|
169
|
+
printf '%s\n' web
|
|
170
|
+
return 0
|
|
171
|
+
}
|
|
172
|
+
node -e '
|
|
173
|
+
const fs = require("fs");
|
|
174
|
+
const path = require("path");
|
|
175
|
+
const root = process.argv[1];
|
|
176
|
+
let pkg = {};
|
|
177
|
+
try {
|
|
178
|
+
pkg = JSON.parse(fs.readFileSync(path.join(root, "package.json"), "utf8"));
|
|
179
|
+
} catch {
|
|
180
|
+
process.stdout.write("web" + "\n");
|
|
181
|
+
process.exit(0);
|
|
182
|
+
}
|
|
183
|
+
const n = pkg.name;
|
|
184
|
+
if (typeof n !== "string" || !n) {
|
|
185
|
+
process.stdout.write("web" + "\n");
|
|
186
|
+
process.exit(0);
|
|
187
|
+
}
|
|
188
|
+
const base = n.includes("/") ? n.split("/").pop() : n;
|
|
189
|
+
const safe = String(base).replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "web";
|
|
190
|
+
process.stdout.write(safe + "\n");
|
|
191
|
+
' "$RUN_PROJECT_ROOT" 2>/dev/null || printf '%s\n' web
|
|
192
|
+
}
|
|
193
|
+
|
|
149
194
|
# True if package.json defines scripts.<name> (Node required).
|
|
150
195
|
run_package_has_script() {
|
|
151
196
|
local name="$1"
|
|
@@ -242,14 +287,16 @@ EOF
|
|
|
242
287
|
|
|
243
288
|
# Start \`pnpm|yarn|npm run <script>\` in the background with a free port and register it.
|
|
244
289
|
# Script name defaults to \`dev\`; set RUNCTL_PM_RUN_SCRIPT (e.g. dev:server) when \`dev\` is the runctl wrapper.
|
|
245
|
-
#
|
|
290
|
+
# Default service name: RUNCTL_SERVICE, else package.json name (basename), else web — see run_default_service_name.
|
|
291
|
+
# Usage: run_start_package_dev [service_name] [base_port|auto=auto] [extra args after script --]
|
|
246
292
|
run_start_package_dev() {
|
|
247
293
|
run_require_node || return 1
|
|
248
294
|
[[ -f "$RUN_PROJECT_ROOT/package.json" ]] || {
|
|
249
295
|
echo "run_start_package_dev: no package.json in $RUN_PROJECT_ROOT" >&2
|
|
250
296
|
return 1
|
|
251
297
|
}
|
|
252
|
-
local svc
|
|
298
|
+
local svc
|
|
299
|
+
svc="$(run_default_service_name)"
|
|
253
300
|
local base_raw="auto"
|
|
254
301
|
if [[ $# -ge 1 ]]; then svc="$1"; shift; fi
|
|
255
302
|
if [[ $# -ge 1 ]]; then base_raw="$1"; shift; fi
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zendero/runctl",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.11",
|
|
4
4
|
"description": "Picks a free port, runs your dev server in the background, and keeps PID + port state in .run/ so projects don't collide.",
|
|
5
5
|
"author": "DoctorKhan",
|
|
6
6
|
"homepage": "https://github.com/DoctorKhan/runctl#readme",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
],
|
|
38
38
|
"scripts": {
|
|
39
39
|
"run": "bash ./run.sh",
|
|
40
|
-
"test": "bash ./tests/
|
|
40
|
+
"test": "bash ./tests/run-all.sh",
|
|
41
41
|
"doctor": "bash ./run.sh doctor",
|
|
42
42
|
"release-check": "bash ./run.sh release-check",
|
|
43
43
|
"promote": "bash ./run.sh promote",
|