From c409f694807e8cd16db8e2affb630073c1aadc48 Mon Sep 17 00:00:00 2001 From: zimbatm Date: Sat, 31 Oct 2020 21:24:36 +0100 Subject: [PATCH] pythonPackages.pip: make reproducible (#102222) The previous attempt wasn't covering all of the bases. It relied on invoking that pip-install-hook, and didn't apply to pip itself. The core issue is that the generated .pyc files embed some of the temporary paths, which are randomly generated. See https://r13y.com/diff/bf8c3ca3148ebff9ecf41f294cc60b9f209c006d49699e356969ff32d736f1c6-8806a7cca91fdd300e48736bfcd57c4d0b54c1cc2fd61609f35143170862b59c.html In this new attempt, the approach is to patch the TempFile implementation directly, so that it creates stable temporary directories. We also assume that if SOURCE_DATE_EPOCH is set, we are in a scenario where reproducible builds are desirable and enter that branch. See also https://github.com/pypa/pip/issues/7808 --- .../python/hooks/pip-install-hook.sh | 2 +- .../bootstrapped-pip/default.nix | 5 +++ .../python-modules/pip/default.nix | 2 +- .../python-modules/pip/reproducible.patch | 38 ++++++++++++------- 4 files changed, 32 insertions(+), 15 deletions(-) diff --git a/pkgs/development/interpreters/python/hooks/pip-install-hook.sh b/pkgs/development/interpreters/python/hooks/pip-install-hook.sh index 770739b36bdea..73d3c3cbbffae 100644 --- a/pkgs/development/interpreters/python/hooks/pip-install-hook.sh +++ b/pkgs/development/interpreters/python/hooks/pip-install-hook.sh @@ -12,7 +12,7 @@ pipInstallPhase() { pushd dist || return 1 mkdir tmpbuild - NIX_PIP_INSTALL_TMPDIR=tmpbuild @pythonInterpreter@ -m pip install ./*.whl --no-index --prefix="$out" --no-cache $pipInstallFlags + @pythonInterpreter@ -m pip install ./*.whl --no-index --prefix="$out" --no-cache $pipInstallFlags rm -rf tmpbuild popd || return 1 diff --git a/pkgs/development/python-modules/bootstrapped-pip/default.nix b/pkgs/development/python-modules/bootstrapped-pip/default.nix index 8bb713b764161..2b1a7dc8786c0 100644 --- a/pkgs/development/python-modules/bootstrapped-pip/default.nix +++ b/pkgs/development/python-modules/bootstrapped-pip/default.nix @@ -23,6 +23,11 @@ stdenv.mkDerivation rec { ]; postPatch = '' + # Apply the pip reproducible patch + pushd "${pip.src.name}" + patch -p1 < ${../pip/reproducible.patch} + popd + mkdir -p $out/bin ''; diff --git a/pkgs/development/python-modules/pip/default.nix b/pkgs/development/python-modules/pip/default.nix index fa566c8951ab0..8dbf2c54a7fee 100644 --- a/pkgs/development/python-modules/pip/default.nix +++ b/pkgs/development/python-modules/pip/default.nix @@ -26,7 +26,7 @@ buildPythonPackage rec { }; # Remove when solved https://github.com/NixOS/nixpkgs/issues/81441 - # Also update pkgs/development/interpreters/python/hooks/pip-install-hook.sh accordingly + # See also https://github.com/pypa/pip/issues/7808 patches = [ ./reproducible.patch ]; nativeBuildInputs = [ bootstrapped-pip ]; diff --git a/pkgs/development/python-modules/pip/reproducible.patch b/pkgs/development/python-modules/pip/reproducible.patch index 528ac2b49b03b..69001f0ef278e 100644 --- a/pkgs/development/python-modules/pip/reproducible.patch +++ b/pkgs/development/python-modules/pip/reproducible.patch @@ -1,13 +1,25 @@ -diff --git a/src/pip/_internal/operations/install/wheel.py b/src/pip/_internal/operations/install/wheel.py -index e7315ee4..4e36b03d 100644 ---- a/src/pip/_internal/operations/install/wheel.py -+++ b/src/pip/_internal/operations/install/wheel.py -@@ -615,6 +615,8 @@ def install_wheel( - direct_url=None, # type: Optional[DirectUrl] - ): - # type: (...) -> None -+ _temp_dir_for_testing = ( -+ _temp_dir_for_testing or os.environ.get("NIX_PIP_INSTALL_TMPDIR")) - with TempDirectory( - path=_temp_dir_for_testing, kind="unpacked-wheel" - ) as unpacked_dir, ZipFile(wheel_path, allowZip64=True) as z: +diff --git a/src/pip/_internal/utils/temp_dir.py b/src/pip/_internal/utils/temp_dir.py +index 201ba6d98..f1569fecd 100644 +--- a/src/pip/_internal/utils/temp_dir.py ++++ b/src/pip/_internal/utils/temp_dir.py +@@ -3,6 +3,7 @@ from __future__ import absolute_import + import errno + import itertools + import logging ++import os + import os.path + import tempfile + from contextlib import contextmanager +@@ -181,6 +182,11 @@ class TempDirectory(object): + # symlinked to another directory. This tends to confuse build + # scripts, so we canonicalize the path by traversing potential + # symlinks here. ++ if "SOURCE_DATE_EPOCH" in os.environ: ++ path = os.path.join(tempfile.gettempdir(), "pip-{}-immobile".format(kind)) ++ os.mkdir(path) ++ return path ++ + path = os.path.realpath( + tempfile.mkdtemp(prefix="pip-{}-".format(kind)) + ) +