@@ -76,74 +76,99 @@
7676 imageName = "nix-container";
7777 imageTag = "latest";
7878
79+ # Fish prompt config, installed at /root/.config/fish/config.fish
80+ # (fish reads $HOME/.config/fish). Built on the host arch — it's just a
81+ # text file, so no Linux builder is needed.
82+ fishConfig = pkgsHost.writeText "config.fish" ''
83+ # No welcome banner.
84+ set -g fish_greeting
85+
86+ # Define the prompt as a function so fish renders it before every
87+ # command; at top level it would run once at startup and be ignored.
88+ function fish_prompt
89+ set -l nix_shell_info
90+ if set -q IN_NIX_SHELL; or set -q IN_NIX_RUN
91+ set nix_shell_info ' ❄️'
92+ end
93+
94+ set -l cwd (prompt_pwd)
95+
96+ set -l bookmark_info
97+ if jj root 2>/dev/null >/dev/null
98+ set -l jj_bookmark (jj log -r 'heads(::@ & bookmarks())' -T 'local_bookmarks ++ "\n"' --no-graph 2>/dev/null | tail -1)
99+ if test -n "$jj_bookmark"
100+ set bookmark_info ' ' (set_color brmagenta) $jj_bookmark (set_color normal)
101+ end
102+ end
103+
104+ echo -n -s (set_color $fish_color_cwd) $cwd (set_color normal) $bookmark_info ' 🏗️' $nix_shell_info '> '
105+ end
106+ '';
107+
108+ # Lay the config out at the path fish reads.
109+ fishRoot = pkgsHost.runCommand "fish-config-root" { } ''
110+ mkdir -p "$out/root/.config/fish"
111+ cp ${fishConfig} "$out/root/.config/fish/config.fish"
112+ '';
113+
79114 # Build the OCI image from a `{ pkgs, nur }: [ ... ]` package function.
80− # The default `cmd` is /bin/fish; `c init` runs your host login shell
81− # instead and adds it automatically (see `copyWithShell`). The warning
82− # below only catches an image left with no shell at all.
115+ # The image always runs /bin/fish; fish, its prompt config, and Nix are
116+ # bundled automatically, so `container.nix` lists only your extras.
83117 mkImage =
84118 packages:
85− let
86− pkgList = packages {
87− pkgs = pkgsLinux;
88− nur = nurPkgs;
119+ n2c.buildImage {
120+ name = imageName;
121+ tag = imageTag;
122+ inherit arch;
123+
124+ # A plain, single-process root filesystem. No NixOS, no systemd.
125+ # buildEnv only *symlinks* paths (never runs them), so build it on
126+ # the host arch — the tree is arch-neutral and links the
127+ # (substituted) Linux store paths, sparing the Linux builder.
128+ #
129+ # Always shipped: fish (the cmd) + its prompt, and Nix (+ CA certs)
130+ # so every container can use Nix; `initializeNixDatabase` registers
131+ # the baked store.
132+ copyToRoot = pkgsHost.buildEnv {
133+ name = "root";
134+ paths =
135+ packages {
136+ pkgs = pkgsLinux;
137+ nur = nurPkgs;
138+ }
139+ ++ [
140+ pkgsLinux.fish
141+ fishRoot
142+ pkgsLinux.nix
143+ pkgsLinux.cacert
144+ ];
145+ pathsToLink = [
146+ "/bin"
147+ "/root"
148+ "/etc/ssl"
149+ ];
150+ };
151+
152+ initializeNixDatabase = true;
153+
154+ config = {
155+ # `cmd` (not `entrypoint`): it's the default command and is
156+ # *replaced* by `container run … -- <cmd>`. An entrypoint would be
157+ # prepended instead, so `run … -- /bin/sh` would become
158+ # `/bin/bash /bin/sh` and fail with "cannot execute binary file".
159+ cmd = [ "/bin/fish" ];
160+ env = [
161+ "PATH=/bin"
162+ "HOME=/root"
163+ # Marker so shells/scripts can detect they're in here.
164+ "NIX_CONTAINER=1"
165+ # Make the bundled Nix usable: TLS for substituters/flakes,
166+ # flake commands on, and single-user (no daemon/nixbld).
167+ "NIX_SSL_CERT_FILE=/etc/ssl/certs/ca-bundle.crt"
168+ "NIX_CONFIG=experimental-features = nix-command flakes\nbuild-users-group =\nsandbox = false"
169+ ];
89170 };
90− hasShell = builtins.any (
91− p:
92− builtins.elem (p.pname or "") [
93− "fish"
94− "bash"
95− "zsh"
96− ]
97− ) pkgList;
98− in
99− pkgsLinux.lib.warnIf (!hasShell)
100− "nix-container: the package set has no shell (fish/bash/zsh) — the container's command may not exist (`c init` runs your host shell)."
101− (
102− n2c.buildImage {
103− name = imageName;
104− tag = imageTag;
105− inherit arch;
106−
107− # A plain, single-process root filesystem. No NixOS, no systemd.
108− # buildEnv only *symlinks* paths (never runs them), so build it
109− # on the host arch — the tree is arch-neutral and links the
110− # (substituted) Linux store paths, sparing the Linux builder.
111− #
112− # Nix itself (+ CA certs) is always shipped, so every container
113− # can use Nix; `initializeNixDatabase` registers the baked store.
114− copyToRoot = pkgsHost.buildEnv {
115− name = "root";
116− paths = pkgList ++ [
117− pkgsLinux.nix
118− pkgsLinux.cacert
119− ];
120− pathsToLink = [
121− "/bin"
122− "/etc/ssl"
123− ];
124− };
125−
126− initializeNixDatabase = true;
127−
128− config = {
129− # `cmd` (not `entrypoint`): it's the default command and is
130− # *replaced* by `container run … -- <cmd>`. An entrypoint would
131− # be prepended instead, so `run … -- /bin/sh` would become
132− # `/bin/bash /bin/sh` and fail with "cannot execute binary file".
133− cmd = [ "/bin/fish" ];
134− env = [
135− "PATH=/bin"
136− "HOME=/root"
137− # Marker so shells/scripts can detect they're in here.
138− "NIX_CONTAINER=1"
139− # Make the bundled Nix usable: TLS for substituters/flakes,
140− # flake commands on, and single-user (no daemon/nixbld).
141− "NIX_SSL_CERT_FILE=/etc/ssl/certs/ca-bundle.crt"
142− "NIX_CONFIG=experimental-features = nix-command flakes\nbuild-users-group =\nsandbox = false"
143− ];
144− };
145− }
146− );
171+ };
147172
148173 # `nix run . -- init|start` runs the bundled `c` manager script. Its
149174 # `init` builds the image from the project's `container.nix` (via
@@ -169,15 +194,6 @@
169194 # skopeo copy app (`skopeo copy nix:<image> "$@"`) for an image built
170195 # from a `{ pkgs, nur }: [ ... ]` function.
171196 copyWith = packages: (mkImage packages).copyTo;
172− # Like `copyWith`, but also adds the named shell package (when it
173− # exists in nixpkgs) so `c init` can run the host shell without it
174− # being listed in `container.nix`.
175− copyWithShell =
176− shell: packages:
177− (mkImage (
178− { pkgs, nur }:
179− packages { inherit pkgs nur; } ++ pkgs.lib.optional (pkgs ? ${shell}) pkgs.${shell}
180− )).copyTo;
181197 };
182198 }
183199 );