Merge pull request #490 from opticon454/feature/docker-uv-uvx

fix(docker): keep CLIs installed from Settings across container updates
This commit is contained in:
Codeman maintainer
2026-09-28 16:20:54 +02:00
4 changed files with 21 additions and 0 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"aicodeman": patch
---
Docker Compose: CLIs installed from Settings (DeepSeek, Pi and any other npm-based CLI) survive `Update-Codeman.sh`. The image's `NPM_CONFIG_PREFIX` (`/opt/codeman-cli`) is image content and was discarded when the container was recreated; `POST /api/clis/:id/install` now installs into `~/.local` on the persistent home mount when running in the container, and `~/.local/bin` is on the image PATH.
+3
View File
@@ -219,6 +219,9 @@ RUN set -eux; \
COPY --from=ghcr.io/astral-sh/uv:0.9 /uv /uvx /usr/local/bin/
ENV NPM_CONFIG_PREFIX=/opt/codeman-cli
ENV PATH=$PATH:/opt/codeman-cli/bin
# CLIs installed at runtime (Settings -> CLIs, npm redirected to ~/.local by installEnv()) live on the
# persistent home mount, so they survive a container recreate. Appended for the same reason as above.
ENV PATH=$PATH:/home/${CODEMAN_RUNTIME_USER}/.local/bin
# pnpm is not an agent CLI: it is here because `dsh plugin` (DeepSeek Harness, which
# this image leaves to be installed at runtime, see SERVER_INTENTIONAL_OMISSIONS in
# test/docker-agent-image-coverage.test.ts) spawns a literal `pnpm` with no npm
+7
View File
@@ -217,6 +217,13 @@ export function installEnv(source: NodeJS.ProcessEnv = process.env): NodeJS.Proc
for (const [key, value] of Object.entries(source)) {
if (!key.startsWith('CODEMAN_')) env[key] = value;
}
// ⚠️ In the Docker Compose deployment the image sets NPM_CONFIG_PREFIX=/opt/codeman-cli, which is IMAGE
// content: `Update-Codeman.sh` recreates the container and every CLI installed there (dsh, pi, ...)
// vanishes. HOME is the persistent bind mount and `~/.local/bin` is already on every resolver's search
// list, so npm-based installs are redirected there. curl|bash installers already target HOME.
if (source.CODEMAN_IN_CONTAINER === '1' && source.HOME) {
env.NPM_CONFIG_PREFIX = `${source.HOME}/.local`;
}
return env;
}
+6
View File
@@ -713,6 +713,12 @@ describe('registry writes are serialized and never clobber a file the reader wou
}
expect(installEnv({ CODEMAN_PASSWORD: 'x', HOME: '/h' })).toEqual({ HOME: '/h' });
});
it('redirects npm installs to the persistent HOME inside the Compose container', () => {
expect(
installEnv({ CODEMAN_IN_CONTAINER: '1', HOME: '/home/codeman', NPM_CONFIG_PREFIX: '/opt/codeman-cli' })
).toEqual({ HOME: '/home/codeman', NPM_CONFIG_PREFIX: '/home/codeman/.local' });
});
});
/**