Skip to content

fix(cowsay): resolve doctor/verify probe at /usr/games packaged path - #391

Merged
ycpss91255 merged 1 commit into
mainfrom
fix/cowsay-doctor-usr-games
Jul 15, 2026
Merged

fix(cowsay): resolve doctor/verify probe at /usr/games packaged path#391
ycpss91255 merged 1 commit into
mainfrom
fix/cowsay-doctor-usr-games

Conversation

@ycpss91255

Copy link
Copy Markdown
Owner

The bug

The Debian/Ubuntu cowsay package installs its binary at /usr/games/cowsay,
and /usr/games is absent from the default non-login PATH used by bash -c,
docker exec ... bash -c, and cron. Both cowsay health probes used a bare
command -v cowsay:

  • TEST_VERIFY_CMD="command -v cowsay" (consumed by verify via
    module_default_verify -> bash -c "${TEST_VERIFY_CMD}")
  • the doctor() override

So on a correctly-installed cowsay, doctor and verify FALSELY reported
failure while dpkg-based is_installed still reported installed -- an
internally inconsistent installed=yes / doctor=fail state.

Scope: only cowsay installs under /usr/games among landed modules.
cmatrix (/usr/bin/cmatrix) and figlet (/usr/bin/figlet) are on the
default PATH and are unaffected -- untouched.

The fix

Both probes now try PATH first, then fall back to the packaged path:

command -v cowsay >/dev/null 2>&1 || [ -x "${COWSAY_GAMES_BIN:-/usr/games/cowsay}" ]
  • The command -v branch stays first, so a hypothetical PATH-resolvable
    cowsay still wins.
  • COWSAY_GAMES_BIN overrides the packaged path (default /usr/games/cowsay),
    matching the repo's established ${BIN_LINK:-/usr/local/bin/...} seam
    convention, and lets unit tests inject a stub instead of writing to the
    container global filesystem.
  • The doctor() warning now names both places it checked (PATH and the
    packaged path).

The unit spec previously MASKED the bug by faking cowsay onto PATH via the
_fake_bin helper, so it never exercised the real /usr/games location. It
now uses a _fake_games_cowsay helper + COWSAY_GAMES_BIN to simulate the
packaged location and asserts doctor + verify pass when cowsay exists ONLY
there, and still fail when absent from both PATH and /usr/games. A separate
test keeps the PATH-resolvable branch (command -v) covered.

RED-then-GREEN test evidence

RED (new tests against the unmodified module -- the "only at /usr/games"
assertions fail while the absent-case assertions pass):

not ok 32 verify passes via real probe when cowsay exists only at /usr/games
ok 33 verify fails via real probe when cowsay is absent from PATH and /usr/games
not ok 36 doctor passes when cowsay exists only at /usr/games, absent from PATH
ok 37 doctor fails when cowsay is absent from both PATH and /usr/games

GREEN (after the module fix -- just -f justfile.ci test-unit cowsay, all 54
pass):

ok 31 verify passes when installed and TEST_VERIFY_CMD succeeds
ok 32 verify passes via real probe when cowsay exists only at /usr/games
ok 33 verify fails via real probe when cowsay is absent from PATH and /usr/games
ok 34 doctor fails when not installed
ok 35 doctor passes when cowsay resolves on PATH (command -v branch)
ok 36 doctor passes when cowsay exists only at /usr/games, absent from PATH
ok 37 doctor fails when cowsay is absent from both PATH and /usr/games

Gates (Docker, blocking foreground)

  • just -f justfile.ci lint -> ShellCheck OK, fish OK, Hadolint OK
  • just -f justfile.ci test-unit -> exit 0 (full suite)
  • just -f justfile.ci test-integration -> exit 0 (24/24 ok)

Provenance

Found by an adversarial code-review workflow. doc/changelog/CHANGELOG.md
gains an [Unreleased] > Fixed entry. No module METADATA changed, so
doc/module/INDEX.md needs no regeneration.

The Debian/Ubuntu cowsay package installs its binary at /usr/games/cowsay,
and /usr/games is absent from the default non-login PATH used by `bash -c`,
`docker exec ... bash -c`, and cron. The doctor() probe and TEST_VERIFY_CMD
used a bare `command -v cowsay`, so on a correctly-installed cowsay both
falsely reported failure while dpkg-based is_installed still reported
installed -- an internally inconsistent installed=yes / doctor=fail state.

Both probes now try PATH first (`command -v cowsay`) and fall back to the
packaged path (`[ -x /usr/games/cowsay ]`, overridable via COWSAY_GAMES_BIN
for tests). The command -v branch stays first so a PATH-resolvable cowsay
still wins.

The unit spec previously masked the bug by faking cowsay onto PATH. It now
simulates the /usr/games location via COWSAY_GAMES_BIN and asserts doctor +
verify pass when cowsay exists only there, and still fail when absent from
both PATH and /usr/games.
@ycpss91255
ycpss91255 enabled auto-merge (squash) July 15, 2026 11:26
@ycpss91255
ycpss91255 merged commit c91ee60 into main Jul 15, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant