@zendero/runctl 0.1.3 → 0.1.5
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 +65 -23
- package/bin/runctl +194 -9
- package/lib/run-lib.sh +58 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -10,59 +10,100 @@ Picks a **free port**, runs your **dev server in the background**, and keeps **P
|
|
|
10
10
|
|
|
11
11
|
## Install
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Published name on npm is **`@zendero/runctl`**; the CLI on your PATH is **`runctl`**.
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
| Goal | What to run |
|
|
16
|
+
|------|-------------|
|
|
17
|
+
| Use runctl **inside one repo** (recommended) | `pnpm add -D @zendero/runctl` — also `npm install -D` / `yarn add -D` |
|
|
18
|
+
| **`runctl` everywhere** (global) | `pnpm add -g @zendero/runctl` or the curl installer below |
|
|
19
|
+
| Track **main from GitHub** as a dev dependency | `pnpm add -D "github:DoctorKhan/runctl#main"` (still resolves as `@zendero/runctl`; reinstall to update) |
|
|
20
|
+
|
|
21
|
+
### Global install: package manager vs script
|
|
22
|
+
|
|
23
|
+
**Package manager** is the straightforward choice if you already use pnpm or npm:
|
|
16
24
|
|
|
17
25
|
```bash
|
|
18
|
-
pnpm add -
|
|
26
|
+
pnpm add -g @zendero/runctl
|
|
19
27
|
```
|
|
20
28
|
|
|
21
|
-
|
|
29
|
+
From Git only:
|
|
22
30
|
|
|
23
31
|
```bash
|
|
24
|
-
pnpm add -g
|
|
32
|
+
pnpm add -g "github:DoctorKhan/runctl#main"
|
|
25
33
|
```
|
|
26
34
|
|
|
27
|
-
**
|
|
35
|
+
**[`scripts/install-global.sh`](scripts/install-global.sh)** is for “one command” setup, **CI**, or when you want **npm first, then Git** without writing two install lines yourself. It requires **bash**, **pnpm or npm** on `PATH`, and network access.
|
|
36
|
+
|
|
37
|
+
One-liner (same URL the script header documents):
|
|
28
38
|
|
|
29
39
|
```bash
|
|
30
|
-
curl -fsSL "https://raw.githubusercontent.com/DoctorKhan/runctl/main/scripts/install-global.sh" |
|
|
31
|
-
bash
|
|
40
|
+
curl -fsSL "https://raw.githubusercontent.com/DoctorKhan/runctl/main/scripts/install-global.sh" | bash
|
|
32
41
|
```
|
|
33
42
|
|
|
34
|
-
|
|
43
|
+
Pass script arguments after `bash` (stdin pipe has no argv). To pick a **mode** explicitly:
|
|
35
44
|
|
|
36
45
|
```bash
|
|
37
|
-
curl -fsSL "https://raw.githubusercontent.com/DoctorKhan/runctl/main/scripts/install-global.sh" |
|
|
38
|
-
bash -s -- --registry
|
|
46
|
+
curl -fsSL "https://raw.githubusercontent.com/DoctorKhan/runctl/main/scripts/install-global.sh" | bash -s -- --registry
|
|
39
47
|
```
|
|
40
48
|
|
|
49
|
+
### `install-global.sh` reference
|
|
50
|
+
|
|
51
|
+
If you do **not** pass **`--registry`**, **`--git`**, **`--auto`**, or **`--interactive`**: on an **interactive TTY** with **`CI` not `1`**, the script **prompts** for install source (and related choices). Otherwise it behaves like **`--auto`**: **global install from the npm registry** first; if that fails, **retry from Git** (same URL/ref as `--git`).
|
|
52
|
+
|
|
53
|
+
**Modes** — each mode picks *where* the global install comes from. Under the hood the script runs **`pnpm add -g …`** or **`npm install -g …`** once per successful path (auto can run **twice**: registry attempt, then Git if the first fails).
|
|
54
|
+
|
|
55
|
+
| Mode | What it does | When to use it |
|
|
56
|
+
|------|----------------|----------------|
|
|
57
|
+
| **`--registry`** | **Only** installs `RUNCTL_PACKAGE` (default `@zendero/runctl`) from the npm registry. **No** Git fallback. | You want the published package only—e.g. CI that must not clone Git, or you know npm is enough. |
|
|
58
|
+
| **`--git`** | **Only** installs from Git: `RUNCTL_GIT_BASE` + `#` + ref (default ref `main`, overridable with `--ref`). **No** registry attempt first. | You want `main`/a branch/tag from the repo, or the registry is unreachable. |
|
|
59
|
+
| **`--auto`** | Tries **`--registry`** first; on **failure**, runs the same Git install as **`--git`**. | Headless installs, pipes, CI: resilient default when you’re fine with either source. |
|
|
60
|
+
| **`--interactive`** | Prompts for **registry / git / auto**, optional **Git ref** when git/auto applies, and **pnpm vs npm** if both exist—**only** when a TTY is available. | You want to choose at install time instead of memorizing flags. |
|
|
61
|
+
|
|
62
|
+
If **`--interactive`** is requested but there is **no usable TTY** (or `CI=1`), the script **falls back to `--auto`** and prints a short notice.
|
|
63
|
+
|
|
64
|
+
**Flags**
|
|
65
|
+
|
|
66
|
+
| Flag | Meaning |
|
|
67
|
+
|------|---------|
|
|
68
|
+
| `--pm pnpm` \| `--pm npm` | Use that package manager (must exist on `PATH`) |
|
|
69
|
+
| `--ref <ref>` | Git ref for `--git` or for the Git step of `--auto` (default: `main`) |
|
|
70
|
+
|
|
71
|
+
**Environment variables** (optional)
|
|
72
|
+
|
|
73
|
+
| Variable | Purpose |
|
|
74
|
+
|----------|---------|
|
|
75
|
+
| `RUNCTL_PACKAGE` | npm package name (default: `@zendero/runctl`) |
|
|
76
|
+
| `RUNCTL_GIT_BASE` | Git URL without fragment (default: `git+https://github.com/DoctorKhan/runctl.git`) |
|
|
77
|
+
| `RUNCTL_GIT_REF` | Default ref when not overridden by `--ref` (default: `main`) |
|
|
78
|
+
|
|
79
|
+
**Examples**
|
|
80
|
+
|
|
81
|
+
Registry only (good for locked-down CI that should not hit Git):
|
|
82
|
+
|
|
41
83
|
```bash
|
|
42
|
-
curl -fsSL "https://raw.githubusercontent.com/DoctorKhan/runctl/main/scripts/install-global.sh" |
|
|
43
|
-
bash -s -- --auto
|
|
84
|
+
curl -fsSL "https://raw.githubusercontent.com/DoctorKhan/runctl/main/scripts/install-global.sh" | bash -s -- --registry
|
|
44
85
|
```
|
|
45
86
|
|
|
87
|
+
Explicit auto (same as non-interactive default, but spelled out):
|
|
88
|
+
|
|
46
89
|
```bash
|
|
47
|
-
curl -fsSL "https://raw.githubusercontent.com/DoctorKhan/runctl/main/scripts/install-global.sh" |
|
|
48
|
-
bash -s -- --git --ref main
|
|
90
|
+
curl -fsSL "https://raw.githubusercontent.com/DoctorKhan/runctl/main/scripts/install-global.sh" | bash -s -- --auto
|
|
49
91
|
```
|
|
50
92
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
**Without curl:**
|
|
93
|
+
Git only, specific ref:
|
|
54
94
|
|
|
55
95
|
```bash
|
|
56
|
-
|
|
57
|
-
pnpm add -g "github:DoctorKhan/runctl#main"
|
|
96
|
+
curl -fsSL "https://raw.githubusercontent.com/DoctorKhan/runctl/main/scripts/install-global.sh" | bash -s -- --git --ref main
|
|
58
97
|
```
|
|
59
98
|
|
|
60
|
-
|
|
99
|
+
Use npm explicitly (e.g. no pnpm on the machine):
|
|
61
100
|
|
|
62
101
|
```bash
|
|
63
|
-
|
|
102
|
+
curl -fsSL "https://raw.githubusercontent.com/DoctorKhan/runctl/main/scripts/install-global.sh" | bash -s -- --pm npm --registry
|
|
64
103
|
```
|
|
65
104
|
|
|
105
|
+
`--help` on the script prints the same usage summary.
|
|
106
|
+
|
|
66
107
|
---
|
|
67
108
|
|
|
68
109
|
## Quick start
|
|
@@ -95,12 +136,13 @@ Add scripts to your `package.json`:
|
|
|
95
136
|
| `runctl start` \| `runctl dev` | Start dev server (same command; picks free port, backgrounds) |
|
|
96
137
|
| `runctl stop [dir]` | Stop daemons & release ports |
|
|
97
138
|
| `runctl status [dir]` | Show `.run` state for this package |
|
|
139
|
+
| `runctl ps` | List running programs with PID, port, service, project |
|
|
98
140
|
| `runctl logs [dir] [service]` | Tail `.run/logs/<service>.log` (default service: `web`) |
|
|
99
141
|
| `runctl ports` | List user-wide port registry (`~/.run`) |
|
|
100
142
|
| `runctl ports gc` | Clean up stale port claims |
|
|
101
143
|
| `runctl env expand <manifest> [--out file]` | Generate `.env.local` from manifest |
|
|
102
144
|
| `runctl doctor [dir]` | Check Node 18+, `lsof`, package manager, `package.json` |
|
|
103
|
-
| `runctl update` |
|
|
145
|
+
| `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)) |
|
|
104
146
|
| `runctl version` | Print package version and install path |
|
|
105
147
|
|
|
106
148
|
**Monorepo:** `runctl start ./apps/web --script dev:server`
|
package/bin/runctl
CHANGED
|
@@ -20,12 +20,13 @@ ${_y}Commands${_r}
|
|
|
20
20
|
${_c}open${_r} [dir] Open running dev server in browser
|
|
21
21
|
${_c}stop${_r} [dir] Stop daemons & release ports
|
|
22
22
|
${_c}status${_r} [dir] Show running state
|
|
23
|
+
${_c}ps${_r} List running programs with ports/projects
|
|
23
24
|
${_c}logs${_r} [dir] [service] Tail .run/logs/<service>.log ${_d}(default service: web)${_r}
|
|
24
25
|
${_c}ports${_r} List user-wide port registry (~/.run)
|
|
25
26
|
${_c}ports gc${_r} Clean up stale port claims
|
|
26
27
|
${_c}env expand${_r} <manifest> [opts] Generate .env.local from manifest
|
|
27
28
|
${_c}doctor${_r} Check Node, tooling, and project basics
|
|
28
|
-
${_c}update${_r}
|
|
29
|
+
${_c}update${_r} [npm|git|auto] [opts] Update global runctl ${_d}(default: auto; see runctl update --help)${_r}
|
|
29
30
|
|
|
30
31
|
${_y}Options${_r}
|
|
31
32
|
${_c}--script${_r} <name> Package script to run ${_d}(default: dev)${_r}
|
|
@@ -156,6 +157,23 @@ cmd_ports() {
|
|
|
156
157
|
esac
|
|
157
158
|
}
|
|
158
159
|
|
|
160
|
+
cmd_ps() {
|
|
161
|
+
# shellcheck source=../lib/run-lib.sh
|
|
162
|
+
source "$RUNCTL_PKG_ROOT/lib/run-lib.sh"
|
|
163
|
+
run_global_list_running
|
|
164
|
+
local stale="${RUN_LAST_STALE_COUNT:-0}"
|
|
165
|
+
if [[ "$stale" =~ ^[0-9]+$ ]] && (( stale > 0 )); then
|
|
166
|
+
echo ""
|
|
167
|
+
echo "runctl ps: found $stale stale registry entr$( (( stale == 1 )) && echo "y" || echo "ies" ); cleaning..."
|
|
168
|
+
(run_global_gc >/dev/null 2>&1) &
|
|
169
|
+
local gc_pid=$!
|
|
170
|
+
wait "$gc_pid" || true
|
|
171
|
+
echo "runctl ps: refreshed"
|
|
172
|
+
echo ""
|
|
173
|
+
run_global_list_running
|
|
174
|
+
fi
|
|
175
|
+
}
|
|
176
|
+
|
|
159
177
|
cmd_env() {
|
|
160
178
|
local sub="${1:-}"
|
|
161
179
|
shift || true
|
|
@@ -174,16 +192,182 @@ cmd_env() {
|
|
|
174
192
|
esac
|
|
175
193
|
}
|
|
176
194
|
|
|
177
|
-
|
|
178
|
-
|
|
195
|
+
cmd_update_usage() {
|
|
196
|
+
cat <<EOF
|
|
197
|
+
${_b}runctl update${_r} — refresh the global CLI (pnpm/npm global install)
|
|
198
|
+
|
|
199
|
+
${_y}Usage:${_r} runctl update [npm|git|auto] [options]
|
|
200
|
+
|
|
201
|
+
${_y}Source${_r} ${_d}(pick one; default when omitted: ${_c}auto${_r})${_r}
|
|
202
|
+
${_c}npm${_r} | ${_c}--registry${_r} Latest from npm only (${_d}${RUNCTL_PACKAGE:-@zendero/runctl}@latest${_r})
|
|
203
|
+
${_c}git${_r} | ${_c}--git${_r} From Git only (${_d}RUNCTL_GIT_BASE + ref${_r})
|
|
204
|
+
${_c}auto${_r} | ${_c}--auto${_r} Try npm @latest, then Git if that fails
|
|
205
|
+
|
|
206
|
+
${_y}Options${_r}
|
|
207
|
+
${_c}--pm${_r} ${_d}pnpm|npm${_r} Package manager ${_d}(default: pnpm if on PATH, else npm)${_r}
|
|
208
|
+
${_c}--ref${_r} ${_d}<ref>${_r} Git ref for --git / Git fallback ${_d}(default: RUNCTL_GIT_REF or main)${_r}
|
|
209
|
+
${_c}-h${_r}, ${_c}--help${_r} This help
|
|
210
|
+
|
|
211
|
+
${_y}Environment${_r} ${_d}(optional; same as install-global.sh)${_r}
|
|
212
|
+
RUNCTL_PACKAGE npm name ${_d}(default: @zendero/runctl)${_r}
|
|
213
|
+
RUNCTL_GIT_BASE Git URL without fragment ${_d}(default: git+https://github.com/DoctorKhan/runctl.git)${_r}
|
|
214
|
+
RUNCTL_GIT_REF default Git ref ${_d}(default: main)${_r}
|
|
215
|
+
EOF
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
cmd_update_pick_pm() {
|
|
219
|
+
if [[ -n "$1" ]]; then
|
|
220
|
+
case "$1" in
|
|
221
|
+
pnpm | npm) ;;
|
|
222
|
+
*)
|
|
223
|
+
echo "runctl update: unsupported --pm: $1 (use pnpm or npm)" >&2
|
|
224
|
+
return 1
|
|
225
|
+
;;
|
|
226
|
+
esac
|
|
227
|
+
if ! command -v "$1" >/dev/null 2>&1; then
|
|
228
|
+
echo "runctl update: package manager not on PATH: $1" >&2
|
|
229
|
+
return 1
|
|
230
|
+
fi
|
|
231
|
+
printf '%s' "$1"
|
|
232
|
+
return 0
|
|
233
|
+
fi
|
|
179
234
|
if command -v pnpm >/dev/null 2>&1; then
|
|
180
|
-
pnpm
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
235
|
+
printf 'pnpm'
|
|
236
|
+
return 0
|
|
237
|
+
fi
|
|
238
|
+
if command -v npm >/dev/null 2>&1; then
|
|
239
|
+
printf 'npm'
|
|
240
|
+
return 0
|
|
241
|
+
fi
|
|
242
|
+
echo "runctl update: pnpm or npm required on PATH." >&2
|
|
243
|
+
return 1
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
cmd_update() {
|
|
247
|
+
local MODE="" PM="" GIT_REF="${RUNCTL_GIT_REF:-main}"
|
|
248
|
+
local PKG="${RUNCTL_PACKAGE:-@zendero/runctl}"
|
|
249
|
+
local GIT_BASE="${RUNCTL_GIT_BASE:-git+https://github.com/DoctorKhan/runctl.git}"
|
|
250
|
+
local -a POS=()
|
|
251
|
+
|
|
252
|
+
while [[ $# -gt 0 ]]; do
|
|
253
|
+
case "$1" in
|
|
254
|
+
--registry)
|
|
255
|
+
MODE="registry"
|
|
256
|
+
shift
|
|
257
|
+
;;
|
|
258
|
+
--git)
|
|
259
|
+
MODE="git"
|
|
260
|
+
shift
|
|
261
|
+
;;
|
|
262
|
+
--auto)
|
|
263
|
+
MODE="auto"
|
|
264
|
+
shift
|
|
265
|
+
;;
|
|
266
|
+
--pm)
|
|
267
|
+
[[ $# -ge 2 ]] || {
|
|
268
|
+
echo "runctl update: --pm requires a value" >&2
|
|
269
|
+
exit 1
|
|
270
|
+
}
|
|
271
|
+
PM="$2"
|
|
272
|
+
shift 2
|
|
273
|
+
;;
|
|
274
|
+
--ref)
|
|
275
|
+
[[ $# -ge 2 ]] || {
|
|
276
|
+
echo "runctl update: --ref requires a value" >&2
|
|
277
|
+
exit 1
|
|
278
|
+
}
|
|
279
|
+
GIT_REF="$2"
|
|
280
|
+
shift 2
|
|
281
|
+
;;
|
|
282
|
+
-h | --help)
|
|
283
|
+
cmd_update_usage
|
|
284
|
+
exit 0
|
|
285
|
+
;;
|
|
286
|
+
-*)
|
|
287
|
+
echo "runctl update: unknown option: $1" >&2
|
|
288
|
+
cmd_update_usage >&2
|
|
289
|
+
exit 1
|
|
290
|
+
;;
|
|
291
|
+
*)
|
|
292
|
+
POS+=("$1")
|
|
293
|
+
shift
|
|
294
|
+
;;
|
|
295
|
+
esac
|
|
296
|
+
done
|
|
297
|
+
|
|
298
|
+
local POS_MODE=""
|
|
299
|
+
if [[ ${#POS[@]} -gt 1 ]]; then
|
|
300
|
+
echo "runctl update: too many arguments (${POS[*]}). Use at most one of: npm, git, auto" >&2
|
|
301
|
+
exit 1
|
|
302
|
+
fi
|
|
303
|
+
if [[ ${#POS[@]} -eq 1 ]]; then
|
|
304
|
+
case "${POS[0]}" in
|
|
305
|
+
npm)
|
|
306
|
+
POS_MODE="registry"
|
|
307
|
+
;;
|
|
308
|
+
git)
|
|
309
|
+
POS_MODE="git"
|
|
310
|
+
;;
|
|
311
|
+
auto)
|
|
312
|
+
POS_MODE="auto"
|
|
313
|
+
;;
|
|
314
|
+
*)
|
|
315
|
+
echo "runctl update: unknown argument: ${POS[0]} (expected: npm, git, auto — runctl update --help)" >&2
|
|
316
|
+
exit 1
|
|
317
|
+
;;
|
|
318
|
+
esac
|
|
319
|
+
fi
|
|
320
|
+
if [[ -n "$MODE" && -n "$POS_MODE" && "$MODE" != "$POS_MODE" ]]; then
|
|
321
|
+
echo "runctl update: conflicting source (npm/git/auto vs --registry/--git/--auto)" >&2
|
|
185
322
|
exit 1
|
|
186
323
|
fi
|
|
324
|
+
if [[ -n "$POS_MODE" ]]; then
|
|
325
|
+
MODE="$POS_MODE"
|
|
326
|
+
fi
|
|
327
|
+
[[ -n "$MODE" ]] || MODE="auto"
|
|
328
|
+
|
|
329
|
+
local pm git_target
|
|
330
|
+
pm="$(cmd_update_pick_pm "$PM")" || exit 1
|
|
331
|
+
git_target="${GIT_BASE}#${GIT_REF}"
|
|
332
|
+
|
|
333
|
+
_update_registry() {
|
|
334
|
+
local spec="${PKG}@latest"
|
|
335
|
+
echo "runctl update: installing latest from registry ($spec)..."
|
|
336
|
+
if [[ "$pm" == "pnpm" ]]; then
|
|
337
|
+
pnpm add -g "$spec"
|
|
338
|
+
else
|
|
339
|
+
npm install -g "$spec"
|
|
340
|
+
fi
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
_update_git() {
|
|
344
|
+
echo "runctl update: installing from Git ($git_target)..."
|
|
345
|
+
if [[ "$pm" == "pnpm" ]]; then
|
|
346
|
+
pnpm add -g "$git_target"
|
|
347
|
+
else
|
|
348
|
+
npm install -g "$git_target"
|
|
349
|
+
fi
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
case "$MODE" in
|
|
353
|
+
registry)
|
|
354
|
+
_update_registry
|
|
355
|
+
;;
|
|
356
|
+
git)
|
|
357
|
+
_update_git
|
|
358
|
+
;;
|
|
359
|
+
auto)
|
|
360
|
+
if _update_registry; then
|
|
361
|
+
return 0
|
|
362
|
+
fi
|
|
363
|
+
echo "runctl update: registry install failed; trying Git..." >&2
|
|
364
|
+
_update_git
|
|
365
|
+
;;
|
|
366
|
+
*)
|
|
367
|
+
echo "runctl update: internal error: unknown mode $MODE" >&2
|
|
368
|
+
exit 1
|
|
369
|
+
;;
|
|
370
|
+
esac
|
|
187
371
|
}
|
|
188
372
|
|
|
189
373
|
cmd_doctor() {
|
|
@@ -283,11 +467,12 @@ main() {
|
|
|
283
467
|
open) cmd_open "$@" ;;
|
|
284
468
|
stop) cmd_stop "$@" ;;
|
|
285
469
|
status) cmd_status "$@" ;;
|
|
470
|
+
ps) cmd_ps "$@" ;;
|
|
286
471
|
logs) cmd_logs "$@" ;;
|
|
287
472
|
ports) cmd_ports "$@" ;;
|
|
288
473
|
env) cmd_env "$@" ;;
|
|
289
474
|
doctor) cmd_doctor "$@" ;;
|
|
290
|
-
update) cmd_update ;;
|
|
475
|
+
update) cmd_update "$@" ;;
|
|
291
476
|
|
|
292
477
|
# Plumbing
|
|
293
478
|
lib-path) printf '%s\n' "$RUNCTL_PKG_ROOT/lib/run-lib.sh" ;;
|
package/lib/run-lib.sh
CHANGED
|
@@ -501,6 +501,64 @@ run_global_list_ports() {
|
|
|
501
501
|
rm -f "$tmp"
|
|
502
502
|
}
|
|
503
503
|
|
|
504
|
+
run_pid_program_name() {
|
|
505
|
+
local pid="$1"
|
|
506
|
+
[[ -n "$pid" ]] || return 1
|
|
507
|
+
ps -p "$pid" -o comm= 2>/dev/null | awk '{$1=$1; print}'
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
run_global_list_running() {
|
|
511
|
+
mkdir -p "$RUN_GLOBAL_STATE/ports"
|
|
512
|
+
local f port tmp pid svc proot _line _k _v prog stale_count
|
|
513
|
+
stale_count=0
|
|
514
|
+
tmp="$(mktemp "${TMPDIR:-/tmp}/runlib-running.XXXXXX")"
|
|
515
|
+
shopt -s nullglob
|
|
516
|
+
for f in "$RUN_GLOBAL_STATE/ports"/*; do
|
|
517
|
+
[[ -f "$f" ]] || continue
|
|
518
|
+
port="$(basename "$f")"
|
|
519
|
+
[[ "$port" =~ ^[0-9]+$ ]] || continue
|
|
520
|
+
pid="" svc="" proot="" prog=""
|
|
521
|
+
while IFS= read -r _line; do
|
|
522
|
+
[[ -z "$_line" ]] && continue
|
|
523
|
+
_k="${_line%%=*}"
|
|
524
|
+
_v="${_line#*=}"
|
|
525
|
+
case "$_k" in
|
|
526
|
+
pid) pid="$_v" ;;
|
|
527
|
+
service) svc="$_v" ;;
|
|
528
|
+
project_root) proot="$_v" ;;
|
|
529
|
+
esac
|
|
530
|
+
done <"$f"
|
|
531
|
+
if [[ -z "$pid" ]]; then
|
|
532
|
+
stale_count=$((stale_count + 1))
|
|
533
|
+
continue
|
|
534
|
+
fi
|
|
535
|
+
if ! run_pid_alive "$pid"; then
|
|
536
|
+
stale_count=$((stale_count + 1))
|
|
537
|
+
continue
|
|
538
|
+
fi
|
|
539
|
+
if ! run_pid_listens_on_port "$pid" "$port"; then
|
|
540
|
+
stale_count=$((stale_count + 1))
|
|
541
|
+
continue
|
|
542
|
+
fi
|
|
543
|
+
prog="$(run_pid_program_name "$pid" || true)"
|
|
544
|
+
[[ -n "$prog" ]] || prog="(unknown)"
|
|
545
|
+
printf '%s\t%s\t%s\t%s\t%s\n' "$pid" "$prog" "$port" "${svc:--}" "${proot:--}" >>"$tmp"
|
|
546
|
+
done
|
|
547
|
+
shopt -u nullglob
|
|
548
|
+
printf '%-8s %-20s %-6s %-10s %s\n' "PID" "PROGRAM" "PORT" "SERVICE" "PROJECT"
|
|
549
|
+
printf '%-8s %-20s %-6s %-10s %s\n' "--------" "--------------------" "------" "----------" "-------"
|
|
550
|
+
if [[ -s "$tmp" ]]; then
|
|
551
|
+
sort -n -k1,1 "$tmp" | while IFS=$'\t' read -r pid prog port svc proot; do
|
|
552
|
+
printf '%-8s %-20s %-6s %-10s %s\n' "$pid" "$prog" "$port" "$svc" "$proot"
|
|
553
|
+
done
|
|
554
|
+
else
|
|
555
|
+
printf '%s\n' "(no running programs)"
|
|
556
|
+
fi
|
|
557
|
+
rm -f "$tmp"
|
|
558
|
+
RUN_LAST_STALE_COUNT="$stale_count"
|
|
559
|
+
export RUN_LAST_STALE_COUNT
|
|
560
|
+
}
|
|
561
|
+
|
|
504
562
|
run_local_status() {
|
|
505
563
|
printf 'runctl status\n'
|
|
506
564
|
printf ' %-10s %s\n' "project:" "$RUN_PROJECT_ROOT"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zendero/runctl",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
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,6 +37,7 @@
|
|
|
37
37
|
],
|
|
38
38
|
"scripts": {
|
|
39
39
|
"run": "bash ./run.sh",
|
|
40
|
+
"test": "bash ./tests/runctl-ps.test.sh",
|
|
40
41
|
"doctor": "bash ./run.sh doctor",
|
|
41
42
|
"release-check": "bash ./run.sh release-check",
|
|
42
43
|
"promote": "bash ./run.sh promote",
|