feat(install): curl | sh one-liner installer for vouch-kb#216
Conversation
curl -fsSL https://raw.githubusercontent.com/vouchdev/vouch/main/install.sh | sh a posix sh script (shellcheck-clean, dash-tested) that: * picks a python >= 3.11 from common candidates (python3.13/3.12/3.11/python3) * installs pipx into user site if missing, no sudo * `pipx install vouch-kb` (or upgrade-in-place when already present) * smoke-tests with `vouch --version` * detects ~/.claude or the `claude` CLI on PATH and points at `vouch install-mcp claude-code` for the per-project wiring step flags: --version X.Y.Z to pin, --no-claude to skip the nudge, --quiet, --help. honours NO_COLOR. re-runnable. ci workflow .github/workflows/install-sh.yml runs shellcheck + a dash syntax check + an end-to-end smoke install on ubuntu-latest that actually exercises the published pypi wheel — catches install-path breakage that the regular pytest run misses. readme install section updated to lead with the one-liner.
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughAdds ChangesPOSIX sh installer, README, and CI
Sequence Diagram(s)sequenceDiagram
actor User
participant installer as install.sh
participant Python as Python 3.11+
participant pipx
participant vouch
User->>installer: curl .../install.sh | sh
installer->>Python: probe python3.13 through python3
Python-->>installer: interpreter path
installer->>pipx: check PATH, install via pip --user if absent
pipx-->>installer: ready, bin dir prepended to PATH
installer->>pipx: pipx install or upgrade vouch-kb
pipx-->>installer: vouch-kb installed
installer->>vouch: vouch --version smoke test
vouch-->>installer: version string
installer->>User: next steps and optional Claude Code wiring nudge
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/install-sh.yml:
- Line 1: Harden the GitHub Actions workflow supply chain security by making
three changes: First, pin all action references from mutable versions (such as
v4, v5) to specific commit SHAs instead of version tags throughout the workflow.
Second, add an explicit permissions block at the workflow level to restrict the
default GITHUB_TOKEN scope to only the minimum required permissions. Third,
locate the actions/checkout action and add the persist-credentials: false
parameter to disable credential persistence and reduce exposure if the workflow
is compromised.
In `@install.sh`:
- Around line 180-189: The script currently constructs the `target` variable
with the pinned version but then ignores it when upgrading an existing package,
causing the `--version` flag to be disregarded. Restructure the conditional
logic to check `PIN_VERSION` first: if PIN_VERSION is set, always perform a
pinned install with the `target` variable using pipx install --force, bypassing
the upgrade path entirely. Otherwise, if the package already exists (when
PIN_VERSION is not set), proceed with the upgrade attempt; if upgrade fails or
the package doesn't exist, fall back to a fresh install without a version pin.
- Around line 143-146: The early return statement in the has_cmd pipx check
prevents the PATH normalization for PIPX_BIN_DIR from executing when pipx is
already installed, making vouch unreachable during smoke_test. Move the code
that normalizes PIPX_BIN_DIR in the current process (typically involving PATH
updates) to execute before the early return in the has_cmd pipx conditional
block, ensuring the PATH is always updated to include pipx's bin directory
regardless of whether pipx pre-exists on the system.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: d0f27d8a-d32b-4ac3-9b6a-7f26f17a96ff
📒 Files selected for processing (3)
.github/workflows/install-sh.ymlREADME.mdinstall.sh
| @@ -0,0 +1,63 @@ | |||
| name: install-sh | |||
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify unpinned actions and missing workflow hardening in this file.
rg -n '^\s*uses:\s*[^@]+@v[0-9]+' .github/workflows/install-sh.yml
rg -n '^\s*permissions:' .github/workflows/install-sh.yml
rg -n 'actions/checkout@' .github/workflows/install-sh.yml -A3 -B1Repository: vouchdev/vouch
Length of output: 275
Harden workflow supply-chain controls: pin actions to commit SHAs, restrict token scope, and disable credential persistence.
The workflow uses mutable action versions (v4, v5) and leaves default token permissions unset. Additionally, actions/checkout@v4 does not explicitly disable credential persistence, increasing exposure if the workflow is compromised.
Suggested hardening
name: install-sh
+permissions:
+ contents: read
@@
- - uses: actions/checkout@v4
+ - uses: actions/checkout@<pinned-commit-sha>
+ with:
+ persist-credentials: false
@@
- - uses: actions/checkout@v4
+ - uses: actions/checkout@<pinned-commit-sha>
+ with:
+ persist-credentials: false
- - uses: actions/setup-python@v5
+ - uses: actions/setup-python@<pinned-commit-sha>Applies to: lines 1, 29–29, 45–47
🧰 Tools
🪛 zizmor (1.25.2)
[warning] 1-64: overly broad permissions (excessive-permissions): default permissions used due to no permissions: block
(excessive-permissions)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/install-sh.yml at line 1, Harden the GitHub Actions
workflow supply chain security by making three changes: First, pin all action
references from mutable versions (such as v4, v5) to specific commit SHAs
instead of version tags throughout the workflow. Second, add an explicit
permissions block at the workflow level to restrict the default GITHUB_TOKEN
scope to only the minimum required permissions. Third, locate the
actions/checkout action and add the persist-credentials: false parameter to
disable credential persistence and reduce exposure if the workflow is
compromised.
Source: Linters/SAST tools
| if has_cmd pipx; then | ||
| ok "pipx already installed ($(pipx --version 2>/dev/null | head -1))" | ||
| return 0 | ||
| fi |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
git ls-files | grep -E "install\.sh$"Repository: vouchdev/vouch
Length of output: 69
🏁 Script executed:
wc -l install.shRepository: vouchdev/vouch
Length of output: 73
🏁 Script executed:
sed -n '130,220p' install.sh | cat -nRepository: vouchdev/vouch
Length of output: 3573
🏁 Script executed:
sed -n '155,210p' install.sh | cat -nRepository: vouchdev/vouch
Length of output: 2156
🏁 Script executed:
sed -n '1,50p' install.sh | cat -nRepository: vouchdev/vouch
Length of output: 2020
🏁 Script executed:
sed -n '195,210p' install.sh | cat -nRepository: vouchdev/vouch
Length of output: 582
🏁 Script executed:
# Check the main script flow to see how these functions are called
sed -n '230,264p' install.sh | cat -nRepository: vouchdev/vouch
Length of output: 1367
Move PATH normalization outside the pre-existence check to ensure pipx's bin dir is always accessible.
When pipx already exists on the system, the function returns early before normalizing PIPX_BIN_DIR in the current process. This leaves vouch unreachable when smoke_test() runs, causing the installation to fail even though vouch was successfully installed.
Proposed fix
ensure_pipx() {
py="$1"
- if has_cmd pipx; then
- ok "pipx already installed ($(pipx --version 2>/dev/null | head -1))"
- return 0
- fi
- info "pipx not found — installing into user site (no sudo)"
- if ! "$py" -m pip install --user --upgrade pipx >/dev/null 2>&1; then
- err "pip install --user pipx failed — try installing it manually"
- err " sudo apt install pipx # Debian/Ubuntu"
- err " brew install pipx # macOS"
- return 1
+ if has_cmd pipx; then
+ ok "pipx already installed ($(pipx --version 2>/dev/null | head -1))"
+ else
+ info "pipx not found — installing into user site (no sudo)"
+ if ! "$py" -m pip install --user --upgrade pipx >/dev/null 2>&1; then
+ err "pip install --user pipx failed — try installing it manually"
+ err " sudo apt install pipx # Debian/Ubuntu"
+ err " brew install pipx # macOS"
+ return 1
+ fi
+ "$py" -m pipx ensurepath >/dev/null 2>&1 || true
fi
- "$py" -m pipx ensurepath >/dev/null 2>&1 || true
- # `ensurepath` edits ~/.profile / ~/.zshrc to add pipx's bin dir, but the
- # current process's PATH is already fixed. Add it explicitly so the rest
- # of this script can find `vouch`.
- pipx_bin=$("$py" -m pipx environment --value PIPX_BIN_DIR 2>/dev/null || true)
+ # Always normalize current-process PATH to include pipx app binaries.
+ pipx_bin=$(pipx environment --value PIPX_BIN_DIR 2>/dev/null || "$py" -m pipx environment --value PIPX_BIN_DIR 2>/dev/null || true)
if [ -z "$pipx_bin" ]; then
pipx_bin="$HOME/.local/bin"
fi🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@install.sh` around lines 143 - 146, The early return statement in the has_cmd
pipx check prevents the PATH normalization for PIPX_BIN_DIR from executing when
pipx is already installed, making vouch unreachable during smoke_test. Move the
code that normalizes PIPX_BIN_DIR in the current process (typically involving
PATH updates) to execute before the early return in the has_cmd pipx conditional
block, ensuring the PATH is always updated to include pipx's bin directory
regardless of whether pipx pre-exists on the system.
| if [ -n "$PIN_VERSION" ]; then | ||
| target="${PKG_NAME}==${PIN_VERSION}" | ||
| fi | ||
|
|
||
| if pipx list 2>/dev/null | grep -q "package $PKG_NAME"; then | ||
| info "upgrading existing $PKG_NAME" | ||
| if ! pipx upgrade --pip-args="" "$PKG_NAME" >/dev/null 2>&1; then | ||
| warn "pipx upgrade failed — falling back to reinstall" | ||
| pipx install --force "$target" >/dev/null | ||
| fi |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, locate and inspect install.sh
find . -name "install.sh" -type fRepository: vouchdev/vouch
Length of output: 71
🏁 Script executed:
# Read the install.sh file with context around lines 180-189
head -c 20000 install.sh | tail -c 15000 | cat -nRepository: vouchdev/vouch
Length of output: 9894
🏁 Script executed:
# Also search for PIN_VERSION and --version handling
rg "PIN_VERSION|--version" install.sh -B 2 -A 2Repository: vouchdev/vouch
Length of output: 1590
--version pin is ignored when vouch-kb is already installed.
When re-running the installer with --version X.Y.Z on an already-installed package, the script upgrades to the latest version instead of respecting the pin. The target variable (containing vouch-kb==X.Y.Z) is only used if the upgrade fails, breaking the documented --version behavior.
Fix: Check for PIN_VERSION first. If set, always perform a pinned install; otherwise, upgrade existing or install fresh.
Proposed fix
install_vouch() {
target="$PKG_NAME"
if [ -n "$PIN_VERSION" ]; then
target="${PKG_NAME}==${PIN_VERSION}"
fi
- if pipx list 2>/dev/null | grep -q "package $PKG_NAME"; then
+ if [ -n "$PIN_VERSION" ]; then
+ info "installing pinned $target"
+ pipx install --force "$target" >/dev/null
+ elif pipx list 2>/dev/null | grep -q "package $PKG_NAME"; then
info "upgrading existing $PKG_NAME"
if ! pipx upgrade --pip-args="" "$PKG_NAME" >/dev/null 2>&1; then
warn "pipx upgrade failed — falling back to reinstall"
pipx install --force "$target" >/dev/null
fi📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if [ -n "$PIN_VERSION" ]; then | |
| target="${PKG_NAME}==${PIN_VERSION}" | |
| fi | |
| if pipx list 2>/dev/null | grep -q "package $PKG_NAME"; then | |
| info "upgrading existing $PKG_NAME" | |
| if ! pipx upgrade --pip-args="" "$PKG_NAME" >/dev/null 2>&1; then | |
| warn "pipx upgrade failed — falling back to reinstall" | |
| pipx install --force "$target" >/dev/null | |
| fi | |
| if [ -n "$PIN_VERSION" ]; then | |
| target="${PKG_NAME}==${PIN_VERSION}" | |
| fi | |
| if [ -n "$PIN_VERSION" ]; then | |
| info "installing pinned $target" | |
| pipx install --force "$target" >/dev/null | |
| elif pipx list 2>/dev/null | grep -q "package $PKG_NAME"; then | |
| info "upgrading existing $PKG_NAME" | |
| if ! pipx upgrade --pip-args="" "$PKG_NAME" >/dev/null 2>&1; then | |
| warn "pipx upgrade failed — falling back to reinstall" | |
| pipx install --force "$target" >/dev/null | |
| fi |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@install.sh` around lines 180 - 189, The script currently constructs the
`target` variable with the pinned version but then ignores it when upgrading an
existing package, causing the `--version` flag to be disregarded. Restructure
the conditional logic to check `PIN_VERSION` first: if PIN_VERSION is set,
always perform a pinned install with the `target` variable using pipx install
--force, bypassing the upgrade path entirely. Otherwise, if the package already
exists (when PIN_VERSION is not set), proceed with the upgrade attempt; if
upgrade fails or the package doesn't exist, fall back to a fresh install without
a version pin.
three findings, all real: 1. workflow supply chain — install-sh.yml now pins both third-party actions to commit SHAs (actions/checkout v4.2.2, setup-python v5.3.0) with human-readable tags in the comment, sets workflow-level `permissions: contents: read`, and disables credential persistence on every checkout. note in the file flags that ci.yml / release.yml / schema-check.yml use the older tag-pin convention; sweeping them is a worthwhile follow-up but kept out of this pr to avoid churn. 2. PATH normalization — the previous `ensure_pipx` returned early when pipx was already installed, so the PIPX_BIN_DIR normalization that guarantees `vouch` is findable never ran. extracted into `ensure_pipx_bin_on_path` and called unconditionally. on ubuntu where pipx ships in apt but ~/.local/bin isn't on PATH, the smoke step would have failed without this. 3. --version pin honoured on re-runs — when the package was already installed AND --version X.Y.Z was passed, the previous code called `pipx upgrade` (which ignores the pin) instead of pinning to the requested version. now: if pinned, force-reinstall to the exact version; if unpinned, upgrade in place. local validation: shellcheck install.sh → clean dash -n install.sh → posix-clean bash -n install.sh → bash-clean ./install.sh --help → still works ./install.sh --bogus → exits 2 with clean error
The smoke job's verification step queried PIPX_BIN_DIR with `python -m pipx environment`, but GitHub runners ship pipx as a standalone binary that is on PATH yet not importable in the setup-python interpreter, so the call died with "No module named pipx" and failed the job under set -e (install.sh itself already tolerates this). Query via the pipx CLI instead and fall back to ~/.local/bin.
curl -fsSL https://raw.githubusercontent.com/vouchdev/vouch/main/install.sh | sh
a posix sh script (shellcheck-clean, dash-tested) that:
pipx install vouch-kb(or upgrade-in-place when already present)vouch --versionclaudeCLI on PATH and points atvouch install-mcp claude-codefor the per-project wiring stepflags: --version X.Y.Z to pin, --no-claude to skip the nudge, --quiet, --help. honours NO_COLOR. re-runnable.
ci workflow .github/workflows/install-sh.yml runs shellcheck + a dash syntax check + an end-to-end smoke install on ubuntu-latest that actually exercises the published pypi wheel — catches install-path breakage that the regular pytest run misses.
readme install section updated to lead with the one-liner.
What changed
Why
What might break
VEP
Tests
make checkpasses locally (lint + mypy + pytest)CHANGELOG.mdupdated under## [Unreleased]Summary by CodeRabbit