@@ -72,22 +72,62 @@
7272 # Host package set, for the wrapper app below.
7373 pkgsHost = nixpkgs.legacyPackages.${system};
7474
75− # Default image name (for `copyWith`); `c init` passes a per-directory
76− # name (`nix-container-<dir>`) to `copyWithShell` instead.
75+ # Name/tag baked into the image's OCI config.
7776 imageName = "nix-container";
7877 imageTag = "latest";
7978
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).
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.
87127 mkImage =
88− name: shell: packages:
128+ packages:
89129 n2c.buildImage {
90− inherit name;
130+ name = imageName;
91131 tag = imageTag;
92132 inherit arch;
93133
@@ -95,6 +135,10 @@
95135 # buildEnv only *symlinks* paths (never runs them), so build it on
96136 # the host arch — the tree is arch-neutral and links the
97137 # (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.
98142 copyToRoot = pkgsHost.buildEnv {
99143 name = "root";
100144 paths =
@@ -102,27 +146,19 @@
102146 pkgs = pkgsLinux;
103147 nur = nurPkgs;
104148 }
105− # The shell (cmd + $SHELL), when it exists in nixpkgs.
106− ++ pkgsLinux.lib.optional (pkgsLinux ? ${shell}) pkgsLinux.${shell}
107149 ++ [
150+ pkgsLinux.fish
151+ fishRoot
108152 pkgsLinux.nix
109153 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
113154 # Networking glue glibc needs: /etc/nsswitch.conf (+passwd,
114155 # group) so DNS/host lookups resolve, and /etc/protocols,
115156 # /etc/services. Without these, hostname resolution fails.
116157 pkgsLinux.fakeNss
117158 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
123159 ];
124160 # Link all of /etc so the above files (nsswitch.conf, protocols,
125− # services, passwd, group, ssl certs) are present.
161+ # services, passwd, group, ssl certs, fish prompt) are present.
126162 pathsToLink = [
127163 "/bin"
128164 "/etc"
@@ -133,25 +169,24 @@
133169
134170 config = {
135171 # `cmd` (not `entrypoint`): the default command, *replaced* by any
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" ];
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+ ];
141181 env = [
142182 "PATH=/bin"
143183 "HOME=/root"
144− # Default shell for tools that spawn one (herdr panes, etc.).
145− "SHELL=/bin/${shell}"
146184 # Marker so shells/scripts can detect they're in here.
147185 "NIX_CONTAINER=1"
148− # Advertise truecolor for 24-bit-aware tools; `c` passes the
149− # resolved TERM via -e (the runtime would otherwise force xterm).
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.
150189 "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"
155190 # CA trust for every TLS client (curl/git/…) and Nix.
156191 "SSL_CERT_FILE=/etc/ssl/certs/ca-bundle.crt"
157192 "NIX_SSL_CERT_FILE=/etc/ssl/certs/ca-bundle.crt"
@@ -164,7 +199,7 @@
164199
165200 # `nix run . -- init|start` runs the bundled `c` manager script. Its
166201 # `init` builds the image from the project's `container.nix` (via
167− # `lib.copyWithShell`); the script finds the flake from its own (store)
202+ # `lib.copyWith`); the script finds the flake from its own (store)
168203 # location, so this works from a checkout or straight from
169204 # `nix run github:…/nix-container`.
170205 cApp = pkgsHost.writeShellScriptBin "c" ''
@@ -177,22 +212,15 @@
177212 default = flake-utils.lib.mkApp { drv = cApp; };
178213 };
179214
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:
215+ # Build helpers. `c init` uses `copyWith` to honour a per-project
216+ # `container.nix`:
183217 # (builtins.getFlake "github:…/nix-container")
184− # .lib.<system>.copyWith (import ./container.nix).packages
218+ # .lib.<system>.copyWith (import ./container.nix)
185219 lib = {
186220 inherit mkImage;
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;
221+ # skopeo copy app (`skopeo copy nix:<image> "$@"`) for an image built
222+ # from a `{ pkgs, nur }: [ ... ]` function.
223+ copyWith = packages: (mkImage packages).copyTo;
196224 };
197225 }
198226 );