mirror of
https://github.com/max-sixty/worktrunk.git
synced 2026-09-14 20:00:38 +08:00
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>
This commit is contained in:
@@ -301,7 +301,7 @@ For each weekly run, check upstream and bump:
|
||||
|
||||
- **`baptiste0928/cargo-install@v3` blocks** in `.github/workflows/{affected,ci,coverage,nightly}.yaml` and `.github/actions/{test,claude}-setup/action.yaml` — every `version: "=X.Y.Z"` against `cargo info <crate>`. Today: `cargo-affected`, `cargo-insta`, `cargo-nextest`, `cargo-llvm-cov`, `cargo-msrv`, `cargo-udeps`, `lychee`, `worktrunk`. `cargo-affected` is pinned twice in `affected.yaml`; move both together. Verify each crate's `rust-version` against the pinned toolchain and note compatibility in the PR body (see PR #1657 for the format).
|
||||
- **`hustcer/setup-nu@v3`** `version:` input — latest from `gh api repos/nushell/nushell/releases/latest --jq '.tag_name'`. Four call sites: `coverage.yaml` (`code-coverage`), `nightly.yaml` (`feature-powerset`), `benchmarks.yaml` (`benchmarks`), and `actions/test-setup/action.yaml`.
|
||||
- **Codex Cloud tools** — `scripts/codex-cloud/codex.sh` pins pre-commit through uv, and Task, cargo-insta, cargo-nextest, Nushell, and PowerShell archives with SHA-256 checksums. Keep overlapping versions aligned with CI and `setup-web`, and update changed archive checksums.
|
||||
- **Codex Cloud tools** — `dev/codex.sh` pins pre-commit, cargo-insta, cargo-nextest, Nushell, and PowerShell; `setup-web` pins Nushell and PowerShell. Keep cargo-insta, cargo-nextest, and Nushell level with `.github/actions/test-setup/action.yaml`, which pins the same three — the gate runs `--all-features`, so Nushell's version moves PTY snapshots. Nothing under `.github/` pins PowerShell (CI runs whatever the runner image ships), so bump that one on its own.
|
||||
- **`taiki-e/install-action@v2.x`** `tool: zola@<ver>` in the `check-docs` job — latest from `gh api repos/getzola/zola/releases/latest --jq '.tag_name'`.
|
||||
- **Runner images** — `ubuntu-24.04`, `macos-15`, `windows-2022`. Keep `windows-2022` pinned (actions/runner-images#12677 — windows-2025 lacks the D: drive).
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ cargo run -- hook pre-merge --yes # all tests + lints (runs automatically in w
|
||||
```
|
||||
|
||||
Claude Code web: run `task setup-web` first. Test commands, isolation, and coverage investigation: `tests/CLAUDE.md`.
|
||||
Codex Cloud: use the setup in `scripts/codex-cloud/README.md`.
|
||||
Codex Cloud: use the setup in `dev/codex.sh`.
|
||||
|
||||
## Project Status
|
||||
|
||||
|
||||
+23
-4
@@ -32,6 +32,7 @@ tasks:
|
||||
cmds:
|
||||
- |
|
||||
set -e
|
||||
export PATH="$HOME/.local/bin:$HOME/bin:$PATH"
|
||||
echo "========================================"
|
||||
echo "Claude Code Web - Worktrunk Setup"
|
||||
echo "========================================"
|
||||
@@ -71,9 +72,10 @@ tasks:
|
||||
# installs: apt resolves the PowerShell .deb's dependencies from
|
||||
# these lists too.
|
||||
if ! command -v zsh &> /dev/null || ! command -v fish &> /dev/null \
|
||||
|| ! command -v jq &> /dev/null || ! command -v pwsh &> /dev/null; then
|
||||
|| ! command -v jq &> /dev/null || ! command -v lsof &> /dev/null \
|
||||
|| ! command -v pwsh &> /dev/null; then
|
||||
apt-get update -qq
|
||||
apt-get install -y -qq zsh fish jq
|
||||
apt-get install -y -qq zsh fish jq lsof
|
||||
fi
|
||||
# PowerShell isn't in Debian's repos. Install the release .deb
|
||||
# rather than the tarball: pwsh aborts at startup without libicu,
|
||||
@@ -88,6 +90,19 @@ tasks:
|
||||
echo "pwsh installed"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Nushell isn't in every web image, and the shell-integration suite
|
||||
# drives it directly.
|
||||
if ! command -v nu &> /dev/null; then
|
||||
NU_VERSION="0.115.0"
|
||||
DIR="nu-${NU_VERSION}-x86_64-unknown-linux-gnu"
|
||||
TEMP=$(mktemp -d)
|
||||
curl -fsSL "https://github.com/nushell/nushell/releases/download/${NU_VERSION}/${DIR}.tar.gz" \
|
||||
| tar -xz -C "$TEMP"
|
||||
install -D -m 0755 "$TEMP/$DIR/nu" "$HOME/.local/bin/nu"
|
||||
rm -rf "$TEMP"
|
||||
echo "nu installed"
|
||||
fi
|
||||
# The shells the feature drives, plus the `jq` its Claude-hook tests
|
||||
# pipe the hook payload through. Run each rather than looking for it
|
||||
# on PATH, because a pwsh missing its libicu is on PATH and aborts at
|
||||
@@ -125,9 +140,13 @@ tasks:
|
||||
# Install dev tools
|
||||
echo ""
|
||||
echo "Installing development tools..."
|
||||
if ! command -v uv &> /dev/null; then
|
||||
curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||
fi
|
||||
uv tool install --force pre-commit
|
||||
cargo install cargo-insta cargo-nextest --quiet
|
||||
cargo install --path . --quiet
|
||||
echo "Installed cargo-insta, cargo-nextest, worktrunk"
|
||||
install -D -m 0755 target/debug/wt "$HOME/.local/bin/wt"
|
||||
echo "Installed pre-commit, cargo-insta, cargo-nextest, worktrunk"
|
||||
|
||||
echo ""
|
||||
echo "Setup complete! Run 'wt --help' to get started."
|
||||
|
||||
Executable
+79
@@ -0,0 +1,79 @@
|
||||
#!/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
|
||||
@@ -1,26 +0,0 @@
|
||||
# Codex Cloud
|
||||
|
||||
The environment uses the `universal` image, caching, unrestricted internet,
|
||||
and no variables or secrets.
|
||||
|
||||
Setup command:
|
||||
|
||||
```bash
|
||||
bash scripts/codex-cloud/codex.sh setup
|
||||
```
|
||||
|
||||
Maintenance command:
|
||||
|
||||
```bash
|
||||
bash scripts/codex-cloud/codex.sh maintain
|
||||
```
|
||||
|
||||
The agent remains root. Toolchain-sensitive Rustup and pre-commit steps run as
|
||||
`ubuntu`; the Cargo wrapper also runs builds and tests as `ubuntu` under `tini`,
|
||||
matching the suite's permission and child-reaping assumptions.
|
||||
|
||||
Validation:
|
||||
|
||||
```bash
|
||||
cargo run -- hook pre-merge --yes
|
||||
```
|
||||
@@ -1,163 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Codex Cloud environment for worktrunk's test suite.
|
||||
#
|
||||
# The universal image is missing the shells, tools, and git version the suite
|
||||
# needs, and it runs the agent as root while the suite assumes a non-root UID
|
||||
# 1000 whose children are reaped by tini. `setup` installs the tools and
|
||||
# replaces cargo with a wrapper that drops to `ubuntu`; `maintain` re-runs only
|
||||
# the ownership and cache preparation that a cached environment still needs.
|
||||
#
|
||||
# Both commands are typed into the Codex Cloud environment settings, so they
|
||||
# stay short and fixed across changes to this file. See README.md.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
TASK_VERSION=3.52.0
|
||||
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]}")/../.."
|
||||
|
||||
require_root_on_universal_image() {
|
||||
if [ "$(id -u)" != 0 ] || [ "$(id -u ubuntu 2>/dev/null)" != 1000 ]; then
|
||||
echo "Codex Cloud $1 requires root and the universal image's ubuntu user" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
download() {
|
||||
curl -fsSL --retry 6 --retry-all-errors --retry-delay 2 \
|
||||
"$1" -o "$tools_tmp/$3"
|
||||
printf '%s %s\n' "$2" "$tools_tmp/$3" | sha256sum -c -
|
||||
}
|
||||
|
||||
install_binary() {
|
||||
download "$1" "$2" "$3"
|
||||
case "$3" in
|
||||
*.tar.xz) tar -xJf "$tools_tmp/$3" -C "$tools_tmp" ;;
|
||||
*.tar.gz) tar -xzf "$tools_tmp/$3" -C "$tools_tmp" ;;
|
||||
esac
|
||||
install -m 0755 "$tools_tmp/$4" "$5"
|
||||
}
|
||||
|
||||
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 tini
|
||||
|
||||
uv tool install pre-commit=="$PRE_COMMIT_VERSION"
|
||||
install -d /root/.local/bin
|
||||
|
||||
tools_tmp="$(mktemp -d)"
|
||||
|
||||
install_binary \
|
||||
"https://github.com/go-task/task/releases/download/v$TASK_VERSION/task_linux_amd64.tar.gz" \
|
||||
02c679ffae53dca791804847d78b31731615894e292948397c971c87ac9e95bd \
|
||||
task.tar.gz task /root/.local/bin/task
|
||||
install_binary \
|
||||
"https://github.com/mitsuhiko/insta/releases/download/$INSTA_VERSION/cargo-insta-x86_64-unknown-linux-gnu.tar.xz" \
|
||||
1c05a480a5a7f755f0ea15b2d8e2f71ad51b9c3a270d38ac72005c57ac0a1487 \
|
||||
cargo-insta.tar.xz cargo-insta-x86_64-unknown-linux-gnu/cargo-insta \
|
||||
/root/.local/bin/cargo-insta
|
||||
install_binary \
|
||||
"https://github.com/nextest-rs/nextest/releases/download/cargo-nextest-$NEXTEST_VERSION/cargo-nextest-$NEXTEST_VERSION-x86_64-unknown-linux-gnu.tar.gz" \
|
||||
66786b9abe23920d022a182d1416b1bbc8130dd4872a9553d76985a1708dcd1e \
|
||||
cargo-nextest.tar.gz cargo-nextest /root/.local/bin/cargo-nextest
|
||||
install_binary \
|
||||
"https://github.com/nushell/nushell/releases/download/$NU_VERSION/nu-$NU_VERSION-x86_64-unknown-linux-gnu.tar.gz" \
|
||||
da83cfe482060d2c34b6b9af829975a313bce6b92e0398c3b2a59cb38630c7b2 \
|
||||
nu.tar.gz "nu-$NU_VERSION-x86_64-unknown-linux-gnu/nu" /root/.local/bin/nu
|
||||
|
||||
pwsh_archive=powershell.tar.gz
|
||||
# The universal image supplies libicu; the version probe below fails if that changes.
|
||||
download \
|
||||
"https://github.com/PowerShell/PowerShell/releases/download/v$PWSH_VERSION/powershell-$PWSH_VERSION-linux-x64.tar.gz" \
|
||||
b34ab3b19acac1d3d4d0d3cfdb02acf62f457b0b6a962ff008132033f7566844 \
|
||||
"$pwsh_archive"
|
||||
install -d /opt/microsoft/powershell/7
|
||||
tar -xzf "$tools_tmp/$pwsh_archive" -C /opt/microsoft/powershell/7
|
||||
chmod 0755 /opt/microsoft/powershell/7/pwsh
|
||||
ln -sf /opt/microsoft/powershell/7/pwsh /usr/local/bin/pwsh
|
||||
rm -r -- "$tools_tmp"
|
||||
|
||||
git_version="$(git version | awk '{print $3}')"
|
||||
dpkg --compare-versions "$git_version" ge 2.54.0
|
||||
chmod 0711 /root
|
||||
chown -hR ubuntu:ubuntu "$PWD"
|
||||
git config --system --add safe.directory "$PWD"
|
||||
|
||||
rm /root/.cargo/bin/cargo
|
||||
install -m 0755 /dev/stdin /root/.cargo/bin/cargo <<'CARGO_WRAPPER'
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
if [ "${CODEX_CARGO_IDENTITY_PROBE:-0}" = 1 ]; then
|
||||
exec /usr/bin/id -u
|
||||
fi
|
||||
cargo_path="$(/root/.cargo/bin/rustup which cargo)"
|
||||
toolchain_bin="${cargo_path%/cargo}"
|
||||
PATH="$toolchain_bin:/root/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
||||
export PATH
|
||||
exec "$cargo_path" "$@"
|
||||
fi
|
||||
|
||||
exec /usr/bin/tini -s -- /usr/sbin/runuser -u ubuntu -- \
|
||||
/usr/bin/env HOME=/home/ubuntu USER=ubuntu LOGNAME=ubuntu \
|
||||
CARGO_HOME=/root/.cargo RUSTUP_HOME=/root/.rustup \
|
||||
PRE_COMMIT_HOME=/root/.cache/pre-commit PATH="$PATH" \
|
||||
CODEX_CARGO_IDENTITY_PROBE="${CODEX_CARGO_IDENTITY_PROBE:-0}" \
|
||||
/root/.cargo/bin/cargo "$@"
|
||||
CARGO_WRAPPER
|
||||
|
||||
for executable in task pre-commit cargo cargo-insta cargo-nextest git lsof pwsh tini zsh fish nu; do
|
||||
command -v "$executable" >/dev/null
|
||||
done
|
||||
pwsh -NoLogo -NoProfile -Command '$PSVersionTable.PSVersion.ToString()'
|
||||
}
|
||||
|
||||
prepare() {
|
||||
chown -R ubuntu:ubuntu /root/.rustup
|
||||
install -d -m 0755 -o ubuntu -g ubuntu target \
|
||||
/root/.cargo/registry /root/.cargo/git /root/.cache/pre-commit
|
||||
find "$PWD" -path "$PWD/target" -prune -o -exec chown -h ubuntu:ubuntu {} +
|
||||
for cache_dir in /root/.cargo/registry /root/.cargo/git /root/.cache/pre-commit; do
|
||||
[ ! -e "$cache_dir" ] || chown -R ubuntu:ubuntu "$cache_dir"
|
||||
done
|
||||
/usr/sbin/runuser -u ubuntu -- \
|
||||
/usr/bin/env HOME=/home/ubuntu USER=ubuntu LOGNAME=ubuntu \
|
||||
RUSTUP_HOME=/root/.rustup \
|
||||
/root/.cargo/bin/rustup component add rust-docs
|
||||
/usr/sbin/runuser -u ubuntu -- \
|
||||
/usr/bin/env HOME=/home/ubuntu USER=ubuntu LOGNAME=ubuntu \
|
||||
CARGO_HOME=/root/.cargo RUSTUP_HOME=/root/.rustup \
|
||||
PRE_COMMIT_HOME=/root/.cache/pre-commit PATH="$PATH" \
|
||||
pre-commit install-hooks
|
||||
test "$(command -v cargo)" = /root/.cargo/bin/cargo
|
||||
test "$(CODEX_CARGO_IDENTITY_PROBE=1 cargo)" = 1000
|
||||
cargo --version
|
||||
cargo fetch --locked
|
||||
}
|
||||
|
||||
case "${1-}" in
|
||||
setup)
|
||||
require_root_on_universal_image setup
|
||||
setup
|
||||
prepare
|
||||
;;
|
||||
maintain)
|
||||
require_root_on_universal_image maintenance
|
||||
prepare
|
||||
;;
|
||||
*)
|
||||
echo "usage: ${BASH_SOURCE[0]} setup|maintain" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
@@ -8,6 +8,24 @@ fn test_repo() -> TestRepo {
|
||||
TestRepo::new()
|
||||
}
|
||||
|
||||
/// Whether mode bits actually restrict reads here. Root ignores them, so the
|
||||
/// permission tests below would assert an error that never arrives, and skip
|
||||
/// instead. Probing is what makes that decision on the uid rather than on
|
||||
/// `$USER`, which a container running as root can leave unset — the same shape
|
||||
/// the permission tests in `tests/` use.
|
||||
#[cfg(unix)]
|
||||
fn permissions_restrict_reads(dir: &std::path::Path) -> bool {
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
let probe = dir.join("permission-probe");
|
||||
std::fs::write(&probe, b"x").unwrap();
|
||||
std::fs::set_permissions(&probe, std::fs::Permissions::from_mode(0o000)).unwrap();
|
||||
let restricted = std::fs::read(&probe).is_err();
|
||||
let _ = std::fs::set_permissions(&probe, std::fs::Permissions::from_mode(0o644));
|
||||
let _ = std::fs::remove_file(&probe);
|
||||
restricted
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_default_config_path_returns_platform_path() {
|
||||
// default_config_path() returns the platform-specific path without
|
||||
@@ -2453,8 +2471,7 @@ fn test_reload_from_permission_error() {
|
||||
}
|
||||
let _guard = RestorePerms(&config_path);
|
||||
|
||||
// Skip this test when running as root (common in CI containers)
|
||||
if std::env::var("USER").as_deref() == Ok("root") {
|
||||
if !permissions_restrict_reads(dir.path()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -3447,8 +3464,7 @@ fn test_save_to_existing_file_with_unreadable_file_returns_read_error() {
|
||||
}
|
||||
let _guard = RestorePerms(&config_path);
|
||||
|
||||
// Skip when running as root (common in CI containers)
|
||||
if std::env::var("USER").as_deref() == Ok("root") {
|
||||
if !permissions_restrict_reads(dir.path()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -4012,7 +4028,7 @@ fn test_with_locked_mutation_propagates_save_error() {
|
||||
}
|
||||
let _guard = RestorePerms(&config_path);
|
||||
|
||||
if std::env::var("USER").as_deref() == Ok("root") {
|
||||
if !permissions_restrict_reads(dir.path()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ cargo test --test integration --features shell-integration-tests # + shell tes
|
||||
|
||||
Every binary the suite spawns is `wt` itself — the mock commands are the same binary linked under other names, dispatching on argv[0] (`testing::mock_stub`) — so no run can spawn missing or stale code: cargo rebuilds a package's own binaries whenever its integration tests build, under every runner and filter, and `wt_bin()` resolves `CARGO_BIN_EXE_wt` — naming that just-built binary — into a hardlink pinned under `target/debug/wt-test-bin/`, which a concurrent `cargo build`'s uplift can't unlink mid-run (see No Retries); outside a cargo runner the suite panics ("CARGO_BIN_EXE_wt not set") rather than guessing a path. `cargo build --bin wt` recompiling right after a test run is the bin-only build being a separate cached unit (a different feature graph), not evidence the tests ran stale code.
|
||||
|
||||
**Claude Code web:** `task setup-web` installs zsh, fish, `jq`, PowerShell, `gh`, and dev tools, and checks that nushell is already there. Install `task` first if needed: `sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b ~/bin` then `export PATH="$HOME/bin:$PATH"`. The permission tests (`test_permission_error_prevents_save`, `test_approval_prompt_permission_error`) skip automatically when running as root.
|
||||
**Claude Code web:** `task setup-web` installs zsh, fish, Nushell, PowerShell, `jq`, `lsof`, `gh`, pre-commit, and the Cargo dev tools. Install `task` first if needed: `sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b ~/bin` then `export PATH="$HOME/bin:$PATH"`. Tests that need an unprivileged uid skip automatically when running as root, which both this environment and Codex Cloud do; that covers the permission tests and the `wt remove` stuck-directory pair, the only automated coverage of that path.
|
||||
|
||||
**Shell/PTY tests** (`shell-integration-tests` feature): approval prompts, picker, progressive rendering, shell wrappers.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user