@runfile/cli 0.15.1 → 0.17.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.
@@ -146,7 +146,7 @@ AES-256-GCM secrets you can commit to git. Auto-decrypted at runtime, never writ
146
146
  tool, no wrapper.
147
147
 
148
148
  ```bash
149
- $ run :env init -p .env.production
149
+ $ run :env init .env.production
150
150
  $ run :env set .env.production DB_PASS "s3cr3t" # auto-encrypted
151
151
  $ run :env set .env.production DB_PASS # omit value → read from stdin (no shell history, no escaping)
152
152
  ```
@@ -160,9 +160,17 @@ You can also use Runfile as a drop-in `dotenvx`/`dotenv` replacement to inject e
160
160
  any command — no Runfile needed:
161
161
 
162
162
  ```bash
163
- $ run :env inject -- node app.js # uses .env by default
164
- $ run :env inject -f .env -f .env.local -- pnpm dev # multiple files, last wins; parent env always wins
165
- $ run :env inject -f .env.production -- ./deploy.sh # encrypted values auto-decrypted
163
+ $ run :env inject .env -- node app.js # one file
164
+ $ run :env inject .env .env.local -- pnpm dev # multiple files, last wins; parent env always wins
165
+ $ run :env inject .env.production -- ./deploy.sh # encrypted values auto-decrypted
166
+ ```
167
+
168
+ In CI, the file path can be supplied via `RUNFILE_ENV_FILE_TARGET` (set automatically by the
169
+ [setup action's `env-file-source` input](.github/actions/setup/action.yml) for open-source
170
+ repos that keep their encrypted `.env` in a GitHub secret):
171
+
172
+ ```bash
173
+ $ run :env inject -- node app.js # uses $RUNFILE_ENV_FILE_TARGET
166
174
  ```
167
175
 
168
176
  #### Powerful argument substitution
@@ -240,15 +248,17 @@ and work as full substitution bodies *or* as chain segments:
240
248
  // trim : trim, trim_start, trim_end
241
249
  // inspect : length, starts_with, ends_with, contains
242
250
  // transform : escape, repeat, replace_all, remove_all
243
- // regex : regex_replace, regex_remove, regex_matches
251
+ // regex : regex_replace, regex_remove, regex_matches, regex_capture
244
252
  // build : concat, join
245
253
  // split : nth, first, last, count_parts
254
+ // math : add, subtract, multiply, divide
255
+ // validate : one_of
246
256
  // encoding : base64_encode, base64_decode
247
257
  // hashing : sha256, md5
248
258
  // files : read_file, file_exists
249
259
  // json : json_get, json_set
250
260
  // error : try
251
- // shell : shell_quote
261
+ // shell : shell_quote, capture
252
262
  // variables : define
253
263
  // cwd : set_cwd
254
264
  "echo deploying-{{ to_upper(ARGS.env) }}",
@@ -259,6 +269,10 @@ and work as full substitution bodies *or* as chain segments:
259
269
  "go test {{ replace_all(ARGS.flags, ' ', ' -tag=') }}",
260
270
  // Strip every match of a regex (here: collapse whitespace runs to a single space):
261
271
  "echo {{ regex_replace(ARGS.text, '\\s+', ' ') }}",
272
+ // Pull a capture group out of the first regex match — no `(?s)^.*X(...)X.*$`
273
+ // greedy-replace trick needed. Group 0 is the whole match; group N is the
274
+ // N-th `(...)`. Out-of-bounds returns "" (same convention as `nth`).
275
+ "echo version={{ regex_capture(read_file('app/build.gradle.kts'), 'versionName = \"([^\"]+)\"', '1') }}",
262
276
  // Split-by-separator scalar accessors — string in, string out, no list type:
263
277
  // basename idiom (last segment after `/`); empty string when input ends in `/`.
264
278
  "echo basename={{ last(ARGS.path, '/') }}",
@@ -274,6 +288,26 @@ and work as full substitution bodies *or* as chain segments:
274
288
  // `shell_quote` picks the right quoting for the active shell:
275
289
  "some-tool --json {{ shell_quote(base64_decode(ENV.SECRET_BASE64)) }}",
276
290
 
291
+ // Slurp shell-command stdout straight into a substitution. `capture` runs
292
+ // the command through the platform's default shell (sh / cmd) and trims
293
+ // the trailing newline. Results are memoized per-target so the same
294
+ // capture in multiple commands runs once. `--dry-run` substitutes a
295
+ // readable placeholder instead of spawning the shell.
296
+ "echo built-at={{ capture('date -u +%Y-%m-%dT%H:%M:%SZ') }}",
297
+ "{{ define(sha, capture('git rev-parse HEAD')) }}",
298
+
299
+ // Arithmetic — variadic (2+ args), coerces strings to numbers, errors on
300
+ // non-numeric input. The result is formatted as an integer when whole and
301
+ // as a decimal otherwise (so `add('5', '3')` → "8", `add('5', '1.1')` →
302
+ // "6.1"). Divide-by-zero errors out.
303
+ "echo next-build={{ add(VARS.versionCode, '1') }}",
304
+ "echo half={{ divide(VARS.total, '2') }}",
305
+
306
+ // Validate a value against a fixed allow-list. Returns the value on
307
+ // match; lists every valid option in the error on mismatch. Collapses
308
+ // the four-case `match { major: define(part, 'major'), ... }` boilerplate.
309
+ "{{ define(part, one_of(ARGS, 'major', 'minor', 'patch', 'build')) }}",
310
+
277
311
  // Cache keys / content fingerprints. `sha256` for security-sensitive use,
278
312
  // `md5` for cheap fingerprinting (NOT cryptographically secure).
279
313
  "echo cache-key={{ sha256(read_file('package-lock.json')) }}",
Binary file
@@ -146,7 +146,7 @@ AES-256-GCM secrets you can commit to git. Auto-decrypted at runtime, never writ
146
146
  tool, no wrapper.
147
147
 
148
148
  ```bash
149
- $ run :env init -p .env.production
149
+ $ run :env init .env.production
150
150
  $ run :env set .env.production DB_PASS "s3cr3t" # auto-encrypted
151
151
  $ run :env set .env.production DB_PASS # omit value → read from stdin (no shell history, no escaping)
152
152
  ```
@@ -160,9 +160,17 @@ You can also use Runfile as a drop-in `dotenvx`/`dotenv` replacement to inject e
160
160
  any command — no Runfile needed:
161
161
 
162
162
  ```bash
163
- $ run :env inject -- node app.js # uses .env by default
164
- $ run :env inject -f .env -f .env.local -- pnpm dev # multiple files, last wins; parent env always wins
165
- $ run :env inject -f .env.production -- ./deploy.sh # encrypted values auto-decrypted
163
+ $ run :env inject .env -- node app.js # one file
164
+ $ run :env inject .env .env.local -- pnpm dev # multiple files, last wins; parent env always wins
165
+ $ run :env inject .env.production -- ./deploy.sh # encrypted values auto-decrypted
166
+ ```
167
+
168
+ In CI, the file path can be supplied via `RUNFILE_ENV_FILE_TARGET` (set automatically by the
169
+ [setup action's `env-file-source` input](.github/actions/setup/action.yml) for open-source
170
+ repos that keep their encrypted `.env` in a GitHub secret):
171
+
172
+ ```bash
173
+ $ run :env inject -- node app.js # uses $RUNFILE_ENV_FILE_TARGET
166
174
  ```
167
175
 
168
176
  #### Powerful argument substitution
@@ -240,15 +248,17 @@ and work as full substitution bodies *or* as chain segments:
240
248
  // trim : trim, trim_start, trim_end
241
249
  // inspect : length, starts_with, ends_with, contains
242
250
  // transform : escape, repeat, replace_all, remove_all
243
- // regex : regex_replace, regex_remove, regex_matches
251
+ // regex : regex_replace, regex_remove, regex_matches, regex_capture
244
252
  // build : concat, join
245
253
  // split : nth, first, last, count_parts
254
+ // math : add, subtract, multiply, divide
255
+ // validate : one_of
246
256
  // encoding : base64_encode, base64_decode
247
257
  // hashing : sha256, md5
248
258
  // files : read_file, file_exists
249
259
  // json : json_get, json_set
250
260
  // error : try
251
- // shell : shell_quote
261
+ // shell : shell_quote, capture
252
262
  // variables : define
253
263
  // cwd : set_cwd
254
264
  "echo deploying-{{ to_upper(ARGS.env) }}",
@@ -259,6 +269,10 @@ and work as full substitution bodies *or* as chain segments:
259
269
  "go test {{ replace_all(ARGS.flags, ' ', ' -tag=') }}",
260
270
  // Strip every match of a regex (here: collapse whitespace runs to a single space):
261
271
  "echo {{ regex_replace(ARGS.text, '\\s+', ' ') }}",
272
+ // Pull a capture group out of the first regex match — no `(?s)^.*X(...)X.*$`
273
+ // greedy-replace trick needed. Group 0 is the whole match; group N is the
274
+ // N-th `(...)`. Out-of-bounds returns "" (same convention as `nth`).
275
+ "echo version={{ regex_capture(read_file('app/build.gradle.kts'), 'versionName = \"([^\"]+)\"', '1') }}",
262
276
  // Split-by-separator scalar accessors — string in, string out, no list type:
263
277
  // basename idiom (last segment after `/`); empty string when input ends in `/`.
264
278
  "echo basename={{ last(ARGS.path, '/') }}",
@@ -274,6 +288,26 @@ and work as full substitution bodies *or* as chain segments:
274
288
  // `shell_quote` picks the right quoting for the active shell:
275
289
  "some-tool --json {{ shell_quote(base64_decode(ENV.SECRET_BASE64)) }}",
276
290
 
291
+ // Slurp shell-command stdout straight into a substitution. `capture` runs
292
+ // the command through the platform's default shell (sh / cmd) and trims
293
+ // the trailing newline. Results are memoized per-target so the same
294
+ // capture in multiple commands runs once. `--dry-run` substitutes a
295
+ // readable placeholder instead of spawning the shell.
296
+ "echo built-at={{ capture('date -u +%Y-%m-%dT%H:%M:%SZ') }}",
297
+ "{{ define(sha, capture('git rev-parse HEAD')) }}",
298
+
299
+ // Arithmetic — variadic (2+ args), coerces strings to numbers, errors on
300
+ // non-numeric input. The result is formatted as an integer when whole and
301
+ // as a decimal otherwise (so `add('5', '3')` → "8", `add('5', '1.1')` →
302
+ // "6.1"). Divide-by-zero errors out.
303
+ "echo next-build={{ add(VARS.versionCode, '1') }}",
304
+ "echo half={{ divide(VARS.total, '2') }}",
305
+
306
+ // Validate a value against a fixed allow-list. Returns the value on
307
+ // match; lists every valid option in the error on mismatch. Collapses
308
+ // the four-case `match { major: define(part, 'major'), ... }` boilerplate.
309
+ "{{ define(part, one_of(ARGS, 'major', 'minor', 'patch', 'build')) }}",
310
+
277
311
  // Cache keys / content fingerprints. `sha256` for security-sensitive use,
278
312
  // `md5` for cheap fingerprinting (NOT cryptographically secure).
279
313
  "echo cache-key={{ sha256(read_file('package-lock.json')) }}",
Binary file
@@ -146,7 +146,7 @@ AES-256-GCM secrets you can commit to git. Auto-decrypted at runtime, never writ
146
146
  tool, no wrapper.
147
147
 
148
148
  ```bash
149
- $ run :env init -p .env.production
149
+ $ run :env init .env.production
150
150
  $ run :env set .env.production DB_PASS "s3cr3t" # auto-encrypted
151
151
  $ run :env set .env.production DB_PASS # omit value → read from stdin (no shell history, no escaping)
152
152
  ```
@@ -160,9 +160,17 @@ You can also use Runfile as a drop-in `dotenvx`/`dotenv` replacement to inject e
160
160
  any command — no Runfile needed:
161
161
 
162
162
  ```bash
163
- $ run :env inject -- node app.js # uses .env by default
164
- $ run :env inject -f .env -f .env.local -- pnpm dev # multiple files, last wins; parent env always wins
165
- $ run :env inject -f .env.production -- ./deploy.sh # encrypted values auto-decrypted
163
+ $ run :env inject .env -- node app.js # one file
164
+ $ run :env inject .env .env.local -- pnpm dev # multiple files, last wins; parent env always wins
165
+ $ run :env inject .env.production -- ./deploy.sh # encrypted values auto-decrypted
166
+ ```
167
+
168
+ In CI, the file path can be supplied via `RUNFILE_ENV_FILE_TARGET` (set automatically by the
169
+ [setup action's `env-file-source` input](.github/actions/setup/action.yml) for open-source
170
+ repos that keep their encrypted `.env` in a GitHub secret):
171
+
172
+ ```bash
173
+ $ run :env inject -- node app.js # uses $RUNFILE_ENV_FILE_TARGET
166
174
  ```
167
175
 
168
176
  #### Powerful argument substitution
@@ -240,15 +248,17 @@ and work as full substitution bodies *or* as chain segments:
240
248
  // trim : trim, trim_start, trim_end
241
249
  // inspect : length, starts_with, ends_with, contains
242
250
  // transform : escape, repeat, replace_all, remove_all
243
- // regex : regex_replace, regex_remove, regex_matches
251
+ // regex : regex_replace, regex_remove, regex_matches, regex_capture
244
252
  // build : concat, join
245
253
  // split : nth, first, last, count_parts
254
+ // math : add, subtract, multiply, divide
255
+ // validate : one_of
246
256
  // encoding : base64_encode, base64_decode
247
257
  // hashing : sha256, md5
248
258
  // files : read_file, file_exists
249
259
  // json : json_get, json_set
250
260
  // error : try
251
- // shell : shell_quote
261
+ // shell : shell_quote, capture
252
262
  // variables : define
253
263
  // cwd : set_cwd
254
264
  "echo deploying-{{ to_upper(ARGS.env) }}",
@@ -259,6 +269,10 @@ and work as full substitution bodies *or* as chain segments:
259
269
  "go test {{ replace_all(ARGS.flags, ' ', ' -tag=') }}",
260
270
  // Strip every match of a regex (here: collapse whitespace runs to a single space):
261
271
  "echo {{ regex_replace(ARGS.text, '\\s+', ' ') }}",
272
+ // Pull a capture group out of the first regex match — no `(?s)^.*X(...)X.*$`
273
+ // greedy-replace trick needed. Group 0 is the whole match; group N is the
274
+ // N-th `(...)`. Out-of-bounds returns "" (same convention as `nth`).
275
+ "echo version={{ regex_capture(read_file('app/build.gradle.kts'), 'versionName = \"([^\"]+)\"', '1') }}",
262
276
  // Split-by-separator scalar accessors — string in, string out, no list type:
263
277
  // basename idiom (last segment after `/`); empty string when input ends in `/`.
264
278
  "echo basename={{ last(ARGS.path, '/') }}",
@@ -274,6 +288,26 @@ and work as full substitution bodies *or* as chain segments:
274
288
  // `shell_quote` picks the right quoting for the active shell:
275
289
  "some-tool --json {{ shell_quote(base64_decode(ENV.SECRET_BASE64)) }}",
276
290
 
291
+ // Slurp shell-command stdout straight into a substitution. `capture` runs
292
+ // the command through the platform's default shell (sh / cmd) and trims
293
+ // the trailing newline. Results are memoized per-target so the same
294
+ // capture in multiple commands runs once. `--dry-run` substitutes a
295
+ // readable placeholder instead of spawning the shell.
296
+ "echo built-at={{ capture('date -u +%Y-%m-%dT%H:%M:%SZ') }}",
297
+ "{{ define(sha, capture('git rev-parse HEAD')) }}",
298
+
299
+ // Arithmetic — variadic (2+ args), coerces strings to numbers, errors on
300
+ // non-numeric input. The result is formatted as an integer when whole and
301
+ // as a decimal otherwise (so `add('5', '3')` → "8", `add('5', '1.1')` →
302
+ // "6.1"). Divide-by-zero errors out.
303
+ "echo next-build={{ add(VARS.versionCode, '1') }}",
304
+ "echo half={{ divide(VARS.total, '2') }}",
305
+
306
+ // Validate a value against a fixed allow-list. Returns the value on
307
+ // match; lists every valid option in the error on mismatch. Collapses
308
+ // the four-case `match { major: define(part, 'major'), ... }` boilerplate.
309
+ "{{ define(part, one_of(ARGS, 'major', 'minor', 'patch', 'build')) }}",
310
+
277
311
  // Cache keys / content fingerprints. `sha256` for security-sensitive use,
278
312
  // `md5` for cheap fingerprinting (NOT cryptographically secure).
279
313
  "echo cache-key={{ sha256(read_file('package-lock.json')) }}",
Binary file
@@ -146,7 +146,7 @@ AES-256-GCM secrets you can commit to git. Auto-decrypted at runtime, never writ
146
146
  tool, no wrapper.
147
147
 
148
148
  ```bash
149
- $ run :env init -p .env.production
149
+ $ run :env init .env.production
150
150
  $ run :env set .env.production DB_PASS "s3cr3t" # auto-encrypted
151
151
  $ run :env set .env.production DB_PASS # omit value → read from stdin (no shell history, no escaping)
152
152
  ```
@@ -160,9 +160,17 @@ You can also use Runfile as a drop-in `dotenvx`/`dotenv` replacement to inject e
160
160
  any command — no Runfile needed:
161
161
 
162
162
  ```bash
163
- $ run :env inject -- node app.js # uses .env by default
164
- $ run :env inject -f .env -f .env.local -- pnpm dev # multiple files, last wins; parent env always wins
165
- $ run :env inject -f .env.production -- ./deploy.sh # encrypted values auto-decrypted
163
+ $ run :env inject .env -- node app.js # one file
164
+ $ run :env inject .env .env.local -- pnpm dev # multiple files, last wins; parent env always wins
165
+ $ run :env inject .env.production -- ./deploy.sh # encrypted values auto-decrypted
166
+ ```
167
+
168
+ In CI, the file path can be supplied via `RUNFILE_ENV_FILE_TARGET` (set automatically by the
169
+ [setup action's `env-file-source` input](.github/actions/setup/action.yml) for open-source
170
+ repos that keep their encrypted `.env` in a GitHub secret):
171
+
172
+ ```bash
173
+ $ run :env inject -- node app.js # uses $RUNFILE_ENV_FILE_TARGET
166
174
  ```
167
175
 
168
176
  #### Powerful argument substitution
@@ -240,15 +248,17 @@ and work as full substitution bodies *or* as chain segments:
240
248
  // trim : trim, trim_start, trim_end
241
249
  // inspect : length, starts_with, ends_with, contains
242
250
  // transform : escape, repeat, replace_all, remove_all
243
- // regex : regex_replace, regex_remove, regex_matches
251
+ // regex : regex_replace, regex_remove, regex_matches, regex_capture
244
252
  // build : concat, join
245
253
  // split : nth, first, last, count_parts
254
+ // math : add, subtract, multiply, divide
255
+ // validate : one_of
246
256
  // encoding : base64_encode, base64_decode
247
257
  // hashing : sha256, md5
248
258
  // files : read_file, file_exists
249
259
  // json : json_get, json_set
250
260
  // error : try
251
- // shell : shell_quote
261
+ // shell : shell_quote, capture
252
262
  // variables : define
253
263
  // cwd : set_cwd
254
264
  "echo deploying-{{ to_upper(ARGS.env) }}",
@@ -259,6 +269,10 @@ and work as full substitution bodies *or* as chain segments:
259
269
  "go test {{ replace_all(ARGS.flags, ' ', ' -tag=') }}",
260
270
  // Strip every match of a regex (here: collapse whitespace runs to a single space):
261
271
  "echo {{ regex_replace(ARGS.text, '\\s+', ' ') }}",
272
+ // Pull a capture group out of the first regex match — no `(?s)^.*X(...)X.*$`
273
+ // greedy-replace trick needed. Group 0 is the whole match; group N is the
274
+ // N-th `(...)`. Out-of-bounds returns "" (same convention as `nth`).
275
+ "echo version={{ regex_capture(read_file('app/build.gradle.kts'), 'versionName = \"([^\"]+)\"', '1') }}",
262
276
  // Split-by-separator scalar accessors — string in, string out, no list type:
263
277
  // basename idiom (last segment after `/`); empty string when input ends in `/`.
264
278
  "echo basename={{ last(ARGS.path, '/') }}",
@@ -274,6 +288,26 @@ and work as full substitution bodies *or* as chain segments:
274
288
  // `shell_quote` picks the right quoting for the active shell:
275
289
  "some-tool --json {{ shell_quote(base64_decode(ENV.SECRET_BASE64)) }}",
276
290
 
291
+ // Slurp shell-command stdout straight into a substitution. `capture` runs
292
+ // the command through the platform's default shell (sh / cmd) and trims
293
+ // the trailing newline. Results are memoized per-target so the same
294
+ // capture in multiple commands runs once. `--dry-run` substitutes a
295
+ // readable placeholder instead of spawning the shell.
296
+ "echo built-at={{ capture('date -u +%Y-%m-%dT%H:%M:%SZ') }}",
297
+ "{{ define(sha, capture('git rev-parse HEAD')) }}",
298
+
299
+ // Arithmetic — variadic (2+ args), coerces strings to numbers, errors on
300
+ // non-numeric input. The result is formatted as an integer when whole and
301
+ // as a decimal otherwise (so `add('5', '3')` → "8", `add('5', '1.1')` →
302
+ // "6.1"). Divide-by-zero errors out.
303
+ "echo next-build={{ add(VARS.versionCode, '1') }}",
304
+ "echo half={{ divide(VARS.total, '2') }}",
305
+
306
+ // Validate a value against a fixed allow-list. Returns the value on
307
+ // match; lists every valid option in the error on mismatch. Collapses
308
+ // the four-case `match { major: define(part, 'major'), ... }` boilerplate.
309
+ "{{ define(part, one_of(ARGS, 'major', 'minor', 'patch', 'build')) }}",
310
+
277
311
  // Cache keys / content fingerprints. `sha256` for security-sensitive use,
278
312
  // `md5` for cheap fingerprinting (NOT cryptographically secure).
279
313
  "echo cache-key={{ sha256(read_file('package-lock.json')) }}",
package/bin/linux-x64/run CHANGED
Binary file
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@runfile/cli",
3
- "version": "0.15.1",
3
+ "version": "0.17.0",
4
4
  "description": "Runfile CLI — an easy-to-use, batteries-included task runner",
5
5
  "repository": "https://github.com/Skiley/runfile",
6
6
  "homepage": "https://github.com/Skiley/runfile",