This commit is contained in:
atagen 2025-01-21 18:10:33 +11:00
commit 90985f332f
8 changed files with 309 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
name

123
README.md Normal file
View File

@ -0,0 +1,123 @@
# ides
## (idempotent devshell ephemeral services)
ides provides automatic idempotent launching of ephemeral services
in your devshell,
right here, right now.
## what ?
### it's automatic
- ides will instantly launch all your declared services,
as soon as you enter the..
### devshell
- works just like regular mkShell
- full support for `shell.nix`, `flake.nix`, and `direnv`
### it's ephemeral
- ides packages and configs are present only in the nix store,
and once shut down all traces effectively disappear
### they're idempotent
- ides services cah only ever run one (1) instance of any package+config combination,
regardless of how many times the devshell is opened, or the launch command invoked
### they're services
- ides runs on systemd user services - no impostor process manager needed
## the bottom line
your dev environment now includes your service dependencies!
## how ?
here's how:
### service configuration (caddy.nix)
```nix
{
pkg = pkgs.caddy;
args = "run -c %CFG% --adapter caddyfile";
config = ''
http://*:8080 {
respond "hello"
}
'';
}
```
we template the provided config into %CFG% so you can feed it to the service as needed
### classic nix(tm)
```nix
let
pkgs = import <nixpkgs> {};
ides = import (fetchGit {
url = "https://git.atagen.co/atagen/ides";
});
mkShell = ides.use pkgs;
in
mkShell {
noCC = true;
services.caddy = import ./caddy.nix;
}
```
### flake enjoyers
```nix
{
inputs = {
ides.url = "git+https://git.atagen.co/atagen/ides";
};
outputs = {
nixpkgs,
ides,
...
}: let
pkgs = nixpkgs.legacyPackages.x86_64-linux;
mkShell = ides.lib.use pkgs;
in {
devShells.x86_64-linux.default = mkShell {
noCC = true;
services = {
caddy = import ./caddy.nix;
};
};
};
}
```
### cli
in case you need manual control, an ides shell provides commands:
- `ides`: raise the service set manually
- `et-tu`: shut down the service set manually
- `restart`: do both of the above in succession
## why not reuse (nixpkgs/hm/...) module system ?
ides was originally conceived with this in mind, but in practice,
it is rather difficult to decouple the module systems from the
deployments they are intended to fulfill.
occasional prodding is ongoing, and some activity appears to have
begin in nixpkgs to modularise services, which would allow ides
to take full advantage of the enormous nixos ecosystem.
## acknowledgements
me
the bald gang
nixpkgs manual writers
devenv probably

68
default.nix Normal file
View File

@ -0,0 +1,68 @@
{
use = pkgs: {
inherit pkgs;
__functor = self: shell: let
inherit (pkgs) writeText writeShellScriptBin;
inherit (pkgs.lib) getExe foldlAttrs;
inherit (builtins) hashString removeAttrs;
noCC = shell.noCC or false;
mkWorks = {
pkg,
args ? "",
config,
ext ? "",
}: let
bin = getExe pkg;
name = pkg.pname;
unitName = "shell-${name}-${cfgHash}";
cfgHash = hashString "sha256" config;
finalConf = writeText "config-${name}-${cfgHash}${ext}" config;
finalArgs = builtins.replaceStrings ["%CFG%"] [finalConf.outPath] args;
in {
runner = ''
echo "[ides]: Starting ${name}.."
systemd-run --user -G -u ${unitName} ${bin} ${finalArgs}
'';
cleaner = ''
echo "[ides]: Stopping ${name}.."
systemctl --user stop ${unitName}
'';
};
works =
foldlAttrs (acc: name: svc: let
pair = mkWorks svc;
in {
runners = acc.runners + pair.runner;
cleaners = acc.cleaners + pair.cleaner;
}) {
runners = "";
cleaners = "";
} (shell.services or {});
runners = writeShellScriptBin "ides" works.runners;
cleaners = writeShellScriptBin "et-tu" (works.cleaners
+ ''
systemctl --user reset-failed
'');
restart = writeShellScriptBin "restart" "et-tu; ides";
final =
(removeAttrs shell ["services" "noCC"])
// {
nativeBuildInputs = (shell.nativeBuildInputs or []) ++ [runners cleaners restart];
shellHook = ''
ides
'';
};
in
if noCC
then self.pkgs.mkShellNoCC final
else self.pkgs.mkShell final;
};
}

9
example/caddy.nix Normal file
View File

@ -0,0 +1,9 @@
{
pkg = pkgs.caddy;
args = "run -c %CFG% --adapter caddyfile";
config = ''
http://*:8080 {
respond "hello"
}
'';
}

20
example/flake.nix Normal file
View File

@ -0,0 +1,20 @@
{
inputs = {
ides.url = "git+https://git.atagen.co/atagen/ides";
};
outputs = {
nixpkgs,
ides,
...
}: let
pkgs = nixpkgs.legacyPackages.x86_64-linux;
mkShell = ides.lib.use pkgs;
in {
devShells.x86_64-linux.default = mkShell {
noCC = true;
services = {
caddy = import ./caddy.nix;
};
};
};
}

11
example/shell.nix Normal file
View File

@ -0,0 +1,11 @@
let
pkgs = import <nixpkgs> {};
ides = import (fetchGit {
url = "https://git.atagen.co/atagen/ides";
});
mkShell = ides.use pkgs;
in
mkShell {
noCC = true;
services.caddy = import ./caddy.nix;
}

9
flake.nix Normal file
View File

@ -0,0 +1,9 @@
{
outputs = {...}: {
lib = import ./default.nix;
templates.default = {
path = ./example;
description = "the ides template";
};
};
}

68
ides.nix Normal file
View File

@ -0,0 +1,68 @@
{
use = pkgs: {
inherit pkgs;
__functor = self: shell: let
inherit (pkgs) writeText writeShellScriptBin;
inherit (pkgs.lib) getExe foldlAttrs;
inherit (builtins) hashString removeAttrs;
noCC = shell.noCC or false;
mkWorks = {
pkg,
args ? "",
config,
ext ? "",
}: let
bin = getExe pkg;
name = pkg.pname;
unitName = "shell-${name}-${cfgHash}";
cfgHash = hashString "sha256" config;
finalConf = writeText "config-${name}-${cfgHash}${ext}" config;
finalArgs = builtins.replaceStrings ["%CFG%"] [finalConf.outPath] args;
in {
runner = ''
echo "[ides]: Starting ${name}.."
systemd-run --user -G -u ${unitName} ${bin} ${finalArgs}
'';
cleaner = ''
echo "[ides]: Stopping ${name}.."
systemctl --user stop ${unitName}
'';
};
works =
foldlAttrs (acc: name: svc: let
pair = mkWorks svc;
in {
runners = acc.runners + pair.runner;
cleaners = acc.cleaners + pair.cleaner;
}) {
runners = "";
cleaners = "";
} (shell.services or {});
runners = writeShellScriptBin "ides" works.runners;
cleaners = writeShellScriptBin "et-tu" (works.cleaners
+ ''
systemctl --user reset-failed
'');
restart = writeShellScriptBin "restart" "et-tu; ides";
final =
(removeAttrs shell ["services" "noCC"])
// {
nativeBuildInputs = (shell.nativeBuildInputs or []) ++ [runners cleaners restart];
shellHook = ''
ides
'';
};
in
if noCC
then self.pkgs.mkShellNoCC final
else self.pkgs.mkShell final;
};
}