C1: strip store refs in shower FOD; autopatchelf wrapper
Run 534 failed with 'fixed-output derivations must not reference store paths: ... gcc-14.3.0-lib' because pip-installed wheels pulled stdenv into the venv (Python's setup, gcc-lib runtime references). Adapts authentik's two-stage pattern: - pyDepsFOD: pip-installs into the venv, then strips every nix store ref it can find (find+remove-references-to). Output is fully self-contained — pinned by outputHash. - pyDeps (non-FOD wrapper): copies the FOD output and runs autoPatchelfHook against runtime buildInputs (libstdc++, zlib, image libs for pillow). This restores RPATHs on the .so files that pillow and scipy ship, against the real on-image library locations. outputHash still fakeHash — next build prints the real one. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
ba4c1e8953
commit
f8598a6612
1 changed files with 59 additions and 18 deletions
|
|
@ -29,13 +29,13 @@ let
|
|||
# dep into a single target dir. FODs get network access in exchange for
|
||||
# a pinned output hash, which means the whole dependency closure is
|
||||
# immutable across rebuilds.
|
||||
pyDeps = pkgs.stdenv.mkDerivation {
|
||||
pname = "shower-python-deps";
|
||||
pyDepsFOD = pkgs.stdenv.mkDerivation {
|
||||
pname = "shower-python-deps-fod";
|
||||
inherit version;
|
||||
|
||||
dontUnpack = true;
|
||||
|
||||
nativeBuildInputs = [ python pkgs.cacert ];
|
||||
nativeBuildInputs = [ python pkgs.cacert pkgs.removeReferencesTo ];
|
||||
|
||||
buildPhase = ''
|
||||
runHook preBuild
|
||||
|
|
@ -44,9 +44,6 @@ let
|
|||
export SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt
|
||||
export PIP_DISABLE_PIP_VERSION_CHECK=1
|
||||
|
||||
# Install into a venv first so pip's bytecode-compile + entry-point
|
||||
# generation pick up the right interpreter, then copy site-packages
|
||||
# + bin into $out at a stable layout.
|
||||
${python}/bin/python -m venv "$TMPDIR/venv"
|
||||
"$TMPDIR/venv/bin/pip" install --upgrade pip
|
||||
"$TMPDIR/venv/bin/pip" install \
|
||||
|
|
@ -65,40 +62,84 @@ let
|
|||
mkdir -p $out/lib/python3.14 $out/bin
|
||||
cp -r "$TMPDIR/venv/lib/python3.14/site-packages" $out/lib/python3.14/site-packages
|
||||
|
||||
# Copy console scripts (gunicorn, django-admin, etc.) but drop the
|
||||
# venv-specific shebang prefix that points at $TMPDIR/venv/bin/python.
|
||||
# Rewrite shebangs to the eventual on-image python path.
|
||||
for script in "$TMPDIR/venv/bin/"*; do
|
||||
[ -f "$script" ] || continue
|
||||
name=$(basename "$script")
|
||||
case "$name" in
|
||||
python*|pip*|activate*) continue ;;
|
||||
esac
|
||||
# Replace the venv python shebang with a path that resolves inside
|
||||
# the docker image (where ${python} ends up in /nix/store).
|
||||
sed -e "1 s|^#!.*python.*|#!${python}/bin/python3.14|" "$script" > "$out/bin/$name"
|
||||
cp "$script" "$out/bin/$name"
|
||||
chmod +x "$out/bin/$name"
|
||||
done
|
||||
|
||||
runHook postInstall
|
||||
'';
|
||||
# --- Strip Nix store references (FOD outputs must be self-contained) ---
|
||||
# The wrapper derivation below restores them via autoPatchelfHook + a
|
||||
# python wrapper that points pyc-less imports at the on-image python.
|
||||
|
||||
# Bytecode files embed absolute paths; deletion forces re-compile inside
|
||||
# the image at first run, with paths matching the image filesystem.
|
||||
postInstall = ''
|
||||
# Strip bytecode entirely — pyc files embed compile-time paths.
|
||||
find $out -type f -name '*.pyc' -delete
|
||||
find $out -type d -name '__pycache__' -exec rm -rf {} + 2>/dev/null || true
|
||||
|
||||
# Dynamically discover all nix store references and strip them. We
|
||||
# don't have a static list because pip pulls in stdenv via Python's
|
||||
# build env (gcc-lib, libstdc++, etc.) and the closure is opaque.
|
||||
{ find $out -type f -print0 \
|
||||
| xargs -0 grep -aohE '/nix/store/[a-z0-9]{32}-[^/"[:space:]]+' 2>/dev/null \
|
||||
|| true; } | sort -u > $TMPDIR/store-refs.txt
|
||||
echo "Found $(wc -l < $TMPDIR/store-refs.txt) unique store path references to strip"
|
||||
|
||||
refs_args=""
|
||||
while IFS= read -r ref; do
|
||||
refs_args="$refs_args -t $ref"
|
||||
done < $TMPDIR/store-refs.txt
|
||||
|
||||
if [ -n "$refs_args" ]; then
|
||||
find $out -type f -exec remove-references-to $refs_args {} + 2>/dev/null || true
|
||||
fi
|
||||
|
||||
remaining=$({ find $out -type f -print0 | xargs -0 grep -cl '/nix/store/' 2>/dev/null || true; } | wc -l)
|
||||
echo "Files with remaining store references: $remaining"
|
||||
|
||||
runHook postInstall
|
||||
'';
|
||||
|
||||
outputHashMode = "recursive";
|
||||
outputHashAlgo = "sha256";
|
||||
# Computed by setting to pkgs.lib.fakeHash and reading the failure.
|
||||
# Pin the dep closure — rebuilds are reproducible until the version bumps.
|
||||
outputHash = pkgs.lib.fakeHash;
|
||||
|
||||
dontFixup = true;
|
||||
};
|
||||
|
||||
# Non-FOD wrapper: re-applies RPATHs to pre-built .so files (pillow,
|
||||
# scipy) so they find libstdc++ / libz / etc. at runtime. autoPatchelfHook
|
||||
# discovers needed libraries from buildInputs.
|
||||
pyDeps = pkgs.stdenv.mkDerivation {
|
||||
pname = "shower-python-deps";
|
||||
inherit version;
|
||||
|
||||
dontUnpack = true;
|
||||
|
||||
nativeBuildInputs = [ pkgs.autoPatchelfHook ];
|
||||
|
||||
buildInputs = with pkgs; [
|
||||
python
|
||||
stdenv.cc.cc.lib # libstdc++, libgcc_s
|
||||
zlib
|
||||
libjpeg
|
||||
libwebp
|
||||
libtiff
|
||||
openjpeg
|
||||
lcms2
|
||||
freetype
|
||||
];
|
||||
|
||||
installPhase = ''
|
||||
cp -r ${pyDepsFOD} $out
|
||||
chmod -R u+w $out
|
||||
'';
|
||||
};
|
||||
|
||||
sitePackages = "${pyDeps}/lib/python3.14/site-packages";
|
||||
|
||||
# Settings shim — config/settings.py's `BASE_DIR = parent.parent` would
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue