Skip to content

fix(pkg): stop the Windows build overwriting the venv's pip - #10396

Merged
dpage merged 2 commits into
pgadmin-org:masterfrom
asheshv:fix/win-venv-site-packages
Sep 7, 2026
Merged

fix(pkg): stop the Windows build overwriting the venv's pip#10396
dpage merged 2 commits into
pgadmin-org:masterfrom
asheshv:fix/win-venv-site-packages

Conversation

@asheshv

@asheshv asheshv commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

The Windows x64 build has been failing since the buildfarm's virtualenv started seeding pip 26.2.x:

File "...\win-temp\venv\Lib\site-packages\pip\_internal\build_env\installer.py", line 22, in <module>
  from pip._internal.utils.misc import get_runnable_pip
ImportError: cannot import name 'get_runnable_pip' from 'pip._internal.utils.misc'

CREATE_VIRTUAL_ENV creates a virtualenv and then copies the whole of the system Python's Lib over it, so the relocated interpreter has a standard library. That copy includes Lib\site-packages, and XCOPY /Y overwrites the files present on both sides without removing the ones only the venv has, so the venv's freshly seeded pip ends up spliced together with whatever pip C:\Python313 happens to have.

That was harmless while the two layouts agreed. pip 26.2 turned pip/_internal/build_env from a module into a package, so on a builder whose system pip predates 26.2 the new build_env/ directory survives the copy whilst utils/misc.py is overwritten by the older one — and the resulting install is internally inconsistent. I checked the pristine 26.2.1 wheel: build_env/ is a package and utils/misc.py does define get_runnable_pip, so the released artifact is fine; only the spliced copy is broken.

It cannot recover on its own, because the next line of the script is the pip invocation that upgrades pip.

Copy the standard library with ROBOCOPY and exclude site-packages, which the venv is entitled to keep to itself. ROBOCOPY rather than XCOPY /EXCLUDE because /XD takes a directory to skip, whereas /EXCLUDE takes a file of path substrings and breaks on quoted paths containing spaces. /R:3 /W:5 so a locked file fails the build instead of retrying a million times.

Note that CALL :CHECK_ROBOCOPY_ERROR is followed by || EXIT /B 1 here: EXIT /B inside the label returns from the label rather than from the caller, so without it a failed copy would be ignored.

Upgrading the system pip on the builder (C:\Python313\python.exe -m pip install --upgrade pip) also unblocks it immediately, but the splice will bite again the next time the two pip layouts diverge.

Not verified on a Windows builder — I have no Windows host to run Make.bat on. A buildfarm run is the real test.

Summary by CodeRabbit

  • Bug Fixes
    • Improved build reliability by stopping setup when virtual environment creation or required file-copy operations fail.
    • Updated environment setup to avoid overwriting bundled package tooling.
    • Simplified file-copy progress output for a cleaner build experience.

CREATE_VIRTUAL_ENV creates a virtualenv and then copies the whole of the
system Python's Lib over it, so that the relocated interpreter has a
standard library to use. That copy includes Lib\site-packages, and XCOPY
/Y overwrites the files that exist on both sides without removing the
ones that only exist in the venv, so the venv's freshly seeded pip ends
up spliced together with whatever pip C:\Python313 happens to have.

That was harmless while the two layouts agreed. pip 26.2 turned
pip/_internal/build_env from a module into a package, so on a builder
whose system pip predates 26.2 the new build_env/ directory survives the
copy whilst utils/misc.py is overwritten by the older one, and every
subsequent pip invocation dies before it does anything:

  File "...\pip\_internal\build_env\installer.py", line 22, in <module>
    from pip._internal.utils.misc import get_runnable_pip
  ImportError: cannot import name 'get_runnable_pip' from
  'pip._internal.utils.misc'

It cannot recover on its own either, since the next line of the script
is the pip invocation that upgrades pip.

Copy the standard library with ROBOCOPY and exclude site-packages, which
the venv is entitled to keep to itself. ROBOCOPY rather than XCOPY
because /XD takes a directory to skip and the existing
:CHECK_ROBOCOPY_ERROR helper already handles its exit codes; /R:3 /W:5
so a locked file fails the build rather than retrying for a million
attempts.
@coderabbitai

coderabbitai Bot commented Sep 7, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Team

Run ID: 6c764f96-9dca-46c2-a7e5-a2cadaaef041

📥 Commits

Reviewing files that changed from the base of the PR and between 5291124 and a002cfb.

📒 Files selected for processing (1)
  • Make.bat

Walkthrough

Windows environment setup now propagates virtualenv.exe and ROBOCOPY failures. Standard-library copying excludes site-packages. ROBOCOPY output is reduced.

Changes

Environment setup

Layer / File(s) Summary
Fail-fast environment copy operations
Make.bat
The setup routines stop on virtualenv.exe or ROBOCOPY failure. Standard-library copying excludes site-packages, and both copy operations suppress file, directory, and progress output.

Estimated code review effort: 2 (Simple) | ~5 minutes

Merge Risk: ⚪ Minimal · up to 52911

Windows virtual-environment setup now preserves its seeded packages while copying the standard library, avoiding pip package conflicts. No concrete merge-blocking risk remains.

Suggested reviewers: dpage

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: preventing the Windows build from overwriting the virtual environment's pip.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 0…
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@asheshv

asheshv commented Sep 7, 2026

Copy link
Copy Markdown
Contributor Author

One consequence worth a reviewer's eye before this is merged.

The venv is seeded with pip alone — the build log shows added seed packages: pip==26.2.1, no setuptools, no wheel — and virtualenv.exe venv runs without --system-site-packages. So every third-party package that was importable inside that venv got there only through the XCOPY this PR narrows. Excluding site-packages means the system Python's globally installed packages are no longer copied in.

What the build actually needs is unaffected, as far as I can tell from the script:

  • setuptools is in requirements.txt (setuptools==84.* for python_version > '3.9'), installed into the venv at line 178
  • pip is seeded by virtualenv, then upgraded at line 177
  • sphinx and sphinxcontrib-youtube are installed explicitly in BUILD_DOCS
  • virtualenv itself is only needed in the system Python, never inside the venv

There is a second effect, which I think is an improvement but is a behaviour change either way. CREATE_PYTHON_ENV copies the venv's site-packages into the shipped installer:

XCOPY /S /I /E /H /Y "%TMPDIR%\venv\Lib\site-packages" "%BUILDROOT%\python\Lib\site-packages"

Under the old behaviour that meant whatever the builder happened to have pip-installed globally shipped inside pgAdmin too — virtualenv and its dependencies at minimum — so installer contents varied with the state of the build machine. After this change they follow from requirements.txt alone.

The risk is the mirror image of that: if some import is currently satisfied only by a globally installed package that never made it into requirements.txt, this will surface it, either as a build failure or as a module missing from the installer. I have no Windows host, so I could not check. Could someone with a builder compare win-build\python\Lib\site-packages before and after, and confirm nothing needed has gone missing? A failure on the buildfarm is the outcome I would prefer over continuing to depend on whatever C:\Python313 happens to contain, but it should be a deliberate choice rather than a surprise.

Follow-up review fixes to the site-packages exclusion.

Exclude site-packages by name rather than by full path. The full path is
built from PGADMIN_PYTHON_DIR, so if that is ever set with a trailing
backslash the source and the /XD argument both gain a doubled separator;
should the match then fail, site-packages is copied over the venv again
and the build fails with the same ImportError this was meant to remove.
No directory inside the standard library is legitimately called
site-packages, so matching by name is both simpler and unconditional.

Drop "> nul" from both ROBOCOPY calls in favour of /NFL /NDL /NP.
ROBOCOPY writes its errors to stdout, so with the redirect in place a
copy that returns 8 or more aborted the build with nothing in the log to
say which file or share was at fault; the switches suppress the per-file
noise whilst keeping the header, the summary and any errors.

Guard the copy of the web directory with "|| EXIT /B 1" as well, since
EXIT /B inside :CHECK_ROBOCOPY_ERROR returns from the label rather than
from the caller and a failed copy was therefore ignored, leaving the
build to package an incomplete tree.

Guard virtualenv.exe likewise: with the standard library now copied by
ROBOCOPY, which creates its destination, a failure there would otherwise
surface later at the activate step instead of where it happened.
@dpage

dpage commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Reviewed, and the diagnosis holds up against the Jenkins log: the traceback is build_env/installer.py (the 26.2 package layout) importing get_runnable_pip from a utils/misc.py that does not define it, which is exactly what a file-by-file overwrite of a newer pip by an older one produces. Excluding site-packages is the right fix, and the CALL :CHECK_ROBOCOPY_ERROR || EXIT /B 1 form is correct, including the point about EXIT /B returning from the label.

I have pushed four follow-up fixes to the branch rather than leaving them as comments, since they are all one-liners:

  • Exclude by name, not by full path. /XD now takes site-packages. The full path was built from PGADMIN_PYTHON_DIR, so if that variable is ever set with a trailing backslash, both the source and the /XD argument gain a doubled separator; if the match then failed, site-packages would be copied again and the build would fail with the same baffling ImportError. Nothing in the standard library is legitimately called site-packages, so the name form is unconditional.
  • > nul dropped from both ROBOCOPY calls in favour of /NFL /NDL /NP. ROBOCOPY writes errors to stdout, so with the redirect a return of 8 or more aborted the build with nothing in the log to say what failed, which /R:3 /W:5 makes rather more likely to be hit than the old million retries did.
  • The web directory copy is now guarded too. CALL :CHECK_ROBOCOPY_ERROR on line 220 had no || EXIT /B 1, so a failed copy was silently ignored and the build carried on packaging a partial tree.
  • virtualenv.exe is guarded. With the standard library copied by ROBOCOPY, which creates its own destination, a virtualenv failure would otherwise have surfaced at the activate step rather than where it happened.

On your question about the shipped site-packages: I agree with the analysis, and requirements.txt does carry setuptools==84.*, so nothing the script itself invokes needs the builder's global packages. Worth being explicit that the residual risk is not build-time but runtime, though: an import satisfied only by a globally installed package would now be absent from the installer and would fail when a user reached that code path, which a green buildfarm run will not catch. A file-list diff of win-build\python\Lib\site-packages before and after is what would settle it, and that needs someone with a builder.

Neither your change nor mine has been run on Windows, so the buildfarm remains the only test either of us can give it; nothing under .github/ touches Make.bat. I am happy for this to go in and be proven there, since the alternative is continuing to depend on whatever C:\Python313 happens to contain.

There is also a pre-existing typo at Make.bat:349, where the pg_dumpall.exe copy ends || EXIT /B 1L%. I have left it out of this branch to keep the change focused, but it wants fixing at some point.

@dpage
dpage merged commit 0b8e8db into pgadmin-org:master Sep 7, 2026
18 of 34 checks passed
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