usage: paired sessions start after the setting-up session; no prelude block
commit 28b2b86895726775f16da5180a1f43db797a8d33 · parent d231dd1498
· browse this tree
Ximianth, 2026-10-27 12:41:53 UTC
The `paired/prelude` block repeated, a few lines later, what the setting-up session had just shown, and read as a confusing second setup. Paired sessions now start where `fresh/setting-up/session` leaves off: its commands are run first, quietly, so the guide's "start as though you'd just done that" is literally what happens, and the block is gone. Finding a session's commands is one function now, shared by the runner and the prelude, rather than two copies of the prompt pattern. Shown output also has its trailing whitespace stripped. Blank context lines in diffs are a single space; an editor that trims trailing whitespace on save turned those into empty lines, and every session showing a diff failed. Trailing whitespace is invisible in the rendered doc, so the doc no longer carries any, and editing it can't break it. Every session's output is otherwise unchanged.
diff --git a/docs/userguide/USAGE.md b/docs/userguide/USAGE.md index 3c13e39..79abaa1 100644 --- a/docs/userguide/USAGE.md +++ b/docs/userguide/USAGE.md @@ -34,14 +34,7 @@ Pairing is the one thing patchette keeps in your repo: a single value, in `.git/ Delete that file to un-pair. The sessions in the rest of this guide each start as though you'd just done that -(apart from the last two sections, which start unpaired, from `~`): - -[testmark]:# (paired/prelude) -```bash -cd ~/src/frob -git init -q -b main ../frob-patches -patchette pair ../frob-patches -``` +(apart from the last two sections, which start unpaired, from `~`). Taking snapshots @@ -100,7 +93,7 @@ index a330cd687e7fdede54bbe0aca5df47c06d2e8b64..c195933372242cdbb3c014665ebcff2b +++ b/README.md @@ -3,3 +3,5 @@ Upstream docs. - + More upstream docs. + +Downstream notes: be kind to the machinery. @@ -151,11 +144,11 @@ index 8b0dda3..1f6c371 100644 author Ada Downstream <ada@downstream.example> 1700000720 +0000 -committer Ada Downstream <ada@downstream.example> 1700000720 +0000 +committer Ada Downstream <ada@downstream.example> 1700086400 +0000 - + ---- > Mention the oiling schedule ---- - + diff --git a/README.md b/README.md -index c195933372242cdbb3c014665ebcff2bb3fb4449..073a43988cb91f0fe493964670f6d52517082346 100644 +index c195933372242cdbb3c014665ebcff2bb3fb4449..61f9c849c8c9962d481e8ce3874abcc47a640eba 100644 @@ -163,7 +156,7 @@ index 8b0dda3..1f6c371 100644 +++ b/README.md @@ -5,3 +5,4 @@ Upstream docs. More upstream docs. - + Downstream notes: be kind to the machinery. -+Oil the gears weekly. ++Oil the gears daily. diff --git a/tests/test-usage.sh b/tests/test-usage.sh index 35e09bb..81d87c1 100755 --- a/tests/test-usage.sh +++ b/tests/test-usage.sh @@ -2,40 +2,53 @@ ## Runs the usage guide's examples. Each `.../session` hunk in docs/userguide/USAGE.md ## is a terminal session: its prompt lines (`~/dir$ command`, or `/dir$ command`) are run in order, in a fresh sandbox, ## and the whole session as it ran -- prompts, commands, output -- must match the hunk. -## A `paired/NAME/session` starts after the doc's `paired/prelude` hunk has run; a `fresh/NAME/session` starts from the bare sandbox. +## A `paired/NAME/session` starts where `fresh/setting-up/session` leaves off (its commands are run first, quietly); a `fresh/NAME/session` starts from the bare sandbox. ## `snapshot/session` is a paired session that also shows two of the files it wrote. +## Shown output has trailing whitespace stripped: it's invisible in the rendered doc, and editors that trim it on save would otherwise break the checks. ## Run with TESTMARK_REGEN=1 to rewrite the sessions from what patchette does now; review with `git diff`. # No set -e up here: testmark-bash mis-reports failing cases under it. Cases run under set -euo pipefail regardless. source "${TESTMARK_BASH:-$(dirname "${BASH_SOURCE[0]}")/../../../warplibs/testmark-bash}/lib/testmark.bash" || exit 1 -## run_prelude runs a prelude script's commands in this shell, one line each, quietly: -## a session's starting point, so its `cd`s and settings carry into the session. -## Blank lines and `#` comments are skipped. +## session_commands prints a session's commands, one per line: its prompt lines (`~/dir$ command`, or `/dir$ command`), without their prompts. +## Everything else in a session is output, and skipped. +## +## - @stdin a session +## - @stdout its commands +session_commands() { + local prompt_re='^[~/][^$]*\$ (.*)$' line + while IFS= read -r line; do + if [[ $line =~ $prompt_re ]]; then printf '%s\n' "${BASH_REMATCH[1]}"; fi + done +} + +## run_prelude runs another session's commands in this shell, quietly: the starting point for a session, +## so its `cd`s and settings carry into the session. ## A failing command stops it, printing the command and its output on stderr, so the case fails, even under regen; ## the caller then passes the session through untouched, so regen can't write over it. ## -## - @param $1 path to the prelude script +## - @param $1 path to the prelude session ## - @stderr on failure only: the failed command, its output, and its exit status run_prelude() { - local line rc capture + local cmd rc capture + local -a cmds capture=$(mktemp) - while IFS= read -r line; do - [[ -n $line && $line != '#'* ]] || continue + mapfile -t cmds < <(session_commands <"$1") + for cmd in "${cmds[@]}"; do rc=0 - eval "$line" </dev/null >"$capture" 2>&1 || rc=$? + eval "$cmd" </dev/null >"$capture" 2>&1 || rc=$? if [[ $rc -ne 0 ]]; then - >&2 printf 'prelude command failed, exit status %s: %s\n' "$rc" "$line" + >&2 printf 'prelude command failed, exit status %s: %s\n' "$rc" "$cmd" >&2 cat "$capture" rm -f "$capture" return 1 fi - done <"$1" + done rm -f "$capture" } ## replay_session runs a session's commands in this shell, in order, and prints the session as it ran. ## Each command is printed after a prompt naming the cwd (as `~`-relative), then what it printed, -## with the home dir shown as `~` there too; a nonzero exit adds an `[exit status: N]` line. +## with the home dir shown as `~` there too, and trailing whitespace stripped; a nonzero exit adds an `[exit status: N]` line. ## A blank line separates each command from the output before it. ## The old session's other lines (its old output, and blank lines) are dropped: they're what gets regenerated. ## Commands get /dev/null on stdin, so nothing can wait on input. @@ -43,31 +56,28 @@ run_prelude() { ## - @stdin the session, as written ## - @stdout the session, as it ran replay_session() { - local prompt_re='^[~/][^$]*\$ (.*)$' line cmd rc out sep='' - local -a lines - local capture + local cmd rc out sep='' capture + local -a cmds capture=$(mktemp) - mapfile -t lines - for line in "${lines[@]}"; do - [[ $line =~ $prompt_re ]] || continue - cmd=${BASH_REMATCH[1]} + mapfile -t cmds < <(session_commands) + for cmd in "${cmds[@]}"; do printf '%s%s$ %s\n' "$sep" "${PWD/#"$HOME"/"~"}" "$cmd" sep=$'\n' rc=0 eval "$cmd" </dev/null >"$capture" 2>&1 || rc=$? - out=$(<"$capture") + out=$(sed 's/[[:space:]]*$//' "$capture") [[ -z $out ]] || printf '%s\n' "${out//"$HOME"/"~"}" [[ $rc -eq 0 ]] || printf '[exit status: %s]\n' "$rc" done rm -f "$capture" } -## run_in_sandbox runs the case's session in a fresh sandbox home, after a prelude hunk, if one is named. +## run_in_sandbox runs the case's session in a fresh sandbox home, after another session's commands, if one is named. ## The sandbox: `~/upstream/frob` is the upstream, and `~/src/frob` a clone of it, with `main` checked out, ## carrying the branches `topic/fancy`, `docs-polish`, and `odd-bytes` (see make_corpus). ## Git's identity and clock are pinned, so commits made in a session are reproducible. ## -## - @param $1 optional: the doc hunk holding the prelude +## - @param $1 optional: the doc hunk of the session to start after run_in_sandbox() { source "$TESTCASE_DIR/lib.sh" testcase_withtmpdir @@ -85,7 +95,7 @@ run_in_sandbox() { if [[ -n ${1:-} ]]; then testcase_doc_hunk "$1" >"$prelude"; fi cd "$HOME" testcase_hunk session | - bash --norc --noprofile -c "$(declare -f run_prelude replay_session); if run_prelude \"\$1\"; then replay_session; else cat; exit 1; fi" _ "$prelude" | + bash --norc --noprofile -c "$(declare -f session_commands run_prelude replay_session); if run_prelude \"\$1\"; then replay_session; else cat; exit 1; fi" _ "$prelude" | testcase_output session } @@ -94,17 +104,17 @@ exercise_fresh() { } exercise_paired() { - run_in_sandbox paired/prelude + run_in_sandbox fresh/setting-up/session } ## exercise_snapshot runs the first-export session, then shows two of the files it wrote, each as an output of its own. exercise_snapshot() { - run_in_sandbox paired/prelude - testcase_output STACK <"$HOME/src/frob-patches/docs-polish/STACK" - testcase_output polish-the-readme.patch <"$HOME/src/frob-patches/docs-polish/polish-the-readme.patch" + run_in_sandbox fresh/setting-up/session + sed 's/[[:space:]]*$//' "$HOME/src/frob-patches/docs-polish/STACK" | testcase_output STACK + sed 's/[[:space:]]*$//' "$HOME/src/frob-patches/docs-polish/polish-the-readme.patch" | testcase_output polish-the-readme.patch } -testmarktest_provide run_prelude replay_session run_in_sandbox +testmarktest_provide session_commands run_prelude replay_session run_in_sandbox testmarktest_rule 'fresh/*/session' exercise_fresh testmarktest_rule 'paired/*/session' exercise_paired testmarktest_rule 'snapshot/session' exercise_snapshot