Files
improvise/scripts/nix-seed-flake-inputs.sh
T
Claude 8cf8f01f12 feat(scripts): make nix develop work in cloud sessions without touching the flake
Nix's `github:` fetcher wants a tarball from
github.com/<owner>/<repo>/archive/<rev>.tar.gz, and that endpoint is not
served in a cloud session. Three things that look like fixes are not:

  - Raising the environment's network access to Full, or allowlisting
    codeload.github.com, changes nothing: GitHub traffic bypasses the
    domain allowlist entirely and goes through a proxy that scopes API
    and release-asset requests to repositories attached to the session.
    An input from any other owner gets a 403.
  - Attaching the dependency repos is refused -- cross-tier adds are not
    supported once the session has sources from another owner.
  - Nix has no setting that makes `github:` inputs use the git transport.

The lock's own integrity machinery is what works. flake.lock records a
narHash per input, and Nix looks a source up in the store and its
substituters before fetching anything. The git and tarball fetchers
produce byte-identical trees for a revision, so prefetching the locked
rev over git+https lands the input at exactly the store path the lock
names:

  lock narHash:  sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=
  git prefetch:  sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=

scripts/nix-seed-flake-inputs.sh does that for every locked input. It
reads flake.lock and never writes it. Nix still verifies each tree
against the recorded hash, so this satisfies the lock rather than working
around it -- a mismatch fails loudly. Rewriting the lock's nodes to `git`
type and carrying the old narHash across would have been the opposite:
that hash is the claim the lock exists to make.

Verified from a fresh clone with a cleared git cache: 15/15 inputs
seeded, then `nix develop` gives rustc 1.98.1, cargo and crate2nix, with
flake.nix and flake.lock unmodified.

Not wired into the session-start hook: it costs minutes and several GB
once the devShell builds, and most work only needs the preinstalled cargo
toolchain.

Corrects three places that claimed nix could not work here --
context/cloud-agents.md, the design-review skill's kb.pl check, and the
hook's environment notes -- all written before the git transport was
tested. The doc also records the two alternatives, a binary-cache
substituter and a self-hosted environment.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DyHK7L8S1XDX3EihTnSKaX
2026-09-19 06:52:17 +00:00

76 lines
3.1 KiB
Bash
Executable File

#!/usr/bin/env bash
# Seed the Nix store with this flake's locked inputs, fetched over git.
#
# Why this exists: Nix's `github:` fetcher downloads a repository as a tarball
# from github.com/<owner>/<repo>/archive/<rev>.tar.gz. In a Claude Code cloud
# session that endpoint is not served — GitHub traffic bypasses the
# environment's domain allowlist and goes through a proxy that scopes API and
# release-asset requests to repositories attached to the session, so a flake
# input from any other owner gets a 403. Raising network access to Full does
# not help, because GitHub never goes through that allowlist.
#
# The git protocol IS served. And `flake.lock` already records a narHash for
# every input, which Nix looks up in the store and its substituters before
# fetching anything (see `nix flake` in the Nix manual). The git and tarball
# fetchers produce byte-identical trees for a given revision, so fetching over
# git lands the input at exactly the store path the lock names.
#
# So this script reads flake.lock and fetches each locked revision over
# git+https. It never writes to flake.lock. Nix still verifies every tree
# against the narHash the lock already records: a mismatch fails loudly here
# rather than being papered over. Afterwards `nix develop` finds every input
# in the store and never reaches for a tarball.
#
# Cost: a few minutes and a few hundred MB of git cache on a cold container;
# the full devShell build is several GB more. Run it when you actually need
# the devShell — the preinstalled cargo toolchain covers ordinary work.
#
# Usage: scripts/nix-seed-flake-inputs.sh && nix develop
set -uo pipefail
cd "$(dirname "${BASH_SOURCE[0]}")/.." || exit 1
for tool in nix jq; do
command -v "$tool" >/dev/null 2>&1 || { echo "error: $tool not found on PATH" >&2; exit 1; }
done
[ -f flake.lock ] || { echo "error: no flake.lock here" >&2; exit 1; }
# Nix needs the proxy CA when it talks to github over https.
if [ -z "${NIX_SSL_CERT_FILE:-}" ] && [ -r /root/.ccr/ca-bundle.crt ]; then
export NIX_SSL_CERT_FILE=/root/.ccr/ca-bundle.crt
fi
NIX_FLAGS=(--extra-experimental-features 'nix-command flakes')
mapfile -t INPUTS < <(
jq -r '.nodes | to_entries[] | .value.locked
| select(.type == "github" and .rev)
| "git+https://github.com/\(.owner)/\(.repo)?rev=\(.rev)&shallow=1"' flake.lock \
| sort -u
)
if [ "${#INPUTS[@]}" -eq 0 ]; then
echo "no github-sourced inputs in flake.lock; nothing to seed"
exit 0
fi
echo "seeding ${#INPUTS[@]} locked inputs over git (flake.lock is not modified)…"
failed=0
for url in "${INPUTS[@]}"; do
rev="${url##*rev=}"; rev="${rev%%&*}"
label="${url#git+https://github.com/}"
label="${label%%\?*}@${rev:0:7}"
if nix "${NIX_FLAGS[@]}" flake prefetch --json "$url" >/dev/null 2>&1; then
printf ' ok %s\n' "$label"
else
printf ' FAIL %s\n' "$label"
failed=$((failed + 1))
fi
done
if [ "$failed" -gt 0 ]; then
echo "$failed input(s) could not be seeded; 'nix develop' will still try a tarball for those." >&2
exit 1
fi
echo "done — 'nix develop' should now resolve without touching the tarball endpoint"