Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,10 @@ jobs:
python-version: '3.x'

# pytest is the TEST RUNNER and is the only thing installed here.
# Nothing this repository ships is Python at all — three bash files —
# and the suite itself imports only the standard library.
# Nothing this repository ships needs a Python package: its commands are
# bash, or `python3` (`lanes-index`, `lane-worktrees`) importing only the
# standard library, and the suite itself imports only the standard
# library.
# PINNED. A CI job that resolves "latest" on every run is a job whose
# result depends on the day it ran, which is the same objection this
# family makes to pinning a tag instead of a commit.
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -407,8 +407,8 @@ https credential prompt fails and is reported; an ssh host-key or passphrase
prompt is ssh's own and still blocks, so run `--fetch` where ssh is already
non-interactive.

Windows: all four files are bash, and there is no PowerShell twin. On Windows
the way in is WSL2, exactly as it is for openRepoShape's `setup.sh`.
Windows: no command here has a PowerShell twin. On Windows the way in is WSL2,
exactly as it is for openRepoShape's `setup.sh`.

## The dependency direction

Expand Down Expand Up @@ -477,7 +477,7 @@ refuses.

CI runs the suite on ubuntu only (Brett Heap's decision of 2026-10-15), with one
ubuntu job that checks out WITHOUT submodules to prove the skip path exits 0.
No job parses the four bash files under macOS's bash 3.2 — the last GPLv2
No job parses the shipped bash files under macOS's bash 3.2 — the last GPLv2
release and what Apple still ships — so that claim is no longer tested in CI.

## Licence
Expand Down
4 changes: 2 additions & 2 deletions docs/README-lanes.md
Original file line number Diff line number Diff line change
Expand Up @@ -594,7 +594,7 @@ under, and it is the COUNT and never `pgrep`'s exit status that decides.
`tests/run.sh` is the canonical implementation of this guard — the lines above
are it in one place, for a person with no checkout in front of them.

The suite copies the four commands and the shipped alias table into a sandbox
The suite copies the lane commands and the shipped alias table into a sandbox
BIN DIRECTORY, seeds a workspace repository with nothing but data in it, and
writes that sandbox's own `workspace.yaml` to join the two — which is the
world after Amendment 9, exactly. It unsets every `LANES_*` seam first, so
Expand Down Expand Up @@ -4078,7 +4078,7 @@ machine whose repository already exists it clones it, adds nothing to a seeded
repository, writes the pointer file and runs `link-estates`:

```sh
openRepoTools --install # the thirteen commands, the three skills, the three command files and the hook entry
openRepoTools --install # the commands, the three skills, the three command files and the hook entry
openRepoTools wip init # create or adopt the workspace, and link it
```

Expand Down
8 changes: 7 additions & 1 deletion lanes
Original file line number Diff line number Diff line change
Expand Up @@ -517,7 +517,13 @@ else
local_err_file="$(mktemp "${TMPDIR:-/tmp}/lanes-local.XXXXXX" 2>/dev/null || printf '')"
if [ -n "$local_err_file" ]; then
out="$("$LANES_EDIT" lanes ${idx_args[@]+"${idx_args[@]}"} ${args[@]+"${args[@]}"} 2>"$local_err_file")" || rc=$?
grep -v 'not fetching; reading origin/' -- "$local_err_file" >&2 || :
# `-e` BEFORE THE `--` (#54, the shape #53 fixed at `skills/restart/SKILL.md`):
# with the pattern as a bare operand a getopt that does not permute —
# BSD's, so every macOS shell, and GNU's under `POSIXLY_CORRECT` — takes
# the pattern for the first operand and reads `--` as a second, a FILE it
# cannot open. `-e` makes the pattern an option's argument, so `--` ends
# the options where it says it does.
grep -v -e 'not fetching; reading origin/' -- "$local_err_file" >&2 || :
rm -f -- "$local_err_file"
else
# No capture file, so nothing is filtered and everything reaches the
Expand Down
2 changes: 1 addition & 1 deletion openRepoTools
Original file line number Diff line number Diff line change
Expand Up @@ -1936,7 +1936,7 @@ install_commands() {
# which is the population the marker exists for anyway.
receipt_record
retire_commands
# ONE PATH NOTE for the four of them: the directory is the same one.
# ONE PATH NOTE for everything it placed: the directory is the same one.
case ":${PATH:-}:" in
*":$dir:"*) ;;
*)
Expand Down
47 changes: 43 additions & 4 deletions tests/test_lane_helpers.sh
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@
# stops the call.
#
# WHERE THE FILES ARE, after lane-collision-protocol Amendment 9's move. The
# four commands and the shipped alias table live at the ROOT of
# lane commands and the shipped alias table live at the ROOT of
# `opensoft/openRepoTools` and are INSTALLED into a bin directory; the person's
# workspace repository holds data and no code at all. This suite reproduces
# exactly that: it copies the five files into a sandbox bin directory, seeds a
# exactly that: it copies those files into a sandbox bin directory, seeds a
# workspace repository with nothing but data in it, and writes the sandbox's own
# `$AGENT_PROTOCOL_ROOT/workspace.yaml` to join the two — which is the default
# resolution under test, since `:55` unsets every LANES_* seam so that the
Expand Down Expand Up @@ -495,7 +495,7 @@ STALE_H=4 # lanes-edit.sh's default, Rule 1's four hours

# ------------------------------------------------------------- the register

# THE BIN DIRECTORY — the post-move world, where `--install` places the four
# THE BIN DIRECTORY — the post-move world, where `--install` places the lane
# commands and the shipped alias table as regular files at 755. The workspace
# repository below gets NONE of them: Amendment 9(a), data only.
export OPENREPOTOOLS_BIN_DIR="$SANDBOX/bin"
Expand Down Expand Up @@ -1517,7 +1517,13 @@ is "--force on a stale claim takes it over" "$rc" 0
G2="$(cat "$LOGD/repoG-2.md")"
has "…writing a TAKEOVER line" "$G2" "TAKEOVER — lane repoG-2, session "
has "…referencing the claim it supersedes with ←" "$G2" "opensoft/repoG#8 ← lane:repoG-1"
hasnt "…and no separate CLAIMED line: TAKEOVER is itself an open verb" "$G2" "CLAIMED — lane repoG-2, session .*opensoft/repoG#8"
# A REAL PATTERN, NOT `hasnt`'S FIXED STRING (issue #66): `hasnt` is a `case`
# containment test, so the `.*` this line used to carry to skip the session uuid
# was the two literal characters, never a wildcard — and a log that DID hold a
# separate CLAIMED line for repoG#8 passed it all the same. `grep -cE` over the
# log is the real thing, and it counts the lines, so it can be 1.
is "…and no separate CLAIMED line: TAKEOVER is itself an open verb" \
"$(printf '%s\n' "$G2" | grep -cE '^CLAIMED — lane repoG-2,.*opensoft/repoG#8')" 0

run "$E" who "opensoft/repoG#8"
has "…so the taker now holds it" "$out" "HOLDS lane repoG-2"
Expand Down Expand Up @@ -4905,6 +4911,39 @@ is "…the other line reaches the person UNPREFIXED, exactly as the helper wro
hasnt "…and grep itself never errors on \`--\` as though it were a missing file" \
"$(cat "$SANDBOX/fence3b.err")" "No such file or directory"

# #54 part a — `lanes`' OWN LOCAL READ HAD THE SAME LINE (#53 fixed the one in
# the file above, and `lane`'s copy already carries the `-e`; this was the last
# one without it). Its stderr is kept minus the one no-fetch sentence, through
# `grep -v 'pattern' -- file`, and with the pattern a bare operand a
# non-permuting getopt reads `--` as a second FILE. The stub says the notice
# AND another line, the way the helper does when this workstation's session
# records are unreadable, and exits 8 — "no lane" — which is the shortest way
# through the command that still reaches the filter. `POSIXLY_CORRECT=1` is the
# same stand-in for BSD's `grep` the case above uses: measured against the
# unfixed line, this case printed `grep: --: No such file or directory` and put
# the temp file's own name in front of the real line.
cat > "$SANDBOX/lanesfence" <<'WRAP'
#!/usr/bin/env bash
case "${1-}" in
lanes)
printf 'LANES_NO_FETCH=1 — not fetching; reading origin/main as the ref already stands here\n' >&2
printf 'session records for this workstation could not be read: permission denied\n' >&2
exit 8 ;;
esac
exit 0
WRAP
chmod +x "$SANDBOX/lanesfence"
lanes_fence_rc=0
( cd -- "$SANDBOX" && POSIXLY_CORRECT=1 LANES_EDIT="$SANDBOX/lanesfence" \
"$OPENREPOTOOLS_BIN_DIR/lanes" </dev/null >/dev/null 2>"$SANDBOX/lanesfence.err" ) || lanes_fence_rc=$?
is "lanes under a NON-PERMUTING getopt (BSD's shape) still answers, \"no lane\" being its 8" "$lanes_fence_rc" 8
is "…and the other stderr line reaches the person UNPREFIXED, exactly as the helper wrote it" \
"$(cat "$SANDBOX/lanesfence.err")" "session records for this workstation could not be read: permission denied"
hasnt "…grep itself never errors on \`--\` as though it were a missing file" \
"$(cat "$SANDBOX/lanesfence.err")" "grep: --:"
hasnt "…and the known no-fetch notice is still dropped from it" \
"$(cat "$SANDBOX/lanesfence.err")" "not fetching"

# AND WITH NO CAPTURE FILE AT ALL, NOTHING IS FILTERED — silence in place of a
# stream this fence could not open would be the same defect the notice fix
# exists to correct, only quieter.
Expand Down
17 changes: 9 additions & 8 deletions tests/test_openrepotools_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -2138,8 +2138,9 @@ def test_bin_dir_overrides_where_it_lands(tmp_path):

@NEEDS_JQ
def test_install_says_how_to_put_it_on_path(tmp_path):
"""ONE path note for the four of them: the directory is the same one, and
four copies of the same `export` line reads as four problems."""
"""ONE path note for everything `--install` places: the directory is the
same one, and the same `export` line once per file reads as a problem for
each of them."""
result = run_cmd("--install", home=tmp_path)
assert f'export PATH="{tmp_path}/.local/bin:$PATH"' in result.stdout
assert result.stdout.count("export PATH=") == 1
Expand Down Expand Up @@ -2198,7 +2199,7 @@ def fake_github(tmp_path, served_names) -> dict:
"""A fake `gh` serving exactly `served_names`, and a `curl` that refuses.

Factored out of the fixture so one test can WITHHOLD a file: `--install`
places all four or none, and the only way to prove "or none" is a server
places all of them or none, and the only way to prove "or none" is a server
that cannot answer for one of them.
"""
served = tmp_path / "served"
Expand Down Expand Up @@ -2248,7 +2249,7 @@ def offline_github(tmp_path):
`fetch_from_repo` tries `gh api` before the raw URL, so a `gh` that
answers the calls the command makes is the whole of the server these tests
need: `contents/<command>` comes back as this checkout's own bytes for each
of the four files `--install` places. `curl` is shadowed by a script that
of the files `--install` places. `curl` is shadowed by a script that
exits 1 — belt and braces, so that a fake `gh` which stopped matching could
never quietly become a real request to raw.githubusercontent.com.
"""
Expand All @@ -2260,7 +2261,7 @@ def test_install_places_all_nine_or_none(tmp_path):
on its #83). One file at a time, dying on the first fetch that failed,
leaves a person with a NEW `openRepoTools` and no `park` — a half-install
that prints `installed at` and is not one — with nothing on screen to say
which of the four were missing.
which of them were missing.

Run from stdin with `park` withheld: NOTHING is placed, nothing already
there is replaced, and the refusal names the file it could not fetch.
Expand Down Expand Up @@ -2335,9 +2336,9 @@ def test_install_from_stdin_fetches_itself_into_a_live_workdir(offline_github,
bash -s -- --install`.

Run from stdin there is no file to copy from — not for this command, not
for its three siblings and not for the four lane helpers, the alias table
or the skill — so `install_commands` fetches each of the ten at this ref
into a temporary directory. THAT DIRECTORY HAS TO STILL BE THERE:
for its siblings and not for the lane helpers, the alias table or the
skill — so `install_commands` fetches each of them at this ref into a
temporary directory. THAT DIRECTORY HAS TO STILL BE THERE:
`workdir()` sets its EXIT trap in the main shell rather than inside a
`$(...)` subshell, whose trap would fire the instant the substitution
closed and take the directory away before a single fetched byte had landed
Expand Down
11 changes: 6 additions & 5 deletions tests/test_upstream_pin.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,12 @@ def pin_scalars() -> dict:
"""Every `key: value` in the pin file, at any indent, comments stripped.

A ~15-line reader rather than a YAML dependency: this repository ships
three bash files and installs nothing, and the file it reads is a flat map
with one nested key written by hand. The nesting is flattened onto the leaf
name (`tree_sha256`), which is unambiguous here because no leaf name
repeats — asserted below, so a second `tree_sha256` under some other block
would fail rather than shadow this one.
bash and standard-library `python3` commands and installs no package, and
the file it reads is a flat map with one nested key written by hand. The
nesting is flattened onto the leaf name (`tree_sha256`), which is
unambiguous here because no leaf name repeats — asserted below, so a
second `tree_sha256` under some other block would fail rather than shadow
this one.
"""
values: dict = {}
for raw in PIN.read_text(encoding="utf-8").splitlines():
Expand Down
Loading