Fix: Install the whole Codex package so codex-code-mode-host sits beside bin/codex

Codex 0.165.0 ships as a package tree and runs bin/codex-code-mode-host,
codex-path/ and codex-resources/ relative to itself. The installer verified
the full archive but kept only bin/codex, so code mode failed. Keep the whole
verified tree, link ~/.local/bin/codex to its bin/codex, and replace the
receipted flat install earlier runs left behind once the new tree verifies.
This commit is contained in:
Gabriel Brown committed 2026-11-05 15:14:47 -05:00
1 parent 86f6e391b7
commit 6c8db4e845
3 files changed
+57 -16

No files matched your search

+4 -1
View File
@@ -77,7 +77,10 @@ matched the `digest` GitHub publishes for that release asset. The installer
accepts a Codex archive only when `tar -tzf` prints exactly the entries in
`codex-package.listing`. Both architectures share that listing. 0.165.0 dropped
the bundled `codex-resources/zsh/` and added `codex-resources/voice/`, and the
archives grew past 128 MiB, so `CODEX_*_MAX_BYTES` is 256 MiB. To bump Codex,
archives grew past 128 MiB, so `CODEX_*_MAX_BYTES` is 256 MiB. The installer keeps the
whole verified tree as the version directory and links `~/.local/bin/codex` to
its `bin/codex`, because Codex runs `bin/codex-code-mode-host` and reads
`codex-path/` and `codex-resources/` relative to itself. To bump Codex,
update `installers.conf`, regenerate the listing with `tar -tzf ARTIFACT`, and
run `tests/setup/package-provenance-contract`.
+25 -12
View File
@@ -417,20 +417,33 @@ _codex_version_matches() {
[[ "$output" =~ (^|[^0-9])${version_pattern}([^0-9]|$) ]]
}
# Codex ships as a package tree: bin/codex finds bin/codex-code-mode-host,
# codex-path/ and codex-resources/ beside itself, so the version directory keeps
# the whole verified tree and the active link points at its bin/codex.
_install_codex() (
local artifact_arch version_dir target bin_link parent expected_listing member
local stage archive listing staged_binary artifact_digest binary_digest
local retire_flat=0
stage=""
trap '[[ -z "$stage" ]] || rm -rf -- "$stage"' EXIT
# A retired flat install goes back if activation never happened.
trap '[[ -z "$stage" ]] || { [[ -d "$stage/retired" && ! -e "$version_dir" ]] \
&& mv -Tn -- "$stage/retired" "$version_dir"; rm -rf -- "$stage"; }' EXIT
trap 'exit 130' INT
trap 'exit 143' TERM
_set_artifact_arch || return 1
version_dir="$HOME/.local/lib/panama/codex/${INSTALLER_PROVENANCE[CODEX_VERSION]}"
target="$version_dir/codex"
target="$version_dir/bin/codex"
bin_link="$HOME/.local/bin/codex"
artifact_digest="${INSTALLER_PROVENANCE[CODEX_${artifact_arch}_SHA256]}"
binary_digest="${INSTALLER_PROVENANCE[CODEX_${artifact_arch}_BINARY_SHA256]}"
if [[ -e "$version_dir" || -L "$version_dir" ]]; then
# Earlier installs of this same reviewed artifact kept only the codex binary
# at the top of the version directory, without the helpers it needs. That
# directory carries our receipt, so it is replaced once the full tree verifies.
if [[ ! -e "$version_dir/bin" && ! -L "$version_dir/bin" ]] \
&& _runtime_receipt_matches "$version_dir" "$version_dir/codex" \
"$artifact_digest" "$binary_digest"; then
retire_flat=1
elif [[ -e "$version_dir" || -L "$version_dir" ]]; then
_runtime_receipt_matches "$version_dir" "$target" \
"$artifact_digest" "$binary_digest" || return 1
[[ -x "$target" ]] || return 1
@@ -459,24 +472,24 @@ _install_codex() (
while IFS= read -r member; do
_archive_path_is_safe "$member" || { rm -rf -- "$stage"; return 1; }
done <<<"$listing"
mkdir -m 0700 "$stage/extract" "$stage/version" \
mkdir -m 0700 "$stage/version" \
|| { rm -rf -- "$stage"; return 1; }
tar -xzf "$archive" --no-same-owner --no-same-permissions -C "$stage/extract" \
tar -xzf "$archive" --no-same-owner --no-same-permissions -C "$stage/version" \
|| { rm -rf -- "$stage"; return 1; }
_tree_links_stay_inside "$stage/extract" || { rm -rf -- "$stage"; return 1; }
_tree_hardlinks_stay_inside "$stage/extract" || { rm -rf -- "$stage"; return 1; }
staged_binary="$stage/extract/bin/codex"
_tree_links_stay_inside "$stage/version" || { rm -rf -- "$stage"; return 1; }
_tree_hardlinks_stay_inside "$stage/version" || { rm -rf -- "$stage"; return 1; }
staged_binary="$stage/version/bin/codex"
[[ -f "$staged_binary" && ! -L "$staged_binary" && -x "$staged_binary"
&& "$(sha256sum "$staged_binary" | awk '{ print $1 }')" == "$binary_digest" ]] \
|| { rm -rf -- "$stage"; return 1; }
_codex_version_matches "$staged_binary" \
|| { rm -rf -- "$stage"; return 1; }
mv -- "$staged_binary" "$stage/version/codex" \
|| { rm -rf -- "$stage"; return 1; }
_write_runtime_receipt "$stage/version" "$artifact_digest" "$binary_digest" \
|| { rm -rf -- "$stage"; return 1; }
_activate_directory_no_replace "$stage/version" "$version_dir" \
|| { rm -rf -- "$stage"; return 1; }
if (( retire_flat )); then
mv -Tn -- "$version_dir" "$stage/retired" || return 1
fi
_activate_directory_no_replace "$stage/version" "$version_dir" || return 1
_runtime_receipt_matches "$version_dir" "$target" \
"$artifact_digest" "$binary_digest" || { rm -rf -- "$stage"; return 1; }
_codex_version_matches "$target" || { rm -rf -- "$stage"; return 1; }
+28 -3
View File
@@ -1375,8 +1375,11 @@ grep -qFx 'nvm:alias default 24.20.0' "$test_tmp/cases/node-x86_64/commands.log"
[[ "$($test_tmp/cases/codex-x86_64/home/.local/bin/codex --version)" == \
'codex-cli 0.165.0' ]] || fail 'x86_64 Codex activation has the wrong version'
[[ "$(readlink "$test_tmp/cases/codex-x86_64/home/.local/bin/codex")" == \
"$test_tmp/cases/codex-x86_64/home/.local/lib/panama/codex/0.165.0/codex" ]] \
"$test_tmp/cases/codex-x86_64/home/.local/lib/panama/codex/0.165.0/bin/codex" ]] \
|| fail 'Codex did not atomically activate the reviewed version path'
[[ -x "$test_tmp/cases/codex-x86_64/home/.local/lib/panama/codex/0.165.0/bin/codex-code-mode-host" \
&& -f "$test_tmp/cases/codex-x86_64/home/.local/lib/panama/codex/0.165.0/codex-package.json" ]] \
|| fail 'Codex dropped the helpers its package ships beside bin/codex'
# Unsupported CPUs stop before curl. RustDesk's reviewed RPM is x86_64-only,
# so aarch64 is also an intentional, recorded soft failure without a download.
@@ -1511,7 +1514,7 @@ done
# Successful updates keep the old version directory and switch only the active
# symlink after the replacement binary has passed its version check.
for component_spec in 'Bun install_bun .bun/bin/bun .bun/versions/1.4.0/bin/bun' \
'Codex install_codex .local/bin/codex .local/lib/panama/codex/0.165.0/codex'; do
'Codex install_codex .local/bin/codex .local/lib/panama/codex/0.165.0/bin/codex'; do
read -r component function_name active_relative target_relative <<<"$component_spec"
name="${component,,}-atomic-update"
reset_installer_fixture
@@ -1528,12 +1531,34 @@ for component_spec in 'Bun install_bun .bun/bin/bun .bun/versions/1.4.0/bin/bun'
fi
done
# Installs before the package-layout fix kept only a receipted flat
# 0.165.0/codex. That directory is ours, so the full tree replaces it.
name=codex-flat-repair
reset_installer_fixture
STUB_SEED_OLD=Codex expect_success run_installer_function "$name" install_codex
home="$test_tmp/cases/$name/home"
flat="$home/.local/lib/panama/codex/0.165.0"
rm -rf -- "$flat"
mkdir -p -- "$flat"
cp "$artifact_root/codex-x86_64.bin" "$flat/codex"
chmod +x "$flat/codex"
printf 'schema=1\nartifact_sha256=%s\nbinary_sha256=%s\n' \
e97c4463d7f146692a955d7b0e1c8cf48f97cd4d616cd45c5dcab2e8d9743316 \
c06a67fd8ac76508dc78bd99ce20c9f317b8d568beda59485f3f4134df9962d5 > "$flat/.panama-provenance"
ln -sfn "$flat/codex" "$home/.local/bin/codex"
STUB_REUSE_CASE=1 expect_success run_installer_function "$name" install_codex
[[ "$(readlink "$home/.local/bin/codex")" == "$flat/bin/codex" \
&& -x "$flat/bin/codex-code-mode-host" && ! -e "$flat/codex" ]] \
|| fail "$name did not replace the flat install with the package tree"
[[ ! -s "$test_tmp/cases/$name/softly-failed" ]] || fail "$name recorded a soft failure"
assert_no_runtime_staging "$name"
# Receipt or executable tampering preserves the directory and leaves an older
# active link unchanged. A matching version string is not an attestation.
for tamper_spec in \
'Node install_node .nvm/versions/node/v24.20.0 bin/node v24.20.0' \
'Bun install_bun .bun/versions/1.4.0 bin/bun 1.4.0' \
'Codex install_codex .local/lib/panama/codex/0.165.0 codex codex-cli_0.165.0'; do
'Codex install_codex .local/lib/panama/codex/0.165.0 bin/codex codex-cli_0.165.0'; do
read -r component function_name target_relative binary_relative version_text <<<"$tamper_spec"
for tamper_kind in receipt binary; do
name="${component,,}-$tamper_kind-tamper"