@runfile/cli 1.9.0 → 2.0.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.
- package/README.md +164 -118
- package/bin/darwin-arm64/run +0 -0
- package/bin/darwin-x64/run +0 -0
- package/bin/linux-arm64/run +0 -0
- 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/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Runfile
|
|
2
2
|
|
|
3
|
-
[Quick start](#quick-start) · [Examples](#what-a-runfile-looks-like) · [Language](#the-language) · [Properties](#properties) · [Commands](#commands) · [Editors](#editor-support)
|
|
3
|
+
[Quick start](#quick-start) · [Examples](#what-a-runfile-looks-like) · [Language](#the-language) · [Environment](#the-environment-the-working-directory-and-failures) · [Properties](#properties) · [Commands](#commands) · [Editors](#editor-support)
|
|
4
4
|
|
|
5
5
|
**One directory. One binary. Every OS.**
|
|
6
6
|
|
|
@@ -30,8 +30,8 @@ file to merge, so two people adding a task never conflict. `run :list` shows the
|
|
|
30
30
|
# runfiles/dev.run
|
|
31
31
|
# Start the dev server
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
.
|
|
33
|
+
import_env_file(".env.local")
|
|
34
|
+
ENV.PORT = ARG.port ? "3000"
|
|
35
35
|
|
|
36
36
|
$ vite
|
|
37
37
|
```
|
|
@@ -255,7 +255,7 @@ script, a SQL file — whatever is already on the machine. The body is that comm
|
|
|
255
255
|
# runfiles/i18n/check.run
|
|
256
256
|
# Check that every translation catalogue has the same keys as the reference
|
|
257
257
|
|
|
258
|
-
.
|
|
258
|
+
ENV.LOCALES = "src/locales"
|
|
259
259
|
|
|
260
260
|
exec python3
|
|
261
261
|
import json, os, pathlib, sys
|
|
@@ -277,7 +277,7 @@ end
|
|
|
277
277
|
# runfiles/deps/list.run
|
|
278
278
|
# Print the first few runtime dependencies
|
|
279
279
|
|
|
280
|
-
.
|
|
280
|
+
ENV.LIMIT = ARG.limit ? "10"
|
|
281
281
|
|
|
282
282
|
exec node
|
|
283
283
|
const { readFileSync } = require("node:fs")
|
|
@@ -326,7 +326,7 @@ end
|
|
|
326
326
|
```
|
|
327
327
|
|
|
328
328
|
One thing to know: the body **is** the command's stdin, so a script cannot also read data from stdin — give it
|
|
329
|
-
what it needs through
|
|
329
|
+
what it needs through `ENV`, a file, or the command's own arguments.
|
|
330
330
|
|
|
331
331
|
An interpolation inside the body arrives **as itself**, unquoted, when the command is not a shell — shell
|
|
332
332
|
quoting is the wrong quoting for anybody else's language, so quote it the way that language wants. A shell
|
|
@@ -338,8 +338,8 @@ body still self-quotes, and `$ cp {{ src }} {{ dst }}` is as safe as it ever was
|
|
|
338
338
|
# runfiles/deploy.run
|
|
339
339
|
# Deploy to production
|
|
340
340
|
|
|
341
|
-
|
|
342
|
-
.
|
|
341
|
+
import_env_file(".env.production")
|
|
342
|
+
ENV.GOOGLE_APPLICATION_CREDENTIALS = temp_file(base64_decode(ENV.SERVICE_ACCOUNT_B64), "json")
|
|
343
343
|
|
|
344
344
|
confirm("Deploy to production?")
|
|
345
345
|
$ terraform apply -auto-approve
|
|
@@ -389,7 +389,8 @@ Line-oriented, with one rule: **the language is the default, the shell is marked
|
|
|
389
389
|
| Line | Meaning |
|
|
390
390
|
| --- | --- |
|
|
391
391
|
| `# text` | A comment, to the end of the line. It may follow code. The leading block is the target's description. |
|
|
392
|
-
| `.name = value` | A property. |
|
|
392
|
+
| `.name = value` | A property: a fact about the whole file, at its top. |
|
|
393
|
+
| `ENV.NAME = value` | Set an environment variable for every line below. |
|
|
393
394
|
| `$ echo hi` | Hand this line to a shell. |
|
|
394
395
|
| `exec python3` … `end` | Run a command with the block as its stdin. |
|
|
395
396
|
| `detach $ npm run dev` | Start this one command and do not wait for it. Also `detach exec …`. |
|
|
@@ -402,7 +403,7 @@ Line-oriented, with one rule: **the language is the default, the shell is marked
|
|
|
402
403
|
| `break` / `continue` | Leave the innermost loop, or start its next pass. |
|
|
403
404
|
| `match` / `case` / `default` / `end` | Dispatch on a value. A label is a quoted string: `case "linux"`. |
|
|
404
405
|
| `retry n [every s]` … `end` | Run the block again while it fails, up to `n` times. |
|
|
405
|
-
| `do` … `end` | A block with no condition,
|
|
406
|
+
| `do` … `end` | A block with no condition, and the one that puts back the environment, working directory and `ignore_errors()` at its `end`. |
|
|
406
407
|
| `run other-target` | Run another target, in this process, starting from this one's environment. |
|
|
407
408
|
| `print(…)` | Anything else is an expression, evaluated for its effect. |
|
|
408
409
|
|
|
@@ -502,7 +503,10 @@ end
|
|
|
502
503
|
after the block, so assigning a name bound outside the branch is a parse error — that value would be lost. A
|
|
503
504
|
`let` directly inside `parallel do` binds nothing anyone can read, and is one too.
|
|
504
505
|
- Every branch **runs to completion before a failure is reported**, so a set of services is never left half
|
|
505
|
-
started.
|
|
506
|
+
started. `ignore_errors()` in effect around the block forgives each branch.
|
|
507
|
+
- Each branch also changes **its own copy of the environment and working directory**: an `ENV.X =` or a
|
|
508
|
+
`set_cwd()` inside a branch reaches that branch's commands and nothing else. One that is a whole branch of its
|
|
509
|
+
own would reach nothing at all, and is a parse error.
|
|
506
510
|
- Every line a branch prints carries its name, **in a colour of its own**: the target it runs, the iteration's
|
|
507
511
|
value, or as many words of its command as tell it from its siblings — `cargo test` and `cargo clippy`.
|
|
508
512
|
Siblings take consecutive colours, so the output you are reading side by side is the output guaranteed to
|
|
@@ -564,8 +568,9 @@ Nothing is declared for that — a `?` chain is what makes a value optional, and
|
|
|
564
568
|
default. A `one_of` with its options written out, `one_of(ARG.env ? "staging", "staging", "production")`, is
|
|
565
569
|
what lists the values a name takes. `run --stdin-args deploy` asks for the same list, in the same order,
|
|
566
570
|
**before anything runs**, and shows the same options; the target's own `one_of` is still what refuses a wrong
|
|
567
|
-
answer. A name the target sets for itself with
|
|
568
|
-
the list at all, since nothing the caller passes would reach it.
|
|
571
|
+
answer. A name the target sets for itself with `ENV.NAME =` on every path to the read — above it, or in a
|
|
572
|
+
`_shared.run` — is not on the list at all, since nothing the caller passes would reach it. One set on only some of
|
|
573
|
+
the paths, or by a file, is still listed.
|
|
569
574
|
|
|
570
575
|
A target that reads `ARGS` is asked for its positionals too, when none were given: one line, split the way a
|
|
571
576
|
command line is, so `"two words"` stays one. Enter passes none, which is what a wrapper usually wants. A
|
|
@@ -628,6 +633,10 @@ A list becomes one argument per item, so give it a word of its own rather than g
|
|
|
628
633
|
`{{ files }} suffix`, not `{{ files }}suffix`. An empty list is no words at all, so `rm -rf {{ dirs }}/cache`
|
|
629
634
|
with an empty `dirs` is `rm -rf /cache`; `run :lint` flags that glued shape.
|
|
630
635
|
|
|
636
|
+
An `exec` line is read by no shell: the runner splits it into the program and its arguments itself, and writes
|
|
637
|
+
each value for that, so a value there arrives the same way — one argument, or one per item of a list, quotes
|
|
638
|
+
and all.
|
|
639
|
+
|
|
631
640
|
### Asking whether a command worked
|
|
632
641
|
|
|
633
642
|
A `$` run may stand as a condition or as a `match` subject. The condition is true when the command exits 0;
|
|
@@ -713,7 +722,7 @@ end
|
|
|
713
722
|
### Stopping early
|
|
714
723
|
|
|
715
724
|
`exit()` ends the target it is written in, with a status — 0 when none is given. Nothing inside that target
|
|
716
|
-
catches it: not a `?` fallback, not
|
|
725
|
+
catches it: not a `?` fallback, not `ignore_errors()`, not a `retry`. A target may forgive a command that failed,
|
|
717
726
|
but being told to stop is not that. It is also how a helper finishes early:
|
|
718
727
|
|
|
719
728
|
```sh
|
|
@@ -730,10 +739,10 @@ $ ./scripts/setup-toolchain.sh
|
|
|
730
739
|
|
|
731
740
|
To a target that ran it, the status is how it went — exactly what `$ run _setup-build` would make of it. After a
|
|
732
741
|
0, the `run _setup-build` line is done and the next line runs. Anything else fails that `run` line, like a
|
|
733
|
-
command that failed:
|
|
742
|
+
command that failed: `ignore_errors()` forgives it, `retry` has another go, `code_of` scores it, and if nothing
|
|
734
743
|
handles it, the whole run ends with that status.
|
|
735
744
|
|
|
736
|
-
`error("…")` fails with a message, the way a command fails, so a `?` falls back from it and
|
|
745
|
+
`error("…")` fails with a message, the way a command fails, so a `?` falls back from it and `ignore_errors()`
|
|
737
746
|
forgives it. `confirm("…")` asks, and stops the whole run if the answer is no. Nothing catches that, not even a
|
|
738
747
|
target that ran this one.
|
|
739
748
|
|
|
@@ -801,91 +810,79 @@ time, and never a line that could be right — no style, no guesses. Your editor
|
|
|
801
810
|
underlines the same findings and `run :lint` reports them; [SHELL-CHECK-RULES.md](SHELL-CHECK-RULES.md) lists
|
|
802
811
|
every rule, what it leaves alone, and why.
|
|
803
812
|
|
|
804
|
-
##
|
|
805
|
-
|
|
806
|
-
Set at the top of the file, or inside a block where marked.
|
|
813
|
+
## The environment, the working directory, and failures
|
|
807
814
|
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
| `.shell` | Which POSIX shell `$` lines use. | | |
|
|
811
|
-
| `.env.NAME` | Set an environment variable. | ✅ | |
|
|
812
|
-
| `.workdir` | Where commands run. | ✅ | |
|
|
813
|
-
| `.ignore-errors` | Keep going when a command fails. | ✅ | ✅ |
|
|
814
|
-
| `.logging` | Announce each command on stderr before it runs. Off unless set. | | ✅ |
|
|
815
|
-
| `.env-file` | Load a `.env` file (encrypted values are decrypted in memory). Appends. | ✅ | |
|
|
816
|
-
| `.add-path` | Prepend a directory to `PATH`. Appends. | ✅ | |
|
|
817
|
-
| `.watch` | Re-run when matching files change. A `!` prefix excludes. | | |
|
|
818
|
-
| `.only-in-directories` | Machine-wide targets only: offer this one inside these directories. Appends. | | |
|
|
815
|
+
Every line runs in what the lines above it left: an **environment**, a **working directory**, and whether a
|
|
816
|
+
failure **stops** the target. Five lines change them, each a line of its own:
|
|
819
817
|
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
a
|
|
818
|
+
| Line | Effect |
|
|
819
|
+
| --- | --- |
|
|
820
|
+
| `ENV.NAME = value` | Set an environment variable — for `ENV.NAME` below, and every command and target the lines below start. |
|
|
821
|
+
| `set_cwd(dir)` | Change where commands run, and what every relative path resolves against. Relative to the one in effect, the way `cd` is. |
|
|
822
|
+
| `add_path(dir)` | Put a directory, or a list of them, at the front of `PATH`. |
|
|
823
|
+
| `import_env_file(path)` | Load a `.env` file, or a list of them. Encrypted values are decrypted in memory; a file that is not there is skipped. |
|
|
824
|
+
| `ignore_errors()` | Keep going when a command fails. |
|
|
826
825
|
|
|
827
|
-
A
|
|
828
|
-
|
|
829
|
-
built from what a `.env-file` above it loaded, or from another `.env` above it:
|
|
826
|
+
A change **lasts past the block it is written in**, the way a `let` does — so a condition can decide something
|
|
827
|
+
for the rest of the target:
|
|
830
828
|
|
|
831
829
|
```sh
|
|
832
830
|
# runfiles/build.run
|
|
833
|
-
# Build
|
|
831
|
+
# Build with whatever toolchain this machine has
|
|
834
832
|
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
.
|
|
833
|
+
if RUN.os == "mac"
|
|
834
|
+
add_path("/opt/homebrew/bin")
|
|
835
|
+
ENV.CC = "clang"
|
|
836
|
+
end
|
|
838
837
|
|
|
839
|
-
$
|
|
838
|
+
$ make
|
|
840
839
|
```
|
|
841
840
|
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
.env-file = ".env"
|
|
847
|
-
.env.DATABASE_URL = concat("postgres://", ENV.DB_HOST, "/app")
|
|
841
|
+
**`do … end` is the block that puts it all back.** What changes inside it — the environment, the working
|
|
842
|
+
directory, `ignore_errors()` — ends at its `end`, however the block is left. That is what replaced writing
|
|
843
|
+
`cd web &&` on every line:
|
|
848
844
|
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
Four of them describe the whole file rather than the commands under them — `.shell`, `.logging`, `.watch` and
|
|
853
|
-
`.only-in-directories` — and those have to be written above its first statement.
|
|
845
|
+
```sh
|
|
846
|
+
# runfiles/setup.run
|
|
847
|
+
# One-time per clone: hooks, then the web dependencies
|
|
854
848
|
|
|
855
|
-
|
|
856
|
-
`busybox` or `brush`. Any other interpreter is named on the line instead, with `exec` — which is the same
|
|
857
|
-
mechanism, and says so where it can be read:
|
|
849
|
+
$ git config core.hooksPath .githooks
|
|
858
850
|
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
# List the current directory, whatever the platform
|
|
851
|
+
do
|
|
852
|
+
set_cwd("web")
|
|
862
853
|
|
|
863
|
-
|
|
864
|
-
exec
|
|
865
|
-
Get-ChildItem
|
|
866
|
-
end
|
|
867
|
-
else
|
|
868
|
-
$ ls -la
|
|
854
|
+
$ pnpm install
|
|
855
|
+
$ pnpm exec playwright install chromium
|
|
869
856
|
end
|
|
870
857
|
```
|
|
871
858
|
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
859
|
+
A loop's next pass starts where the last one left, as any block but a `do` does — so a relative `set_cwd()` in a
|
|
860
|
+
loop wants a `do` around it, and is refused without one where every pass would move on from the last
|
|
861
|
+
(`relative-cwd-in-loop`). Each attempt of a `retry` starts from where the `retry` began, and the attempt that
|
|
862
|
+
succeeds keeps what it changed. A branch of a `parallel` block changes a copy: what it sets reaches its own
|
|
863
|
+
commands and nothing else.
|
|
864
|
+
|
|
865
|
+
`ignore_errors()` goes at the top of a file or directly inside a `do`, where it is plain how far it reaches;
|
|
866
|
+
anywhere else it would forgive every failure to the end of the target, and is refused. A single command that may
|
|
867
|
+
fail is `code_of($ …)` on a line of its own.
|
|
868
|
+
|
|
869
|
+
**What a target sets wins over the caller's environment; a file does not.** An assignment written in the file is
|
|
870
|
+
the one way a target can force a value, so `PORT=4000 run serve` still serves on 3000 under `ENV.PORT = "3000"`.
|
|
871
|
+
A file is a default, the dotenv convention: a value it holds fills a name nothing has set, or replaces an earlier
|
|
872
|
+
file's — never what the caller exported, and never an `ENV.NAME =`, whichever line comes first. A default the
|
|
873
|
+
caller *may* override is spelled out, and `--help` shows it as one:
|
|
877
874
|
|
|
878
875
|
```sh
|
|
879
876
|
# runfiles/serve.run
|
|
880
877
|
# Serve locally
|
|
881
878
|
|
|
882
|
-
.
|
|
879
|
+
ENV.PORT = ENV.PORT ? "3000"
|
|
883
880
|
|
|
884
881
|
$ node server.js
|
|
885
882
|
```
|
|
886
883
|
|
|
887
|
-
|
|
888
|
-
|
|
884
|
+
Every value is an expression, read where its line is — so it can be built from what a file above it loaded, or
|
|
885
|
+
named by something the target worked out first:
|
|
889
886
|
|
|
890
887
|
```sh
|
|
891
888
|
# runfiles/deploy.run
|
|
@@ -893,10 +890,11 @@ block's file can be named by something that block worked out:
|
|
|
893
890
|
|
|
894
891
|
let env = one_of(first(ARGS), "staging", "production")
|
|
895
892
|
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
.add-path = "vendor/prod-tools"
|
|
893
|
+
import_env_file(".env.{{ env }}")
|
|
894
|
+
ENV.DATABASE_URL = concat("postgres://", ENV.DB_HOST, "/app")
|
|
899
895
|
|
|
896
|
+
if env == "production"
|
|
897
|
+
add_path("vendor/prod-tools")
|
|
900
898
|
confirm("Deploy to production?")
|
|
901
899
|
end
|
|
902
900
|
|
|
@@ -912,11 +910,10 @@ runfiles/_aws.run → run _aws (a helper other targets call)
|
|
|
912
910
|
runfiles/deploy.run → run deploy
|
|
913
911
|
```
|
|
914
912
|
|
|
915
|
-
**A target that `run`s another hands it its environment.** Whatever a command on that line would be given —
|
|
916
|
-
the caller
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
its own:
|
|
913
|
+
**A target that `run`s another hands it its environment.** Whatever a command on that line would be given — what
|
|
914
|
+
the caller set, imported and added, and what the caller was run with itself — is where the target it runs starts.
|
|
915
|
+
That target changes its own copy the way it would your shell's: its own `ENV.NAME =` wins, and its own
|
|
916
|
+
`add_path()` goes in front. Nothing it changes comes back. So a helper needs no environment of its own:
|
|
920
917
|
|
|
921
918
|
```sh
|
|
922
919
|
# runfiles/_aws.run
|
|
@@ -929,46 +926,73 @@ $ docker run --rm -e AWS_ACCESS_KEY_ID -e AWS_SECRET_ACCESS_KEY amazon/aws-cli {
|
|
|
929
926
|
# runfiles/backups.run
|
|
930
927
|
# List the production backups
|
|
931
928
|
|
|
932
|
-
|
|
929
|
+
import_env_file(".env.production")
|
|
933
930
|
|
|
934
931
|
run _aws s3 ls s3://example-backups
|
|
935
932
|
```
|
|
936
933
|
|
|
937
|
-
A value that only a
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
934
|
+
A value that only a file supplied is still a default when it arrives, so the target's own file replaces it: a `ci`
|
|
935
|
+
target that loads `.env` and runs `test` does not push the development database over `test`'s own `.env.test`.
|
|
936
|
+
`$ run test` is different. A new process can only be handed variables, so there the caller's file values arrive
|
|
937
|
+
as if you had exported them, and they beat `test`'s own file.
|
|
941
938
|
|
|
942
|
-
|
|
943
|
-
|
|
939
|
+
**A value handed to `run` stays a value.** `run deploy {{ ARG.branch }}` gives `deploy` a positional however the
|
|
940
|
+
branch is spelled — a branch named `--force` does not set `deploy`'s `FLAG.force` — and `run deploy --env
|
|
941
|
+
{{ ARG.env }}` gives `--env` its value whatever it begins with. What you write yourself is read as written, and
|
|
942
|
+
so is a list handed on whole, which is how a wrapper forwards its command line: `run build {{ ARGS }}` still
|
|
943
|
+
passes `--release` along as a flag.
|
|
944
944
|
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
945
|
+
`_shared.run` runs ahead of every target in its directory, line for line the way the target does, and the target
|
|
946
|
+
starts from what it left — its bindings, the environment, the working directory. A nested one runs after the
|
|
947
|
+
directory above it, so `runfiles/api/_shared.run` adds to `runfiles/_shared.run`. Its commands run once for each
|
|
948
|
+
target, every time it runs, including a target another one `run`s.
|
|
948
949
|
|
|
949
|
-
|
|
950
|
+
```sh
|
|
951
|
+
# runfiles/_shared.run
|
|
952
|
+
# Settings every target here inherits
|
|
950
953
|
|
|
951
|
-
|
|
952
|
-
|
|
954
|
+
add_path("node_modules/.bin")
|
|
955
|
+
ENV.CARGO_TERM_COLOR = "always"
|
|
953
956
|
|
|
954
|
-
|
|
955
|
-
$ pnpm exec playwright install chromium
|
|
956
|
-
end
|
|
957
|
+
let image = "ghcr.io/example/builder:v2"
|
|
957
958
|
```
|
|
958
959
|
|
|
959
|
-
|
|
960
|
+
## Properties
|
|
960
961
|
|
|
961
|
-
|
|
962
|
-
one layers over the directory above it, so `runfiles/api/_shared.run` adds to `runfiles/_shared.run`.
|
|
962
|
+
A property is a fact about the whole file, written at its top, above the first statement.
|
|
963
963
|
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
964
|
+
| Property | Effect | Flag? |
|
|
965
|
+
| --- | --- | :-: |
|
|
966
|
+
| `.shell` | Which POSIX shell `$` lines use. | |
|
|
967
|
+
| `.logging` | Announce each command on stderr before it runs. Off unless set. | ✅ |
|
|
968
|
+
| `.watch` | Re-run when matching files change. A `!` prefix excludes. | |
|
|
969
|
+
| `.only-in-directories` | Machine-wide targets only: offer this one inside these directories. | |
|
|
970
|
+
|
|
971
|
+
A **flag** is written bare for `= true`, or given a bool. A constant that can never be one is refused where it
|
|
972
|
+
is written — `.logging = 23` and `.logging = "true"` are both errors, and the second says to drop the quotes. A
|
|
973
|
+
value the run works out is left to the run: `.logging = ENV.CI` and `.logging = ARG.verbose ? "false"` are how a
|
|
974
|
+
flag is decided from outside the file, and there `true`, `false`, `1` and `0` are all understood. Anything else
|
|
975
|
+
stops the run rather than reading as `false`, because a flag that quietly did not take effect is found out much
|
|
976
|
+
later.
|
|
967
977
|
|
|
968
|
-
.
|
|
969
|
-
|
|
978
|
+
A property's value is worked out before the file's first line runs, after every `_shared.run` above it has — so a
|
|
979
|
+
`.watch` is relative to the working directory those left, and reads what they set.
|
|
970
980
|
|
|
971
|
-
|
|
981
|
+
`.shell` names one of the eight shells `$` knows how to drive: `sh`, `bash`, `dash`, `ash`, `zsh`, `ksh`,
|
|
982
|
+
`busybox` or `brush`. Any other interpreter is named on the line instead, with `exec` — which is the same
|
|
983
|
+
mechanism, and says so where it can be read:
|
|
984
|
+
|
|
985
|
+
```sh
|
|
986
|
+
# runfiles/list.run
|
|
987
|
+
# List the current directory, whatever the platform
|
|
988
|
+
|
|
989
|
+
if RUN.os == "windows"
|
|
990
|
+
exec pwsh
|
|
991
|
+
Get-ChildItem
|
|
992
|
+
end
|
|
993
|
+
else
|
|
994
|
+
$ ls -la
|
|
995
|
+
end
|
|
972
996
|
```
|
|
973
997
|
|
|
974
998
|
## How targets are found
|
|
@@ -995,9 +1019,16 @@ separated the way `PATH` separates them, or `*` for all.
|
|
|
995
1019
|
`$HOME/.runfiles/` holds machine-wide targets, available in every project. If you would rather see the
|
|
996
1020
|
directory than hide it, `$HOME/runfiles/` and `$HOME/Runfiles/` are read too — but only one of the three may
|
|
997
1021
|
hold anything. `run :list` puts them first, under `global:`: they are the part of the listing you cannot see
|
|
998
|
-
by looking at the project.
|
|
1022
|
+
by looking at the project. That holds for `~/runfiles` too, though it sits where the walk up from anywhere in
|
|
1023
|
+
your home directory looks: it is never taken for a project, so from a directory with no `runfiles/` of its
|
|
1024
|
+
own you get your global targets — and not every project under your home as a subproject.
|
|
999
1025
|
|
|
1000
|
-
A machine-wide target
|
|
1026
|
+
A machine-wide target's `runfiles/` parent is your home directory, so that is where its commands run. One that
|
|
1027
|
+
acts on the project in front of you says so with `set_cwd(RUN.cwd)` — and every relative path follows it, so
|
|
1028
|
+
`read_file("package.json")` and `import_env_file(".env")` read the project's files, not your home's. A `_shared.run`
|
|
1029
|
+
in the machine-wide directory can say it once for all of them.
|
|
1030
|
+
|
|
1031
|
+
A machine-wide target can also say where it belongs, so one directory can hold work for several places at once:
|
|
1001
1032
|
|
|
1002
1033
|
```sh
|
|
1003
1034
|
# ~/.runfiles/deploy.run
|
|
@@ -1005,6 +1036,8 @@ A machine-wide target can say where it belongs, so one directory can hold work f
|
|
|
1005
1036
|
|
|
1006
1037
|
.only-in-directories = ["~/work/acme", "~/work/zed"]
|
|
1007
1038
|
|
|
1039
|
+
set_cwd(RUN.cwd)
|
|
1040
|
+
|
|
1008
1041
|
$ ./scripts/ship
|
|
1009
1042
|
```
|
|
1010
1043
|
|
|
@@ -1021,9 +1054,16 @@ project's lint leaves the directory out otherwise.
|
|
|
1021
1054
|
**Not in CI.** A runner's home directory is nobody's, so `run` reads none of the three there: what runs is
|
|
1022
1055
|
what is checked in and reviewable. This is also why nothing has to be cleaned up after a job.
|
|
1023
1056
|
|
|
1024
|
-
Everything relative —
|
|
1025
|
-
— resolves against **the
|
|
1026
|
-
|
|
1057
|
+
Everything relative — a command, `glob`, `read_file` and the other file functions, `import_env_file()`,
|
|
1058
|
+
`add_path()`, `.watch`, and `set_cwd()` itself — resolves against **the working directory in effect where it is
|
|
1059
|
+
written**. One rule, so a path names the same file to the language as it does to the shell on the next line. The
|
|
1060
|
+
working directory starts at the parent of `runfiles/`, which is `{{ RUN.parent }}`, and a `set_cwd()` moves it
|
|
1061
|
+
for every line below, to the end of the `do` block it is in, if any. It is relative to the one in effect, the way
|
|
1062
|
+
`cd` is: `set_cwd("src")` after `set_cwd("web")` is `web/src`. A path given *before* a `set_cwd()` keeps the
|
|
1063
|
+
directory it was given in, so `add_path("web/node_modules/.bin")` above `set_cwd("web")` still names the
|
|
1064
|
+
subproject's tools. An absolute path is taken as written, and an absolute `glob` pattern answers with
|
|
1065
|
+
absolute paths. `RUN.parent` and `RUN.cwd` never move: the first is the project, the second is where you typed
|
|
1066
|
+
`run`.
|
|
1027
1067
|
|
|
1028
1068
|
## Setup targets
|
|
1029
1069
|
|
|
@@ -1054,7 +1094,7 @@ whose state is still worth keeping, so a `setup` run under it is still recorded.
|
|
|
1054
1094
|
| `run :init` | Create `runfiles/` with an example |
|
|
1055
1095
|
| `run :lint [path…]` | Format runfiles in place and report anything the runner would refuse (`--check` writes nothing, `--stdout`) |
|
|
1056
1096
|
| `run :env <sub>` | Manage `.env` files: `init`, `get`, `set`, `encrypt`, `decrypt`, `rotate`, `inject`, `secret-keys` |
|
|
1057
|
-
| `run :completions <command> <shell>` | `install`, `uninstall` or `output` a completion script for `bash`, `zsh`, `fish` or `powershell` — after upgrading from 1.
|
|
1097
|
+
| `run :completions <command> <shell>` | `install`, `uninstall` or `output` a completion script for `bash`, `zsh`, `fish` or `powershell` — after upgrading from 1.11 or older, `install bash` again: the installed script is a copy, and an older one completes a session's first `run :comp<Tab>` to `run ::completions` |
|
|
1058
1098
|
| `run :generate <editor>` | Task files for `zed`, `jetbrains` or `vscode`, merged into what is there |
|
|
1059
1099
|
| `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 |
|
|
1060
1100
|
|
|
@@ -1114,11 +1154,17 @@ run :env rotate .env.production # re-encrypt under a new key
|
|
|
1114
1154
|
falls back to plaintext: for a file that records none, `--key <prefix>` names the key to use, and `--plain`
|
|
1115
1155
|
stores the value as it is.
|
|
1116
1156
|
|
|
1117
|
-
Keys live in the OS credential store — Keychain, Credential Manager, or Secret Service with
|
|
1118
|
-
|
|
1157
|
+
Keys live in the OS credential store — Keychain, Credential Manager, or Secret Service. A Linux machine with no
|
|
1158
|
+
Secret Service, such as a server, keeps them in `~/.local/share/runfile/keys.json` (under `$XDG_DATA_HOME` when
|
|
1159
|
+
it is set) instead, so nothing has to be installed there and they survive a reboot. The file is created
|
|
1160
|
+
owner-only, and refused if anyone else owns it or can read or write it, the same rule ssh applies to a private
|
|
1161
|
+
key; it is not encrypted on disk, so a backup of the home directory holds the keys. Keys stored there are still
|
|
1162
|
+
read when a Secret Service is available. Keys an older version left in kernel keyutils, which a reboot clears,
|
|
1163
|
+
are moved into the store the first time they are read. `run :env secret-keys list` shows where each key is. In
|
|
1164
|
+
CI, pass them as `RUNFILE_PRIVATE_KEYS` (newline-separated) and no credential store is involved.
|
|
1119
1165
|
|
|
1120
1166
|
Only a value a `.env` file holds is decrypted — the one place an `encrypted:` value is the author's own. A
|
|
1121
|
-
ciphertext arriving any other way (exported in the caller's shell, or
|
|
1167
|
+
ciphertext arriving any other way (exported in the caller's shell, or set with `ENV.X = ARG.x` from
|
|
1122
1168
|
data an outsider controls) is passed through unchanged, never decrypted with the run's keys, so it cannot be
|
|
1123
1169
|
turned back into plaintext by a target that merely echoes a variable.
|
|
1124
1170
|
|
|
@@ -1139,7 +1185,7 @@ where it is written, with the name you probably meant. It is the check that stop
|
|
|
1139
1185
|
knows what the `_shared.run` above the file binds.
|
|
1140
1186
|
|
|
1141
1187
|
Hover anything: a function shows its signature, what it does and a worked example; a property adds whether it
|
|
1142
|
-
|
|
1188
|
+
is a flag; `$`, `exec`, `run`, `retry`, `match` and the rest explain the line form itself.
|
|
1143
1189
|
|
|
1144
1190
|
Completion knows where it is. At the start of a line it offers the keywords that fit there — `break` inside a
|
|
1145
1191
|
loop, `case` inside a `match`, `end` only when something is open — and everywhere a value goes, the variables in
|
package/bin/darwin-arm64/run
CHANGED
|
Binary file
|
package/bin/darwin-x64/run
CHANGED
|
Binary file
|
package/bin/linux-arm64/run
CHANGED
|
Binary file
|
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