refactor: Bottle.exec takes a user= kwarg, default node
test / unit (pull_request) Successful in 26s
test / integration (pull_request) Successful in 41s

Promote the user-switch from a hardcoded `node` to a keyword arg
so callers can opt into root (or any other user) when needed.
Default stays `node` — matches the docker image's USER and the
smolmachines runuser default.

Lifts the change through the base ABC, docker, and smolmachines
backends:
- Base: `def exec(self, script, *, user="node")`.
- Docker: adds `-u <user>` to `docker exec` (no-op when user is
  node, the image's default).
- Smolmachines: `runuser -l <user> -c <script>` — `runuser -l
  root` is the trivial no-op form when the caller asked for root.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-27 15:00:13 -04:00
parent e26d459a97
commit af65c10361
3 changed files with 32 additions and 15 deletions
+6 -3
View File
@@ -51,12 +51,15 @@ class DockerBottle(Bottle):
self.claude_docker_argv(argv, tty=tty), check=False,
).returncode
def exec(self, script: str) -> ExecResult:
def exec(self, script: str, *, user: str = "node") -> ExecResult:
# Pipe via stdin to `sh -s` so the caller never has to worry
# about quoting; the script source lands inside the container
# without crossing argv.
# without crossing argv. `-u <user>` overrides the image's
# default USER — defaults to `node` which is already the
# image's USER, so the explicit flag is a no-op there but
# keeps the cross-backend contract uniform.
result = subprocess.run(
["docker", "exec", "-i", self.name, "sh", "-s"],
["docker", "exec", "-u", user, "-i", self.name, "sh", "-s"],
input=script,
capture_output=True,
text=True,