Files
Maximilian Roos 8faeabf239 refactor(setup): run Codex Cloud as root and simplify both setup paths (#3841)
Consolidates the two remaining Codex Cloud drafts, #3835 and #3838, onto
what #3839 landed, then cuts what was left. Main's setup is a 163-line
script and a 26-line README under `scripts/codex-cloud/`; this is one
79-line file at `dev/codex.sh`, beside the repo's other development
files, with the README folded into its header. That empties `scripts/` —
the directory existed only for this.

**Codex Cloud runs as root** (from #3835). Setup used to replace
`/root/.cargo/bin/cargo` with a wrapper that re-executed cargo as a
UID-1000 `ubuntu` user under `tini`, and maintenance chowned the
checkout, the rustup home, and three cache directories to match. All of
it existed to keep the suite's permission tests from skipping, since
root can write to a read-only file.

Worth stating plainly: ten tests now skip on Codex Cloud. The pair
carrying the most weight is
`test_remove_foreground_succeeds_with_stuck_directory` and its
`_detached` twin, the only automated coverage of `wt remove` against a
directory it cannot delete. It is not a new hole — `setup-web` creates
no non-root user, so the Claude Code web environment has always skipped
them, and `tests/integration_tests/approval_pty.rs:157` carries a
standing TODO about it. Codex Cloud was the one environment buying an
exception, and a cargo wrapper, `tini`, `runuser`, and four chown passes
were the price. Both environments now agree about what the suite
observes, and that TODO is the single place to fix it for both.

Three of the ten decided that skip by reading `$USER` rather than by
probing the filesystem, which fails open: a container that runs as root
without exporting `USER` runs them and asserts an error root never gets.
They probe now, through one helper, like the other seven.

**Task, the checksums, and the retries are gone.** Nothing invoked Task
on Codex Cloud once #3839 stopped routing the launchers through it, so
it is no longer installed. The archive checksums follow the Taskfile
digest for the same reason that one went: HTTPS authenticates GitHub and
the container is disposable and secret-free, so verifying each download
bought a helper pair and a 64-char line per tool for very little. Each
install is now `curl | tar` and an `install`.

The version numbers stay. The gate runs `--all-features`, so nu and pwsh
drive PTY snapshots their own versions can move, and
`.github/actions/test-setup/action.yaml` pins cargo-insta,
cargo-nextest, and nu to the same three versions — unpinning those would
make the environment and CI disagree about snapshot output. Nothing
under `.github/` pins PowerShell, so CI runs whatever the runner image
ships and that version answers to nothing but these scripts.

**`setup-web` catches up** (from #3838). It gains `lsof`, installs
Nushell from its release archive rather than checking that one is
already present, bootstraps `uv` with pre-commit, and puts
`$HOME/.local/bin` on PATH. `wt` installs from the debug build produced
a few lines earlier instead of through a second full compile. Its `cargo
install` of cargo-insta and cargo-nextest stays unconditional, as on
main: neither form pins, and a `command -v` guard would have frozen
whatever versions the image happened to carry.

## Testing

`cargo run -- hook pre-merge --yes` passes, shellcheck is clean at
warning level, and `task --list` still parses. The `.tar.xz` and
`.tar.gz` extractions were run against the real release archives to
confirm the tar flags and the paths inside them. The three converted
tests pass unprivileged; their skip branch rests on the same probe the
other seven root-skipping tests already use.

Neither setup path is executable from a dev machine — Codex Cloud needs
Linux and root on the universal image, `setup-web` needs a web image —
so the first real exercise is the next environment build.

> _This was written by Claude Code on behalf of max-sixty_

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 16:58:55 -07:00

80 lines
3.4 KiB
Bash
Executable File

#!/usr/bin/env bash
# Codex Cloud environment for worktrunk's test suite.
#
# The environment uses the `universal` image, caching, unrestricted internet,
# and no variables or secrets. Its two settings fields hold these commands, so
# they stay short and fixed across changes to this file:
#
# bash dev/codex.sh setup
# bash dev/codex.sh maintain
#
# Validate a built environment with `cargo run -- hook pre-merge --yes`.
#
# The universal image is missing the shells, tools, and git version the suite
# needs. `setup` installs them; `maintain` re-warms the toolchain and caches for
# an environment restored from cache. Both run as root, which is how Codex Cloud
# runs the agent — so the tests that need an unprivileged uid skip, as they
# already do under `task setup-web`. The standing TODO is in
# tests/integration_tests/approval_pty.rs.
#
# The gate runs `--all-features`, so nu and pwsh drive PTY snapshots their own
# versions can move. cargo-insta, cargo-nextest, and nu are kept level with the
# pins in .github/actions/test-setup/action.yaml. CI has no pwsh pin — it takes
# whatever the runner image ships — so that version answers to nothing but this
# file.
set -euo pipefail
PRE_COMMIT_VERSION=4.6.2
INSTA_VERSION=1.48.0
NEXTEST_VERSION=0.9.143
NU_VERSION=0.115.0
PWSH_VERSION=7.6.5
cd "$(dirname "${BASH_SOURCE[0]}")/.."
setup() {
export DEBIAN_FRONTEND=noninteractive
apt-get update -qq
apt-get install -y -qq --no-install-recommends software-properties-common
add-apt-repository -y ppa:git-core/ppa
apt-get update -qq
apt-get install -y -qq --no-install-recommends git zsh fish xz-utils lsof
uv tool install --force pre-commit=="$PRE_COMMIT_VERSION"
tmp="$(mktemp -d)"
curl -fsSL "https://github.com/mitsuhiko/insta/releases/download/$INSTA_VERSION/cargo-insta-x86_64-unknown-linux-gnu.tar.xz" | tar -xJ -C "$tmp"
install -D -m 0755 "$tmp/cargo-insta-x86_64-unknown-linux-gnu/cargo-insta" /root/.local/bin/cargo-insta
curl -fsSL "https://github.com/nextest-rs/nextest/releases/download/cargo-nextest-$NEXTEST_VERSION/cargo-nextest-$NEXTEST_VERSION-x86_64-unknown-linux-gnu.tar.gz" | tar -xz -C "$tmp"
install -D -m 0755 "$tmp/cargo-nextest" /root/.local/bin/cargo-nextest
curl -fsSL "https://github.com/nushell/nushell/releases/download/$NU_VERSION/nu-$NU_VERSION-x86_64-unknown-linux-gnu.tar.gz" | tar -xz -C "$tmp"
install -D -m 0755 "$tmp/nu-$NU_VERSION-x86_64-unknown-linux-gnu/nu" /root/.local/bin/nu
rm -r -- "$tmp"
install -d /opt/microsoft/powershell/7
curl -fsSL "https://github.com/PowerShell/PowerShell/releases/download/v$PWSH_VERSION/powershell-$PWSH_VERSION-linux-x64.tar.gz" | tar -xz -C /opt/microsoft/powershell/7
chmod 0755 /opt/microsoft/powershell/7/pwsh
ln -sf /opt/microsoft/powershell/7/pwsh /usr/local/bin/pwsh
# pwsh needs the image's libicu, and without it lands on PATH but aborts at
# startup — so run it rather than looking for it.
pwsh -NoLogo -NoProfile -Command '$PSVersionTable.PSVersion.ToString()'
# A stale git fails the suite obscurely; fail here instead.
dpkg --compare-versions "$(git version | awk '{print $3}')" ge 2.54.0
git config --system --add safe.directory "$PWD"
}
maintain() {
rustup component add rust-docs
pre-commit install-hooks
cargo fetch --locked
}
case "${1-}" in
setup) setup; maintain ;;
maintain) maintain ;;
*) echo "usage: ${BASH_SOURCE[0]} setup|maintain" >&2; exit 1 ;;
esac