···3737 - fontconfig
3838 - pipenv
3939 - cacert
4040+ # Shell utilities — Nixery base image is minimal; grep, sed, head,
4141+ # tr, mkdir, etc. are NOT available unless these are listed.
4242+ - coreutils
4343+ - gnugrep
4444+ - gnused
4545+ - findutils
40464147environment:
4248 PIPENV_VENV_IN_PROJECT: "1"
···130136 command: |
131137 echo "=== Creating virtualenv ==="
132138 python3 -m venv .venv
139139+140140+ # psycopg_c needs pg_config to find PostgreSQL headers/libs at build
141141+ # time. In a Nixery container the binary exists in the Nix store but
142142+ # the build subprocess may not find it unless we set PG_CONFIG and
143143+ # tell the C compiler where headers and libraries live (mirrors the
144144+ # PG_CONFIG / LDFLAGS / CPPFLAGS set in flake.nix devShell).
145145+ echo "=== Configuring PostgreSQL build flags ==="
146146+ PG_CONFIG_BIN="$(which pg_config 2>/dev/null || true)"
147147+ if [ -z "$PG_CONFIG_BIN" ]; then
148148+ # Fallback: search the Nix store directly
149149+ PG_CONFIG_BIN="$(find /nix/store -maxdepth 3 -name pg_config -type f 2>/dev/null | head -1)"
150150+ fi
151151+ if [ -n "$PG_CONFIG_BIN" ]; then
152152+ PG_INCLUDEDIR="$($PG_CONFIG_BIN --includedir)"
153153+ PG_LIBDIR="$($PG_CONFIG_BIN --libdir)"
154154+ export PG_CONFIG="$PG_CONFIG_BIN"
155155+ export LDFLAGS="${LDFLAGS:-} -L${PG_LIBDIR}"
156156+ export CPPFLAGS="${CPPFLAGS:-} -I${PG_INCLUDEDIR}"
157157+ echo " PG_CONFIG = $PG_CONFIG"
158158+ echo " LDFLAGS = $LDFLAGS"
159159+ echo " CPPFLAGS = $CPPFLAGS"
160160+ else
161161+ echo "WARNING: pg_config not found — psycopg_c build may fail"
162162+ fi
163163+133164 echo "=== Installing production dependencies ==="
134165 pipenv install --deploy --categories "packages"
135166 echo "=== Virtualenv contents ==="
···242273 # Method 1: Check if NIX_PATH is already set and valid
243274 if [ -n "${NIX_PATH:-}" ]; then
244275 echo "NIX_PATH is set: ${NIX_PATH}"
245245- NIXPKGS_PATH=$(echo "$NIX_PATH" | tr ':' '\n' | grep 'nixpkgs=' | head -1 | sed 's/nixpkgs=//')
276276+ # Parse NIX_PATH using pure shell — no grep/sed needed
277277+ IFS=':' read -ra _nix_entries <<< "$NIX_PATH"
278278+ for _entry in "${_nix_entries[@]}"; do
279279+ case "$_entry" in
280280+ nixpkgs=*) NIXPKGS_PATH="${_entry#nixpkgs=}"; break ;;
281281+ esac
282282+ done
246283 fi
247284248285 # Method 2: Find nixpkgs in the Nix store via a known package derivation
···254291 NIXPKGS_PATH=$(find /nix/store -maxdepth 1 -name '*-nixpkgs-src' -type d 2>/dev/null | head -1)
255292 fi
256293257257- # Method 3: Look for a channel-style nixpkgs directory
294294+ # Method 3: Look for a channel-style nixpkgs directory (exclude .drv files via find)
258295 if [ -z "$NIXPKGS_PATH" ] || [ ! -d "$NIXPKGS_PATH" ]; then
259259- NIXPKGS_PATH=$(find /nix/store -maxdepth 1 -name '*-nixos-*' -type d 2>/dev/null | grep -v '\.drv$' | head -1)
296296+ NIXPKGS_PATH=$(find /nix/store -maxdepth 1 -name '*-nixos-*' -type d ! -name '*.drv' 2>/dev/null | head -1)
260297 fi
261298262299 # Method 4: Find any path containing a top-level pkgs/top-level/all-packages.nix
263300 if [ -z "$NIXPKGS_PATH" ] || [ ! -d "$NIXPKGS_PATH" ]; then
264264- NIXPKGS_PATH=$(find /nix/store -maxdepth 3 -path '*/pkgs/top-level/all-packages.nix' 2>/dev/null | head -1 | sed 's|/pkgs/top-level/all-packages.nix||')
301301+ _allpkgs=$(find /nix/store -maxdepth 3 -path '*/pkgs/top-level/all-packages.nix' 2>/dev/null | head -1)
302302+ if [ -n "$_allpkgs" ]; then
303303+ NIXPKGS_PATH="${_allpkgs%/pkgs/top-level/all-packages.nix}"
304304+ fi
265305 fi
266306267307 # Method 5: Use nix-instantiate --eval to locate nixpkgs from the registry
···299339 ls -lh result
300340 echo ""
301341302302- # Inspect the image
342342+ # Inspect the image — skopeo needs /var/tmp for temp files and a
343343+ # containers policy.json to exist.
303344 echo "=== Image layers ==="
345345+ mkdir -p /var/tmp
346346+ mkdir -p /etc/containers
347347+ if [ ! -f /etc/containers/policy.json ]; then
348348+ echo '{"default":[{"type":"insecureAcceptAnything"}]}' > /etc/containers/policy.json
349349+ fi
304350 if command -v skopeo &> /dev/null; then
305351 skopeo inspect docker-archive:result | jq '{Layers: .Layers | length, Digest: .Digest, Created: .Created}' 2>/dev/null || echo "(inspection skipped)"
306352 fi
···341387 exit 0
342388 fi
343389390390+ # skopeo needs /var/tmp for temp files and a trust policy
391391+ mkdir -p /var/tmp
392392+ mkdir -p /etc/containers
393393+ if [ ! -f /etc/containers/policy.json ]; then
394394+ echo '{"default":[{"type":"insecureAcceptAnything"}]}' > /etc/containers/policy.json
395395+ fi
396396+344397 echo "Logging in to ${REGISTRY_URL}..."
345398 echo "${REGISTRY_TOKEN}" | skopeo login "${REGISTRY_URL}" \
346399 --username "${REGISTRY_USERNAME}" \
···348401349402 echo ""
350403 echo "Pushing image..."
404404+ PUSH_FAILED=0
351405 for TAG in $TAGS; do
352406 DEST="${REGISTRY_URL}/${REGISTRY_IMAGE}:${TAG}"
353407 echo " -> ${DEST}"
354354- skopeo copy \
408408+ if ! skopeo copy \
355409 "docker-archive:result" \
356410 "docker://${DEST}" \
357357- --retry-times 3
411411+ --retry-times 3; then
412412+ echo " !! FAILED to push ${DEST}"
413413+ PUSH_FAILED=1
414414+ fi
358415 done
416416+417417+ if [ "$PUSH_FAILED" -ne 0 ]; then
418418+ echo ""
419419+ echo "=== Some tags failed to push ==="
420420+ exit 1
421421+ fi
359422360423 echo ""
361424 echo "=== All tags pushed successfully ==="
+15
care/CLAUDE.md
···400400- `pipenv install --deploy --categories "packages"` installs only production deps
401401- The `Pipfile.lock` must exist and match (--deploy enforces this)
402402403403+### psycopg_c build in CI:
404404+- `psycopg[c]` (the C-accelerated PostgreSQL adapter) compiles `psycopg_c` from source
405405+- The build subprocess shells out to `pg_config` to locate PostgreSQL headers and
406406+ libraries — this fails with `No such file or directory: 'pg_config'` unless we
407407+ explicitly set `PG_CONFIG`, `LDFLAGS`, and `CPPFLAGS`
408408+- The flake.nix dev shell sets these via `envVars` (`PG_CONFIG`, `LDFLAGS`, `CPPFLAGS`)
409409+ pointing at `${pkgs.postgresql_15}`
410410+- In the Nixery CI container, `postgresql_15` is a dependency but the install step must
411411+ discover `pg_config` at runtime (`which pg_config` or Nix store search) and export:
412412+ - `PG_CONFIG=/nix/store/…/bin/pg_config`
413413+ - `LDFLAGS=-L$($PG_CONFIG --libdir)`
414414+ - `CPPFLAGS=-I$($PG_CONFIG --includedir)`
415415+- Without these, `pipenv install` succeeds for pure-Python packages but fails on
416416+ `psycopg_c` metadata generation
417417+403418### Plugin installation:
404419- Happens AFTER pipenv install
405420- Reads `ADDITIONAL_PLUGS` env var (JSON array)