mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-02 21:49:42 +02:00
codeman@1.32.1
6
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
5cf5a45438 |
feat(docker): opt-in gh + az CLIs with git credential helpers for private repos
Add Case -> Clone Repo could only reach public repositories in the Docker deployment. This lets a deployment opt in to the GitHub CLI and the Azure CLI (+ azure-devops extension) as git credential helpers. Codeman itself still collects no credentials. - server.Dockerfile / agent.Dockerfile: CODEMAN_INSTALL_GH / CODEMAN_INSTALL_AZ build args (0 or 1, default 0; anything else stops the build). Off leaves no apt repository, package, extension, helper script or credential entry, so a default build is unchanged. On installs from the vendors' apt repositories and configures system gitconfig helpers: github.com / gist.github.com -> `gh auth git-credential`, dev.azure.com / *.visualstudio.com -> new docker/git-credential-azure-cli (an Entra ID token from `az account get-access-token`, or AZURE_DEVOPS_EXT_PAT). A helper whose CLI is not signed in prints nothing, so a private clone still fails fast. - The extension lives in AZURE_EXTENSION_DIR outside HOME (/opt/codeman-az-extensions, runtime-owned; /opt/az-extensions, gid-0 group-writable in the agent image). - Hosts turn them on in docker-compose.override.yml: `build: args:` for the server image, `environment:` CODEMAN_AGENT_IMAGE_INSTALL_GH / _AZ for the agent image. build-agent-image.mjs and the in-app auto-build share one env -> ARG table (pinned by the parity test) and pass nothing when unset. docker-compose.yaml is untouched; .env.example only gains a comment, so the self-updater's environment gate sees no new keys. - Docker cases seed the gh sign-in (~/.config/gh/hosts.yml, config.yml) and the az sign-in files from ~/.azure per file, read-only, like pi/grok. - The Clone Repo AUTH_REQUIRED message says how to sign the server's git in instead of claiming private repositories cannot be cloned. - Docs: docker/README.md "Private repositories", docker-compose.md, docker-cases.md, the Quick-Start / Core-Concepts / Docker-Cases wiki pages, security-architecture.md, architecture-invariants.md, changeset. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0167CiuzLrmjYWxwKp3rMWjw |
||
|
|
1851d80f3a |
fix(docker): keep SIGTERM reaching the server, pin root's PATH, probe writability
Three changes to how the Compose container starts as root and drops to PUID:PGID, each reproduced on Docker 29.1.3 / Compose v5.5.0 with a minimal image of the same shape as server.Dockerfile. - cap_add gains KILL. `init: true` makes tini PID 1, and tini stays root while the entrypoint drops the server to PUID. Signalling a process of a different uid needs CAP_KILL, and `cap_drop: ALL` had removed it, so every `docker compose down`/`restart` ended in `[FATAL tini (1)] Unexpected error when forwarding signal: 'Operation not permitted'` and the server being SIGKILLed instead of running `server.stop()`. Measured: without KILL the trap never fires, with it the child logs `GOT SIGTERM`. - /opt/codeman-cli/bin is appended to PATH, never prepended, and entrypoint.sh pins its own PATH to the system directories before its first command. The prefix is chowned to the runtime account so sessions can update the agent CLIs in place, and the root entrypoint resolved stat/chown/setpriv by bare name through it: a `setpriv` planted there by the unprivileged uid ran as uid 0 at the next start. The image's full PATH is handed back to the server at the exec (`env PATH=...`), since Codeman resolves the CLIs through it. - The ownership gate becomes a writability probe. A directory owned by neither root nor PUID:PGID is no longer refused on ownership alone; it is tested with `setpriv --reuid PUID --regid PGID --groups <same groups> test -w`, the exact identity the server gets, so a group-writable tree, an ACL or a CIFS/NFS mount reporting some unrelated uid all pass, and the refusal names path, owner and PUID:PGID. Root-owned directories are still chowned first. Also: a pre-flight runs the drop before touching anything and, when it fails, prints the cap_add list the compose file needs, so an out-of-tree compose file (Unraid's Compose Manager) gets a one-line diagnosis instead of a restart loop; `--bounding-set -all` is gone, since it is a silent no-op without CAP_SETPCAP; a root:root Docker socket now produces a warning that Docker cases will not work rather than silently losing group 0 at the drop; and CODEMAN_ALLOWED_HOSTS is forwarded from .env with an empty default (documented as a commented entry in .env.example so the parity test and the updater's env gate both stay quiet). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> |
||
|
|
ae32daf135 |
fix(docker): address maintainer review on #377
Two real bugs the review caught, both verified live against a real build on the Unraid host: 1. entrypoint.sh's chown fired on ANY ownership mismatch, not just a directory the daemon itself created root-owned. A host tree legitimately owned by some other account - an existing CODEMAN_CASES_PATH the README already allows pointing at a normal projects directory, or appdata under a different PUID/PGID convention than the one in use - got silently recursively re-owned with one log line to explain it. Now gated on the target actually being root-owned; anything else is a clean refusal naming the directory, its owner, and PUID/PGID. Start-Codeman.sh also now pre-creates CODEMAN_CASES_PATH the same way it already did CODEMAN_APPDATA_PATH, so Compose never has to materialise a missing bind source as root in the first place - the in-container chown becomes a safety net, not the primary mechanism. 2. The CLI-update chown (chown -R .../node_modules /usr/local/bin) handed the runtime account write access to entrypoint.sh itself (root-owned, executed as root on every container start with CHOWN/DAC_OVERRIDE/SETUID/SETGID) and the node binary - owning the DIRECTORY is enough to rename it aside and drop a replacement, which would let a compromised session arrange for its own script to run as root at the next restart. The four CLIs now install into a dedicated /opt/codeman-cli prefix (NPM_CONFIG_PREFIX); only that directory is chowned, /usr/local stays root-owned throughout. Smaller fixes from the same review: - Start-Codeman.sh's volume-refresh label filter wasn't project-scoped: a second Compose stack on the same host sharing the `codeman-dist` volume KEY could have had ITS volume deleted. Added a com.docker.compose.project filter, resolved from this stack's own `compose config --format json`. - Override-file precedence was backwards (checked .yaml before .yml; Compose actually prefers .yml) - swapped, plus a warning when both exist. - entrypoint.sh's setpriv now also passes --bounding-set -all, so CapBnd actually clears post-drop rather than just CapPrm/CapEff. - A comment on git_head_commit() noting it returns nothing for a worktree checkout (.git as a file), consistent with the script's existing -d .git convention elsewhere. - Doc drift: CLAUDE.md's Docker Compose section still described the old pre-created-and-chowned-by-hand model and didn't mention the root-then-drop entrypoint; the state-files list was missing docker-build-source.json; docs/docker-compose.md and docker/.env.example still had the pre-rename `Coding/codeman` path in one place each. Verified end to end against a real build on the Unraid host: a root-owned bind source is corrected as before; a directory owned by neither root nor PUID:PGID is refused rather than silently rewritten; a correctly-owned directory is left alone entirely; the four CLIs resolve via PATH from /opt/codeman-cli while /usr/local/bin, /usr/local/lib/node_modules and entrypoint.sh itself stay root-owned; CapBnd is fully cleared post-drop. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R9ZSTEenc8soSu9bTi8Xru |
||
|
|
9702126046 |
chore(docker): name the default runtime account codeman
CODEMAN_RUNTIME_USER defaulted to `opencode`, which no longer matches the project and is confusing in a deployment whose every other identifier is codeman. Rename the default in .env.example and in the Dockerfile ARG that mirrors it, and correct the example comment that referred to /home/opencode/codeman-cases. Also drop the `Coding/` component from the example application-data path. CODEMAN_APPDATA_PATH and CODEMAN_CASES_PATH now suggest /mnt/user/appdata/codeman and its codeman-cases child, matching the account name and removing a directory level that meant nothing outside the original author's host. README.md is updated to match, including the chown example. The npm package `opencode-ai` and the references to the OpenCode CLI are deliberately left alone: those name a different tool, not this account. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
66eb01ba8f |
feat(docker): restore in-app self-update in the Compose deployment
Codeman running under docker/docker-compose.yaml lost the ability to update
itself from App Settings -> Updates. The image had no .git (excluded by
.dockerignore), so the install reported as "unknown"; there was no init system
for detectSupervisor() to find; the runtime stage had neither devDependencies
nor a build toolchain; and a pull into the baked /opt/codeman would have landed
in the container's writable layer and been discarded by the next `up`.
Restore it through configuration rather than a second updater, so the release
channel, auto-stash, status file and boot reconcile are all reused unchanged:
- The checkout Compose builds from is bind-mounted over /opt/codeman, so the
update's git checkout and rebuild land on the host and survive recreation.
- The restart is the server exiting; `restart: unless-stopped` relaunches the
container on the new dist/. This is the one supervisor whose updater does NOT
outlive the restart, which is safe only because the terminal "restarting"
marker is written first.
- node_modules and dist are named volumes over the bind mount, so
container-compiled native modules never enter the host checkout.
- The runtime image keeps devDependencies and gains python3/make/g++, since
`npm run build` is tsc + esbuild and node-pty has no Linux prebuild.
An in-place container update applies code only, because a restart reuses the
existing image and config. evaluateEnvironmentGate() reads the target release's
own files with `git show <tag>:<path>` and refuses when server.Dockerfile or
docker-compose.yaml changed, when .env.example gained keys the user's .env
lacks, or when the restart policy would not bring the container back. The
missing-key check matters most: Compose resolves an unset ${VAR} to the empty
string and starts anyway, so a new required setting would otherwise arrive as a
silently blank variable. Every unknown fails open, and the gate is re-evaluated
server-side on POST /api/system/update.
The four global agent CLIs are pinned, because an unpinned CLI bump is the one
environment change no diff-derived gate can see; pinning turns it into a
Dockerfile change the gate already detects.
Adds test/docker-compose-env-parity.test.ts as the merge-side guard (every
compose ${VAR} has an .env.example entry and the reverse) and
test/docker-self-update.test.ts for the pure gate decisions.
Documented in docs/docker-self-update.md.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013yAQ2y9t81jzSfpStUxx5T
|
||
|
|
b85f7659b7 | feat(docker): add Compose deployment support |