Skip to content

fix(setup): resolve pip and python from PATH before installing - #781

Open
ffantl-ld wants to merge 10 commits into
setup-ldfrom
fix/setup-resolve-python-pip
Open

fix(setup): resolve pip and python from PATH before installing#781
ffantl-ld wants to merge 10 commits into
setup-ldfrom
fix/setup-resolve-python-pip

Conversation

@ffantl-ld

@ffantl-ld ffantl-ld commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Requirements

  • I have added test coverage for new or changed functionality
  • I have followed the repository's pull request submission guidelines
  • I have validated my changes against all supported platform versions

Related issues

REL-15432 — found in the ldcli setup bug bash. Targets setup-ld (#776), where internal/setup/installer.go lives.

Describe the solution you've provided

  • Use whichever of pip3 and pip is on PATH instead of hardcoding pip. Recent macOS and Homebrew installs ship pip3 with no bare pip, so the install failed outright on a common developer box. pip3 is preferred so a stale Python 2 pip is never selected.
  • Check every package-manager executable exists before shelling out, so a missing tool warns with what to install rather than surfacing exec: "pip": executable file not found in $PATH.
  • Setup never installs tooling. It will not fall back to python3 -m pip, and will not bootstrap pip with ensurepip. Only a pip that is already present is used; when none is found, setup warns and runs nothing.
  • Install into the project's virtualenv when there is one — VIRTUAL_ENV, else .venv/bin/pip, else venv/bin/pip — in preference to any pip on PATH. Dependencies belong there, and on a PEP 668 interpreter it is the only place pip can write at all.
  • Recognise pip's externally-managed-environment refusal and explain it: name the project, give the two commands that create and activate a venv, and note that setup will use .venv automatically once it exists. Previously this surfaced as pip's raw error with no next step.
  • InstallArgs takes the project directory, so the command previewed on the plan screen is the one that actually runs, virtualenv pip included.

Describe alternatives you've considered

  • Falling back to python3 -m pip when no pip shim exists. Rejected: on Debian and Ubuntu the pip module is packaged separately, so this either fails anyway or amounts to installing tooling on the user's machine.
  • Bootstrapping with python3 -m ensurepip --upgrade. Rejected for the same reason — modifying the user's Python installation is out of scope for a flag-setup wizard.
  • Leaving the failure to the exec error and improving only the error screen (REL-15433). That fixes the message but still runs a command known to be absent.
  • Passing --break-system-packages to force the install through. Rejected outright: that is pip's own escape hatch for overwriting an OS-managed Python, and it can break tools the system shipped with.
  • Creating .venv on the user's behalf when none exists. Rejected: the wizard would be inventing project structure. It uses a venv that exists and otherwise explains how to make one.

Additional context

A project with no virtualenv at all on a PEP 668 interpreter still cannot install — that is the point of PEP 668, and forcing past it is not something setup should do. It now fails with instructions instead of pip's raw refusal. Poetry, uv, and pipenv projects were never affected, since those tools manage their own environment.

Also out of scope: known issue #3 from the bug bash — pip install does not record the SDK in requirements.txt, unlike poetry/uv/pipenv/bundler.

Testing approaches

  • go test ./... passes.
  • Table coverage for tool resolution: only pip3, only pip, both (pip3 wins), interpreters present but no pip (bare form kept, nothing run), nothing available.
  • Install coverage: a missing tool returns Failed with guidance and never calls the runner; a present tool runs the expected argv.
  • Stubbed PATH in the two pre-existing install tests that reached for a real npm, which the new pre-flight check made environment-dependent.
  • Virtualenv coverage: a project .venv beats a system pip3; a venv directory works too; an active VIRTUAL_ENV beats a project one; an empty directory argument does not search relatively.
  • PEP 668 coverage: the refusal returns a Failed result rather than an error, names the project directory and the venv commands, and never mentions --break-system-packages. Unrelated install failures keep their existing error path.
  • Verified with a built binary on a Homebrew Mac, which carries a real EXTERNALLY-MANAGED marker: in a project with .venv, install resolves .venv/bin/pip and reports "success": true. In a requirements.txt project with no venv, it reports the refusal with the venv instructions. With PATH reduced to python3 only, it warns that pip is missing and runs nothing.

Note

Overview
Makes ldcli setup install Python SDKs into a real environment instead of hardcoding pip, which failed on common macOS/Homebrew boxes.

InstallArgs now takes the project directory. It prefers VIRTUAL_ENV, then .venv/venv pip (absolute path, platform layout), then pip3 over pip on PATH. Setup never bootstraps pip. Missing tools, pip-less uv venvs, and PEP 668 externally-managed-environment refusals return a Failed result with next steps instead of a raw exec error. Successful bare pip installs warn when requirements.txt was not updated.

The wizard plan preview, dry-run, and done screen use the same resolved command and surface Warning.

Reviewed by Cursor Bugbot for commit c9f1d48. Bugbot is set up for automated code reviews on this repo. Configure here.

@ffantl-ld
ffantl-ld requested a review from a team as a code owner August 17, 2026 05:02
Comment thread internal/setup/installer.go
Comment thread internal/setup/installer_test.go
Comment thread internal/setup/installer_test.go
@ffantl-ld
ffantl-ld force-pushed the fix/setup-resolve-python-pip branch from 3ceb77b to 2ed8a45 Compare August 19, 2026 14:12
Comment thread internal/setup/installer.go
@ffantl-ld
ffantl-ld force-pushed the fix/setup-resolve-python-pip branch from 7798f33 to 1797852 Compare August 19, 2026 16:08
Comment thread internal/setup/installer.go
Comment thread internal/setup/installer.go
ffantl-ld and others added 6 commits August 19, 2026 12:51
Recent macOS and Homebrew installs ship python3/pip3 with no bare python or
pip, so the hardcoded `pip install` failed outright on a common developer box.
Probe pip3 then pip, falling back to `<interpreter> -m pip` for interpreters
installed without a pip shim.

Every package manager is now checked for existence before being run, so a
missing tool reports what to install instead of an exec "not found" error.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Debian and Ubuntu package pip separately from the interpreter, so a present
python3 does not mean `python3 -m pip` can run. The pre-flight check only looked
at the executable, so those boxes got "No module named pip" instead of guidance.

Stub PATH in the two install tests that reached for a real npm, which the
pre-flight check made environment-dependent.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Using `python3 -m pip`, or bootstrapping pip with ensurepip, would install
tooling onto the user's machine. Setup only ever uses a pip that is already
there; when none is found it warns and runs nothing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
pip refuses to write into an OS-managed Python, which Homebrew and most current
distributions now mark, so a project with no active virtualenv could not complete
setup at all. A virtualenv's pip is used when the project or the environment has
one, and the refusal is explained rather than passed through as pip's raw error.

InstallArgs takes the project directory so the previewed command is the one that
actually runs.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
pipInstallCmd prefers VIRTUAL_ENV over anything on PATH, so running the suite
inside an activated environment resolved install commands to that environment's
pip and failed assertions about pip and pip3. Tests that want an active
virtualenv opt in with stubVirtualEnv.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The install runs with its working directory set to the project, and a relative
executable path is resolved after that change, so a relative project directory was
applied twice: "app/.venv/bin/pip" run in "app" was looked for at
"app/app/.venv/bin/pip" and the install failed even though the venv was found.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@ffantl-ld
ffantl-ld force-pushed the fix/setup-resolve-python-pip branch from 1797852 to 046e70c Compare August 19, 2026 16:52
`uv venv` creates a virtualenv without pip, which was read as no virtualenv at all:
setup then reached for a pip on PATH, installing outside the project the user set
up — or being refused by PEP 668 and advising them to create the virtualenv already
sitting there. That case is now named, with how to install into it.

A PEP 668 refusal no longer carries the command that refused. The done screen
offers a non-empty command as "install it yourself with", which contradicted the
reason telling them not to run it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Comment thread internal/setup/installer.go
The pip-less virtualenv guard sat only in Install, so the plan screen, --dry-run
and the picker still previewed a pip from PATH that Install would refuse to run —
and a command shown there is one a reader may run by hand, installing outside the
project or hitting a raw PEP 668 error.

The command now names the virtualenv's own pip whenever the project has one,
present or not, so every surface and the runner agree. Install still explains why
an unseeded virtualenv cannot be installed into.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Comment thread internal/setup/installer.go Outdated
…ed dependency

A virtualenv with no pip was always named bin/pip, so on Windows the plan and the
previewed command pointed at a layout that environment never uses. The platform's
own layout is named first, and both are still considered.

A bare pip install also leaves the project's manifest untouched, so a fresh checkout
and CI miss the SDK. poetry, uv and pipenv record it themselves and Ruby has
`bundle add`; pip has no equivalent, and editing someone's manifest unasked is not
something setup does, so a successful install now says what is still missing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit e4f6310. Configure here.

Comment thread internal/setup/installer.go Outdated
A substring match read a related pin such as launchdarkly-server-sdk-otel as the SDK
itself, so the warning stayed quiet while the project still lacked the dependency.
The whole-name matcher the install-skipping check already uses does the job.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.

2 participants