Repository navigation
fix(test): keep a caller's ZPFX out of the test shell - #144
Conversation
zi_test isolated HOME, ZDOTDIR and the XDG directories but passed an exported ZPFX through. Run from a shell that loads Zi, the ice suite's sbin case wrote its shim into the developer's own polaris/bin, pointing at test data the next run deletes, which broke that developer's login shell and failed the suite's own artifact check under ZI_DATA. Set ZPFX under ZI_DATA in the inner shell, add a shell-state case that exports a different ZPFX and checks the inner one, and say what zi_test isolates in docs/writing-tests.md.
The plugins suite runs `go install' in an atclone. With an exported GOPATH it built into the developer's own Go bin directory and module cache; CI exports none, so Go used $HOME/go, the test home the helper already puts on PATH. Set GOPATH there explicitly, check it in the shell-state case, and quote the fixture's comparison so a data path with pattern characters is compared literally.
ss-o
left a comment
There was a problem hiding this comment.
Fallback review under ADR-0026: Copilot request not registered on 26a9bfd
Sole-maintainer exception (ADR-0035). zd is Class 1 and Copilot is configured. Copilot requests at 2026-10-15T05:59:58Z (on e1464cd) and 06:09:33Z on this head both returned no requested reviewer; there is no Copilot review_requested event or review, cause unconfirmed. The maintainer declared that no second human reviewer is available and elected the sole-maintainer exception for #144 at 26a9bfd.
Verdict: no blocking findings. One optional docs suggestion is tracked outside this pull request.
Note
This review was executed by the agent workflow that authored the change. It and the separate reviews below are not a second human review or proof of independent judgment.
Separate reviews. A separate read-only reviewing session reviewed e1464cd and approved with a suggestion (quote the fixture's pattern) and a related leak (GOPATH, used by the go install in tests/plugins.zunit). Both were fixed in 26a9bfd. The same session then reviewed exactly 26a9bfd and approved, with one suggestion: GOBIN and GOMODCACHE are still inherited, so the docs' "stays under $ZI_DATA" holds only while the caller exports neither. Disposition: tracked as a follow-up; setting them here would be a further head for a case no current caller hits.
Checklist
- Source contracts:
zi_testnow passesZPFX="${ZI_DATA}/polaris"(what Zi derives itself, and whattests/ice.zunitand the docs assume) andGOPATH="${HOME}/go"under the test home (Go's default when nothing is exported, and the directory the helper already puts onpath). The docs sentence matches the helper and namesPATHandFPATHas still inherited. - Failure paths: the new shell-state case exports decoy
ZPFXandGOPATHwithlocal -x, scoped to the test function and its subshell. With either helper line removed it fails naming the leaked value; with both it passes. The comparison is quoted, so a data path with pattern characters compares literally. - Cleanup: the read-only Go module cache now created under the test home is removed by the existing
chmod -R u+rwXthenrm -rfin setup and teardown; verified empty afterplugins. - CI and image: no workflow, Dockerfile or
docker/**change. CI exports neither variable, so its effective values are unchanged; merging publishes no image. - Supply chain: no dependency, action or download source changes.
Checks on this head: 45 succeed, 3 skipped by design, none failing.
Verification. Locally, with decoy ZPFX and GOPATH exported: shell-state 5 of 5, ice 5 of 5 (including sbin ice, which failed locally before because of this leak) and plugins 3 of 3; nothing was written to either decoy or to the real Zi and Go directories. Limits: annexes, compat, packages, snippets and utils were not rerun locally for this change (CI ran them); the docker matrix was not run locally.
Recovery. Revert the squash commit; only test helpers, a fixture and one docs sentence change.
Summary
zi_testgave the inner shell its ownHOME,ZDOTDIRand XDG directories but passed an exportedZPFXthrough. Any developer whose shell loads Zi exportsZPFX, so a localmake testran Zi against the developer's own prefix: theicesuite'ssbincase wrote anfzfshim into the realpolaris/bin, pointing at test data the next run deletes. After that every login shell of that developer failed onfzf(seen on 2026-10-15). The same leak is whysbin icefailed locally while passing in CI: its artifact check looks under${ZI_DATA}/polaris.ZPFX="${ZI_DATA}/polaris", the path Zi would derive itself and the one the docs andtests/ice.zunitalready assume.shell-statecase exports a differentZPFX, runszi_test, and checks from a fixture that the innerZPFXis${ZI[HOME_DIR]}/polaris.GOPATH:tests/plugins.zunitrunsgo installin an atclone, which with an exportedGOPATHbuiltgo-md2maninto the developer's own Go bin directory.zi_testnow setsGOPATH="${HOME}/go"under the test home, which is what CI already gets (it exports noGOPATH) and what the helper'spathline assumes. The shell-state case checks both variables, and its comparison is quoted so a data path with pattern characters compares literally.docs/writing-tests.mdsays whatzi_testisolates and thatPATHandFPATHare still inherited.CI does not export
ZPFX, so CI results do not change.Closes #145
Verification
All runs with
ZPFX,ZI_DATAandTMPDIRpointed at a scratch directory, and the real Zi prefix checked unchanged afterwards:zpfx-leaked: ZPFX=.../caller-polaris); the other fourshell-statecases pass.make test FILE=shell-statepasses 5 of 5 andmake test FILE=icepasses 5 of 5, includingsbin ice, which failed locally before; nothing is written to the outerZPFXafter the change.GOPATHline removed, the case fails onGOPATH; with it,shell-state5 of 5 andplugins3 of 3 (including thego installcase) pass with an exported decoyGOPATHandZPFX, and nothing is written to either decoy or to the real Go and Zi directories.zsh -f -npasses ontests/helpers.zshand the new fixture.Agent handoff
No handoff needed.