Verifying the build scripts skill
Drive this repo's build/release scripts end-to-end without touching the network.
by anthropics·MIT license·★ 8,221 Stars on the repo·GitHub ↗
npx degit anthropics/claude-agent-sdk-python/.claude/skills/verify#main ~/.claude/skills/verifyChecked ·commit main
Files of Verifying the build scripts
Show the full text80 lines
Verifying the build scripts
The SDK itself is a library (drive it through import claude_agent_sdk), but
the scripts/ directory is a set of CLIs run by the release workflows.
Verify them by running them, not by importing them.
NEVER run the real installer
scripts/download_cli.py shells out to curl https://claude.ai/install.sh | bash
(and the PowerShell equivalent). Running it for real overwrites the claude
binary on the developer's machine. Do not run bash install.sh, and do not
invoke download_cli.py with latest/stable (or any case variant) against
the real network.
Instead, put stub curl / bash / powershell on a temp PATH and drive the
script under env -i with an isolated HOME (so find_installed_cli() cannot
discover the real binary and copy it into src/claude_agent_sdk/_bundled/).
The harness
SB=$(mktemp -d); mkdir -p $SB/stub $SB/home $SB/repo/src/claude_agent_sdk
cp scripts/{download_cli.py,build_wheel.py,update_cli_version.py,_cli_version_validation.py} $SB/repo/scripts/
printf '__cli_version__ = "2.1.208"\n' > $SB/repo/src/claude_agent_sdk/_cli_version.py
cat > $SB/stub/curl <<'EOF'
#!/bin/bash
out=""; prev=""; for a in "$@"; do [ "$prev" = "-o" ] && out="$a"; prev="$a"; done
echo "[stub curl] argv: $*" >> "$MARKER"
[ -n "$out" ] && printf '%b' "${CURL_BODY:-#!/bin/bash\necho hi\n}" > "$out"
exit ${CURL_EXIT:-0}
EOF
cat > $SB/stub/bash <<'EOF'
#!/bin/bash
{ echo "[stub bash] argc=$#"; for a in "$@"; do echo " <$a>"; done; } >> "$MARKER"
EOF
chmod +x $SB/stub/*
cd $SB/repo
env -i HOME=$SB/home PATH=$SB/stub:/usr/bin:/bin MARKER=$SB/m \
CLAUDE_CLI_VERSION=2.1.208 .venv/bin/python scripts/download_cli.py
run_command() captures the child's output, so the stubs must log to a
$MARKER file — printing to stderr is swallowed.
Flows worth driving
update_cli_version.py <version>— the whole validator surface is reachable here: a concrete version writes the file;latest/stable,v2.1.207,next,2.1, and a quote-breakout string each exit 1 with a distinct message and leave the file untouched.build_wheel.py --skip-sdist— reads the pin, then chains intodownload_cli.py. Rewrite_cli_version.pyin the sandbox to a moving tag / single quotes / garbage / delete it: each must fail before any subprocess is spawned.--cli-version <v>bypasses the pin (intentional escape hatch; the release workflow does not use it).download_cli.py— setCURL_BODYto an HTML error page or an empty string to prove the body check refuses it with no retry; empty thePATHto prove a missingcurlfails fast in one attempt;CURL_EXIT=22to prove a genuine transient failure still retries 3×.- The Windows path is unreachable on Linux. It is behind
platform.system() == "Windows". Drive it with a small runner thatpatch.object(m.platform, "system", return_value="Windows")and a stubpowershellthat reads the-Commandtext and honours$env:CLAUDE_CLI_INSTALL_SCRIPT. This is the one place where forcing the platform is legitimate.
Gotchas
${CURL_BODY:-default}treats an empty body as unset. Use a separate stub that does: > "$out"to test the empty-body branch.- The retry path really sleeps (jitter up to 5s, then 2s + 4s). A full three-attempt run takes ~10s; budget for it rather than assuming a hang.
| 1 | |
| 2 | name verify |
| 3 | description Drive this repo's build/release scripts end-to-end without touching the network. Use when verifying changes to scripts/download_cli.py, scripts/build_wheel.py, scripts/update_cli_version.py, or scripts/_cli_version_validation.py. |
| 4 | |
| 5 | |
| 6 | # Verifying the build scripts |
| 7 | |
| 8 | The SDK itself is a library (drive it through `import claude_agent_sdk`), but |
| 9 | the `scripts/` directory is a set of **CLIs** run by the release workflows. |
| 10 | Verify them by running them, not by importing them. |
| 11 | |
| 12 | ## NEVER run the real installer |
| 13 | |
| 14 | `scripts/download_cli.py` shells out to `curl https://claude.ai/install.sh | bash` |
| 15 | (and the PowerShell equivalent). Running it for real **overwrites the `claude` |
| 16 | binary on the developer's machine**. Do not run `bash install.sh`, and do not |
| 17 | invoke `download_cli.py` with `latest`/`stable` (or any case variant) against |
| 18 | the real network. |
| 19 | |
| 20 | Instead, put stub `curl` / `bash` / `powershell` on a temp `PATH` and drive the |
| 21 | script under `env -i` with an isolated `HOME` (so `find_installed_cli()` cannot |
| 22 | discover the real binary and copy it into `src/claude_agent_sdk/_bundled/`). |
| 23 | |
| 24 | ## The harness |
| 25 | |
| 26 | |
| 27 | SB=$(mktemp -d); mkdir -p $SB/stub $SB/home $SB/repo/src/claude_agent_sdk |
| 28 | cp scripts/{download_cli.py,build_wheel.py,update_cli_version.py,_cli_version_validation.py} $SB/repo/scripts/ |
| 29 | printf '__cli_version__ = "2.1.208"\n' > $SB/repo/src/claude_agent_sdk/_cli_version.py |
| 30 | |
| 31 | cat > $SB/stub/curl <<'EOF' |
| 32 | #!/bin/bash |
| 33 | out=""; prev=""; for a in "$@"; do [ "$prev" = "-o" ] && out="$a"; prev="$a"; done |
| 34 | echo "[stub curl] argv: $*" >> "$MARKER" |
| 35 | [ -n "$out" ] && printf '%b' "${CURL_BODY:-#!/bin/bash\necho hi\n}" > "$out" |
| 36 | exit ${CURL_EXIT:-0} |
| 37 | EOF |
| 38 | cat > $SB/stub/bash <<'EOF' |
| 39 | #!/bin/bash |
| 40 | { echo "[stub bash] argc=$#"; for a in "$@"; do echo " <$a>"; done; } >> "$MARKER" |
| 41 | EOF |
| 42 | chmod +x $SB/stub/* |
| 43 | |
| 44 | cd $SB/repo |
| 45 | env -i HOME=$SB/home PATH=$SB/stub:/usr/bin:/bin MARKER=$SB/m \ |
| 46 | CLAUDE_CLI_VERSION=2.1.208 .venv/bin/python scripts/download_cli.py |
| 47 | |
| 48 | |
| 49 | `run_command()` captures the child's output, so the stubs must log to a |
| 50 | `$MARKER` file — printing to stderr is swallowed. |
| 51 | |
| 52 | ## Flows worth driving |
| 53 | |
| 54 | **`update_cli_version.py <version>`** — the whole validator surface is |
| 55 | reachable here: a concrete version writes the file; `latest`/`stable`, |
| 56 | `v2.1.207`, `next`, `2.1`, and a quote-breakout string each exit 1 with a |
| 57 | distinct message and leave the file untouched. |
| 58 | **`build_wheel.py --skip-sdist`** — reads the pin, then chains into |
| 59 | `download_cli.py`. Rewrite `_cli_version.py` in the sandbox to a moving tag / |
| 60 | single quotes / garbage / delete it: each must fail *before* any subprocess |
| 61 | is spawned. `--cli-version <v>` bypasses the pin (intentional escape hatch; |
| 62 | the release workflow does not use it). |
| 63 | **`download_cli.py`** — set `CURL_BODY` to an HTML error page or an empty |
| 64 | string to prove the body check refuses it with no retry; empty the `PATH` to |
| 65 | prove a missing `curl` fails fast in one attempt; `CURL_EXIT=22` to prove a |
| 66 | genuine transient failure still retries 3×. |
| 67 | **The Windows path is unreachable on Linux.** It is behind |
| 68 | `platform.system() == "Windows"`. Drive it with a small runner that |
| 69 | `patch.object(m.platform, "system", return_value="Windows")` and a stub |
| 70 | `powershell` that reads the `-Command` text and honours |
| 71 | `$env:CLAUDE_CLI_INSTALL_SCRIPT`. This is the one place where forcing the |
| 72 | platform is legitimate. |
| 73 | |
| 74 | ## Gotchas |
| 75 | |
| 76 | `${CURL_BODY:-default}` treats an *empty* body as unset. Use a separate stub |
| 77 | that does `: > "$out"` to test the empty-body branch. |
| 78 | The retry path really sleeps (jitter up to 5s, then 2s + 4s). A full |
| 79 | three-attempt run takes ~10s; budget for it rather than assuming a hang. |
| 80 |
Discussion
Browse more free Claude skills.