feat: add Python pip & pipx to the base image (#121)#122
Conversation
Every image already carried python3 transitively (via software-properties-common) but shipped no pip, so pip-distributed CLIs like APM couldn't be installed. Add python3-pip, python3-venv and pipx to the shared system-packages snippet, so all 22 image variants pick them up. Debian 12 enforces PEP 668, so pipx is the supported way to install CLI apps; pip still works inside venvs. Put ~/.local/bin on PATH (ENV for the run command and non-login shells, plus an /etc/profile.d drop-in for login shells) so pipx-installed tools are reachable without a per-session 'pipx ensurepath'. Regenerated all docker/generated Dockerfiles and updated README plus docs/docker-images.md. Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: GitButler <gitbutler@gitbutler.com>
There was a problem hiding this comment.
Code Review
This pull request adds Python tooling (pip, venv, and pipx) to the base Docker image and updates the corresponding documentation. It also configures the environment path to include the local bin directory for pipx-installed CLIs. The review feedback suggests explicitly declaring python3 in the package list to avoid relying on transitive dependencies, and using standard POSIX parameter expansion when modifying PATH to prevent security risks associated with an empty PATH variable.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
There was a problem hiding this comment.
Pull request overview
Adds Python packaging support (pip, venv, pipx) to the shared Docker image base so pip-distributed CLI tools can be installed in a PEP 668-compliant way across all image variants, with documentation updates reflecting the new base capabilities.
Changes:
- Add
python3-pip,python3-venv, andpipxto the shared system-packages snippet and wire~/.local/binontoPATH. - Regenerate all
docker/generated/*Dockerfiles to pick up the shared snippet changes. - Update README and Docker image docs to mention Python/pip/pipx availability in the base image.
Reviewed changes
Copilot reviewed 3 out of 14 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| README.md | Documents Python (pip & pipx) as part of the base image and updates the image table description. |
| docs/docker-images.md | Notes Python/pipx availability and the PEP 668 rationale in the base image documentation. |
| docker/snippets/system-packages.Dockerfile | Installs pip/pipx tooling and adjusts PATH behavior for pipx-installed CLIs. |
| docker/generated/Dockerfile.default | Regenerated to include the updated system-packages snippet. |
| docker/generated/Dockerfile.dotnet | Regenerated to include the updated system-packages snippet. |
| docker/generated/Dockerfile.dotnet-8 | Regenerated to include the updated system-packages snippet. |
| docker/generated/Dockerfile.dotnet-9 | Regenerated to include the updated system-packages snippet. |
| docker/generated/Dockerfile.dotnet-10 | Regenerated to include the updated system-packages snippet. |
| docker/generated/Dockerfile.playwright | Regenerated to include the updated system-packages snippet. |
| docker/generated/Dockerfile.dotnet-playwright | Regenerated to include the updated system-packages snippet. |
| docker/generated/Dockerfile.rust | Regenerated to include the updated system-packages snippet. |
| docker/generated/Dockerfile.dotnet-rust | Regenerated to include the updated system-packages snippet. |
| docker/generated/Dockerfile.golang | Regenerated to include the updated system-packages snippet. |
| docker/generated/Dockerfile.java | Regenerated to include the updated system-packages snippet. |
- Declare python3 explicitly in the apt list instead of relying on it being
pulled in transitively by software-properties-common, so the Python tooling
install is self-contained and won't break if that transitive dep changes.
- Use POSIX ${PATH:+:${PATH}} expansion in the profile.d drop-in so an empty or
unset PATH doesn't produce a trailing colon (which would implicitly add the
cwd to the search path).
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: GitButler <gitbutler@gitbutler.com>
The entrypoint runs as root and resolves tools (getent, groupadd, node, ...) via PATH before dropping to appuser. Prepending the user-writable (and possibly bind-mounted) /home/appuser/.local/bin meant a planted binary there could be executed as root. Append it instead so system binaries always take precedence; pipx-installed apps still resolve since their names don't collide with system binaries. Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: GitButler <gitbutler@gitbutler.com>
Closes #121.
What
python3was already present in the images (pulled in transitively bysoftware-properties-common), but there was nopip, so pip-distributed CLIs like APM couldn't be installed. This addspython3-pip,python3-venvandpipxto the sharedsystem-packagessnippet, so every image variant gets them in one place.Why pipx + PATH
The base is Debian 12 (bookworm), which enforces PEP 668 — a bare
pip install <tool>fails withexternally-managed-environment.pipxis the supported way to install CLI apps (it drops each into its own venv);pipstill works inside venvs.pipx installs to
~/.local/bin, which isn't on PATH by default. Since the entrypoint always runs asappuserwithHOME=/home/appuser, I put that dir on PATH two ways so it's reachable everywhere:ENV PATH=...— covers the exec'd run command and non-login shells./etc/profile.d/copilot-local-bin.sh— re-adds it for login shells, which otherwise reset PATH via/etc/profile(Debian zsh sources it too).End result:
pipx install apmthenapmjust works, nopipx ensurepathneeded.Changes
docker/snippets/system-packages.Dockerfile— add the three packages + PATH wiring.docker/generated/*(copilot variants). Theclaude-*variants live on the still-open feat: add Claude Code as a CLI provider with published images #120 branch; their regen lands when the branches combine (CI auto-regenerates Dockerfiles).README.md+docs/docker-images.md— note Python/pip/pipx in the base image.Test plan
Built
Dockerfile.defaultand verified:Confirmed
apmis on PATH inbash -lc,zsh -lc, and non-login shells.pwsh docker/verify-generated.ps1reports the generated Dockerfiles are up to date.Note: the PyPI package literally named
apm(v0.0.1) is ancient Python-2 code and unrelated to the issue author's "Agent Package Manager" — confirming the package name is up to the user. This PR's job is making pip/pipx available, which is verified.🤖 Generated with Claude Code