From a5d07260ca2226d8d8eb7296179b7217cade47e1 Mon Sep 17 00:00:00 2001 From: leonidb Date: Thu, 18 Jun 2026 15:35:19 +0300 Subject: [PATCH] fix: prevent a stray audio_recap/ in the cwd from shadowing the installed plugin The Stop hook and slash commands run `python3 -m audio_recap`, which puts the process working directory first on sys.path. A working directory containing its own `audio_recap/` (a checkout of this repo, or a git worktree) therefore shadowed the installed plugin and ran stale code. run.sh now cd's to the plugin root before exec so the installed package always wins; production never derives its working directory from the process anyway (the Stop hook reads cwd from its stdin payload and the slash commands pass `--cwd`). --- scripts/run.sh | 9 ++++++ tests/test_run_sh_cwd_isolation.py | 52 ++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 tests/test_run_sh_cwd_isolation.py diff --git a/scripts/run.sh b/scripts/run.sh index 2891aa9..b448789 100755 --- a/scripts/run.sh +++ b/scripts/run.sh @@ -35,6 +35,15 @@ else fi export PYTHONPATH="${PLUGIN_ROOT}${PYTHONPATH:+:${PYTHONPATH}}" + +# ``python3 -m`` puts the process working directory first on sys.path, so a +# stray ``audio_recap/`` directory in the user's CWD (a checkout of this repo +# or a git worktree) would shadow the installed plugin and run stale code. +# cd into the plugin root so the installed package always wins. Production +# never derives its working directory from the process anyway: the Stop hook +# reads ``cwd`` from its stdin payload and the slash commands pass ``--cwd "$PWD"``. +cd "$PLUGIN_ROOT" + # ``python3`` resolves via the user's PATH — on macOS that is the # system interpreter (/usr/bin/python3) unless they have installed # another. Audio Recap is stdlib-only and floors at 3.9, so whatever diff --git a/tests/test_run_sh_cwd_isolation.py b/tests/test_run_sh_cwd_isolation.py new file mode 100644 index 0000000..32bec81 --- /dev/null +++ b/tests/test_run_sh_cwd_isolation.py @@ -0,0 +1,52 @@ +"""``scripts/run.sh`` must run the installed package, not a cwd decoy. + +``python3 -m`` puts the process working directory first on ``sys.path``, so a +stray ``audio_recap/`` directory in the user's working directory (a checkout of +this repo, or a worktree) would shadow the installed plugin and run stale code. +``run.sh`` cd's to the plugin root before exec to stop that; this test locks in +the fix end to end. +""" + +from __future__ import annotations + +import shutil +import subprocess +from pathlib import Path + +import pytest + +REPO_ROOT = Path(__file__).resolve().parents[1] +RUN_SH = REPO_ROOT / "scripts" / "run.sh" + + +def _plant_decoy(directory: Path) -> None: + """Plant an ``audio_recap/`` package that exits 123 loudly if imported.""" + + pkg = directory / "audio_recap" + pkg.mkdir() + (pkg / "__init__.py").write_text("", encoding="utf-8") + (pkg / "__main__.py").write_text( + 'import sys\nsys.stdout.write("DECOY\\n")\nsys.exit(123)\n', + encoding="utf-8", + ) + + +@pytest.mark.skipif(shutil.which("bash") is None, reason="bash required to run run.sh") +def test_run_sh_decoy_in_cwd_does_not_shadow_installed_package(tmp_path: Path) -> None: + """Invoked from a cwd holding a decoy ``audio_recap/``, run.sh runs the real one.""" + + _plant_decoy(tmp_path) + + result = subprocess.run( + ["bash", str(RUN_SH), str(REPO_ROOT)], + cwd=tmp_path, + capture_output=True, + text=True, + ) + + output = result.stdout + result.stderr + # The decoy prints DECOY and exits 123; the real package prints its usage + # banner and exits 2 when handed no subcommand. + assert "DECOY" not in output, f"cwd decoy shadowed the installed package:\n{output}" + assert result.returncode == 2, output + assert "usage: python -m audio_recap" in output