clam.nix
is for modularizing shell hooks TODO link in Nix expressions (usually stored in shell.nix
files) that are to be used with the nix-shell
command line tool. The body of the clam.nix
function is a shell script template, and the input attributes enable plugging in custom shell commands at specific stages TODO link of its execution.
For example, taking the reference usage demonstrated in elixir-phoenix-postgres/shell.nix
(see its README), a sub-shell will be set up for a Phoenix (an Elixir web framework) project, with the required language packages and a PostgreSQL instance running in the background (actions phase TODO:link), all of which will be cleaned up upon exiting the shell (i.e., packages deleted, database stopped, etc.). Another example is setting up git secret
.
The idea itself was suggested by @SRGOM in issue #1, but instead of modularizing shell scripts themselves (e.g., like this), decided to solve it with the Nix language using import
s.
2. Phases of the clam.nix
shell script template and the input attributes controlling them
where
The clam.nix
function expects an attribute set in the form of
{ nixShellDataDir ? ".nix-shell"
, cavern ? ""
, rump ? ""
}:
where
nixShellDataDir
(default value:.nix-shell
)
is a string used to name of a temporary directory that will be created in the directory where the Nix expression is invoked with
nix-shell
; see setup phase (TODO link)
cavern
(default value:""
)
is a string that should evaluate to valid shell commands; see actions phase (TODO link) section for the details and examples
rump
(default value:""
)
is a string that should also evaluate to valid shell commands; see clean-up phase (TODO link)
The template can be broken up into the following phases (these are also marked in the source):
The items in parentheses refer to attribute names in the clam.nix
function's input attribute set that play a role in the respective phase.
├── _helpers
│ └── shell-hook
│ │
│ ├── clam.nix - the shell script template
│ │
│ ├── inserts - generic shell script snippets
│ │ │ that add extra functionality;
│ │ │ see "2.3 Inserts"
│ │ │
│ │ ├── mix.nix
│ │ └── postgres.nix
│ │
│ └── README.md - this readme
│
├── elixir-phoenix-postgres - other project- or app-specific
├── ... shell.nix-es below this point
:
NOTE: This phase doesn't accept any custom shell commands at the moment, only the name of the temporary directory specified by
nixShellDataDir
.
-
Creates a temporary directory (set by
nixShellDataDir
, see above)Used to store application-specific data, instead of having those scattered around in the system (e.g., in the user's home directory,
/run
, etc.); it will be deleted when exiting the shell (see 1.3 Clean-up TODO link section).Examples of application specific data:
-
language modules (e.g., Mix-related files for Elixir, Node.js modules etc.)
-
runtime and configuration files (e.g., see comment on
unix_socket_directories
† TODO link in./inserts/postgres.nix
) -
(what else?)
-
-
Makes the path of the temporary directory available in the
NIX_SHELL_DIR
environment variable in the new sub-shell created bynix-shell
NIX_SHELL_DIR
can then be used in custom shell commands (see example usage in./inserts/postgres.nix
for example). -
Set up empty
CLEANUP_CALLBACKS
array to register callback functions to be run in the clean-up phase TODO link. (See 2.3 Clean-up callbacks section TODO link)
This phase has no default actions, and the cavern
input attribute is an empty string by default - so without any user-specified actions this phase will be skipped, and the clean-up phase TODO link will commence.
elixir-phoenix-postgres/shell.nix
provides the reference usage for clam.nix
.
Default actions:
cd $PWD # (1) rm -rf $NIX_SHELL_DIR # (2)
That is,
- return to the project directory (see comment), and
- delete the
$NIX_SHELL_DIR
directory (see 2.1.1 Setup phase section), but any other clean-up measures need to be provided by the user.
TODO: Is there a need to retain it? My workflow is usually to (1) enter nix-shell
, (2) do stuff, and (3) suspend the system at the end of the day/session, so I start in the same environment the next time. This also means if I make changes to the environment, and these won't be reflected when I'll enter it the next time. I would argue that deleting it is good because it forces one to update the shell.nix
on change (and some action will definitely warrant this anyway, such as changing environment variables).
For example, if a database instance has been started previously, it will keep running when exiting Nix shell, unless it is explicitly stopped in the clean-up phase (via shell commands specified in rump
; see 2.2.3 rump
).
Not unsetting environment variables is alright, because nix-shell
starts a sub-shell so these will go out of scope when leaving; nix shell
may be another matter though.
(i.e., the contents of the ./inserts/
script snippets directory)
Inserts are just string Nix expressions that evaluate to shell commands pertaining to a specific topic or tool; they are basically modules that can be plugged into a shell.nix
on demand. The main rationale behind clam.nix
was to promote re-use of parts of shell hooks.
For example, elixir-phoenix-postgres/shell.nix
sets up an development environment for an Elixir web framework, and it spins up a PostgreSQL server via the ./inserts/postgres.nix
insert. This script could have been used for a Python or Go web project just as easily.
TODO link to Nix strings, explain .nix files, import, or just add string directly, sho extending postgres.nix in elixir shell.nix, go into clean up callbacks and elaborate the pros of including them in inserts (self-contained scripts that clean up after themselves). Add direnv to the mix (i.e., include that fora specific project, but these shell.nix templates good for starting out with a new project)
TODO
TODO
TODO
I believe that nix-shell
's Nix manual entry is grossly over-simplified, and its behaviour does raise questions.
Shell hooks (in this context) are shell scripts executed once before entering the sub-shell set up by nix-shell
. From nix-shell
's Nix manual entry:
If the derivation defines the variable
shellHook
, it will be evaluated after$stdenv/setup
has been sourced. Since this hook is not executed by regular Nix builds, it allows you to perform initialisation specific tonix-shell
. For example, the derivation attributeshellHook = '' echo "Hello shell" '';
will cause
nix-shell
to print "Hello shell".
[†] There is a difference between Unix and TCP/IP sockets and not all Unix sockets are files (see wiki and this question).