fix(mkrep): stop the test suite creating real repos, and confirm implicit ones #152

Merged
rootiest merged 1 commits from fix/mkrep-implicit-remote-confirm into main 2026-09-16 08:20:06 +00:00
Owner

The visible symptom

tests/run-tests.fish reported 6 failing mkrep assertions — default run exits 0, cwd entered, --no-cd exits 0, --clean exits 0, --clean --strict together succeeds, -s exits 0 — each failing with Error: no available login from tea.

Running the suite on its own reported something different: 63/64, with a single unrelated failure (bare $GITEA_URL creates no remote). That split is what made it look flaky.

What was actually happening

mkrep resolves a server from $GIT_SERVER plus $GITEA_URL/$GITEA_HOST, and this repository doubles as a real ~/.config/fish where all of them are exported for day-to-day use. Left ambient, the six sections that call a bare mkrep <dir> with no remote flag took the auto-create branch and contacted the live forge.

It succeeded at least once. An empty rootiest/repo existed on git.rootiest.dev, created_at 2026-09-14T23:49:50Z — created by a test run during #150's development. It has been deleted.

That also explains the two different results:

Mode XDG_CONFIG_HOME tea Outcome
Standalone real authenticated repo already existed → mkrep linked instead of creating → exit 0, passed
run-tests.fish throwaway Error: no available login exit 1, 6 failures

The standalone run was green because an earlier run had already created the repo. A test that passes for that reason is worse than one that fails.

Fix 1 — test isolation

Five variables neutralized once at suite setup, using the same shadow-to-empty pattern the suite already applied per-section for --check-existing — whose comment flagged this exact hazard ("these are real exported vars in this dev environment"). Empty reads as unset to mkrep, so real globals are untouched, and each isolated suite is its own fish --no-config process, so nothing leaks between suites.

Nothing depended on the ambient value: every section that wants a server sets its own set -lx GIT_SERVER gitea, and one section asserts the opposite outright ($GITEA_URL alone (no $GIT_SERVER) does not trigger anything — the standalone failure).

Also corrected a stale header comment claiming the suite "runs autoloaded straight in the driver's own process". run-tests.fish:113 runs it as fish --no-config $suite. That claim is what one consults to judge whether a suite-wide set -gx is safe, so it mattered here.

Fix 2 — confirm an implicit remote-create

The test bug was a symptom of a sharp edge in mkrep. Creating a repository on a forge is the only outward-facing thing mkrep does, and on the $GIT_SERVER path an exported variable is all it takes to reach it — so mkrep foo, which reads as purely local, would make a repo on a server without ever saying so.

It now asks first, defaulting to no:

?  Create new remote rootiest/repo on gitea? [y/N]

Provenance gates the prompt, not the resolved value. --server, --remote and --new-remote each state outright what they are going to do, so none of them prompts; only an ambient $GIT_SERVER does. --yes/-y skips the question.

  • Declining leaves the local repo in place with no remote and still exits 0 — the local work succeeded; only the surprising part was skipped.
  • Where it cannot be asked (a script, a pipe, any non-interactive shell) creation is skipped rather than assumed, with a note on stderr naming the flags that would allow it. That note is deliberately not gated on -s: declining something the caller may expect is a diagnostic, and mkrep already writes its errors to stderr regardless of -s.

Linking an already-existing repo is unaffected, as is --check-existing.

Testing

750/750 assertions pass (mkrep 73/73, up from 64), 249/249 syntax, 249/249 indent, docs 84/84.

Nine new assertions cover the non-interactive skip, its stderr note, --yes, and --server not prompting. The non-interactive case is itself one of the behaviors under test, since the suite runs under fish --no-config.

The interactive read is not in the suite — that needs a pseudo-terminal, and the answer parsing it guards is a single string match. It was verified by hand under script(1), both answers, against a stubbed $MKREP_REMOTE_CMD:

n → → Skipped creating rootiest/repo on gitea — pass --yes or --server gitea to create it
    EXIT=0  RESULT=skipped
y → ✔ Created new remote https://gitea.example.invalid/rootiest/repo.git on gitea
    EXIT=0  RESULT=created

Behavior change

mkrep <dir> with $GIT_SERVER set no longer creates a remote unprompted. Scripts relying on that need --yes (or --server <type>). The path is one day old, so the blast radius is small, but it is a real change and not just a new flag.

## The visible symptom `tests/run-tests.fish` reported 6 failing `mkrep` assertions — `default run exits 0`, `cwd entered`, `--no-cd exits 0`, `--clean exits 0`, `--clean --strict together succeeds`, `-s exits 0` — each failing with `Error: no available login` from `tea`. Running the suite on its own reported something different: 63/64, with a *single* unrelated failure (`bare $GITEA_URL creates no remote`). That split is what made it look flaky. ## What was actually happening mkrep resolves a server from `$GIT_SERVER` plus `$GITEA_URL`/`$GITEA_HOST`, and this repository doubles as a real `~/.config/fish` where all of them are exported for day-to-day use. Left ambient, the six sections that call a bare `mkrep <dir>` with no remote flag took the **auto-create branch and contacted the live forge**. **It succeeded at least once.** An empty `rootiest/repo` existed on `git.rootiest.dev`, `created_at 2026-09-14T23:49:50Z` — created by a test run during #150's development. It has been deleted. That also explains the two different results: | Mode | `XDG_CONFIG_HOME` | `tea` | Outcome | |---|---|---|---| | Standalone | real | authenticated | repo already existed → mkrep linked instead of creating → **exit 0, passed** | | `run-tests.fish` | throwaway | `Error: no available login` | **exit 1, 6 failures** | The standalone run was green *because* an earlier run had already created the repo. A test that passes for that reason is worse than one that fails. ## Fix 1 — test isolation Five variables neutralized once at suite setup, using the same shadow-to-empty pattern the suite already applied per-section for `--check-existing` — whose comment flagged this exact hazard ("these are real exported vars in this dev environment"). Empty reads as unset to mkrep, so real globals are untouched, and each isolated suite is its own `fish --no-config` process, so nothing leaks between suites. Nothing depended on the ambient value: every section that wants a server sets its own `set -lx GIT_SERVER gitea`, and one section asserts the opposite outright (`$GITEA_URL alone (no $GIT_SERVER) does not trigger anything` — the standalone failure). Also corrected a stale header comment claiming the suite "runs autoloaded straight in the driver's own process". `run-tests.fish:113` runs it as `fish --no-config $suite`. That claim is what one consults to judge whether a suite-wide `set -gx` is safe, so it mattered here. ## Fix 2 — confirm an implicit remote-create The test bug was a symptom of a sharp edge in mkrep. Creating a repository on a forge is the only outward-facing thing mkrep does, and on the `$GIT_SERVER` path an exported variable is all it takes to reach it — so `mkrep foo`, which reads as purely local, would make a repo on a server without ever saying so. It now asks first, defaulting to no: ``` ? Create new remote rootiest/repo on gitea? [y/N] ``` **Provenance gates the prompt, not the resolved value.** `--server`, `--remote` and `--new-remote` each state outright what they are going to do, so none of them prompts; only an ambient `$GIT_SERVER` does. `--yes`/`-y` skips the question. - Declining leaves the local repo in place with no remote and still exits **0** — the local work succeeded; only the surprising part was skipped. - Where it cannot be asked (a script, a pipe, any non-interactive shell) creation is **skipped rather than assumed**, with a note on stderr naming the flags that would allow it. That note is deliberately not gated on `-s`: declining something the caller may expect is a diagnostic, and mkrep already writes its errors to stderr regardless of `-s`. Linking an already-existing repo is unaffected, as is `--check-existing`. ## Testing 750/750 assertions pass (`mkrep` 73/73, up from 64), 249/249 syntax, 249/249 indent, docs 84/84. Nine new assertions cover the non-interactive skip, its stderr note, `--yes`, and `--server` not prompting. The non-interactive case is itself one of the behaviors under test, since the suite runs under `fish --no-config`. The interactive `read` is **not** in the suite — that needs a pseudo-terminal, and the answer parsing it guards is a single `string match`. It was verified by hand under `script(1)`, both answers, against a stubbed `$MKREP_REMOTE_CMD`: ``` n → → Skipped creating rootiest/repo on gitea — pass --yes or --server gitea to create it EXIT=0 RESULT=skipped y → ✔ Created new remote https://gitea.example.invalid/rootiest/repo.git on gitea EXIT=0 RESULT=created ``` ## Behavior change `mkrep <dir>` with `$GIT_SERVER` set no longer creates a remote unprompted. Scripts relying on that need `--yes` (or `--server <type>`). The path is one day old, so the blast radius is small, but it is a real change and not just a new flag.
rootiest added the Kind/BugArea/FunctionsArea/DocsArea/Tests labels 2026-09-16 08:18:00 +00:00
rootiest added 1 commit 2026-09-16 08:18:02 +00:00
Two related defects on the `$GIT_SERVER` auto-create path added in #150.

The test suite was creating repositories on a live forge. mkrep resolves a
server from `$GIT_SERVER` plus `$GITEA_URL`/`$GITEA_HOST`, and this repo
doubles as a real ~/.config/fish where all of them are exported, so the six
sections that call a bare `mkrep <dir>` took the auto-create branch and
contacted the server. That is how an empty `rootiest/repo` came to exist on
git.rootiest.dev on 2026-09-14 (since deleted). It also explains why those
cases looked flaky rather than broken: standalone they passed, because the
repo existed and mkrep linked instead of creating, while under
run-tests.fish they failed with `Error: no available login` — a throwaway
XDG_CONFIG_HOME leaves `tea` with no credentials. Passing for that reason is
worse than failing.

The suite now neutralizes all five variables once at setup, the same
shadow-to-empty pattern already used per-section for --check-existing, whose
comment flagged this exact hazard. Nothing depended on the ambient value:
every section wanting a server sets its own `set -lx GIT_SERVER`, and one
section asserts the opposite outright.

mkrep itself now confirms before an implicit create. Creating a repository
on a forge is the only outward-facing thing mkrep does, and on this path an
exported variable is all it takes to reach it, so `mkrep foo` — which reads
as purely local — would make a repo on a server without saying so. It asks
first, defaulting to no; declining leaves the local repo with no remote and
still exits 0. Provenance is what gates the prompt, not the resolved value:
--server, --remote and --new-remote all state outright what they will do, so
none of them prompts, and --yes skips the question. Where it cannot be asked
(a script, a pipe, any non-interactive shell) creation is skipped rather than
assumed, with a note on stderr naming the flags that would allow it.

Nine new assertions cover the skip, its stderr note, --yes, and --server not
prompting. The interactive read is verified by hand under a PTY, both
answers, but is not in the suite: that needs a pseudo-terminal, and the
answer parsing it guards is a single `string match`.
rootiest merged commit a35ddf9e96 into main 2026-09-16 08:20:06 +00:00
rootiest deleted branch fix/mkrep-implicit-remote-confirm 2026-09-16 08:20:07 +00:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: rootiest/fish-config#152