entrypoint: put back the shell state a recreate eats
cli_utils' install.sh reaches PATH by symlinking bin/ into ~/.local/bin. That is persistent on a host and ephemeral in a container, so the same installer produced opposite durability and every --force-recreate sent the human back to typing /workspace/cli_utils/bin/git-status-all. Re-link at start instead, and add a per-device boot hook so the next question of this shape needs no image change at all. Symlinks rather than a PATH edit in an rc file, deliberately: ~/.local/bin is already ahead of /usr/local/bin in ENV PATH, so links resolve in NON-interactive shells too (docker exec, agent tool shells, scripts). An rc-file PATH edit cannot reach those because ~/.bashrc returns early when not interactive — measured, that asymmetry is exactly why `command -v git-status-all` failed in one shell and worked in another on the same box. Shell FUNCTIONS remain the sourced file's job; a symlink cannot carry them. Guards, because ~/.local/bin is shared: a real file is never clobbered, a symlink pointing elsewhere is never stolen, ours are refreshed, and links into a cli_utils/bin whose target vanished are pruned — a dangling link on PATH reads as a broken container rather than a removed script. The hook (~/.config/devbox-shell/init.sh) adds no trust boundary: that dir is already sourced into every interactive shell by the baked bash_aliases, so it is already arbitrary code from the same owner. Only WHEN it runs is new. bash <file>, never sourced, exit status ignored, output to a log. Caught before commit, and the reason the loops use `if` bodies instead of `&&` chains: under `set -euo pipefail` a for-loop whose last command is a false test exits non-zero, and with no match the /workspace/*/cli_utils glob stays literal — so the first draft would have failed to START a container on every machine that does not have this repo, rather than merely skipping the links. Re-tested with set -e in place: no-cli_utils/empty-HOME no-op, guards, idempotence, CLI_UTILS_LINK=0, a hook that exits 7, and a hook that tries to mutate CLI_UTILS_BIN — all exit 0 with intact state. Not covered by CI: docker-publish.yml runs only on tags and lint.yml lints workflow run: steps, so neither executes this file. Validated by extracting both sections and running them against fixtures, then for real in a live v1.8.10 container. No smoke assertion added on purpose — the positive path needs a /workspace mount smoke does not have, and asserting it there would repeat the v1.8.0 mistake of a smoke check written against a stage that does not exist at run time. Moves the base hash (base-decide folds `cat entrypoint.sh entrypoint-user.sh`), so this rides along with the next tag's ~40-minute base rebuild rather than justifying a tag of its own.
This commit is contained in:
@@ -538,6 +538,35 @@ to refresh.
|
||||
Anything not on a volume is on the writable layer and is lost on
|
||||
container recreate.
|
||||
|
||||
### Rebuilding ephemeral shell state at start
|
||||
|
||||
Two entrypoint steps put back the kind of state that the writable layer eats, so a
|
||||
recreate does not cost you a manual re-install:
|
||||
|
||||
- **`cli_utils` commands.** If a `cli_utils` checkout is mounted, every
|
||||
executable in its `bin/` is symlinked into `~/.local/bin` on start, so
|
||||
`git-status-all` and friends are on `PATH` without a path prefix. Detection:
|
||||
`CLI_UTILS_CONTAINER_PATH` → `/workspace/cli_utils` → `$HOME/cli_utils` →
|
||||
`/workspace/*/cli_utils`. Set `CLI_UTILS_LINK=0` to disable. Existing real files
|
||||
in `~/.local/bin` and symlinks pointing elsewhere are left alone, so a
|
||||
deliberate override still wins; links whose target disappeared are pruned.
|
||||
Do **not** run a host installer's `install.sh` inside the container to achieve
|
||||
this — it writes to the ephemeral home and dies on the next recreate.
|
||||
- **A per-device boot hook.** If `~/.config/devbox-shell/init.sh` exists it is run
|
||||
once at start (`bash`, never sourced, exit status ignored), with output in
|
||||
`~/.pi/agent/devbox-init.log`. `~/.config/devbox-shell/` is the host-owned
|
||||
bind-mount whose `bash_aliases` is already sourced into every interactive shell,
|
||||
so a hook there persists across recreates with no image change. Use it for
|
||||
fixups that must exist *before any shell* — symlinks, directories, one-off
|
||||
migrations.
|
||||
|
||||
The distinction that decides which mechanism you want: `~/.local/bin` is on `ENV
|
||||
PATH`, so symlinks there work in **non-interactive** shells too (`docker exec <c>
|
||||
<cmd>`, agent tool shells, scripts). A `PATH` edit in `bash_aliases` reaches only
|
||||
*interactive* shells, because `~/.bashrc` returns early when non-interactive —
|
||||
which is also why shell **functions** (fzf helpers and the like) can only come
|
||||
from the sourced file, never from a symlink.
|
||||
|
||||
## MemPalace integration
|
||||
|
||||
MemPalace is installed in the base image and pre-warmed with the
|
||||
|
||||
Reference in New Issue
Block a user