module overhaul
module
This commit is contained in:
parent
8fe3ddc38e
commit
ae78cb7026
1
.gitignore
vendored
1
.gitignore
vendored
@ -1 +1,2 @@
|
||||
name
|
||||
TODO
|
||||
|
155
README.md
155
README.md
@ -1,7 +1,7 @@
|
||||
# ides
|
||||
## idempotent devshell ephemeral services
|
||||
|
||||
ides provides automatic idempotent launching of ephemeral services
|
||||
ides provides automated, idempotent launching of ephemeral services
|
||||
|
||||
in your devshell,
|
||||
|
||||
@ -31,83 +31,119 @@ right here, right now.
|
||||
|
||||
|
||||
## the bottom line
|
||||
your dev environment now includes your service dependencies!
|
||||
your dev environment now reproducibly includes your service dependencies!
|
||||
|
||||
|
||||
## how ?
|
||||
- bring ides into your nix expression (flake input/fetchGit)
|
||||
- set it up by invoking its `use` function on your nixpkgs instance
|
||||
- use `mkShell` like you normally would, but with *spicy extras*
|
||||
- import it
|
||||
- optionally provide a `pkgs` instance, mkShell-like function, or ides modules
|
||||
- use ides like a normal `mkShell`, but with *spicy extras*
|
||||
- make sure you run `et-tu` before you log out!
|
||||
|
||||
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's path as %CFG% in the `args` option.
|
||||
|
||||
### classic nix(tm)
|
||||
### classic nix(tm) ([shell.nix](example/shell.nix))
|
||||
```nix
|
||||
let
|
||||
pkgs = import <nixpkgs> {};
|
||||
ides = import (fetchGit {
|
||||
pkgs = import <nixpkgs> { };
|
||||
|
||||
ides = fetchGit {
|
||||
url = "https://git.atagen.co/atagen/ides";
|
||||
});
|
||||
mkShell = ides.use pkgs;
|
||||
};
|
||||
|
||||
mkIdes = import ides {
|
||||
# optional instantiation args
|
||||
inherit pkgs;
|
||||
shell = pkgs.mkShell.override {
|
||||
stdenv = pkgs.stdenvNoCC;
|
||||
};
|
||||
modules = [ ];
|
||||
};
|
||||
in
|
||||
mkShell {
|
||||
noCC = true;
|
||||
services.caddy = import ./caddy.nix;
|
||||
}
|
||||
mkIdes {
|
||||
# ides-specific options
|
||||
imports = [ ./caddy.nix ];
|
||||
services.redis = {
|
||||
enable = true;
|
||||
port = 6889;
|
||||
logLevel = "verbose";
|
||||
};
|
||||
# regular mkShell options
|
||||
nativeBuildInputs = [ pkgs.hello ];
|
||||
someEnv = "this";
|
||||
}
|
||||
```
|
||||
|
||||
### flake enjoyers
|
||||
### flake enjoyers ([flake.nix](example/flake.nix))
|
||||
```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;
|
||||
outputs =
|
||||
{
|
||||
nixpkgs,
|
||||
ides,
|
||||
...
|
||||
}:
|
||||
let
|
||||
pkgs = nixpkgs.legacyPackages.x86_64-linux;
|
||||
mkIdes = import ides {
|
||||
inherit pkgs;
|
||||
shell = pkgs.mkShell.override {
|
||||
stdenv = pkgs.stdenvNoCC;
|
||||
};
|
||||
modules = [ ];
|
||||
};
|
||||
in
|
||||
{
|
||||
devShells.x86_64-linux.default = mkIdes {
|
||||
imports = [ ./caddy.nix ];
|
||||
services.redis = {
|
||||
enable = true;
|
||||
port = 6889;
|
||||
logLevel = "verbose";
|
||||
};
|
||||
nativeBuildInputs = [ pkgs.hello ];
|
||||
someEnv = "this";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### options
|
||||
- `services: attrset of service configs`: set up your services for ides
|
||||
- `noCC: bool`: sets whether to use mkShell or mkShellNoCC
|
||||
- `...`: all other options are passed directly to mkShell as per usual
|
||||
### concrete service definition ([caddy.nix](example/caddy.nix))
|
||||
```nix
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
# as simple as possible
|
||||
serviceDefs.caddy = {
|
||||
pkg = pkgs.caddy;
|
||||
# ides injects the config path whereever %CFG% is used in `args`
|
||||
args = "run -c %CFG% --adapter caddyfile";
|
||||
config.text = ''
|
||||
http://*:8888 {
|
||||
respond "hello"
|
||||
}
|
||||
'';
|
||||
};
|
||||
}
|
||||
```
|
||||
here, we use a simple plaintext config, but ides also supports converting
|
||||
attribute sets into the following formats (via `config.content` & `config.format`):
|
||||
- `json`
|
||||
- `yaml`
|
||||
- `toml`
|
||||
- `ini`
|
||||
- `xml`
|
||||
- `php`
|
||||
- `java`
|
||||
|
||||
#### service config attributes
|
||||
- `pkg`: the package to launch as a service
|
||||
- `args`: the arguments to the service. writing `%CFG%` in this will template to your config location
|
||||
- `ext`: in case your service is picky about its file extension, set it here
|
||||
- `config`: your service config.
|
||||
### writing a service module
|
||||
see [the provided redis module](modules/redis.nix) for an example
|
||||
|
||||
if plaintext isn't your thing, check out pkgs.writers and lib.generators
|
||||
for ways to generate json, yaml, etc from nix attribute sets.
|
||||
### more detail
|
||||
for fully commented examples, see [here](example)
|
||||
|
||||
### cli
|
||||
in case you need manual control, an ides shell provides commands:
|
||||
@ -115,17 +151,6 @@ in case you need manual control, an ides shell provides commands:
|
||||
- `et-tu`: shut down the service set
|
||||
- `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.
|
||||
### documentation
|
||||
see [module docs](docs.md)
|
||||
|
||||
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
|
||||
- bald gang
|
||||
- nixpkgs manual authors
|
||||
- devenv, for the idea of flake services
|
||||
|
111
default.nix
111
default.nix
@ -1,68 +1,51 @@
|
||||
# import stage args
|
||||
{
|
||||
use = pkgs: {
|
||||
inherit pkgs;
|
||||
__functor = self: shell: let
|
||||
inherit (pkgs) writeText writeShellScriptBin;
|
||||
inherit (pkgs.lib) getExe foldlAttrs;
|
||||
inherit (builtins) hashString removeAttrs;
|
||||
pkgs ? import <nixpkgs>,
|
||||
shell ? pkgs.mkShell,
|
||||
modules ? [ ],
|
||||
...
|
||||
}:
|
||||
# shell creation args
|
||||
{
|
||||
services ? { },
|
||||
imports ? [ ],
|
||||
...
|
||||
}@args:
|
||||
let
|
||||
# filter ides args out
|
||||
# for passthrough to mkShell
|
||||
shellArgs = builtins.removeAttrs args [
|
||||
"services"
|
||||
"serviceDefs"
|
||||
"imports"
|
||||
];
|
||||
# include some premade services
|
||||
baseModules = [ ./modules/redis.nix ];
|
||||
# eval the config
|
||||
eval = pkgs.lib.evalModules {
|
||||
modules =
|
||||
[
|
||||
# ides
|
||||
./ides.nix
|
||||
# service config and build params
|
||||
(
|
||||
{ ... }:
|
||||
{
|
||||
inherit services;
|
||||
_buildIdes.shellFn = shell;
|
||||
_buildIdes.shellArgs = shellArgs;
|
||||
}
|
||||
)
|
||||
]
|
||||
++ baseModules
|
||||
++ modules
|
||||
++ imports;
|
||||
|
||||
noCC = shell.noCC or false;
|
||||
specialArgs = {
|
||||
inherit pkgs;
|
||||
};
|
||||
|
||||
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 = (shell.shellHook or "") + ''
|
||||
ides
|
||||
'';
|
||||
};
|
||||
in
|
||||
if noCC
|
||||
then self.pkgs.mkShellNoCC final
|
||||
else self.pkgs.mkShell final;
|
||||
class = "ides";
|
||||
};
|
||||
}
|
||||
in
|
||||
eval.config._buildIdes.shell
|
||||
|
459
docs.md
Normal file
459
docs.md
Normal file
@ -0,0 +1,459 @@
|
||||
## serviceDefs
|
||||
|
||||
Concrete service definitions, as per submodule options\.
|
||||
Please put service-related options into ` services ` instead, and use this to implement them\.
|
||||
|
||||
|
||||
|
||||
*Type:*
|
||||
attribute set of (submodule)
|
||||
|
||||
*Declared by:*
|
||||
- [ides\.nix](https://git.atagen.co/atagen/ides/ides.nix)
|
||||
|
||||
|
||||
|
||||
## serviceDefs\.\<name>\.args
|
||||
|
||||
|
||||
|
||||
Arguments to supply to the service binary\. Writing %CFG% in this will template to your config location\.
|
||||
|
||||
|
||||
|
||||
*Type:*
|
||||
string
|
||||
|
||||
|
||||
|
||||
*Default:*
|
||||
` "" `
|
||||
|
||||
|
||||
|
||||
*Example:*
|
||||
` "run -c %CFG% --adapter caddyfile" `
|
||||
|
||||
*Declared by:*
|
||||
- [ides\.nix](https://git.atagen.co/atagen/ides/ides.nix)
|
||||
|
||||
|
||||
|
||||
## serviceDefs\.\<name>\.config
|
||||
|
||||
|
||||
|
||||
Options for setting the service’s configuration\.
|
||||
|
||||
|
||||
|
||||
*Type:*
|
||||
submodule
|
||||
|
||||
|
||||
|
||||
*Default:*
|
||||
` { } `
|
||||
|
||||
*Declared by:*
|
||||
- [ides\.nix](https://git.atagen.co/atagen/ides/ides.nix)
|
||||
|
||||
|
||||
|
||||
## serviceDefs\.\<name>\.config\.content
|
||||
|
||||
|
||||
|
||||
Attributes that define your config values\.
|
||||
|
||||
|
||||
|
||||
*Type:*
|
||||
null or (attribute set)
|
||||
|
||||
|
||||
|
||||
*Default:*
|
||||
` null `
|
||||
|
||||
|
||||
|
||||
*Example:*
|
||||
|
||||
```
|
||||
{
|
||||
this = "that";
|
||||
}
|
||||
```
|
||||
|
||||
*Declared by:*
|
||||
- [ides\.nix](https://git.atagen.co/atagen/ides/ides.nix)
|
||||
|
||||
|
||||
|
||||
## serviceDefs\.\<name>\.config\.ext
|
||||
|
||||
|
||||
|
||||
If your service config requires a file extension, set it here\. This overrides ` format `’s output path’\.
|
||||
|
||||
|
||||
|
||||
*Type:*
|
||||
string
|
||||
|
||||
|
||||
|
||||
*Default:*
|
||||
` "" `
|
||||
|
||||
|
||||
|
||||
*Example:*
|
||||
` "json" `
|
||||
|
||||
*Declared by:*
|
||||
- [ides\.nix](https://git.atagen.co/atagen/ides/ides.nix)
|
||||
|
||||
|
||||
|
||||
## serviceDefs\.\<name>\.config\.file
|
||||
|
||||
|
||||
|
||||
Path to config file\. This overrides all other values\.
|
||||
|
||||
|
||||
|
||||
*Type:*
|
||||
null or path
|
||||
|
||||
|
||||
|
||||
*Default:*
|
||||
` null `
|
||||
|
||||
|
||||
|
||||
*Example:*
|
||||
` /home/bolt/code/ides/configs/my-config.ini `
|
||||
|
||||
*Declared by:*
|
||||
- [ides\.nix](https://git.atagen.co/atagen/ides/ides.nix)
|
||||
|
||||
|
||||
|
||||
## serviceDefs\.\<name>\.config\.format
|
||||
|
||||
|
||||
|
||||
Config output format\.
|
||||
One of:
|
||||
` java json yaml toml ini xml php `\.
|
||||
|
||||
|
||||
|
||||
*Type:*
|
||||
null or one of “java”, “json”, “yaml”, “toml”, “ini”, “xml”, “php”
|
||||
|
||||
|
||||
|
||||
*Default:*
|
||||
` null `
|
||||
|
||||
|
||||
|
||||
*Example:*
|
||||
` "json" `
|
||||
|
||||
*Declared by:*
|
||||
- [ides\.nix](https://git.atagen.co/atagen/ides/ides.nix)
|
||||
|
||||
|
||||
|
||||
## serviceDefs\.\<name>\.config\.formatter
|
||||
|
||||
|
||||
|
||||
Serialisation/writer function to apply to ` content `\.
|
||||
` format ` will auto-apply the correct format if the option value is valid\.
|
||||
Should take ` path: attrs: ` and return a storepath\.
|
||||
|
||||
|
||||
|
||||
*Type:*
|
||||
anything
|
||||
|
||||
|
||||
|
||||
*Default:*
|
||||
` null `
|
||||
|
||||
|
||||
|
||||
*Example:*
|
||||
` "pkgs.formats.yaml {}.generate" `
|
||||
|
||||
*Declared by:*
|
||||
- [ides\.nix](https://git.atagen.co/atagen/ides/ides.nix)
|
||||
|
||||
|
||||
|
||||
## serviceDefs\.\<name>\.config\.text
|
||||
|
||||
|
||||
|
||||
Plaintext configuration to use\.
|
||||
|
||||
|
||||
|
||||
*Type:*
|
||||
string
|
||||
|
||||
|
||||
|
||||
*Default:*
|
||||
` "" `
|
||||
|
||||
|
||||
|
||||
*Example:*
|
||||
|
||||
```
|
||||
''
|
||||
http://*:8080 {
|
||||
respond "hello"
|
||||
}
|
||||
''
|
||||
```
|
||||
|
||||
*Declared by:*
|
||||
- [ides\.nix](https://git.atagen.co/atagen/ides/ides.nix)
|
||||
|
||||
|
||||
|
||||
## serviceDefs\.\<name>\.exec
|
||||
|
||||
|
||||
|
||||
Alternative executable name to use from ` pkg `\.
|
||||
|
||||
|
||||
|
||||
*Type:*
|
||||
string
|
||||
|
||||
|
||||
|
||||
*Default:*
|
||||
` "" `
|
||||
|
||||
|
||||
|
||||
*Example:*
|
||||
` "caddy" `
|
||||
|
||||
*Declared by:*
|
||||
- [ides\.nix](https://git.atagen.co/atagen/ides/ides.nix)
|
||||
|
||||
|
||||
|
||||
## serviceDefs\.\<name>\.pkg
|
||||
|
||||
|
||||
|
||||
Package to use for service\.
|
||||
|
||||
|
||||
|
||||
*Type:*
|
||||
package
|
||||
|
||||
|
||||
|
||||
*Example:*
|
||||
` "pkgs.caddy" `
|
||||
|
||||
*Declared by:*
|
||||
- [ides\.nix](https://git.atagen.co/atagen/ides/ides.nix)
|
||||
|
||||
|
||||
|
||||
## services\.redis\.enable
|
||||
|
||||
|
||||
|
||||
Whether to enable Enable Redis…
|
||||
|
||||
|
||||
|
||||
*Type:*
|
||||
boolean
|
||||
|
||||
|
||||
|
||||
*Default:*
|
||||
` false `
|
||||
|
||||
|
||||
|
||||
*Example:*
|
||||
` true `
|
||||
|
||||
*Declared by:*
|
||||
- [modules/redis\.nix](https://git.atagen.co/atagen/ides/modules/redis.nix)
|
||||
|
||||
|
||||
|
||||
## services\.redis\.bind
|
||||
|
||||
|
||||
|
||||
List of IPs to bind to\.
|
||||
|
||||
|
||||
|
||||
*Type:*
|
||||
list of string
|
||||
|
||||
|
||||
|
||||
*Default:*
|
||||
|
||||
```
|
||||
[
|
||||
"127.0.0.1"
|
||||
"::1"
|
||||
]
|
||||
```
|
||||
|
||||
*Declared by:*
|
||||
- [modules/redis\.nix](https://git.atagen.co/atagen/ides/modules/redis.nix)
|
||||
|
||||
|
||||
|
||||
## services\.redis\.databases
|
||||
|
||||
|
||||
|
||||
Number of databases\.
|
||||
|
||||
|
||||
|
||||
*Type:*
|
||||
signed integer
|
||||
|
||||
|
||||
|
||||
*Default:*
|
||||
` 16 `
|
||||
|
||||
*Declared by:*
|
||||
- [modules/redis\.nix](https://git.atagen.co/atagen/ides/modules/redis.nix)
|
||||
|
||||
|
||||
|
||||
## services\.redis\.extraConfig
|
||||
|
||||
|
||||
|
||||
Additional config directives\.
|
||||
|
||||
|
||||
|
||||
*Type:*
|
||||
string
|
||||
|
||||
|
||||
|
||||
*Default:*
|
||||
` "" `
|
||||
|
||||
*Declared by:*
|
||||
- [modules/redis\.nix](https://git.atagen.co/atagen/ides/modules/redis.nix)
|
||||
|
||||
|
||||
|
||||
## services\.redis\.logLevel
|
||||
|
||||
|
||||
|
||||
Logging verbosity level\.
|
||||
|
||||
|
||||
|
||||
*Type:*
|
||||
one of “debug”, “verbose”, “notice”, “warning”, “nothing”
|
||||
|
||||
|
||||
|
||||
*Default:*
|
||||
` "notice" `
|
||||
|
||||
*Declared by:*
|
||||
- [modules/redis\.nix](https://git.atagen.co/atagen/ides/modules/redis.nix)
|
||||
|
||||
|
||||
|
||||
## services\.redis\.port
|
||||
|
||||
|
||||
|
||||
Port to bind to\.
|
||||
|
||||
|
||||
|
||||
*Type:*
|
||||
integer between 1024 and 65535 (both inclusive)
|
||||
|
||||
|
||||
|
||||
*Default:*
|
||||
` 6379 `
|
||||
|
||||
*Declared by:*
|
||||
- [modules/redis\.nix](https://git.atagen.co/atagen/ides/modules/redis.nix)
|
||||
|
||||
|
||||
|
||||
## services\.redis\.socket
|
||||
|
||||
|
||||
|
||||
Unix socket to bind to\.
|
||||
|
||||
|
||||
|
||||
*Type:*
|
||||
null or string
|
||||
|
||||
|
||||
|
||||
*Default:*
|
||||
` null `
|
||||
|
||||
*Declared by:*
|
||||
- [modules/redis\.nix](https://git.atagen.co/atagen/ides/modules/redis.nix)
|
||||
|
||||
|
||||
|
||||
## services\.redis\.socketPerms
|
||||
|
||||
|
||||
|
||||
Permissions for the unix socket\.
|
||||
|
||||
|
||||
|
||||
*Type:*
|
||||
null or signed integer
|
||||
|
||||
|
||||
|
||||
*Default:*
|
||||
` null `
|
||||
|
||||
*Declared by:*
|
||||
- [modules/redis\.nix](https://git.atagen.co/atagen/ides/modules/redis.nix)
|
||||
|
||||
|
34
docs.nix
Normal file
34
docs.nix
Normal file
@ -0,0 +1,34 @@
|
||||
with import <nixpkgs> {};
|
||||
{}: let
|
||||
eval = lib.evalModules {
|
||||
specialArgs = {inherit pkgs;};
|
||||
modules = [./ides.nix ./modules];
|
||||
};
|
||||
optionsDoc = nixosOptionsDoc {
|
||||
inherit (eval) options;
|
||||
|
||||
transformOptions = opt:
|
||||
opt
|
||||
// {
|
||||
# Clean up declaration sites to not refer to the NixOS source tree.
|
||||
declarations = let
|
||||
devDir = toString /home/bolt/code/ides;
|
||||
inherit (lib) hasPrefix removePrefix;
|
||||
in
|
||||
map
|
||||
(decl:
|
||||
if hasPrefix (toString devDir) (toString decl)
|
||||
then let
|
||||
subpath = removePrefix "/" (removePrefix (toString devDir) (toString decl));
|
||||
in {
|
||||
url = "https://git.atagen.co/atagen/ides/${subpath}";
|
||||
name = subpath;
|
||||
}
|
||||
else decl)
|
||||
opt.declarations;
|
||||
};
|
||||
};
|
||||
in
|
||||
runCommand "docs.md" {} ''
|
||||
cat ${optionsDoc.optionsCommonMark} > $out
|
||||
''
|
@ -1,9 +1,13 @@
|
||||
{ pkgs, ... }:
|
||||
{
|
||||
pkg = pkgs.caddy;
|
||||
args = "run -c %CFG% --adapter caddyfile";
|
||||
config = ''
|
||||
http://*:8080 {
|
||||
respond "hello"
|
||||
}
|
||||
'';
|
||||
# simplest possible concrete service definition
|
||||
serviceDefs.caddy = {
|
||||
pkg = pkgs.caddy;
|
||||
args = "run -c %CFG% --adapter caddyfile";
|
||||
config.text = ''
|
||||
http://*:8888 {
|
||||
respond "hello"
|
||||
}
|
||||
'';
|
||||
};
|
||||
}
|
||||
|
@ -1,20 +1,53 @@
|
||||
{
|
||||
|
||||
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;
|
||||
|
||||
outputs =
|
||||
{
|
||||
nixpkgs,
|
||||
ides,
|
||||
...
|
||||
}:
|
||||
let
|
||||
pkgs = nixpkgs.legacyPackages.x86_64-linux;
|
||||
|
||||
# create an instance of ides to spawn shells from
|
||||
mkIdes = import ides {
|
||||
# ides needs a pkgs instance to work with in flake mode
|
||||
inherit pkgs;
|
||||
|
||||
# all other args here are optional
|
||||
|
||||
# shell function to wrap -
|
||||
# could also use pkgs.mkShellNoCC, but override demonstrates
|
||||
# more clearly how to change any aspect of the shell
|
||||
shell = pkgs.mkShell.override {
|
||||
stdenv = pkgs.stdenvNoCC;
|
||||
};
|
||||
|
||||
# input for extra modules that provide service options
|
||||
# see modules/redis.nix in ides source for example
|
||||
modules = [ ];
|
||||
# if you want to include a premade service def,
|
||||
# use `imports` in the shell instead
|
||||
};
|
||||
in
|
||||
{
|
||||
devShells.x86_64-linux.default = mkIdes {
|
||||
# import a concrete service definition
|
||||
imports = [ ./caddy.nix ];
|
||||
# use the options provided by a module
|
||||
services.redis = {
|
||||
enable = true;
|
||||
port = 6889;
|
||||
logLevel = "verbose";
|
||||
};
|
||||
# use normal mkShell options
|
||||
nativeBuildInputs = [ pkgs.hello ];
|
||||
someEnv = "this";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,11 +1,41 @@
|
||||
let
|
||||
pkgs = import <nixpkgs> {};
|
||||
ides = import (fetchGit {
|
||||
pkgs = import <nixpkgs> { };
|
||||
|
||||
ides = fetchGit {
|
||||
url = "https://git.atagen.co/atagen/ides";
|
||||
});
|
||||
mkShell = ides.use pkgs;
|
||||
};
|
||||
|
||||
# create an instance of ides to spawn shells from
|
||||
mkIdes = import ides {
|
||||
# all args here are optional
|
||||
|
||||
# define the pkgs instance to work with
|
||||
inherit pkgs;
|
||||
|
||||
# shell function to wrap -
|
||||
# could also use pkgs.mkShellNoCC, but override demonstrates
|
||||
# more clearly how to change any aspect of the shell
|
||||
shell = pkgs.mkShell.override {
|
||||
stdenv = pkgs.stdenvNoCC;
|
||||
};
|
||||
|
||||
# input for extra modules that provide service options
|
||||
# see modules/redis.nix in ides source for example
|
||||
modules = [ ];
|
||||
# if you want to include a premade service def,
|
||||
# use `imports` in the shell instead
|
||||
};
|
||||
in
|
||||
mkShell {
|
||||
noCC = true;
|
||||
services.caddy = import ./caddy.nix;
|
||||
}
|
||||
mkIdes {
|
||||
# import a concrete service definition
|
||||
imports = [ ./caddy.nix ];
|
||||
# use the options provided by a module
|
||||
services.redis = {
|
||||
enable = true;
|
||||
port = 6889;
|
||||
logLevel = "verbose";
|
||||
};
|
||||
# use normal mkShell options
|
||||
nativeBuildInputs = [ pkgs.hello ];
|
||||
someEnv = "this";
|
||||
}
|
||||
|
16
flake.nix
16
flake.nix
@ -1,9 +1,11 @@
|
||||
{
|
||||
outputs = {...}: {
|
||||
lib = import ./default.nix;
|
||||
templates.default = {
|
||||
path = ./example;
|
||||
description = "the ides template";
|
||||
};
|
||||
};
|
||||
outputs =
|
||||
{ ... }:
|
||||
{
|
||||
lib = import ./default.nix;
|
||||
templates.default = {
|
||||
path = ./example;
|
||||
description = "the ides template";
|
||||
};
|
||||
};
|
||||
}
|
||||
|
268
ides.nix
Normal file
268
ides.nix
Normal file
@ -0,0 +1,268 @@
|
||||
{
|
||||
pkgs,
|
||||
config,
|
||||
...
|
||||
}:
|
||||
{
|
||||
#
|
||||
# interface
|
||||
#
|
||||
options =
|
||||
let
|
||||
inherit (pkgs) lib;
|
||||
inherit (lib) types mkOption;
|
||||
serviceConfig =
|
||||
with types;
|
||||
submodule {
|
||||
options = {
|
||||
pkg = mkOption {
|
||||
type = package;
|
||||
description = "Package to use for service.";
|
||||
example = "pkgs.caddy";
|
||||
};
|
||||
exec = mkOption {
|
||||
type = str;
|
||||
description = "Alternative executable name to use from `pkg`.";
|
||||
example = "caddy";
|
||||
default = "";
|
||||
};
|
||||
args = mkOption {
|
||||
type = str;
|
||||
description = "Arguments to supply to the service binary. Writing %CFG% in this will template to your config location.";
|
||||
example = "run -c %CFG% --adapter caddyfile";
|
||||
default = "";
|
||||
};
|
||||
config = mkOption {
|
||||
description = "Options for setting the service's configuration.";
|
||||
default = { };
|
||||
type = submodule {
|
||||
options = {
|
||||
text = mkOption {
|
||||
type = str;
|
||||
default = "";
|
||||
description = "Plaintext configuration to use.";
|
||||
example = ''
|
||||
http://*:8080 {
|
||||
respond "hello"
|
||||
}
|
||||
'';
|
||||
};
|
||||
ext = mkOption {
|
||||
type = str;
|
||||
default = "";
|
||||
description = "If your service config requires a file extension, set it here. This overrides `format`'s output path'.";
|
||||
example = "json";
|
||||
};
|
||||
file = mkOption {
|
||||
type = nullOr path;
|
||||
description = "Path to config file. This overrides all other values.";
|
||||
example = ./configs/my-config.ini;
|
||||
default = null;
|
||||
};
|
||||
content = mkOption {
|
||||
type = nullOr attrs;
|
||||
description = "Attributes that define your config values.";
|
||||
default = null;
|
||||
example = {
|
||||
this = "that";
|
||||
};
|
||||
};
|
||||
format = mkOption {
|
||||
type = nullOr (enum [
|
||||
"java"
|
||||
"json"
|
||||
"yaml"
|
||||
"toml"
|
||||
"ini"
|
||||
"xml"
|
||||
"php"
|
||||
]);
|
||||
description = "Config output format.\nOne of:\n`java json yaml toml ini xml php`.";
|
||||
example = "json";
|
||||
default = null;
|
||||
};
|
||||
formatter = mkOption {
|
||||
type = types.anything;
|
||||
description = "Serialisation/writer function to apply to `content`.\n`format` will auto-apply the correct format if the option value is valid.\nShould take `path: attrs:` and return a storepath.";
|
||||
example = "pkgs.formats.yaml {}.generate";
|
||||
default = null;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
in
|
||||
{
|
||||
serviceDefs = mkOption {
|
||||
type = types.attrsOf serviceConfig;
|
||||
description = "Concrete service definitions, as per submodule options.\nPlease put service-related options into `services` instead, and use this to implement them.";
|
||||
};
|
||||
|
||||
# lol https://github.com/NixOS/nixpkgs/issues/293510
|
||||
_module.args = lib.mkOption {
|
||||
internal = true;
|
||||
};
|
||||
|
||||
# for internal use
|
||||
_buildIdes = mkOption {
|
||||
type = types.attrs;
|
||||
internal = true;
|
||||
};
|
||||
};
|
||||
|
||||
#
|
||||
# implementation
|
||||
#
|
||||
config =
|
||||
let
|
||||
branchOnConfig =
|
||||
cfg:
|
||||
{
|
||||
text,
|
||||
file,
|
||||
content,
|
||||
contentFmt,
|
||||
}:
|
||||
if (cfg.text != "") then
|
||||
text
|
||||
else if (cfg.file != null) then
|
||||
file
|
||||
else if (cfg.content != { }) then
|
||||
if (cfg.format != null) then
|
||||
content
|
||||
else if (cfg.formatter != null) then
|
||||
contentFmt
|
||||
else
|
||||
throw "`format` or `formatter` must be set for `content` ${cfg.content}!"
|
||||
else
|
||||
"";
|
||||
in
|
||||
{
|
||||
# validate and complete the service configurations
|
||||
_buildIdes.finalServices = builtins.mapAttrs (
|
||||
name:
|
||||
{
|
||||
pkg,
|
||||
args ? "",
|
||||
exec ? "",
|
||||
config,
|
||||
}:
|
||||
let
|
||||
bin = if (exec == "") then pkgs.lib.getExe pkg else pkgs.lib.getExe' pkg exec;
|
||||
ext =
|
||||
if (config.ext != "") || (config.format != null) then "." + (config.ext or config.format) else "";
|
||||
# we need this to create unit names that correspond to configs
|
||||
cfgHash =
|
||||
let
|
||||
hashContent = builtins.hashString "sha256" (builtins.toJSON config.content);
|
||||
in
|
||||
branchOnConfig config {
|
||||
text = builtins.hashString "sha256" config.text;
|
||||
file = builtins.hashFile "sha256" config.file;
|
||||
content = hashContent;
|
||||
contentFmt = hashContent;
|
||||
};
|
||||
|
||||
confFile =
|
||||
let
|
||||
writers = {
|
||||
java = pkgs.formats.javaProperties { };
|
||||
json = pkgs.formats.json { };
|
||||
yaml = pkgs.formats.yaml { };
|
||||
ini = pkgs.formats.ini { };
|
||||
toml = pkgs.formats.toml { };
|
||||
xml = pkgs.formats.xml { };
|
||||
php = pkgs.formats.php { finalVariable = null; };
|
||||
};
|
||||
confPath = "config-${name}-${cfgHash}${ext}";
|
||||
in
|
||||
branchOnConfig config {
|
||||
text = pkgs.writeText confPath config.text;
|
||||
inherit (config) file;
|
||||
content = writers.${config.format}.generate confPath config.content;
|
||||
contentFmt = config.formatter confPath config.content;
|
||||
};
|
||||
|
||||
finalArgs = builtins.replaceStrings [ "%CFG%" ] [ "${confFile}" ] args;
|
||||
in
|
||||
{
|
||||
inherit name bin;
|
||||
args = finalArgs;
|
||||
unitName = "shell-${name}-${cfgHash}";
|
||||
}
|
||||
) config.serviceDefs;
|
||||
|
||||
# generate service scripts and create the shell
|
||||
_buildIdes.shell =
|
||||
let
|
||||
mkWorks =
|
||||
{
|
||||
name,
|
||||
unitName,
|
||||
bin,
|
||||
args,
|
||||
}:
|
||||
{
|
||||
runner = ''
|
||||
echo "[ides]: Starting ${name}.."
|
||||
systemd-run --user -G -u ${unitName} ${bin} ${args}
|
||||
'';
|
||||
cleaner = ''
|
||||
echo "[ides]: Stopping ${name}.."
|
||||
systemctl --user stop ${unitName}
|
||||
'';
|
||||
};
|
||||
|
||||
works =
|
||||
let
|
||||
inherit (pkgs.lib) foldlAttrs;
|
||||
in
|
||||
foldlAttrs
|
||||
(
|
||||
acc: name: svc:
|
||||
let
|
||||
pair = mkWorks svc;
|
||||
in
|
||||
{
|
||||
runners = acc.runners + pair.runner;
|
||||
cleaners = acc.cleaners + pair.cleaner;
|
||||
}
|
||||
)
|
||||
{
|
||||
runners = "";
|
||||
cleaners = "";
|
||||
}
|
||||
config._buildIdes.finalServices;
|
||||
|
||||
inherit (pkgs) writeShellScriptBin;
|
||||
runners = writeShellScriptBin "ides" works.runners;
|
||||
cleaners = writeShellScriptBin "et-tu" (
|
||||
works.cleaners
|
||||
+ ''
|
||||
systemctl --user reset-failed
|
||||
''
|
||||
);
|
||||
restart = writeShellScriptBin "restart" "et-tu; ides";
|
||||
|
||||
final =
|
||||
let
|
||||
shellArgs = config._buildIdes.shellArgs;
|
||||
in
|
||||
shellArgs
|
||||
// {
|
||||
nativeBuildInputs = (shellArgs.nativeBuildInputs or [ ]) ++ [
|
||||
runners
|
||||
cleaners
|
||||
restart
|
||||
];
|
||||
shellHook =
|
||||
(shellArgs.shellHook or "")
|
||||
+ ''
|
||||
ides
|
||||
'';
|
||||
};
|
||||
in
|
||||
config._buildIdes.shellFn final;
|
||||
};
|
||||
}
|
6
modules/default.nix
Normal file
6
modules/default.nix
Normal file
@ -0,0 +1,6 @@
|
||||
{ ... }:
|
||||
{
|
||||
imports = [
|
||||
./redis.nix
|
||||
];
|
||||
}
|
118
modules/redis.nix
Normal file
118
modules/redis.nix
Normal file
@ -0,0 +1,118 @@
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
{
|
||||
# create some options
|
||||
options.services.redis =
|
||||
let
|
||||
inherit (lib) mkOption types;
|
||||
in
|
||||
{
|
||||
enable = lib.mkEnableOption "Enable Redis.";
|
||||
|
||||
bind = mkOption {
|
||||
type = with types; listOf str;
|
||||
description = "List of IPs to bind to.";
|
||||
default = [
|
||||
"127.0.0.1"
|
||||
"::1"
|
||||
];
|
||||
};
|
||||
|
||||
port = mkOption {
|
||||
type = types.ints.between 1024 65535;
|
||||
description = "Port to bind to.";
|
||||
default = 6379;
|
||||
};
|
||||
|
||||
socket = mkOption {
|
||||
type = with types; nullOr str;
|
||||
description = "Unix socket to bind to.";
|
||||
default = null;
|
||||
};
|
||||
|
||||
socketPerms = mkOption {
|
||||
type = with types; nullOr int;
|
||||
description = "Permissions for the unix socket.";
|
||||
default = null;
|
||||
};
|
||||
|
||||
logLevel = mkOption {
|
||||
type = types.enum [
|
||||
"debug"
|
||||
"verbose"
|
||||
"notice"
|
||||
"warning"
|
||||
"nothing"
|
||||
];
|
||||
description = "Logging verbosity level.";
|
||||
default = "notice";
|
||||
};
|
||||
|
||||
databases = mkOption {
|
||||
type = types.int;
|
||||
description = "Number of databases.";
|
||||
default = 16;
|
||||
};
|
||||
|
||||
# escape hatch due to redis config being massive
|
||||
extraConfig = mkOption {
|
||||
type = types.str;
|
||||
description = "Additional config directives.";
|
||||
default = "";
|
||||
};
|
||||
};
|
||||
|
||||
config.serviceDefs.redis =
|
||||
let
|
||||
cfg = config.services.redis;
|
||||
in
|
||||
lib.mkIf cfg.enable {
|
||||
pkg = pkgs.redis;
|
||||
# make sure we get the server binary, not cli
|
||||
exec = "redis-server";
|
||||
args = "%CFG%";
|
||||
config = {
|
||||
ext = ".conf";
|
||||
# these need to be made to match redis config
|
||||
# variable names here
|
||||
content = {
|
||||
inherit (cfg) bind port databases;
|
||||
unixsocket = cfg.socket;
|
||||
unixsocketperm = cfg.socketPerms;
|
||||
loglevel = cfg.logLevel;
|
||||
};
|
||||
# a formatter needs to take in a set of
|
||||
# attrs and write out a file
|
||||
formatter =
|
||||
let
|
||||
# set up serialisation for all types
|
||||
serialise = {
|
||||
int = builtins.toString;
|
||||
bool = b: if b then "yes" else "no";
|
||||
string = s: s;
|
||||
path = builtins.toString;
|
||||
null = _: _;
|
||||
list = builtins.concatStringsSep " ";
|
||||
float = builtins.toString;
|
||||
set = throw "cannot serialise a set in redis format";
|
||||
lambda = throw "cannot serialise a lambda, wtf?";
|
||||
};
|
||||
in
|
||||
# create a lambda that can serialise to redis config
|
||||
path: attrs:
|
||||
let
|
||||
text =
|
||||
(lib.foldlAttrs (
|
||||
acc: n: v:
|
||||
if (v != null) then acc + "${n} ${serialise.${builtins.typeOf v} v}" + "\n" else acc
|
||||
) "" attrs)
|
||||
+ cfg.extraConfig;
|
||||
in
|
||||
(pkgs.writeText path text).outPath;
|
||||
};
|
||||
};
|
||||
}
|
Loading…
Reference in New Issue
Block a user