From 3982a4b80caf811edd42a32acf7cd2a035ad40a2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 10 Jan 2026 21:21:15 -0500 Subject: [PATCH 1/7] chore(deps): lock file maintenance (#16) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> From 49c35bec4a291b5b072cdd2684f948287b9e6d6d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 10 Jan 2026 21:36:13 -0500 Subject: [PATCH 2/7] chore(deps): lock file maintenance (#17) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> From 03ddd7e57c01bc3b75fa81d12d2c177e1f83bf22 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 28 Jan 2026 16:59:59 -0800 Subject: [PATCH 3/7] chore(deps): lock file maintenance (#21) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Michael From 84e834397037319ab7e5f09fd044140c11157f89 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 14 Feb 2026 18:33:07 -0800 Subject: [PATCH 4/7] chore(deps): lock file maintenance (#23) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Michael From 4cd715e4674e4a5240d8328cd066e00389a1eeb0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 20 Feb 2026 22:23:26 -0800 Subject: [PATCH 5/7] chore(deps): lock file maintenance (#24) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Michael From 89195e433981dd2c28c503affc5ef9a340012488 Mon Sep 17 00:00:00 2001 From: mldangelo Date: Tue, 24 Feb 2026 11:21:16 -0800 Subject: [PATCH 6/7] fix: implement PROMPTFOO_VERSION, add KeyboardInterrupt handling, update nvm version - Implement PROMPTFOO_VERSION env var to allow pinning the npx version (was documented in README but never actually implemented) - Add KeyboardInterrupt handling so Ctrl+C exits cleanly with code 130 instead of printing a Python traceback - Update nvm install script references from v0.39.0 to v0.40.1 Co-Authored-By: Claude Sonnet 4.6 --- src/promptfoo/cli.py | 55 +++++++++++++++++++---------------- src/promptfoo/instructions.py | 12 ++++---- 2 files changed, 36 insertions(+), 31 deletions(-) diff --git a/src/promptfoo/cli.py b/src/promptfoo/cli.py index 7bb0a67..2d35896 100644 --- a/src/promptfoo/cli.py +++ b/src/promptfoo/cli.py @@ -15,6 +15,7 @@ _WRAPPER_ENV = "PROMPTFOO_PY_WRAPPER" _WINDOWS_SHELL_EXTENSIONS = (".bat", ".cmd") +_VERSION_ENV = "PROMPTFOO_VERSION" def check_node_installed() -> bool: @@ -178,33 +179,37 @@ def main() -> NoReturn: Executes promptfoo using subprocess.run() with minimal configuration. """ - # Check for Node.js installation - if not check_node_installed(): - print_installation_help() - sys.exit(1) - - # Build command: try external promptfoo first, fall back to npx - promptfoo_path = None if os.environ.get(_WRAPPER_ENV) else _find_external_promptfoo() - if promptfoo_path: - record_wrapper_used("global") - cmd = [promptfoo_path] + sys.argv[1:] - env = os.environ.copy() - env[_WRAPPER_ENV] = "1" - result = _run_command(cmd, env=env) - else: - npx_path = shutil.which("npx") - if npx_path: - record_wrapper_used("npx") - cmd = [npx_path, "-y", "promptfoo@latest"] + sys.argv[1:] - result = _run_command(cmd) - else: - record_wrapper_used("error") - print("ERROR: Neither promptfoo nor npx is available.", file=sys.stderr) - print("Please install promptfoo: npm install -g promptfoo", file=sys.stderr) - print("Or ensure Node.js is properly installed.", file=sys.stderr) + try: + # Check for Node.js installation + if not check_node_installed(): + print_installation_help() sys.exit(1) - sys.exit(result.returncode) + # Build command: try external promptfoo first, fall back to npx + promptfoo_path = None if os.environ.get(_WRAPPER_ENV) else _find_external_promptfoo() + if promptfoo_path: + record_wrapper_used("global") + cmd = [promptfoo_path] + sys.argv[1:] + env = os.environ.copy() + env[_WRAPPER_ENV] = "1" + result = _run_command(cmd, env=env) + else: + npx_path = shutil.which("npx") + if npx_path: + record_wrapper_used("npx") + version = os.environ.get(_VERSION_ENV, "latest") + cmd = [npx_path, "-y", f"promptfoo@{version}"] + sys.argv[1:] + result = _run_command(cmd) + else: + record_wrapper_used("error") + print("ERROR: Neither promptfoo nor npx is available.", file=sys.stderr) + print("Please install promptfoo: npm install -g promptfoo", file=sys.stderr) + print("Or ensure Node.js is properly installed.", file=sys.stderr) + sys.exit(1) + + sys.exit(result.returncode) + except KeyboardInterrupt: + sys.exit(130) if __name__ == "__main__": diff --git a/src/promptfoo/instructions.py b/src/promptfoo/instructions.py index 0133f05..dfe1994 100644 --- a/src/promptfoo/instructions.py +++ b/src/promptfoo/instructions.py @@ -198,7 +198,7 @@ def _get_wsl_instructions() -> list[str]: "Recommended approach:", " 1. Use your Linux distribution's package manager (see below)", " 2. Or use nvm for version management:", - " curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash", + " curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash", " source ~/.bashrc", " nvm install 20", "", @@ -255,7 +255,7 @@ def _get_debian_instructions(env: Environment) -> list[str]: lines.extend( [ "You don't have sudo access. Use nvm (Node Version Manager):", - " curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash", + " curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash", " source ~/.bashrc", " nvm install 20", ] @@ -290,7 +290,7 @@ def _get_rhel_instructions(env: Environment) -> list[str]: lines.extend( [ "Use nvm (Node Version Manager) - no sudo needed:", - " curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash", + " curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash", " source ~/.bashrc", " nvm install 20", ] @@ -315,7 +315,7 @@ def _get_rhel_instructions(env: Environment) -> list[str]: lines.extend( [ "Use nvm (Node Version Manager) - no sudo needed:", - " curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash", + " curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash", " source ~/.bashrc", " nvm install 20", ] @@ -362,7 +362,7 @@ def _get_generic_linux_instructions() -> list[str]: "Use your package manager to install Node.js, or use nvm:", "", "Option 1 - nvm (Node Version Manager, works on any Linux):", - " curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash", + " curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash", " source ~/.bashrc", " nvm install 20", "", @@ -379,7 +379,7 @@ def _get_macos_instructions() -> list[str]: " brew install node", "", "Option 2 - nvm (Node Version Manager, for version management):", - " curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash", + " curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash", " source ~/.zshrc # or ~/.bashrc", " nvm install 20", "", From b687b6adb0c4c556797f020473325a00c2041cd4 Mon Sep 17 00:00:00 2001 From: mldangelo Date: Tue, 24 Feb 2026 11:31:39 -0800 Subject: [PATCH 7/7] ci: increase test job timeout from 15 to 30 minutes Windows smoke tests hit the 15-minute limit due to npm install + npx download time. 30 minutes gives enough headroom. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f58eeaf..be532a4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -72,7 +72,7 @@ jobs: test: name: Test (py${{ matrix.python-version }}, ${{ matrix.os }}) runs-on: ${{ matrix.os }} - timeout-minutes: 15 + timeout-minutes: 30 strategy: matrix: # Temporarily excluding macos-latest due to GitHub Actions runner resource constraints @@ -152,7 +152,7 @@ jobs: test-npx-fallback: name: Test npx fallback (py${{ matrix.python-version }}, ${{ matrix.os }}) runs-on: ${{ matrix.os }} - timeout-minutes: 15 + timeout-minutes: 30 strategy: matrix: # Test npx fallback (without global install)