mirror of
https://github.com/codewhale-hq/Codewhale.git
synced 2026-11-07 17:09:13 -10:00
fix(release): chain the tag check, re-prompt an empty token, make the rerun command cwd-proof
Third fix-up for the crate rerun blocks (8b92463a2c,9ded7ce896,3fd44b5501) after re-review. The verdict was push, with two low and two nit findings. All are fixed here, plus one hazard found while probing the fixes (Refs CW-7445). What - Token check in the founder's shell. In the `cargo owner --list` block, the trailing `# prints the owner, e.g. `Hmbown (Hunter Bown)`` was not a comment in interactive zsh, where interactivecomments is off by default. It became five extra arguments and a backtick command substitution, and cargo failed with `unexpected argument '#'` before it checked any token. The example moves into the sentence before the block. - Tag guard. Step 5 now reads `./scripts/release/require-release-tag-checkout.sh X.Y.Z && {`. Before, a failed check still prompted for the token, created a root and started publish-crates.sh, and only the script's inner guard stopped it. - Empty token. Both prompts loop with `token=; while [ -z "${token}" ]; do ... read -rs token || break; ... done`. Without bracketed paste (macOS /bin/bash 3.2), a stray blank line pasted after the block was read as an empty token. The block then did nothing, and a token typed at the next shell prompt would run as a command and land in history. End of input still stops the block. - Refusal command. It prints `cd "<repo root>" && mkdir -p ... && mkdir ... && ... python3 "<abs>/build-lock.py" ... -- "<abs>/publish-crates.sh" <mode>`. Pasted from another directory, the old `$0` form failed with FileNotFoundError, and an absolute script path alone still failed at `cargo metadata` (no Cargo.toml in cwd). - Wording. An interrupted run from the blocks leaves package/ under `${root}/target/package` with any cargo. build-lock.py serializes only with dev-cargo.sh and other build-lock.py users, not with a cargo wrapper that keeps its own build slots (such as cargowhale on this Mac). Verification (source:41ef8e0eb4plus this diff; blocks extracted from the doc; a cargo shim exits 99 on publish, or prints argc; no token, no network) - Step 5 with X.Y.Z=0.10.2 (no tag), under bash -s and zsh -f -s: printed "::error::Local release tag v0.10.2 does not exist.", gave no prompt and created no root. - Step 5 with the guard replaced by `true` and publish-crates.sh by an env printer: - token line: "child token=set"; - two blank lines then a token: three prompts, then "child token=set"; - end of input: one prompt, nothing run; - a second run in the same second stopped at "mkdir: ... File exists". Each was run under bash -s and zsh -f -s. - Owner block: blank line then token under bash -s and zsh -f -s, and token under `zsh -f -i` (interactivecomments off): "ARGSHIM token=set argc=3". - Real-state refusal (shim, default ~/.cargo, repo ./target): the printed command, run from a scratch directory outside the repo, reached "Verifying all 29 release package tarballs" (shim exit 99, CODEWHALE_BUILD_LOCK_HELD=1, target and home under the new root). Run again with the same printed root, it stopped at "File exists". - publish-crates.test.py under build-lock.py: OK (6 tests). validate-crate-publish-order.test.sh: passed. .github/scripts/release-workflows.test.js: exit 0. `bash -n`: OK. node web/scripts/check-facts.mjs: OK. RELEASE_RUNBOOK.md is not in agent-docs.generated.json, so nothing was regenerated. - npm test and check:web were last run ona5326d10db(101/19/420(413+7)/7, vitest 772/772, check:web exit 0). This change touches only docs/RELEASE_RUNBOOK.md and one echo line in publish-crates.sh. Not verified: an interactive tty paste with bracketed paste in zsh 5.9, which is the founder's shell. A real token and the publish itself are the founder's. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01NBKbhx19BRbZtvWK8rVKjz
This commit is contained in:
1 parent
41ef8e0eb4
commit
3cf53b6cb7
2 files changed
+21
-15
No files matched your search
+19
-13
@@ -165,7 +165,9 @@ That run was a cold build of about 19 GB and 962 compile steps, so check
|
||||
`scripts/dev-cache.sh`. `CARGO_TARGET_DIR` moves the build directory with
|
||||
plain Cargo and with `scripts/dev-cargo.sh`, which honors an inherited target
|
||||
directory, and `scripts/build-lock.py` holds the machine build lock whichever
|
||||
`cargo` is on `PATH`. The 0.10.2 receipt run set `CODEWHALE_CACHE_ROOT`
|
||||
`cargo` is on `PATH`. That lock serializes only with `scripts/dev-cargo.sh`
|
||||
and other `build-lock.py` users; a `cargo` wrapper with its own build slots
|
||||
does not take it. The 0.10.2 receipt run set `CODEWHALE_CACHE_ROOT`
|
||||
instead, which only `scripts/dev-cargo.sh` reads, and kept its lock inside the
|
||||
new root. `mkdir` without `-p` refuses a root that already exists, and the
|
||||
`&&` chain stops the run there. Every run leaves state that the next run at
|
||||
@@ -186,9 +188,11 @@ does not use.
|
||||
|
||||
If a dry-run is interrupted (Ctrl-C, or a shell that gets killed), inspect the
|
||||
half-packed `package/` directory in Cargo's build directory before retrying and
|
||||
coordinate with active builds. With plain Cargo that is `target/package`;
|
||||
`scripts/dev-cargo.sh` moves it under `${CODEWHALE_CACHE_ROOT}/build/`, and
|
||||
`cargo metadata --format-version 1 --no-deps` reports it as `build_directory`.
|
||||
coordinate with active builds. For a run from the blocks above that is
|
||||
`${root}/target/package` with any `cargo`. Otherwise plain Cargo uses
|
||||
`target/package` and `scripts/dev-cargo.sh` moves it under
|
||||
`${CODEWHALE_CACHE_ROOT}/build/`; `cargo metadata --format-version 1
|
||||
--no-deps` reports it as `build_directory`.
|
||||
Cargo can resume against it and every unpacked tarball may then fail with
|
||||
`error: Current directory is invalid:
|
||||
No such file or directory (os error 2)` — which looks like a compiler or
|
||||
@@ -396,13 +400,12 @@ and fails branch-only release sources before assets are published.
|
||||
`main`, with `HEAD` still matching the immutable remote release tag:
|
||||
|
||||
```bash
|
||||
./scripts/release/require-release-tag-checkout.sh X.Y.Z
|
||||
{
|
||||
./scripts/release/require-release-tag-checkout.sh X.Y.Z && {
|
||||
mode=publish
|
||||
cache="${CODEWHALE_CACHE_ROOT:-${XDG_CACHE_HOME:-$HOME/.cache}/codewhale}"
|
||||
lock="${CODEWHALE_BUILD_LOCK_FILE:-${cache}/build.lock}"
|
||||
root="${cache}/release-verify-$(git rev-parse --short=10 HEAD)-${mode}-$(date -u +%Y%m%dT%H%M%SZ)"
|
||||
printf 'crates.io token: ' >&2; read -rs token; echo >&2
|
||||
token=; while [ -z "${token}" ]; do printf 'crates.io token: ' >&2; read -rs token || break; echo >&2; done
|
||||
[ -n "${token}" ] && mkdir -p "${cache}" && mkdir "${root}" &&
|
||||
CARGO_REGISTRY_TOKEN="${token}" CARGO_HOME="${root}/cargo-home" \
|
||||
CARGO_TARGET_DIR="${root}/target" \
|
||||
@@ -420,9 +423,12 @@ and fails branch-only release sources before assets are published.
|
||||
empty `CARGO_HOME` and build directory of its own, as the package preflight
|
||||
above describes; the dry run's state makes it refuse otherwise. The braces
|
||||
make the shell read the whole block before `read` runs, so a pasted or
|
||||
piped block never takes its own next line as the token. `read -rs` keeps
|
||||
the token out of shell history, an empty token stops the block, and the
|
||||
environment variable keeps the token out of `credentials.toml`.
|
||||
piped block never takes its own next line as the token, and the prompt
|
||||
repeats on an empty line, so a stray blank line from a paste is not
|
||||
taken as the token. `read -rs` keeps the token out of shell history,
|
||||
end of input stops the block, and the environment variable keeps the
|
||||
token out of `credentials.toml`. The tag check is chained with `&&`, so a
|
||||
failed check asks for no token.
|
||||
If shared `main` advances or the checkout becomes dirty, stop and coordinate
|
||||
a new candidate or separately authorized recovery with Hunter. Do not switch
|
||||
branches, create another checkout, force-fetch a conflicting tag or bypass
|
||||
@@ -619,12 +625,12 @@ for that version — bump to the next patch instead.
|
||||
`403 {"errors":[{"detail":"this action can only be performed on the
|
||||
crates.io website"}]}` even for a good token (checked 2026-09-21 with a
|
||||
token that `cargo owner` accepts), so a 403 there proves nothing about the
|
||||
credential.
|
||||
credential. A good token prints the owner, e.g. `Hmbown (Hunter Bown)`:
|
||||
|
||||
```bash
|
||||
{
|
||||
printf 'crates.io token: ' >&2; read -rs token; echo >&2
|
||||
[ -n "${token}" ] && CARGO_REGISTRY_TOKEN="${token}" cargo owner --list codewhale-tui # prints the owner, e.g. `Hmbown (Hunter Bown)`
|
||||
token=; while [ -z "${token}" ]; do printf 'crates.io token: ' >&2; read -rs token || break; echo >&2; done
|
||||
[ -n "${token}" ] && CARGO_REGISTRY_TOKEN="${token}" cargo owner --list codewhale-tui
|
||||
unset token
|
||||
}
|
||||
```
|
||||
|
||||
@@ -91,8 +91,8 @@ if (( ${#reused[@]} > 0 )); then
|
||||
printf ' %s\n' "${reused[@]}"
|
||||
echo "Cargo treats crates served from its temporary package registry as immutable by name and version. It skips extraction once .cargo-ok exists and keeps compiled units that do not track their sources, so this run would verify old code. Nothing was deleted."
|
||||
echo "Rerun from a new, empty Cargo home and build directory. Every run leaves state the next run at this version refuses, so publish and each partial-publish rerun need their own root:"
|
||||
echo " mkdir -p \"${cache_root}\" && mkdir \"${verify_root}\" &&"
|
||||
echo " CARGO_HOME=\"${verify_root}/cargo-home\" CARGO_TARGET_DIR=\"${verify_root}/target\" python3 \"$(dirname "${script_dir}")/build-lock.py\" \"${CODEWHALE_BUILD_LOCK_FILE:-${cache_root}/build.lock}\" -- $0 ${mode}"
|
||||
echo " cd \"$(cd "${script_dir}/../.." && pwd)\" && mkdir -p \"${cache_root}\" && mkdir \"${verify_root}\" &&"
|
||||
echo " CARGO_HOME=\"${verify_root}/cargo-home\" CARGO_TARGET_DIR=\"${verify_root}/target\" python3 \"$(dirname "${script_dir}")/build-lock.py\" \"${CODEWHALE_BUILD_LOCK_FILE:-${cache_root}/build.lock}\" -- \"${script_dir}/publish-crates.sh\" ${mode}"
|
||||
echo "CARGO_TARGET_DIR moves the build directory with plain cargo and with scripts/dev-cargo.sh, and build-lock.py holds the machine build lock whichever cargo is on PATH."
|
||||
echo "For publish mode, pass the crates.io token as CARGO_REGISTRY_TOKEN for that one command, so Cargo writes no credentials.toml under the new root. After cargo login with that CARGO_HOME instead, run cargo logout with it once the publish finishes. See docs/RELEASE_RUNBOOK.md."
|
||||
} >&2
|
||||
|
||||
Reference in new issue
Block a user