Repository navigation
test(docker): give the Update-Codeman.sh smoke harness a non-root appdata owner - #738
JDProfresh wants to merge 2 commits into
Conversation
…data owner Both Update-Codeman.sh and Start-Codeman.sh derive PUID:PGID from the CODEMAN_APPDATA_PATH owner and refuse a root-owned directory. The smoke harness created that directory with mkdirSync, so under a root test runner (CI or agent containers) it was owned 0:0 and the script exited at that guard before its `build --no-cache` and `down`, failing the default and --keep-volumes smoke tests with an empty build/down log. The guard is correct; the harness now chowns appdata to 99:100 (docker/README.md's own example) when running as root, matching a real deployment.
|
Thanks @JDProfresh, and thank you for the careful root-runner measurements. This PR makes the I reproduced it as real root in a One small ask:
if (process.getuid?.() === 0) {
try {
chownSync(appdataPath, 99, 100);
} catch {
// No CAP_CHOWN, or a single-uid user namespace: leave it root-owned and
// let the scripts' own guard refuse, exactly as before this change.
}
}I ran that version as root with If you push that, great; otherwise I will apply it on top when merging. Thanks again for tracking this down to the ownership guard rather than loosening the scripts. |
Root without CAP_CHOWN (--cap-drop ALL, as Codeman's own Docker case containers run) or a single-uid user namespace makes chownSync throw, which failed all five smoke tests in harness setup instead of master's two.
|
Thanks, good catch. It's in ee97daa, your version with the comment naming both cases. I checked it the same way, as root in
|
|
Thanks @JDProfresh for pushing the follow-up so quickly. This PR makes the
This merges as soon as the CI test job finishes. Thanks again for tracking it down to the ownership guard rather than loosening the scripts. |
Under a root test runner, two of the
Update-Codeman.shsmoke tests intest/docker-entrypoint.test.tsfail because the harness creates its appdata directory withmkdirSync, so it is owned by root, and the script correctly refuses a root-owned appdata directory before it ever reachesbuild --no-cache.Problem
I hit this when the suite ran as root inside an agent container. Two tests fail there, and only there:
default: build --no-cache, THEN a plain down, ...fails withAssertionError: expected -1 to be greater than -1atexpect(buildIdx).toBeGreaterThan(-1)--keep-volumes: a plain down, with no --volumes flagfails withAssertionError: expected undefined to be definedatexpect(downLine).toBeDefined()The stub
dockerlog ends after the twocompose configcalls and the collision-guarddocker ps, with nobuildand nodown. Under uid 0 the script's stderr isError: CODEMAN_APPDATA_PATH is owned by root: .../appdata.Cause
Both
docker/Update-Codeman.shanddocker/Start-Codeman.shderivePUID:PGIDfrom the owner ofCODEMAN_APPDATA_PATHand exit when that owner is uid 0. That guard is right. The smoke harness makes the directory withmkdirSync, which gives it the runner's uid, so as a normal user it passes and as root it trips the guard. The three other smoke tests are unaffected because they stop at the config or collision checks, which run before the ownership guard.What it does
When the runner is root (
process.getuid?.() === 0), the harness chowns the appdata directory to99:100, the ownerdocker/README.md's ownchown -R 99:100example sets up, which is what a real deployment would have. Nothing changes for a non-root runner, including GitHub'subuntu-latestrunners, so CI behaves exactly as before. No script changes.Verified
All root runs were in a
node:22container started with-u 0(Node 22.23.4, bash 5.2) with the checkout bind-mounted:chownSyncline removed: the same 2 failures again.npm testas my normal user: everything passes excepttest/tui/tui-switch-guard.test.ts, which fails on my host's tmux 3.2a and has nothing to do with this change (it only touches the docker test).Neither script's refuse-root branch has a behavioural test today, before or after this change. I left that out to keep this one small, and can follow up with one if you want it.