@@ -72,62 +72,22 @@
7272 # Host package set, for the wrapper app below.
7373 pkgsHost = nixpkgs.legacyPackages.${system};
7474
75− # Name/tag baked into the image's OCI config.
75+ # Default image name (for `copyWith`); `c init` passes a per-directory
76+ # name (`nix-container-<dir>`) to `copyWithShell` instead.
7677 imageName = "nix-container";
7778 imageTag = "latest";
7879
79− # Fish prompt config. Installed at /etc/fish/prompt.fish and loaded via
80− # `fish --no-config --init-command` (see `cmd` below), NOT from
81− # $HOME/.config/fish — so the `~/.config` mount can't shadow it. Built
82− # on the host arch (just a text file), so no Linux builder is needed.
83− fishConfig = pkgsHost.writeText "prompt.fish" ''
84− # No welcome banner.
85− set -g fish_greeting
86−
87− # The container runtime hands fish a bare TERM=xterm (8 colors) and
88− # no COLORTERM, washing out colors. The bundled terminfo DB (via
89− # ncurses) has the 256-color entry, so promote xterm→xterm-256color
90− # and advertise truecolor for 24-bit-aware tools.
91− if test "$TERM" = xterm; or test -z "$TERM"
92− set -gx TERM xterm-256color
93− end
94− set -q COLORTERM; or set -gx COLORTERM truecolor
95−
96− # Define the prompt as a function so fish renders it before every
97− # command; at top level it would run once at startup and be ignored.
98− function fish_prompt
99− set -l nix_shell_info
100− if set -q IN_NIX_SHELL; or set -q IN_NIX_RUN
101− set nix_shell_info ' ❄️'
102− end
103−
104− set -l cwd (prompt_pwd)
105−
106− set -l bookmark_info
107− if jj root 2>/dev/null >/dev/null
108− set -l jj_bookmark (jj log -r 'heads(::@ & bookmarks())' -T 'local_bookmarks ++ "\n"' --no-graph 2>/dev/null | tail -1)
109− if test -n "$jj_bookmark"
110− set bookmark_info ' ' (set_color brmagenta) $jj_bookmark (set_color normal)
111− end
112− end
113−
114− echo -n -s (set_color $fish_color_cwd) $cwd (set_color normal) $bookmark_info ' 🏗️' $nix_shell_info '> '
115− end
116− '';
117−
118− # Lay the prompt out at the path the cmd sources.
119− fishRoot = pkgsHost.runCommand "fish-prompt" { } ''
120− mkdir -p "$out/etc/fish"
121− cp ${fishConfig} "$out/etc/fish/prompt.fish"
122− '';
123−
124− # Build the OCI image from a `{ pkgs, nur }: [ ... ]` package function.
125− # The image always runs /bin/fish; fish, its prompt config, and Nix are
126− # bundled automatically, so `container.nix` lists only your extras.
80+ # Build the OCI image (named `name`) from a `{ pkgs, nur }: [ ... ]`
81+ # package function, for the named shell. The image's `cmd` is herdr
82+ # (the session `c start` attaches to); the shell is the default `$SHELL`
83+ # (what herdr panes spawn) and is added automatically, so `container.nix`
84+ # lists only your extras. Nix (+ CA certs) and the ncurses terminfo
85+ # database are always shipped too. Nothing here is fish-specific: the
86+ # TERM fixup happens host-side in `c` (passed via `-e TERM` at create).
12787 mkImage =
128− packages:
88+ name: shell: packages:
12989 n2c.buildImage {
130− name = imageName;
90+ inherit name;
13191 tag = imageTag;
13292 inherit arch;
13393
@@ -135,10 +95,6 @@
13595 # buildEnv only *symlinks* paths (never runs them), so build it on
13696 # the host arch — the tree is arch-neutral and links the
13797 # (substituted) Linux store paths, sparing the Linux builder.
138− #
139− # Always shipped: fish (the cmd) + its prompt, and Nix (+ CA certs)
140− # so every container can use Nix; `initializeNixDatabase` registers
141− # the baked store.
14298 copyToRoot = pkgsHost.buildEnv {
14399 name = "root";
144100 paths =
@@ -146,19 +102,27 @@
146102 pkgs = pkgsLinux;
147103 nur = nurPkgs;
148104 }
105+ # The shell (cmd + $SHELL), when it exists in nixpkgs.
106+ ++ pkgsLinux.lib.optional (pkgsLinux ? ${shell}) pkgsLinux.${shell}
149107 ++ [
150− pkgsLinux.fish
151− fishRoot
152108 pkgsLinux.nix
153109 pkgsLinux.cacert
110+ # Basic userland (ls, cat, cp, sleep, …) so the container is
111+ # usable out of the box and tools that shell out don't fail.
112+ pkgsLinux.coreutils-full
154113 # Networking glue glibc needs: /etc/nsswitch.conf (+passwd,
155114 # group) so DNS/host lookups resolve, and /etc/protocols,
156115 # /etc/services. Without these, hostname resolution fails.
157116 pkgsLinux.fakeNss
158117 pkgsLinux.iana-etc
118+ # The terminfo database (+ tput/clear/reset) so TUIs find the
119+ # entry for whatever TERM `c` passes in and render correctly.
120+ pkgsLinux.ncurses
121+ # The session `c start` always launches.
122+ pkgsLinux.herdr
159123 ];
160124 # Link all of /etc so the above files (nsswitch.conf, protocols,
161− # services, passwd, group, ssl certs, fish prompt) are present.
125+ # services, passwd, group, ssl certs) are present.
162126 pathsToLink = [
163127 "/bin"
164128 "/etc"
@@ -169,24 +133,25 @@
169133
170134 config = {
171135 # `cmd` (not `entrypoint`): the default command, *replaced* by any
172− # command passed to `container run/create`. `--no-config` skips
173− # $HOME/.config/fish (so a mounted ~/.config can't override the
174− # prompt); `--init-command` loads the bundled prompt instead.
175− cmd = [
176− "/bin/fish"
177− "--no-config"
178− "--init-command"
179− "source /etc/fish/prompt.fish"
180− ];
136+ # command passed to `container run/create`. Plain herdr here;
137+ # `c init` overrides it with `herdr session attach <dir>-container` so
138+ # the session is named per project. `c start` attaches via
139+ # `container start -ai`; exiting herdr (PID1) stops the container.
140+ cmd = [ "/bin/herdr" ];
181141 env = [
182142 "PATH=/bin"
183143 "HOME=/root"
144+ # Default shell for tools that spawn one (herdr panes, etc.).
145+ "SHELL=/bin/${shell}"
184146 # Marker so shells/scripts can detect they're in here.
185147 "NIX_CONTAINER=1"
186− # Advertise truecolor so `container exec` sessions (which don't
187− # source the fish init) still get 24-bit color. TERM is fixed up
188− # in the fish prompt init, since the runtime overrides it here.
148+ # Advertise truecolor for 24-bit-aware tools; `c` passes the
149+ # resolved TERM via -e (the runtime would otherwise force xterm).
189150 "COLORTERM=truecolor"
151+ # A UTF-8 locale so tools handle multibyte/wide characters
152+ # correctly. glibc ships C.UTF-8 built in, so this needs no
153+ # locale-archive.
154+ "LANG=C.UTF-8"
190155 # CA trust for every TLS client (curl/git/…) and Nix.
191156 "SSL_CERT_FILE=/etc/ssl/certs/ca-bundle.crt"
192157 "NIX_SSL_CERT_FILE=/etc/ssl/certs/ca-bundle.crt"
@@ -199,7 +164,7 @@
199164
200165 # `nix run . -- init|start` runs the bundled `c` manager script. Its
201166 # `init` builds the image from the project's `container.nix` (via
202− # `lib.copyWith`); the script finds the flake from its own (store)
167+ # `lib.copyWithShell`); the script finds the flake from its own (store)
203168 # location, so this works from a checkout or straight from
204169 # `nix run github:…/nix-container`.
205170 cApp = pkgsHost.writeShellScriptBin "c" ''
@@ -212,15 +177,22 @@
212177 default = flake-utils.lib.mkApp { drv = cApp; };
213178 };
214179
215− # Build helpers. `c init` uses `copyWith` to honour a per-project
216− # `container.nix`:
180+ # Build helpers take a `{ pkgs, nur }: [ ... ]` packages function (the
181+ # `packages` attr of a project's `container.nix`). `c init` uses
182+ # `copyWithShell` (host shell baked in); `copyWith` is the generic form:
217183 # (builtins.getFlake "github:…/nix-container")
218− # .lib.<system>.copyWith (import ./container.nix)
184+ # .lib.<system>.copyWith (import ./container.nix).packages
219185 lib = {
220186 inherit mkImage;
221− # skopeo copy app (`skopeo copy nix:<image> "$@"`) for an image built
222− # from a `{ pkgs, nur }: [ ... ]` function.
223− copyWith = packages: (mkImage packages).copyTo;
187+ # Build the image's `copyTo` script (a skopeo-copy wrapper) from a
188+ # `{ pkgs, nur }: [ ... ]` function, named `imageName`. Defaults the
189+ # shell (cmd + $SHELL) to bash; use `copyWithShell` to pick another.
190+ copyWith = packages: (mkImage imageName "bash" packages).copyTo;
191+ # Like `copyWith`, but for the given image `name` and shell — `c init`
192+ # passes `nix-container-<dir>` and the host login shell, which is added
193+ # to the image and used as cmd + $SHELL without being listed in
194+ # `container.nix`.
195+ copyWithShell = name: shell: packages: (mkImage name shell packages).copyTo;
224196 };
225197 }
226198 );