@runfile/cli 0.25.0 → 0.26.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/bin/darwin-arm64/README.md +120 -58
- package/bin/darwin-arm64/run +0 -0
- package/bin/darwin-x64/README.md +120 -58
- package/bin/darwin-x64/run +0 -0
- package/bin/linux-arm64/README.md +120 -58
- package/bin/linux-arm64/run +0 -0
- package/bin/linux-x64/README.md +120 -58
- package/bin/linux-x64/run +0 -0
- package/bin/win32-arm64/run.exe +0 -0
- package/bin/win32-x64/run.exe +0 -0
- package/package.json +1 -1
package/bin/linux-x64/README.md
CHANGED
|
@@ -17,7 +17,7 @@ $ run dev --port=4000
|
|
|
17
17
|
"dev": {
|
|
18
18
|
"description": "Starts the dev server",
|
|
19
19
|
"commands": "vite",
|
|
20
|
-
"env": { "PORT": "{{
|
|
20
|
+
"env": { "PORT": "{{ ARG.port ? '3000' }}" }
|
|
21
21
|
},
|
|
22
22
|
"build": {
|
|
23
23
|
"description": "Type-checks and builds in parallel",
|
|
@@ -25,11 +25,11 @@ $ run dev --port=4000
|
|
|
25
25
|
"@type-check",
|
|
26
26
|
"vite build",
|
|
27
27
|
{
|
|
28
|
-
"if": "{{ RUN.os == 'windows' &&
|
|
28
|
+
"if": "{{ RUN.os == 'windows' && FLAG.wsl }}",
|
|
29
29
|
"then": "wsl --shell-type login -- vite build"
|
|
30
30
|
}
|
|
31
31
|
],
|
|
32
|
-
"envFiles": [".env", ".env.{{
|
|
32
|
+
"envFiles": [".env", ".env.{{ ARG.env ? 'development' }}"],
|
|
33
33
|
"parallel": true
|
|
34
34
|
},
|
|
35
35
|
"type-check": {
|
|
@@ -190,21 +190,24 @@ Positional, named, flags, env vars, and runtime context (`{{ RUN.os }}` / `{{ RU
|
|
|
190
190
|
`{{ RUN.cwd }}` / `{{ RUN.file }}` / `{{ RUN.parent }}`) — with chained fallbacks and required values. The
|
|
191
191
|
`{{ ... }}` syntax avoids collisions with shell `$(...)` command substitution.
|
|
192
192
|
|
|
193
|
+
The source prefixes are `{{ ARG.<name> }}` (named arg), `{{ ENV.<name> }}`, `{{ FLAG.<name> }}`,
|
|
194
|
+
`{{ VAR.<name> }}`, and `{{ RUN.<key> }}`. Bare `{{ ARGS }}` expands to **all** positional arguments.
|
|
195
|
+
|
|
193
196
|
```jsonc
|
|
194
|
-
"PORT": "{{
|
|
195
|
-
"OPTS": "{{
|
|
197
|
+
"PORT": "{{ ARG.port ? ENV.PORT ? '3000' }}",
|
|
198
|
+
"OPTS": "{{ FLAG.release ? '--release' : }} {{ FLAG.verbose ? '-v' : }}",
|
|
196
199
|
"CARGO_TARGET_DIR": "target-{{ RUN.os }}",
|
|
197
200
|
// Inline OS/shell branches — the boolean DSL goes inside a single `{{ ... }}` block:
|
|
198
201
|
{ "if": "{{ RUN.os == 'windows' }}", "then": ["del /S /Q build"], "else": ["rm -rf build"] },
|
|
199
|
-
// User-supplied flags branch through `{{
|
|
200
|
-
{ "if": "{{
|
|
202
|
+
// User-supplied flags branch through `{{ FLAG.x }}` (resolves to "true" / "false"):
|
|
203
|
+
{ "if": "{{ FLAG.debug }}", "then": ["./tool --verbose"], "else": ["./tool"] },
|
|
201
204
|
// workingDirectory is a free-form path (defaults to {{ RUN.parent }}):
|
|
202
|
-
"workingDirectory": "{{
|
|
205
|
+
"workingDirectory": "{{ ARG.workdir ? RUN.cwd }}"
|
|
203
206
|
```
|
|
204
207
|
|
|
205
208
|
Strict format: exactly one space after `{{` and before `}}`, exactly one space around `?` and `:` operators.
|
|
206
209
|
**String literals must be wrapped in single quotes** — `'production'`, not `production`. Source references
|
|
207
|
-
(`
|
|
210
|
+
(`ARG.x`, `VAR.x`, etc.) and function calls remain bare. Use `\{{` / `\}}` to emit a literal `{{` / `}}` in
|
|
208
211
|
your output.
|
|
209
212
|
|
|
210
213
|
#### Boolean conditions inside substitutions
|
|
@@ -216,41 +219,41 @@ but you can use it anywhere:
|
|
|
216
219
|
```jsonc
|
|
217
220
|
"commands": [
|
|
218
221
|
// Branch on a CLI flag — `if` checks if the substitution resolves to the literal "true":
|
|
219
|
-
{ "if": "{{
|
|
222
|
+
{ "if": "{{ ARG.env == 'production' }}",
|
|
220
223
|
"then": ["./deploy-prod.sh"],
|
|
221
224
|
"else": ["./deploy-staging.sh"] },
|
|
222
225
|
|
|
223
226
|
// Compose AND/OR/NOT freely:
|
|
224
|
-
{ "if": "{{
|
|
227
|
+
{ "if": "{{ ARG.env != 'development' && ARG.env != 'production' }}",
|
|
225
228
|
"then": ["./deploy-staging.sh"] },
|
|
226
229
|
|
|
227
230
|
// Invert any boolean-returning value with unary `!` — including the
|
|
228
231
|
// comparison helpers (`less_than`, `is_number`, …) and `contains` etc.:
|
|
229
|
-
{ "if": "{{ !is_number(
|
|
232
|
+
{ "if": "{{ !is_number(ARG.port) }}",
|
|
230
233
|
"then": ["echo 'port must be a number' && exit 1"] },
|
|
231
234
|
|
|
232
|
-
//
|
|
233
|
-
{ "if": "{{ RUN.os == 'windows' &&
|
|
235
|
+
// FLAG.x works as a bare boolean inside DSL — no `== 'true'` needed:
|
|
236
|
+
{ "if": "{{ RUN.os == 'windows' && FLAG.wsl }}",
|
|
234
237
|
"then": "wsl --shell-type login -- vite build" },
|
|
235
238
|
|
|
236
239
|
// Inline in a command — useful for passing booleans to other tools:
|
|
237
|
-
"my-command --resolve {{
|
|
240
|
+
"my-command --resolve {{ ARG.env == 'production' }}"
|
|
238
241
|
]
|
|
239
242
|
```
|
|
240
243
|
|
|
241
|
-
**Strict boolean rule.** Any value used as a bare boolean — both inside the DSL Truthy check (e.g. `&&
|
|
242
|
-
`!
|
|
244
|
+
**Strict boolean rule.** Any value used as a bare boolean — both inside the DSL Truthy check (e.g. `&& FLAG.x`,
|
|
245
|
+
`!ARG.y`) and as the entire `if` condition — must resolve to exactly one of:
|
|
243
246
|
|
|
244
247
|
- `"true"` → truthy (the `then` branch runs / left arm of `&&` continues)
|
|
245
248
|
- `"false"` → falsy
|
|
246
249
|
- `""` (empty) → falsy
|
|
247
250
|
|
|
248
251
|
Anything else (`"True"`, `"1"`, `"yes"`, `"hello"`, etc.) errors out with a clear message pointing you toward
|
|
249
|
-
the explicit comparison form. So `if: "{{
|
|
250
|
-
exactly `"true"` / `"false"` / empty — for any other check, use a comparison: `{{
|
|
252
|
+
the explicit comparison form. So `if: "{{ ARG.x }}"` and `{{ ARG.x && ... }}` only work when `ARG.x` is
|
|
253
|
+
exactly `"true"` / `"false"` / empty — for any other check, use a comparison: `{{ ARG.x == 'yes' }}`.
|
|
251
254
|
|
|
252
255
|
Comparisons (`==` / `!=`) operate on raw strings — *those* don't have the boolean restriction, so
|
|
253
|
-
`{{
|
|
256
|
+
`{{ ARG.env == 'staging' }}` works for any value of `ARG.env`.
|
|
254
257
|
|
|
255
258
|
#### Functions: transform values inline
|
|
256
259
|
|
|
@@ -262,30 +265,32 @@ and work as full substitution bodies *or* as chain segments:
|
|
|
262
265
|
// Built-ins:
|
|
263
266
|
// case : to_upper, to_lower, capitalize
|
|
264
267
|
// trim : trim, trim_start, trim_end
|
|
265
|
-
// inspect : length, starts_with, ends_with, contains
|
|
268
|
+
// inspect : length, starts_with, ends_with, contains, substring
|
|
266
269
|
// transform : escape, repeat, replace_all, remove_all
|
|
267
270
|
// regex : regex_replace, regex_remove, regex_matches, regex_capture, regex_capture_all
|
|
268
271
|
// build : concat, join
|
|
269
272
|
// split : nth, first, last, count_parts
|
|
270
|
-
//
|
|
273
|
+
// path : basename, dirname, extname, stem, join_path
|
|
274
|
+
// math : add, subtract, multiply, divide, modulo, power, min, max, abs, round, floor, ceil
|
|
271
275
|
// compare : less_than, less_than_or_equal, greater_than, greater_than_or_equal, is_number
|
|
272
276
|
// validate : one_of
|
|
273
|
-
// encoding : base64_encode, base64_decode
|
|
277
|
+
// encoding : base64_encode, base64_decode, url_encode, url_decode
|
|
274
278
|
// hashing : sha256, md5
|
|
279
|
+
// ids/time : uuid, now
|
|
275
280
|
// files : read_file, write_file, file_exists
|
|
276
281
|
// json : json_get, json_set
|
|
277
|
-
//
|
|
282
|
+
// control : try, error
|
|
278
283
|
// shell : shell_quote, capture
|
|
279
284
|
// variables : define
|
|
280
285
|
// cwd : set_cwd
|
|
281
|
-
"echo deploying-{{ to_upper(
|
|
286
|
+
"echo deploying-{{ to_upper(ARG.env) }}",
|
|
282
287
|
"curl -H \"X-Auth: {{ base64_encode(ENV.TOKEN) }}\" ...",
|
|
283
|
-
"echo {{ concat('hello-',
|
|
284
|
-
"echo {{ join(' AND ', 'flag-1', 'flag-2',
|
|
288
|
+
"echo {{ concat('hello-', ARG.name, '-2026') }}",
|
|
289
|
+
"echo {{ join(' AND ', 'flag-1', 'flag-2', ARG.extra) }}",
|
|
285
290
|
// Tokenise-and-rejoin-style transforms via replace_all:
|
|
286
|
-
"go test {{ replace_all(
|
|
291
|
+
"go test {{ replace_all(ARG.flags, ' ', ' -tag=') }}",
|
|
287
292
|
// Strip every match of a regex (here: collapse whitespace runs to a single space):
|
|
288
|
-
"echo {{ regex_replace(
|
|
293
|
+
"echo {{ regex_replace(ARG.text, '\\s+', ' ') }}",
|
|
289
294
|
// Pull a capture group out of the first regex match — no `(?s)^.*X(...)X.*$`
|
|
290
295
|
// greedy-replace trick needed. Group 0 is the whole match; group N is the
|
|
291
296
|
// N-th `(...)`. Out-of-bounds returns "" (same convention as `nth`).
|
|
@@ -295,14 +300,14 @@ and work as full substitution bodies *or* as chain segments:
|
|
|
295
300
|
"echo deps={{ regex_capture_all(read_file('deps.txt'), 'name = \"([^\"]+)\"', '1', ',') }}",
|
|
296
301
|
// Split-by-separator scalar accessors — string in, string out, no list type:
|
|
297
302
|
// basename idiom (last segment after `/`); empty string when input ends in `/`.
|
|
298
|
-
"echo basename={{ last(
|
|
303
|
+
"echo basename={{ last(ARG.path, '/') }}",
|
|
299
304
|
// Pull out the N-th comma-separated field; out-of-bounds returns "".
|
|
300
|
-
"echo target={{ nth(
|
|
305
|
+
"echo target={{ nth(ARG.csv, ',', '1') }}",
|
|
301
306
|
// Pair `count_parts` with `nth` to bound-check before indexing:
|
|
302
|
-
// "if": "{{ count_parts(
|
|
307
|
+
// "if": "{{ count_parts(ARG.csv, ',') == '3' }}"
|
|
303
308
|
// Boolean-returning helpers are valid DSL `Truthy` values — use them in `if`:
|
|
304
|
-
// "if": "{{ starts_with(
|
|
305
|
-
// "if": "{{ regex_matches(
|
|
309
|
+
// "if": "{{ starts_with(ARG.path, '/usr') }}"
|
|
310
|
+
// "if": "{{ regex_matches(ARG.tag, '^v[0-9]+\\.[0-9]+$') }}"
|
|
306
311
|
|
|
307
312
|
// Safely inline arbitrary content (newlines, quotes, JSON) as a CLI arg —
|
|
308
313
|
// `shell_quote` picks the right quoting for the active shell:
|
|
@@ -320,17 +325,36 @@ and work as full substitution bodies *or* as chain segments:
|
|
|
320
325
|
// non-numeric input. The result is formatted as an integer when whole and
|
|
321
326
|
// as a decimal otherwise (so `add('5', '3')` → "8", `add('5', '1.1')` →
|
|
322
327
|
// "6.1"). Divide-by-zero errors out.
|
|
323
|
-
"echo next-build={{ add(
|
|
324
|
-
"echo half={{ divide(
|
|
328
|
+
"echo next-build={{ add(VAR.versionCode, '1') }}",
|
|
329
|
+
"echo half={{ divide(VAR.total, '2') }}",
|
|
330
|
+
// …plus modulo, power, min, max, abs, round, floor, ceil:
|
|
331
|
+
"echo shard={{ modulo(ARG.id, '8') }} clamped={{ max('0', min('100', ARG.pct)) }}",
|
|
332
|
+
|
|
333
|
+
// Path helpers (std::path semantics). basename/dirname/extname/stem/join_path:
|
|
334
|
+
"echo name={{ basename(ARG.file) }} ext={{ extname(ARG.file) }}",
|
|
335
|
+
"cp {{ ARG.file }} {{ join_path(ARG.outdir, basename(ARG.file)) }}",
|
|
336
|
+
|
|
337
|
+
// Substring (char-indexed; 3rd arg = length, optional → to end). Short SHA:
|
|
338
|
+
"echo short={{ substring(capture('git rev-parse HEAD'), '0', '7') }}",
|
|
339
|
+
|
|
340
|
+
// Current UTC time. Formats: unix-timestamp, unix-millis, iso, iso-date,
|
|
341
|
+
// iso-time, rfc3339, year/month/day/hour/minute/second.
|
|
342
|
+
"echo built-at={{ now('iso') }} tag=build-{{ now('unix-timestamp') }}",
|
|
343
|
+
|
|
344
|
+
// UUID (v4-shaped) for unique temp / cache names. `--dry-run` → `<uuid>`.
|
|
345
|
+
"echo tmp=/tmp/job-{{ uuid() }}",
|
|
346
|
+
|
|
347
|
+
// URL percent-encode / decode (space → %20; symmetric round-trip):
|
|
348
|
+
"curl 'https://api/search?q={{ url_encode(ARG.query) }}'",
|
|
325
349
|
|
|
326
350
|
// Numeric comparisons — coerce both args to numbers (same rules as the
|
|
327
351
|
// arithmetic family, so non-numeric input errors) and return "true"/"false",
|
|
328
352
|
// so they work as DSL `Truthy` values in `if` conditions:
|
|
329
|
-
// "if": "{{ greater_than(
|
|
330
|
-
// "if": "{{ less_than_or_equal(
|
|
353
|
+
// "if": "{{ greater_than(VAR.count, '50') }}"
|
|
354
|
+
// "if": "{{ less_than_or_equal(ARG.retries, '3') }}"
|
|
331
355
|
// `is_number` tests whether a value parses as a (finite) number — it returns
|
|
332
356
|
// "false" instead of erroring on non-numeric input:
|
|
333
|
-
// "if": "{{ is_number(
|
|
357
|
+
// "if": "{{ is_number(ARG.port) }}"
|
|
334
358
|
|
|
335
359
|
// Validate a value against a fixed allow-list. Returns the value on
|
|
336
360
|
// match; lists every valid option in the error on mismatch. Collapses
|
|
@@ -366,29 +390,35 @@ and work as full substitution bodies *or* as chain segments:
|
|
|
366
390
|
|
|
367
391
|
// `try(expr)` swallows inner errors. Standalone returns "" on failure;
|
|
368
392
|
// chained, the next segment runs as a fallback.
|
|
369
|
-
"echo {{ try(base64_decode(
|
|
393
|
+
"echo {{ try(base64_decode(ARG.maybe_b64)) ? ARG.maybe_b64 }}",
|
|
394
|
+
|
|
395
|
+
// `error('message')` fails the current command on purpose: prints the
|
|
396
|
+
// message to stderr and marks the step failed — but the failure flows
|
|
397
|
+
// through the normal walker, so `when: failure` / `when: always` steps
|
|
398
|
+
// still run and `ignoreErrors` still suppresses it. Great for guard clauses:
|
|
399
|
+
// { "if": "{{ ARG.env == 'prod' }}", "then": ["{{ error('refusing to deploy to prod from a laptop') }}"] }
|
|
370
400
|
|
|
371
401
|
// Nested:
|
|
372
|
-
"echo {{ to_upper(to_lower(
|
|
402
|
+
"echo {{ to_upper(to_lower(ARG.x)) }}",
|
|
373
403
|
|
|
374
404
|
// As a chain fallback:
|
|
375
|
-
"echo host={{
|
|
405
|
+
"echo host={{ ARG.host ? to_lower(ENV.HOST) }}",
|
|
376
406
|
|
|
377
|
-
// Capture a value with `define(...)` and read it later via `
|
|
407
|
+
// Capture a value with `define(...)` and read it later via `VAR.<name>`:
|
|
378
408
|
"{{ define(sdk, ENV.SDK ? '/opt/sdk') }}",
|
|
379
|
-
"{{
|
|
380
|
-
"echo using sdk at {{
|
|
409
|
+
"{{ VAR.sdk }}/bin/build",
|
|
410
|
+
"echo using sdk at {{ VAR.sdk }}",
|
|
381
411
|
|
|
382
412
|
// `set_cwd(path)` switches the cwd subsequent commands spawn in — like
|
|
383
413
|
// shell `cd`, but works on every shell / OS without forking. Relative
|
|
384
414
|
// paths chain (matching `cd a; cd b` → `a/b`); absolute paths replace.
|
|
385
|
-
"{{ set_cwd(
|
|
415
|
+
"{{ set_cwd(ARG.subproject ? 'packages/api') }}",
|
|
386
416
|
"npm install",
|
|
387
417
|
"npm run build",
|
|
388
418
|
|
|
389
419
|
// Single-quoted strings interpolate nested {{ }} substitutions:
|
|
390
|
-
"{{ define(cmd, 'docker compose -f {{
|
|
391
|
-
"{{
|
|
420
|
+
"{{ define(cmd, 'docker compose -f {{ VAR.compose }} pull') }}",
|
|
421
|
+
"{{ VAR.cmd }}"
|
|
392
422
|
]
|
|
393
423
|
```
|
|
394
424
|
|
|
@@ -397,6 +427,38 @@ to only whitespace (i.e. one consisting solely of a `{{ define(...) }}` call) is
|
|
|
397
427
|
being dispatched to the shell. `define`s in a parent target are visible to `@target` children. The `name` MUST
|
|
398
428
|
be a bareword identifier matching `[A-Za-z_][A-Za-z0-9_-]*` — quotes are NOT allowed on the name.
|
|
399
429
|
|
|
430
|
+
#### Declaring variables up front (`vars`)
|
|
431
|
+
|
|
432
|
+
Instead of (or alongside) `define(...)`, you can declare variables directly on a target — or on `globals` to
|
|
433
|
+
apply them to every target — and read them as `{{ VAR.<key> }}`. This mirrors `env`: each value is a `{{ ... }}`
|
|
434
|
+
template resolved **after** the env is built (so it can reference `{{ ENV.* }}`), plus `{{ ARG.* }}`,
|
|
435
|
+
`{{ FLAG.* }}`, `{{ RUN.* }}`, and earlier vars. If a reference has no default and isn't supplied, it errors —
|
|
436
|
+
exactly like everywhere else.
|
|
437
|
+
|
|
438
|
+
```jsonc
|
|
439
|
+
{
|
|
440
|
+
"globals": {
|
|
441
|
+
"vars": { "appName": "skiley" } // available to every target
|
|
442
|
+
},
|
|
443
|
+
"targets": {
|
|
444
|
+
"deploy": {
|
|
445
|
+
"env": { "REGION": "us-east-1" },
|
|
446
|
+
"vars": {
|
|
447
|
+
"region": "{{ ENV.REGION }}", // vars resolve after env
|
|
448
|
+
"retries": "3", // numbers/bools are stringified
|
|
449
|
+
"tag": "{{ ARG.tag ? 'latest' }}" // chain fallbacks work
|
|
450
|
+
},
|
|
451
|
+
"commands": ["echo deploying {{ VAR.appName }} to {{ VAR.region }} as {{ VAR.tag }}"]
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
Global `vars` are merged into each target's `vars` at parse time (target keys win on conflict). Declared vars are
|
|
458
|
+
**scoped per-target like `env`**: a parent's vars are visible inside an `@target` dependency, but a dependency's
|
|
459
|
+
own declared vars don't leak back to the parent. A runtime `define(...)` of the same name overrides the declared
|
|
460
|
+
value for the rest of the target. Var keys must match `[A-Za-z_][A-Za-z0-9_-]*`.
|
|
461
|
+
|
|
400
462
|
`set_cwd(path)` is the cwd analog of `define`: returns an empty string and changes the cwd that subsequent shell
|
|
401
463
|
commands in the *current target* spawn in. Behaves like shell `cd`, but works uniformly across every shell / OS
|
|
402
464
|
(no forked process, no `cd` binary needed). Resolution rules:
|
|
@@ -419,12 +481,12 @@ Function args are separated by `, ` (comma + exactly one space).
|
|
|
419
481
|
|
|
420
482
|
- **Single quotes (`'...'`) — interpolated string**: surrounding quotes stripped, with any nested `{{ ... }}`
|
|
421
483
|
resolved through the regular substitution machinery. Use these for almost every literal — they handle the
|
|
422
|
-
full range of values including embedded substitutions: `concat('a, b',
|
|
423
|
-
`'/var/log/{{ RUN.os }}.log'`, `define(cmd, 'docker -f {{
|
|
484
|
+
full range of values including embedded substitutions: `concat('a, b', ARG.x)`,
|
|
485
|
+
`'/var/log/{{ RUN.os }}.log'`, `define(cmd, 'docker -f {{ VAR.compose }} pull')`.
|
|
424
486
|
- **Double quotes (`"..."`) — fully literal value**: the quote characters are part of the value (so `"test"` is
|
|
425
487
|
the 6-character string `"test"`). No interpolation. Useful when the literal you want to emit really should
|
|
426
488
|
contain `"` chars.
|
|
427
|
-
- **Plain barewords are rejected** — `{{
|
|
489
|
+
- **Plain barewords are rejected** — `{{ ARG.env ? development }}` is a parse-time error. Wrap in quotes.
|
|
428
490
|
|
|
429
491
|
#### Conditionals and loops, no shell required
|
|
430
492
|
|
|
@@ -433,11 +495,11 @@ evaluated by Runfile itself, so the logic works the same on every shell and plat
|
|
|
433
495
|
|
|
434
496
|
```jsonc
|
|
435
497
|
"commands": [
|
|
436
|
-
{ "if": "{{
|
|
498
|
+
{ "if": "{{ ARG.env == 'production' }}",
|
|
437
499
|
"then": ["./deploy-prod.sh"],
|
|
438
500
|
"else": ["./deploy-staging.sh"] },
|
|
439
501
|
|
|
440
|
-
{ "match": "{{
|
|
502
|
+
{ "match": "{{ ARG.tier ? '1' }}", // chain default; case "1" runs when --tier missing
|
|
441
503
|
"cases": {
|
|
442
504
|
"1": "flutter emulators --launch Tier_1_Android_9",
|
|
443
505
|
"2": "flutter emulators --launch Tier_2_Android_11",
|
|
@@ -451,12 +513,12 @@ evaluated by Runfile itself, so the logic works the same on every shell and plat
|
|
|
451
513
|
{ "for": "service",
|
|
452
514
|
"in": ["api", "web", "worker"],
|
|
453
515
|
"parallel": true,
|
|
454
|
-
//
|
|
455
|
-
"do": ["echo [{{
|
|
516
|
+
// VAR.<name>_index exposes the 0-based iteration counter.
|
|
517
|
+
"do": ["echo [{{ VAR.service_index }}] docker build -t {{ VAR.service }} services/{{ VAR.service }}"] },
|
|
456
518
|
|
|
457
519
|
{ "for": "file",
|
|
458
520
|
"shell": "git diff --name-only HEAD~1",
|
|
459
|
-
"do": ["clang-format -i {{
|
|
521
|
+
"do": ["clang-format -i {{ VAR.file }}"] }
|
|
460
522
|
]
|
|
461
523
|
```
|
|
462
524
|
|
|
@@ -476,7 +538,7 @@ namespace.
|
|
|
476
538
|
"@test --coverage",
|
|
477
539
|
"@build {{ ARGS }}",
|
|
478
540
|
{ "if": "{{ RUN.os == 'windows' }}", "then": "@deploy-win", "else": "@deploy-unix" },
|
|
479
|
-
{ "for": "ns", "in": "namespaces", "do": "@?{{
|
|
541
|
+
{ "for": "ns", "in": "namespaces", "do": "@?{{ VAR.ns }}:adb-forward" } // skip namespaces without the target
|
|
480
542
|
]
|
|
481
543
|
```
|
|
482
544
|
|
|
@@ -489,7 +551,7 @@ lines instead of corrupting each other's output. The label shows the full resolv
|
|
|
489
551
|
(`[@dev --port 5000]`) for target-call branches, or the raw command truncated to 12 characters (`[docker compo]`)
|
|
490
552
|
for shell branches; each label gets one of six cycling colors so adjacent branches stay distinct.
|
|
491
553
|
SGR colors flow through unchanged. The prefix propagates through `@target` invocations too,
|
|
492
|
-
so monorepo fan-outs like `{ "for": "ns", "in": "namespaces", "do": "@{{
|
|
554
|
+
so monorepo fan-outs like `{ "for": "ns", "in": "namespaces", "do": "@{{ VAR.ns }}:dev" }` tag every nested shell
|
|
493
555
|
with its branch identity. Set `RUNFILE_NO_LINE_PREFIX=1` to opt out and inherit raw stdio.
|
|
494
556
|
|
|
495
557
|
```text
|
package/bin/linux-x64/run
CHANGED
|
Binary file
|
package/bin/win32-arm64/run.exe
CHANGED
|
Binary file
|
package/bin/win32-x64/run.exe
CHANGED
|
Binary file
|
package/package.json
CHANGED