You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit e8110c2
Browse filesBrowse the repository at this point in the historyBrowse files
refactor: converge transport, exit-code and masking seams, mask @export by default
Behavior changes (consumers must act):
- `@export <VAR>[,<VAR>] [--public]`: a captured value now joins the mask
registry for every render path (terminal, `-v` preview, `--log-file`). The
old rule decided whether to mask by whether the *variable name* looked
secret, so a credential exported as `DATABASE_URL` landed in the audit trail
in the clear while an `@env` literal of the same value was masked. `--public`
is the explicit, reviewable opt-out for a value that is meant to be read
(`VERSION`, `STAMP`, a service name).
- A step that exceeds `--timeout`/`@timeout` now exits 5 instead of being
classified as 4 (script failure), so a CI retry policy can tell "the deploy
broke" from "the box was slow". `bin/shellflow/src/exit.rs` is the single
definition of both protocols (the CLI's and the target-side helper's, which
is a wire contract with the generated payloads).
Architecture:
- `ssh` options (`BatchMode`, `-T`, the connect bound, `ControlMaster`) are
decided once in `executor::transport`; all eight argv builders now go through
it. `--no-ssh-reuse` went from covering 1 of 8 call sites to all of them, and
two sites that had silently lost `-T` got it back. `SshSpec::rsync_transport`
is deleted: a transport policy string no longer lives in the pure grammar
crate.
- The login-shell probe is a per-destination single flight instead of a global
lock held across a network round trip, so `--parallel N` no longer degrades
to serial probing (20 hosts: 250s worst case -> ~12.5s).
- `RunError`/`RunOutcome` moved to `executor/error.rs` (the crate-level
taxonomy no longer lives in the largest leaf module and no longer flows
backwards into its siblings), and the systemd helper protocol moved to
`executor/helper.rs`: the exit-code ladder that was spelled out four times is
now one `classify_helper_exit`.
- `ExecutionPlan` fields are private behind accessors, public enums are
`#[non_exhaustive]`, and the `Step`/`Assertion` matches that were duplicated
five to six times collapse into `Step::target()` plus honest fallback arms.
- `rbatch` lost its synchronous/async twins; both now share one implementation
behind `spawn_blocking`.
- Masking moved down into the sink (`print_line`, `Ui::emit_host_line`), so a
call site that forgets to redact can no longer resurrect the leak; the two
`eprintln!` setup failures and every subcommand error report now go through
`ui`.
- The `--no-default-features` build compiles again: `StepClass` is exported for
the ssh-only configuration, where it is a parameter of the step dispatcher.
Security:
- The age identity check now verifies the owner as well as the mode bits and
opens with `O_NOFOLLOW`, so a 0400 file owned by another user or a symlink
planted at the identity path is refused.
- `fsx` fsyncs the parent directory after `rename`/`hard_link`, closing the
window where a crash could drop a file the tool reported as written.
- `template render` keeps its rendered document in a `Zeroizing` buffer.
Docs: docs/design.md (4.1, 5.1, 5.2, 6, 7.6, 9, S5.3), README.md and SKILL.md
now match the code.
@@ -64,7 +64,10 @@ deploy.sh` runs it unchanged, and your editor highlights it perfectly.
64
64
warning instead of being performed: a dry run never writes to a target.
65
65
-**State passing** — `@export VAR` captures a variable from a local block and
66
66
injects it into later `@copy` paths, `@remote` blocks, and `@only_if` guards
67
-
as `$VAR` / `${VAR}` / env.
67
+
as `$VAR` / `${VAR}` / env. A captured value is **masked in all output** by
68
+
default (it is a value the tool did not see at parse time, and a playbook
69
+
routinely exports a credential it just fetched); add `--public` for a value
70
+
that is meant to be read, such as a version or a commit.
68
71
-**Graceful file copies** — `@copy` uses rsync when the target has it (with a
69
72
`mkdir -p` remote wrapper), and automatically falls back to
70
73
`ssh mkdir -p` + `scp` on hosts without rsync. Destination directories are
@@ -142,7 +145,7 @@ lines belong to the current block.
142
145
|`@local`|`# @local`| Following lines run locally (default) |
143
146
|`@remote`|`# @remote <target>`| Following lines stream to the target (alias, group, or raw spec) |
144
147
|`@copy`|`# @copy <src> -> <dst> @<target> [--delete] [--exclude <glob>]`| Copy a local path to the target (rsync, or scp fallback); a directory source is flattened into `dst` (contents semantics, identical on GNU rsync and macOS openrsync); supports `$VAR` interpolation; `--exclude` is repeatable and honored by rsync (warned as ignored on the scp fallback and in `--local` mode) |
145
-
|`@export`|`# @export <VAR>[,<VAR>…]`| Capture variables from the preceding local block into run state |
148
+
|`@export`|`# @export <VAR>[,<VAR>…] [--public]`| Capture variables from the preceding local block into run state. Captured values are masked in all output by default; `--public` opts out for a value that is meant to be read|
146
149
|`@timeout`|`# @timeout <seconds>`| Per-step timeout for the next block |
147
150
|`@name`|`# @name <label>`| Name the next block (for `--only`/`--skip` and reporting) |
148
151
|`@only_if`|`# @only_if <command>`| Skip the next block where `<command>` fails |
Copy file name to clipboardExpand all lines: SKILL.md
+5-2Lines changed: 5 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -70,7 +70,7 @@ lines belong to the current block.
70
70
|`@local`|`# @local`| Following lines run locally (default) |
71
71
|`@remote`|`# @remote <target>`| Following lines stream to the target (alias, group, raw spec) |
72
72
|`@copy`|`# @copy <src> -> <dst> @<target> [--delete] [--exclude <glob>]`| Copy a local path (rsync, or scp fallback); creates the destination dir; `$VAR` interpolated; `--exclude` is repeatable and honored by rsync. On a host without rsync the scp fallback cannot filter or mirror, so a step that asked for `--delete`/`--exclude`**fails closed** unless `--allow-unfiltered-copy` accepts the downgrade |
73
-
|`@export`|`# @export <VAR>[,<VAR>…]`| Capture variables from the preceding local block into run state |
73
+
|`@export`|`# @export <VAR>[,<VAR>…] [--public]`| Capture variables from the preceding local block into run state. Every captured value is masked in the output (terminal, previews, `--log-file`) unless the line says `--public`; use `--public` for versions, timestamps, paths and other non-secret values|
74
74
|`@timeout`|`# @timeout <seconds>`| Per-step timeout for the next block |
75
75
|`@name`|`# @name <label>`| Name the next block (for `--only`/`--skip`/reporting) |
76
76
|`@only_if`|`# @only_if <command>`| Skip the next block where `<command>` fails |
@@ -112,6 +112,9 @@ Behavioral rules to remember:
112
112
-`@copy` paths are parsed by shellflow, not Bash. Capture values with
113
113
`@export` and reference them as `$VAR` / `${VAR}`. Unresolved variables are
114
114
a hard error in a real run, and a warning + skip in `--dry-run`/`--check`.
115
+
A captured value is masked in every rendered line unless the declaration says
116
+
`--public`; a secret exported under an ordinary name (`DATABASE_URL`) is
117
+
therefore still not printed.
115
118
-`@only_if` guards run per host with the same env as the block; a failed
116
119
guard reports `SKIPPED`.
117
120
-`@copy``src`/`dst` must not contain `->`; the target is the last
@@ -141,7 +144,7 @@ mutations confined to `/tmp` unless the user explicitly asks otherwise.
0 commit comments