@runfile/cli 1.7.2 → 1.8.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 CHANGED
@@ -59,7 +59,8 @@ irm https://git.joaoverona.com/joaaoverona/runfile/releases/download/latest/inst
59
59
 
60
60
  Either one installs a single binary: `~/.local/bin/run`, or `run.exe` under `%LOCALAPPDATA%\runfile\bin`, which
61
61
  the PowerShell script adds to your PATH for you — open a new shell and `run` is there. `RUNFILE_VERSION` installs
62
- a release other than the latest, and `RUNFILE_INSTALL_DIR` chooses where it goes.
62
+ a release other than the latest (`v1.8.4`, or `1.8.4`), and `RUNFILE_INSTALL_DIR` chooses where it goes. Both
63
+ are read by the shell running the script, so that is where they go: `curl -fsSL … | RUNFILE_VERSION=v1.8.4 sh`.
63
64
 
64
65
  ```bash
65
66
  run :init # creates runfiles/ with an example
@@ -74,8 +75,9 @@ run :completions install bash # tab-completion (or zsh, fish, powershell)
74
75
  ```
75
76
 
76
77
  Runner flags go **before** the target name; everything after it belongs to the target. `run build --dry-run`
77
- passes `--dry-run` to `build` as `FLAG.dry-run` and runs it for real — so the position is the whole meaning.
78
- `run` warns when a target is handed a flag it never reads, which is what catches that.
78
+ hands `--dry-run` to `build` and runs it for real — as `FLAG.dry-run` if `build` reads that, or in `ARGS`, its
79
+ positional arguments, if it reads those — so the position is the whole meaning. A target that reads neither
80
+ refuses the flag rather than running without it, which is what catches a `--dry-run` on the wrong side.
79
81
 
80
82
  In CI, the setup action installs `run` and puts it on PATH, so every step after it is a target name:
81
83
 
@@ -88,7 +90,9 @@ In CI, the setup action installs `run` and puts it on PATH, so every step after
88
90
  ```
89
91
 
90
92
  `@v1` is the major alias, moved to each release as it ships — so a fix arrives without editing every
91
- workflow, and a new major never arrives unannounced. Pin `@v1.0.0` instead to hold one exact version.
93
+ workflow, and a new major never arrives unannounced. Pin `@v1.8.4`, or a commit SHA, instead to hold one exact
94
+ version: the action installs the release its own ref belongs to, so the pin holds the binary as well as the
95
+ action. `version:` overrides that — a tag such as `v1.8.4`, or `latest` for whatever was published last.
92
96
 
93
97
  ## What a runfile looks like
94
98
 
@@ -341,8 +345,11 @@ confirm("Deploy to production?")
341
345
  $ terraform apply -auto-approve
342
346
  ```
343
347
 
344
- The temp file is deleted when the run ends, however it ends — including a failure half-way, which is exactly
345
- when a decoded credential must not be left behind.
348
+ The temp file is deleted when the run ends, however it ends — including a failure half-way, or a SIGTERM/SIGHUP,
349
+ which is exactly when a decoded credential must not be left behind — and it is created readable only by you
350
+ (`0600`). The runner also keeps interpolated secrets out of **its own** output: a failed command and a
351
+ `.logging` line show `{{ ENV.TOKEN }}` where the value would be, not the value, so a secret does not land in
352
+ terminal scrollback or a CI log.
346
353
 
347
354
  ### A pre-commit hook
348
355
 
@@ -594,6 +601,21 @@ $ cp {{ ARG.src }} {{ ARG.dest }}
594
601
  left to quote: the substitution already did it. The same rule holds one layer up inside a `json` block, where
595
602
  an interpolation becomes one JSON value.
596
603
 
604
+ Where the shell around an interpolation is quoted already — a message in `"…"`, a heredoc, a command string for
605
+ another shell — the value is written for those quotes too, so it stays text whatever it holds:
606
+
607
+ ```sh
608
+ $ ssh deploy@host "cd {{ ARG.dir }} && ./restart"
609
+ ```
610
+
611
+ The remote shell is handed `cd 'my dir' && ./restart`, quoted for it, and a `$( )` or a backtick in the value
612
+ is text to both shells. A value that would end the heredoc or the shell comment it sits in is refused rather
613
+ than written.
614
+
615
+ A list becomes one argument per item, so give it a word of its own rather than gluing text onto its end —
616
+ `{{ files }} suffix`, not `{{ files }}suffix`. An empty list is no words at all, so `rm -rf {{ dirs }}/cache`
617
+ with an empty `dirs` is `rm -rf /cache`; `run :lint` flags that glued shape.
618
+
597
619
  ### Asking whether a command worked
598
620
 
599
621
  A `$` run may stand as a condition or as a `match` subject. The condition is true when the command exits 0;
@@ -678,9 +700,30 @@ end
678
700
 
679
701
  ### Stopping early
680
702
 
681
- `exit()` ends the run with a status; `error("…")` fails with a message; `confirm("…")` asks, and stops if the
682
- answer is no. Nothing catches any of them: not a `?` fallback, not `.ignore-errors`. A target may forgive a
683
- command that failed, but being told to stop is not that.
703
+ `exit()` ends the target it is written in, with a status — 0 when none is given. Nothing inside that target
704
+ catches it: not a `?` fallback, not `.ignore-errors`, not a `retry`. A target may forgive a command that failed,
705
+ but being told to stop is not that. It is also how a helper finishes early:
706
+
707
+ ```sh
708
+ # runfiles/_setup-build.run
709
+ # Prepare the toolchain; Linux needs nothing
710
+
711
+ if RUN.os == "linux"
712
+ print("no setup needed")
713
+ exit()
714
+ end
715
+
716
+ $ ./scripts/setup-toolchain.sh
717
+ ```
718
+
719
+ To a target that ran it, the status is how it went — exactly what `$ run _setup-build` would make of it. After a
720
+ 0, the `run _setup-build` line is done and the next line runs. Anything else fails that `run` line, like a
721
+ command that failed: `.ignore-errors` forgives it, `retry` has another go, `code_of` scores it, and if nothing
722
+ handles it, the whole run ends with that status.
723
+
724
+ `error("…")` fails with a message, the way a command fails, so a `?` falls back from it and `.ignore-errors`
725
+ forgives it. `confirm("…")` asks, and stops the whole run if the answer is no. Nothing catches that, not even a
726
+ target that ran this one.
684
727
 
685
728
  `confirm` is a function rather than a property, so the question can depend on what is about to happen — and is
686
729
  skipped by `-y`, in CI, and under `--dry-run`, where there is nothing to approve.
@@ -929,6 +972,13 @@ runfiles/api/deploy.run → run api:deploy
929
972
  Inside a subproject, `run build` means *that* subproject's `build` — so a file reads the same wherever you
930
973
  invoke `run` from.
931
974
 
975
+ **The `runfiles/` the walk finds has to be yours.** A directory any account can write — `/tmp`, or the root of
976
+ a Windows drive — would otherwise hand its targets to every directory beneath it that has none of its own. So
977
+ it must belong to you, to the machine's administrators, or to whoever owns the directory you started in, which
978
+ keeps working inside a colleague's checkout as deliberate as it always was. A directory meant to be shared
979
+ between accounts is trusted by naming it, or the project holding it, in `RUNFILE_SAFE_DIRECTORIES` — paths
980
+ separated the way `PATH` separates them, or `*` for all.
981
+
932
982
  `$HOME/.runfiles/` holds machine-wide targets, available in every project. If you would rather see the
933
983
  directory than hide it, `$HOME/runfiles/` and `$HOME/Runfiles/` are read too — but only one of the three may
934
984
  hold anything. `run :list` puts them first, under `global:`: they are the part of the listing you cannot see
@@ -959,7 +1009,8 @@ project's lint leaves the directory out otherwise.
959
1009
  what is checked in and reviewable. This is also why nothing has to be cleaned up after a job.
960
1010
 
961
1011
  Everything relative — `.env-file`, `.add-path`, `glob`, `read_file`, `{{ RUN.parent }}`, the working directory
962
- — resolves against **the parent of `runfiles/`**. One anchor, one rule.
1012
+ — resolves against **the parent of `runfiles/`**. One anchor, one rule. An absolute path is taken as written,
1013
+ and an absolute `glob` pattern answers with absolute paths.
963
1014
 
964
1015
  ## Setup targets
965
1016
 
@@ -972,8 +1023,9 @@ error: `setup` has never been run
972
1023
  run setup
973
1024
  ```
974
1025
 
975
- It re-triggers when `setup.run` itself changes, so a new dependency is not silently missed. `--dry-run` is
976
- exempt, CI is exempt, and `RUNFILE_SKIP_PREPARE=1` bypasses it.
1026
+ Only a setup that succeeded counts: one that failed, or ended with a non-zero `exit()`, leaves the gate shut. It
1027
+ re-triggers when `setup.run` itself changes, so a new dependency is not silently missed. `--dry-run` is exempt,
1028
+ CI is exempt, and `RUNFILE_SKIP_PREPARE=1` bypasses it.
977
1029
 
978
1030
  The record lives in `state.json` in the platform state directory. In CI there is none: a runner is built and
979
1031
  thrown away, so there is no earlier session whose `setup` this one could be relying on — the gate is not
@@ -989,7 +1041,7 @@ whose state is still worth keeping, so a `setup` run under it is still recorded.
989
1041
  | `run :init` | Create `runfiles/` with an example |
990
1042
  | `run :lint [path…]` | Format runfiles in place and report anything the runner would refuse (`--check` writes nothing, `--stdout`) |
991
1043
  | `run :env <sub>` | Manage `.env` files: `init`, `get`, `set`, `encrypt`, `decrypt`, `rotate`, `inject`, `secret-keys` |
992
- | `run :completions <command> <shell>` | `install`, `uninstall` or `output` a completion script for `bash`, `zsh`, `fish` or `powershell` |
1044
+ | `run :completions <command> <shell>` | `install`, `uninstall` or `output` a completion script for `bash`, `zsh`, `fish` or `powershell` — after upgrading from 1.8.2 or older, `install bash` again: the installed script is a copy |
993
1045
  | `run :generate <editor>` | Task files for `zed`, `jetbrains` or `vscode`, merged into what is there |
994
1046
  | `run :update [version]` | Update the binary to the newest release, or to the tag named; `--channel=github` takes it from the GitHub mirror, `--force` reinstalls |
995
1047
 
@@ -997,7 +1049,7 @@ whose state is still worth keeping, so a `setup` run under it is still recorded.
997
1049
  | --- | --- |
998
1050
  | `-y`, `--yes` | Skip confirmation prompts |
999
1051
  | `--stdin-args` | Prompt for every input the target reads, before it runs |
1000
- | `--dry-run` | Print what would run, without running it |
1052
+ | `--dry-run` | Print what would run, without running it — a preview reads no files outside the project, probes none, and never unlocks the credential store |
1001
1053
  | `--dir <path>` | Start discovery somewhere else |
1002
1054
  | `-h`, `--help` | Show the help for `run` or any of its commands |
1003
1055
  | `-v`, `--version` | Print the version |
@@ -1026,7 +1078,8 @@ you wrote it: only the layout of a file is ever rewritten.
1026
1078
  Formatting has no settings: one shape, everywhere. It reindents with tabs, spaces expressions, places blank
1027
1079
  lines, lays out `json` blocks, and leaves the three things that are not the language's to touch — strings, the
1028
1080
  text after `$ `, and `exec` bodies — exactly as written. A file that does not parse is left alone, and the
1029
- formatter checks that its output still means the same thing before writing it.
1081
+ formatter checks that its output still means the same thing before writing it. The file is then replaced in one
1082
+ step, keeping its permissions, so an interrupted write never leaves half of one behind.
1030
1083
 
1031
1084
  ## Encrypted environment variables
1032
1085
 
@@ -1044,13 +1097,23 @@ run :env get .env.production KEY # decrypts on read
1044
1097
  run :env rotate .env.production # re-encrypt under a new key
1045
1098
  ```
1046
1099
 
1100
+ `set` encrypts under the public key the file itself records — `:env init` writes it at the top — and never
1101
+ falls back to plaintext: for a file that records none, `--key <prefix>` names the key to use, and `--plain`
1102
+ stores the value as it is.
1103
+
1047
1104
  Keys live in the OS credential store — Keychain, Credential Manager, or Secret Service with a keyutils
1048
1105
  fallback. In CI, pass them as `RUNFILE_PRIVATE_KEYS` (newline-separated) and no credential store is involved.
1049
1106
 
1107
+ Only a value a `.env` file holds is decrypted — the one place an `encrypted:` value is the author's own. A
1108
+ ciphertext arriving any other way (exported in the caller's shell, or copied into `.env.X = {{ ARG.x }}` from
1109
+ data an outsider controls) is passed through unchanged, never decrypted with the run's keys, so it cannot be
1110
+ turned back into plaintext by a target that merely echoes a variable.
1111
+
1050
1112
  Decryption happens in memory; secrets never reach disk. The credential store is only touched when something
1051
1113
  actually decrypts, so a locked keyring never gets in the way of an unrelated target — and it is asked once in
1052
1114
  a run, however many of the targets that run calls decrypt. In watch mode each re-run asks again, so unlocking
1053
- the keyring or adding a missing key takes effect on the next save.
1115
+ the keyring or adding a missing key takes effect on the next save. `--dry-run` never touches it: a preview
1116
+ reads no `.env` files and asks for no key.
1054
1117
 
1055
1118
  ## Editor support
1056
1119
 
@@ -1104,7 +1167,8 @@ vim.filetype.add({ extension = { run = "runfile" } })
1104
1167
  ```
1105
1168
 
1106
1169
  `run :generate zed` writes every target into `.zed/tasks.json`, and `run :generate jetbrains` into
1107
- `.idea/runConfigurations/`; both leave entries you wrote yourself alone and replace only their own.
1170
+ `.idea/runConfigurations/`; both leave entries you wrote yourself alone and replace only their own, and a file
1171
+ they cannot read is left as it is rather than written over.
1108
1172
 
1109
1173
  ## Why a language
1110
1174
 
Binary file
Binary file
Binary file
package/bin/linux-x64/run CHANGED
Binary file
Binary file
Binary file
package/package.json CHANGED
@@ -13,5 +13,5 @@
13
13
  "license": "MIT",
14
14
  "name": "@runfile/cli",
15
15
  "repository": "https://github.com/JoaaoVerona/runfile",
16
- "version": "1.7.2"
16
+ "version": "1.8.5"
17
17
  }