Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 26 additions & 3 deletions docs/getting_started.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,9 +161,32 @@ packages = [
]
```

### Wrapping up

## Adding background services

Many projects need background services to be running during development or to
run tests (e.g. a database, caching server, webserver, ...). This is supported
in devshell through the concept of service groups.

A service group defines a collection of long-running processes that can be
started and stopped.

An example service group could be configured like this:
```toml
[serviceGroups.database]
description = "Runs a database in the backgroup"
[serviceGroups.database.services.postgres]
command = "postgres"
[serviceGroups.database.services.memcached]
command = "memcached"
```

This will add two commands to the devshell: `database:start` and
`database:stop`. `database:start` starts the defined services in the `database`
group in the foreground and shows their output. `database:stop` can be executed
in a different shell to stop the processes (or press Ctrl-C in the main shell).

## Wrapping up

**devshell** is extensible in many different ways. In the next chapters we will
discuss the various ways in which it can be adapted to your project's needs.
to find
of the configuration options available.
1 change: 1 addition & 0 deletions modules/modules.nix
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ let
branch = "main";
}];
}
./services.nix
];

pkgsModule = { config, ... }: {
Expand Down
97 changes: 97 additions & 0 deletions modules/services.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
{ config, lib, pkgs, ... }:
with lib;
let
serviceOptions = {
name = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
Name of this service. Defaults to attribute name in group services.
'';
};
command = mkOption {
type = types.str;
description = ''
Command to execute.
'';
};
};
groupOptions = {
name = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
Name of the service group. Defaults to attribute name in groups.
'';
};
description = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
Short description of the service group, shown in generated commands
'';
};
services = mkOption {
type = types.attrsOf (types.submodule { options = serviceOptions; });
default = { };
description = ''
Attrset of services that should be run in this group.
'';
};
};
groupToProcfile = name: g: pkgs.writeText "Procfile.${name}" (concatLines
(mapAttrsToList (sName: s: "${if s.name == null then sName else s.name}: ${s.command}") g.services)
);
groupToCommands = gName: g:
let
procfile = groupToProcfile gName g;
description = if g.description == null then gName else g.description;
in
[
{
name = "${gName}:start";
category = "service groups";
help = "Start ${description} services";
command = (pkgs.writeShellScript "${gName}-services-start" ''
if [ -e "$PRJ_DATA_DIR/pids/${gName}.pid" ]; then
echo "Already running, refusing to start"
exit 1
fi
mkdir -p "$PRJ_DATA_DIR/pids/"
${pkgs.honcho}/bin/honcho start -f ${procfile} -d $PRJ_ROOT &
pid=$!
echo $pid > "$PRJ_DATA_DIR/pids/${gName}.pid"
on_stop() {
kill -TERM $pid
rm "$PRJ_DATA_DIR/pids/${gName}.pid"
wait $pid
}
trap "on_stop" SIGINT SIGTERM SIGHUP
wait $pid
'').outPath;
}
{
name = "${gName}:stop";
category = "service groups";
help = "Stop ${description} services";
command = (pkgs.writeShellScript "${gName}-services-stop" ''
if [ -e "$PRJ_DATA_DIR/pids/${gName}.pid" ]; then
pid=$(cat "$PRJ_DATA_DIR/pids/${gName}.pid")
kill -TERM $pid
rm "$PRJ_DATA_DIR/pids/${gName}.pid"
fi
'').outPath;
}
];
in
{
options.serviceGroups = mkOption {
type = types.attrsOf (types.submodule { options = groupOptions; });
default = { };
description = ''
Add services to the environment. Services can be used to group long-running processes.
'';
};

config.commands = foldl (l: r: l ++ r) [ ] (mapAttrsToList (gName: g: groupToCommands (if g.name == null then gName else g.name) g) config.serviceGroups);
}