cosmic checkpoint
This commit is contained in:
parent
4031a614a8
commit
f623faa0ef
26 changed files with 1163 additions and 538 deletions
|
@ -2,17 +2,18 @@
|
|||
imports = [
|
||||
./home.nix
|
||||
./util/local-webapp.nix
|
||||
./programs/stable-diffusion.nix
|
||||
./programs/ollama.nix
|
||||
./util/containers.nix
|
||||
# ./programs/stable-diffusion.nix
|
||||
./programs/openwebui.nix
|
||||
];
|
||||
|
||||
home = {
|
||||
home = rec {
|
||||
username = "bolt";
|
||||
homeDirectory = "/home/bolt";
|
||||
sessionVariables.FLAKE = "${homeDirectory}/.nix";
|
||||
};
|
||||
|
||||
home.packages = with pkgs; [
|
||||
nicotine-plus
|
||||
private-gpt
|
||||
];
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ in {
|
|||
./modules/theming.nix
|
||||
./util/ez.nix
|
||||
./util/flatpak.nix
|
||||
./util/cosmic.nix
|
||||
inputs.nix-index-database.hmModules.nix-index
|
||||
];
|
||||
|
||||
|
@ -24,7 +25,32 @@ in {
|
|||
options = "--delete-older-than 3d";
|
||||
};
|
||||
|
||||
xdg.enable = true;
|
||||
xdg = {
|
||||
enable = true;
|
||||
# mimeApps = {
|
||||
# enable = true;
|
||||
# # TODO?
|
||||
# # also figure out how cosmic detects default terminal
|
||||
# };
|
||||
portal = let
|
||||
xdg-cosmic = pkgs.xdg-desktop-portal-cosmic;
|
||||
in {
|
||||
enable = true;
|
||||
config = {
|
||||
common = {
|
||||
default = [
|
||||
"cosmic"
|
||||
];
|
||||
};
|
||||
};
|
||||
configPackages = [
|
||||
xdg-cosmic
|
||||
];
|
||||
extraPortals = [
|
||||
xdg-cosmic
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
home.packages = with pkgs;
|
||||
rice.fonts.pkgs
|
||||
|
@ -42,6 +68,18 @@ in {
|
|||
};
|
||||
|
||||
services.udiskie.enable = true;
|
||||
# fix reliance on nonexistent graphical-session-pre.target
|
||||
systemd.user.services.udiskie = lib.mkForce {
|
||||
Unit = {
|
||||
Description = "udiskie mount daemon";
|
||||
After = [];
|
||||
PartOf = ["graphical-session.target"];
|
||||
};
|
||||
|
||||
Service.ExecStart = ["${pkgs.udiskie}/bin/udiskie --appindicator"];
|
||||
|
||||
Install.WantedBy = ["graphical-session.target"];
|
||||
};
|
||||
|
||||
# programs.nix-index-database.comma.enable = true;
|
||||
# programs.nix-index = {
|
||||
|
|
BIN
home/icons/ollama.png
Normal file
BIN
home/icons/ollama.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 36 KiB |
|
@ -6,14 +6,14 @@
|
|||
programs.firefox.webapps = {
|
||||
"Microsoft-Teams" = {
|
||||
url = "https://teams.microsoft.com";
|
||||
id = 1;
|
||||
# id = 1;
|
||||
extraSettings = config.programs.firefox.profiles.default.settings;
|
||||
name = "Microsoft Teams";
|
||||
icon = ../icons/ms_teams.png;
|
||||
};
|
||||
"Facebook-Messenger" = {
|
||||
url = "https://www.messenger.com";
|
||||
id = 2;
|
||||
# id = 2;
|
||||
extraSettings = config.programs.firefox.profiles.default.settings;
|
||||
name = "Facebook Messenger";
|
||||
icon = ../icons/fb_msg.png;
|
||||
|
|
163
home/modules/cosmic.nix
Normal file
163
home/modules/cosmic.nix
Normal file
|
@ -0,0 +1,163 @@
|
|||
{
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}: let
|
||||
inherit (lib) range mapAttrsToList;
|
||||
inherit (builtins) toString;
|
||||
workspaceRange = range 1 6;
|
||||
makeWorkspaceBinding = modifiers: action:
|
||||
map (i: {
|
||||
inherit modifiers;
|
||||
key = toString i;
|
||||
action = {
|
||||
type = action;
|
||||
data = i;
|
||||
};
|
||||
})
|
||||
workspaceRange;
|
||||
focusWsBindings = makeWorkspaceBinding ["Super"] "Workspace";
|
||||
moveWsBindings = makeWorkspaceBinding ["Super" "Shift"] "SendToWorkspace";
|
||||
|
||||
hjkl = {
|
||||
"h" = "Left";
|
||||
"j" = "Down";
|
||||
"k" = "Up";
|
||||
"l" = "Right";
|
||||
# "i" = "In";
|
||||
# "u" = "Out";
|
||||
};
|
||||
makeDirBinding = modifiers: action:
|
||||
mapAttrsToList (
|
||||
key: dir: {
|
||||
inherit key modifiers;
|
||||
action = {
|
||||
type = action;
|
||||
data = dir;
|
||||
};
|
||||
}
|
||||
)
|
||||
hjkl;
|
||||
focusBindings = makeDirBinding ["Super"] "Focus";
|
||||
moveBindings = makeDirBinding ["Super" "Shift"] "Move";
|
||||
winManagementBindings =
|
||||
focusWsBindings
|
||||
++ moveWsBindings
|
||||
++ focusBindings
|
||||
++ moveBindings;
|
||||
binding = key: modifiers: action: {
|
||||
inherit key modifiers action;
|
||||
};
|
||||
spawnBinding = key: modifiers: app: {
|
||||
inherit key modifiers;
|
||||
action = {
|
||||
type = "Spawn";
|
||||
data = app;
|
||||
};
|
||||
};
|
||||
systemBinding = key: modifiers: action: {
|
||||
inherit key modifiers;
|
||||
action = {
|
||||
type = "System";
|
||||
data = action;
|
||||
};
|
||||
};
|
||||
in {
|
||||
imports = [
|
||||
../util/cosmic.nix
|
||||
];
|
||||
config.cosmic = {
|
||||
enable = true;
|
||||
defaultKeybindings = false;
|
||||
# otherSettings = {
|
||||
# "com.system76.CosmicPanel.Dock" = {
|
||||
# option.opacity = 0.8;
|
||||
# };
|
||||
# };
|
||||
keybindings =
|
||||
winManagementBindings
|
||||
++ [
|
||||
# System bindings appear broken?
|
||||
(binding "q" ["Super" "Shift"] "Close")
|
||||
(binding "w" ["Super"] "ToggleStacking")
|
||||
(binding "s" ["Super"] "ToggleOrientation")
|
||||
(binding "space" ["Super"] "ToggleWindowFloating")
|
||||
(binding "space" ["Super" "Shift"] "Maximize")
|
||||
(binding "minus" ["Super"] "Minimize")
|
||||
(binding "r" ["Super"] {
|
||||
type = "Resizing";
|
||||
data = "Outwards";
|
||||
})
|
||||
(binding "r" ["Super" "Shift"] {
|
||||
type = "Resizing";
|
||||
data = "Inwards";
|
||||
})
|
||||
(binding "tab" ["Super"] "NextOutput")
|
||||
(binding "tab" ["Super" "Shift"] "MoveToNextOutput")
|
||||
(binding "grave" ["Super"] "PreviousOutput")
|
||||
(binding "grave" ["Super" "Shift"] "MoveToPreviousOutput")
|
||||
(spawnBinding "equal" ["Super"] "keepassxc")
|
||||
(systemBinding "f" ["Super"] "WebBrowser")
|
||||
(systemBinding "e" ["Super"] "HomeFolder")
|
||||
# broken at the moment
|
||||
# (systemBinding "return" ["Super"] "Terminal")
|
||||
(spawnBinding "return" ["Super"] "kitty")
|
||||
(systemBinding "s" ["Super" "Shift"] "Screenshot")
|
||||
(systemBinding null ["Super"] "Launcher")
|
||||
(systemBinding "d" ["Super"] "AppLibrary")
|
||||
(systemBinding "XF86AudioRaiseVolume" [] "VolumeRaise")
|
||||
(systemBinding "XF86AudioLowerVolume" [] "VolumeLower")
|
||||
(systemBinding "XF86AudioMute" [] "Mute")
|
||||
(spawnBinding "XF86AudioNext" [] "playerctl next")
|
||||
(spawnBinding "XF86AudioPrev" [] "playerctl previous")
|
||||
(spawnBinding "XF86AudioPlay" [] "playerctl play-pause")
|
||||
(spawnBinding "XF86AudioStop" [] "playerctl stop")
|
||||
(systemBinding "XF86MonBrightnessUp" [] "BrightnessUp")
|
||||
(systemBinding "XF86MonBrightnessDown" [] "BrightnessDown")
|
||||
(spawnBinding "e" ["Super" "Shift"] "wlogout")
|
||||
|
||||
# old, pre-System bindings
|
||||
# (binding "q" ["Super" "Shift"] "Close")
|
||||
# (binding "w" ["Super"] "ToggleStacking")
|
||||
# (binding "s" ["Super"] "ToggleOrientation")
|
||||
# (binding "space" ["Super"] "ToggleWindowFloating")
|
||||
# (binding "space" ["Super" "Shift"] "Maximize")
|
||||
# (binding "minus" ["Super"] "Minimize")
|
||||
# (binding "r" ["Super"] {
|
||||
# type = "Resizing";
|
||||
# data = "Outwards";
|
||||
# })
|
||||
# (binding "r" ["Super" "Shift"] {
|
||||
# type = "Resizing";
|
||||
# data = "Inwards";
|
||||
# })
|
||||
# (binding "tab" ["Super"] "NextOutput")
|
||||
# (binding "tab" ["Super" "Shift"] "MoveToNextOutput")
|
||||
# (binding "grave" ["Super"] "PreviousOutput")
|
||||
# (binding "grave" ["Super" "Shift"] "MoveToPreviousOutput")
|
||||
# (spawnBinding "f" ["Super"] "firefox")
|
||||
# (spawnBinding "e" ["Super"] "nautilus")
|
||||
# (spawnBinding "equal" ["Super"] "keepassxc")
|
||||
# (spawnBinding "return" ["Super"] "kitty")
|
||||
# (spawnBinding "s" ["Super" "Shift"] "cosmic-screenshot")
|
||||
# (spawnBinding null ["Super"] "cosmic-launcher")
|
||||
# (spawnBinding "d" ["Super"] "cosmic-app-library")
|
||||
# (spawnBinding "XF86AudioRaiseVolume" [] "amixer sset Master 5%+")
|
||||
# (spawnBinding "XF86AudioLowerVolume" [] "amixer sset Master 5%-")
|
||||
# (spawnBinding "XF86AudioMute" [] "amixer sset Master toggle")
|
||||
# (spawnBinding "XF86AudioNext" [] "playerctl next")
|
||||
# (spawnBinding "XF86AudioPrev" [] "playerctl previous")
|
||||
# (spawnBinding "XF86AudioPlay" [] "playerctl play-pause")
|
||||
# (spawnBinding "XF86AudioStop" [] "playerctl stop")
|
||||
# (
|
||||
# spawnBinding "XF86MonBrightnessUp" []
|
||||
# "busctl --user call com.system76.CosmicSettingsDaemon /com/system76/CosmicSettingsDaemon com.system76.CosmicSettingsDaemon IncreaseDisplayBrightness"
|
||||
# )
|
||||
# (
|
||||
# spawnBinding "XF86MonBrightnessDown" []
|
||||
# "busctl --user call com.system76.CosmicSettingsDaemon /com/system76/CosmicSettingsDaemon com.system76.CosmicSettingsDaemon DecreaseDisplayBrightness"
|
||||
# )
|
||||
# (spawnBinding "e" ["Super" "Shift"] "wlogout")
|
||||
];
|
||||
};
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
{pkgs, ...}: {
|
||||
imports = [
|
||||
./cosmic.nix
|
||||
./media-players.nix
|
||||
./webapps.nix
|
||||
./documents.nix
|
||||
|
@ -9,8 +10,8 @@
|
|||
../programs/wlogout.nix
|
||||
];
|
||||
home.packages = with pkgs; [
|
||||
gnome.file-roller
|
||||
gnome.nautilus
|
||||
file-roller
|
||||
nautilus
|
||||
thunderbird
|
||||
keepassxc
|
||||
];
|
||||
|
|
|
@ -3,15 +3,8 @@
|
|||
../util/flatpak.nix
|
||||
];
|
||||
flatpaks = [
|
||||
{
|
||||
name = "md.obsidian.Obsidian";
|
||||
overrides = {
|
||||
Environment = {
|
||||
OBSIDIAN_DISABLE_GPU = "1";
|
||||
};
|
||||
};
|
||||
}
|
||||
"org.onlyoffice.desktopeditors"
|
||||
"md.obsidian.Obsidian"
|
||||
"com.logseq.Logseq"
|
||||
"org.libreoffice.LibreOffice"
|
||||
"com.jgraph.drawio.desktop"
|
||||
];
|
||||
|
|
7
home/modules/niri.nix
Normal file
7
home/modules/niri.nix
Normal file
|
@ -0,0 +1,7 @@
|
|||
{...}: {
|
||||
programs.niri = {
|
||||
enable = true;
|
||||
settings = {
|
||||
};
|
||||
};
|
||||
}
|
|
@ -5,14 +5,14 @@
|
|||
programs.firefox.webapps = {
|
||||
"Syncthing" = {
|
||||
url = "http://127.0.0.1:8384";
|
||||
id = 3;
|
||||
# id = 3;
|
||||
extraSettings = config.programs.firefox.profiles.default.settings;
|
||||
name = "Syncthing";
|
||||
icon = ../icons/syncthing.png;
|
||||
};
|
||||
"StudyTAFE" = {
|
||||
url = "https://www.studytafensw.edu.au";
|
||||
id = 4;
|
||||
# id = 4;
|
||||
extraSettings = config.programs.firefox.profiles.default.settings;
|
||||
name = "TAFE Study";
|
||||
icon = ../icons/tafe.jpg;
|
||||
|
|
|
@ -2,34 +2,30 @@
|
|||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}: let
|
||||
inherit (lib) getExe;
|
||||
inherit (builtins) toString;
|
||||
inherit (pkgs) writeShellScript;
|
||||
in {
|
||||
localWebApps = {
|
||||
openwebui = rec {
|
||||
name = "Ollama (OpenWebUI)";
|
||||
genericName = "Ollama";
|
||||
icon = ../icons/openwebui.png;
|
||||
id = 6;
|
||||
port = 3021;
|
||||
service = let
|
||||
docker = getExe pkgs.docker;
|
||||
in {
|
||||
Type = "exec";
|
||||
ExecStartPre = "${writeShellScript "openwebui-check" ''
|
||||
set -euo pipefail
|
||||
echo Checking for container existence..
|
||||
if [[ $(${docker} inspect openwebui &> /dev/null; printf $?) -ne 0 ]]; then
|
||||
echo Not found. Creating OpenWebUI/Ollama container..
|
||||
${docker} create -e PORT=${toString port} --restart no --network host --gpus all -v ollama:/root/.ollama -v open-webui:/app/backend/data --name openwebui --restart always ghcr.io/open-webui/open-webui:ollama
|
||||
fi
|
||||
echo Check complete.
|
||||
''}";
|
||||
ExecStart = "${docker} start -a openwebui";
|
||||
ExecStop = "${docker} stop openwebui";
|
||||
};
|
||||
}: {
|
||||
services.podman = {
|
||||
containers.ollama = {
|
||||
# serviceName = "ollama";
|
||||
image = "ollama/ollama:latest";
|
||||
# ports = ["11434:11434"];
|
||||
devices = ["nvidia.com/gpu=all"];
|
||||
autostart = false;
|
||||
networks = ["ollama"];
|
||||
extraOptions = [
|
||||
"--health-cmd"
|
||||
(lib.escapeShellArg "bash -c 'cat < /dev/null > /dev/tcp/localhost/11434'")
|
||||
];
|
||||
};
|
||||
|
||||
networks.ollama = {
|
||||
Subnet = "192.168.10.0/24";
|
||||
Gateway = "192.168.10.1";
|
||||
};
|
||||
};
|
||||
|
||||
# containers.ollama = {
|
||||
# image = "ollama/ollama:latest";
|
||||
# # TODO: volume for models!
|
||||
# extraOptions = ["--network=host" "--device=nvidia.com/gpu=all"];
|
||||
# };
|
||||
}
|
||||
|
|
40
home/programs/openwebui.nix
Normal file
40
home/programs/openwebui.nix
Normal file
|
@ -0,0 +1,40 @@
|
|||
{
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}: let
|
||||
port = 3021;
|
||||
in {
|
||||
imports = [./ollama.nix];
|
||||
|
||||
localWebApps = {
|
||||
openwebui = {
|
||||
name = "OpenWebUI";
|
||||
genericName = "LLM";
|
||||
icon = ../icons/openwebui.png;
|
||||
inherit port;
|
||||
requires.containers = ["openwebui" "ollama"];
|
||||
};
|
||||
};
|
||||
|
||||
services.podman = {
|
||||
containers.openwebui = let
|
||||
str = builtins.toString;
|
||||
in {
|
||||
# serviceName = "openwebui";
|
||||
image = "ghcr.io/open-webui/open-webui:main";
|
||||
ports = ["${str port}:${str port}"];
|
||||
environment = {
|
||||
WEBUI_AUTH = "False";
|
||||
PORT = "${str port}";
|
||||
};
|
||||
autostart = false;
|
||||
networks = ["ollama"];
|
||||
unitConfig = {Requires = ["podman-ollama.service"];};
|
||||
extraOptions = [
|
||||
"--health-cmd"
|
||||
(lib.escapeShellArg "bash -c 'cat < /dev/null > /dev/tcp/localhost/${str port}'")
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
|
@ -9,15 +9,12 @@
|
|||
in {
|
||||
localWebApps = {
|
||||
stable-diffusion = {
|
||||
name = "Stable Diffusion (ComfyUI)";
|
||||
genericName = "Stable Diffusion";
|
||||
name = "Stable Diffusion";
|
||||
genericName = "Image Generator";
|
||||
icon = ../icons/comfyui.png;
|
||||
id = 5;
|
||||
port = 7860;
|
||||
service = {
|
||||
WorkingDirectory = "${homeDirectory}/code/etc/stable-diffusion-webui-docker";
|
||||
ExecStart = "${getExe pkgs.docker} compose --profile comfy up --build";
|
||||
};
|
||||
requires = ["stable-diffusion"];
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
365
home/util/containers.nix
Normal file
365
home/util/containers.nix
Normal file
|
@ -0,0 +1,365 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}: let
|
||||
containerOptions = {...}: {
|
||||
options = {
|
||||
pullPolicy = mkOption {
|
||||
type = with types; str;
|
||||
description = "Podman container pulling policy";
|
||||
default = "newer";
|
||||
};
|
||||
image = mkOption {
|
||||
type = with types; str;
|
||||
description = "OCI image to run.";
|
||||
example = "library/hello-world";
|
||||
};
|
||||
|
||||
imageFile = mkOption {
|
||||
type = with types; nullOr package;
|
||||
default = null;
|
||||
description = ''
|
||||
Path to an image file to load before running the image. This can
|
||||
be used to bypass pulling the image from the registry.
|
||||
|
||||
The `image` attribute must match the name and
|
||||
tag of the image contained in this file, as they will be used to
|
||||
run the container with that image. If they do not match, the
|
||||
image will be pulled from the registry as usual.
|
||||
'';
|
||||
example = literalExpression "pkgs.dockerTools.buildImage {...};";
|
||||
};
|
||||
|
||||
login = {
|
||||
username = mkOption {
|
||||
type = with types; nullOr str;
|
||||
default = null;
|
||||
description = "Username for login.";
|
||||
};
|
||||
|
||||
passwordFile = mkOption {
|
||||
type = with types; nullOr str;
|
||||
default = null;
|
||||
description = "Path to file containing password.";
|
||||
example = "/etc/nixos/dockerhub-password.txt";
|
||||
};
|
||||
|
||||
registry = mkOption {
|
||||
type = with types; nullOr str;
|
||||
default = null;
|
||||
description = "Registry where to login to.";
|
||||
example = "https://docker.pkg.github.com";
|
||||
};
|
||||
};
|
||||
|
||||
cmd = mkOption {
|
||||
type = with types; listOf str;
|
||||
default = [];
|
||||
description = "Commandline arguments to pass to the image's entrypoint.";
|
||||
example = literalExpression ''
|
||||
["--port=9000"]
|
||||
'';
|
||||
};
|
||||
|
||||
labels = mkOption {
|
||||
type = with types; attrsOf str;
|
||||
default = {};
|
||||
description = "Labels to attach to the container at runtime.";
|
||||
example = literalExpression ''
|
||||
{
|
||||
"traefik.https.routers.example.rule" = "Host(`example.container`)";
|
||||
}
|
||||
'';
|
||||
};
|
||||
|
||||
entrypoint = mkOption {
|
||||
type = with types; nullOr str;
|
||||
description = "Override the default entrypoint of the image.";
|
||||
default = null;
|
||||
example = "/bin/my-app";
|
||||
};
|
||||
|
||||
environment = mkOption {
|
||||
type = with types; attrsOf str;
|
||||
default = {};
|
||||
description = "Environment variables to set for this container.";
|
||||
example = literalExpression ''
|
||||
{
|
||||
DATABASE_HOST = "db.example.com";
|
||||
DATABASE_PORT = "3306";
|
||||
}
|
||||
'';
|
||||
};
|
||||
|
||||
environmentFiles = mkOption {
|
||||
type = with types; listOf path;
|
||||
default = [];
|
||||
description = "Environment files for this container.";
|
||||
example = literalExpression ''
|
||||
[
|
||||
/path/to/.env
|
||||
/path/to/.env.secret
|
||||
]
|
||||
'';
|
||||
};
|
||||
|
||||
log-driver = mkOption {
|
||||
type = types.str;
|
||||
default = "journald";
|
||||
description = ''
|
||||
Logging driver for the container. The default of
|
||||
`"journald"` means that the container's logs will be
|
||||
handled as part of the systemd unit.
|
||||
|
||||
For more details and a full list of logging drivers, refer to podman documentation.
|
||||
|
||||
For Docker:
|
||||
[Docker engine documentation](https://docs.docker.com/engine/reference/run/#logging-drivers---log-driver)
|
||||
|
||||
For Podman:
|
||||
Refer to the docker-run(1) man page.
|
||||
'';
|
||||
};
|
||||
|
||||
ports = mkOption {
|
||||
type = with types; listOf str;
|
||||
default = [];
|
||||
description = ''
|
||||
Network ports to publish from the container to the outer host.
|
||||
|
||||
Valid formats:
|
||||
- `<ip>:<hostPort>:<containerPort>`
|
||||
- `<ip>::<containerPort>`
|
||||
- `<hostPort>:<containerPort>`
|
||||
- `<containerPort>`
|
||||
|
||||
Both `hostPort` and `containerPort` can be specified as a range of
|
||||
ports. When specifying ranges for both, the number of container
|
||||
ports in the range must match the number of host ports in the
|
||||
range. Example: `1234-1236:1234-1236/tcp`
|
||||
|
||||
When specifying a range for `hostPort` only, the `containerPort`
|
||||
must *not* be a range. In this case, the container port is published
|
||||
somewhere within the specified `hostPort` range.
|
||||
Example: `1234-1236:1234/tcp`
|
||||
|
||||
Refer to the
|
||||
[Docker engine documentation](https://docs.docker.com/engine/reference/run/#expose-incoming-ports) for full details.
|
||||
'';
|
||||
example = literalExpression ''
|
||||
[
|
||||
"8080:9000"
|
||||
]
|
||||
'';
|
||||
};
|
||||
|
||||
user = mkOption {
|
||||
type = with types; nullOr str;
|
||||
default = null;
|
||||
description = ''
|
||||
Override the username or UID (and optionally groupname or GID) used
|
||||
in the container.
|
||||
'';
|
||||
example = "nobody:nogroup";
|
||||
};
|
||||
|
||||
volumes = mkOption {
|
||||
type = with types; listOf str;
|
||||
default = [];
|
||||
description = ''
|
||||
List of volumes to attach to this container.
|
||||
|
||||
Note that this is a list of `"src:dst"` strings to
|
||||
allow for `src` to refer to `/nix/store` paths, which
|
||||
would be difficult with an attribute set. There are
|
||||
also a variety of mount options available as a third
|
||||
field; please refer to the
|
||||
[docker engine documentation](https://docs.docker.com/engine/reference/run/#volume-shared-filesystems) for details.
|
||||
'';
|
||||
example = literalExpression ''
|
||||
[
|
||||
"volume_name:/path/inside/container"
|
||||
"/path/on/host:/path/inside/container"
|
||||
]
|
||||
'';
|
||||
};
|
||||
|
||||
workdir = mkOption {
|
||||
type = with types; nullOr str;
|
||||
default = null;
|
||||
description = "Override the default working directory for the container.";
|
||||
example = "/var/lib/hello_world";
|
||||
};
|
||||
|
||||
dependsOn = mkOption {
|
||||
type = with types; listOf str;
|
||||
default = [];
|
||||
description = ''
|
||||
Define which other containers this one depends on. They will be added to both After and Requires for the unit.
|
||||
|
||||
Use the same name as the attribute under `virtualisation.oci-containers.containers`.
|
||||
'';
|
||||
example = literalExpression ''
|
||||
containers = {
|
||||
node1 = {};
|
||||
node2 = {
|
||||
dependsOn = [ "node1" ];
|
||||
}
|
||||
}
|
||||
'';
|
||||
};
|
||||
|
||||
hostname = mkOption {
|
||||
type = with types; nullOr str;
|
||||
default = null;
|
||||
description = "The hostname of the container.";
|
||||
example = "hello-world";
|
||||
};
|
||||
|
||||
preRunExtraOptions = mkOption {
|
||||
type = with types; listOf str;
|
||||
default = [];
|
||||
description = "Extra options for podman that go before the `run` argument.";
|
||||
example = ["--runtime" "runsc"];
|
||||
};
|
||||
|
||||
extraOptions = mkOption {
|
||||
type = with types; listOf str;
|
||||
default = [];
|
||||
description = "Extra options for podman run`.";
|
||||
example = literalExpression ''
|
||||
["--network=host"]
|
||||
'';
|
||||
};
|
||||
|
||||
autoStart = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
When enabled, the container is automatically started on boot.
|
||||
If this option is set to false, the container has to be started on-demand via its service.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
mkService = name: container: let
|
||||
podman = lib.getExe pkgs.podman;
|
||||
rm = lib.getExe' pkgs.coreutils "rm";
|
||||
printf = lib.getExe' pkgs.coreutils "printf";
|
||||
getId = ''"$(${lib.getExe' pkgs.coreutils "id"} -u)"'';
|
||||
dependsOn = map (x: "podman-${x}.service") container.dependsOn;
|
||||
escapedName = lib.escapeShellArg name;
|
||||
preStartScript = pkgs.writeShellApplication {
|
||||
name = "pre-start";
|
||||
runtimeInputs = [];
|
||||
text = ''
|
||||
${printf} "Running pre-start script.."
|
||||
${podman} rm -f ${name} || true
|
||||
${lib.optionalString (container.imageFile != null) ''
|
||||
${podman} load -i ${container.imageFile}
|
||||
''}
|
||||
${rm} -f /run/user/${getId}/podman-${escapedName}.ctr-id
|
||||
${printf} " success.\nStarting Podman...\n"
|
||||
'';
|
||||
};
|
||||
script = concatStringsSep " \\\n " (
|
||||
[
|
||||
"exec ${podman} "
|
||||
]
|
||||
++ map escapeShellArg container.preRunExtraOptions
|
||||
++ [
|
||||
"run"
|
||||
"--log-level=debug"
|
||||
"--rm"
|
||||
"--name=${escapedName}"
|
||||
"--log-driver=${container.log-driver}"
|
||||
]
|
||||
++ optional (container.entrypoint != null)
|
||||
"--entrypoint=${escapeShellArg container.entrypoint}"
|
||||
++ optional (container.hostname != null)
|
||||
"--hostname=${escapeShellArg container.hostname}"
|
||||
++ optional (container.pullPolicy != null)
|
||||
"--pull=${container.pullPolicy}"
|
||||
++ [
|
||||
"--cidfile=/run/user/${getId}/podman-${escapedName}.ctr-id"
|
||||
# "--sdnotify=ignore"
|
||||
"--cgroups=no-conmon"
|
||||
"--sdnotify=conmon"
|
||||
"-d"
|
||||
"--replace"
|
||||
]
|
||||
++ (mapAttrsToList (k: v: "-e ${escapeShellArg k}=${escapeShellArg v}") container.environment)
|
||||
++ map (f: "--env-file ${escapeShellArg f}") container.environmentFiles
|
||||
++ map (p: "-p ${escapeShellArg p}") container.ports
|
||||
++ optional (container.user != null) "-u ${escapeShellArg container.user}"
|
||||
++ map (v: "-v ${escapeShellArg v}") container.volumes
|
||||
++ (mapAttrsToList (k: v: "-l ${escapeShellArg k}=${escapeShellArg v}") container.labels)
|
||||
++ optional (container.workdir != null) "-w ${escapeShellArg container.workdir}"
|
||||
++ map escapeShellArg container.extraOptions
|
||||
++ [container.image]
|
||||
++ map escapeShellArg container.cmd
|
||||
);
|
||||
|
||||
inherit (lib) concatStringsSep escapeShellArg optional optionalAttrs optionalString mapAttrsToList;
|
||||
in {
|
||||
Unit = {
|
||||
WantedBy = [] ++ lib.optional (container.autoStart) "default.target"; # graphical-session instead maybe?
|
||||
After = dependsOn;
|
||||
Requires = dependsOn;
|
||||
# StopWhenUnneeded = true;
|
||||
};
|
||||
# TODO make network target..
|
||||
# wants = lib.optional (container.imageFile == null) "network-online.target";
|
||||
# after = lib.optionals (container.imageFile == null) [ "network-online.target" ]
|
||||
# ++ dependsOn;
|
||||
# environment = proxy_env;
|
||||
|
||||
Service = {
|
||||
### There is no generalized way of supporting `reload` for docker
|
||||
### containers. Some containers may respond well to SIGHUP sent to their
|
||||
### init process, but it is not guaranteed; some apps have other reload
|
||||
### mechanisms, some don't have a reload signal at all, and some docker
|
||||
### images just have broken signal handling. The best compromise in this
|
||||
### case is probably to leave ExecReload undefined, so `systemctl reload`
|
||||
### will at least result in an error instead of potentially undefined
|
||||
### behaviour.
|
||||
###
|
||||
### Advanced users can still override this part of the unit to implement
|
||||
### a custom reload handler, since the result of all this is a normal
|
||||
### systemd service from the perspective of the NixOS module system.
|
||||
###
|
||||
# ExecReload = ...;
|
||||
###
|
||||
ExecStartPre = ["${preStartScript}/bin/pre-start"];
|
||||
ExecStart = [
|
||||
"${pkgs.writeShellScript "start" script}"
|
||||
];
|
||||
ExecStop = [
|
||||
"${podman} stop --ignore --cidfile=/run/user/${getId}/podman-${escapedName}.ctr-id"
|
||||
"${podman} rm -f --ignore --cidfile=/run/user/${getId}/podman-${escapedName}.ctr-id"
|
||||
];
|
||||
# TimeoutStartSec = 0;
|
||||
# TimeoutStopSec = 120;
|
||||
# Restart = "always";
|
||||
Environment = ["PODMAN_SYSTEMD_UNIT=podman-${name}.service"];
|
||||
Type = "notify";
|
||||
NotifyAccess = "all";
|
||||
# Type = "exec";
|
||||
};
|
||||
};
|
||||
|
||||
cfg = config.containers;
|
||||
inherit (lib) mapAttrs' nameValuePair mkOption types mkIf literalExpression;
|
||||
in {
|
||||
options.containers = mkOption {
|
||||
default = {};
|
||||
type = with types; attrsOf (submodule containerOptions);
|
||||
};
|
||||
|
||||
config = mkIf (cfg != {}) {
|
||||
systemd.user.services = mapAttrs' (k: v: nameValuePair "podman-${k}" (mkService k v)) cfg;
|
||||
};
|
||||
}
|
176
home/util/cosmic.nix
Normal file
176
home/util/cosmic.nix
Normal file
|
@ -0,0 +1,176 @@
|
|||
{
|
||||
lib,
|
||||
config,
|
||||
...
|
||||
}: let
|
||||
inherit (lib) filterAttrs concatStrings concatStringsSep mapAttrsToList concatLists foldlAttrs concatMapAttrs mapAttrs mapAttrs' nameValuePair boolToString;
|
||||
inherit (builtins) typeOf toString stringLength;
|
||||
|
||||
# build up serialisation machinery from here for various types
|
||||
|
||||
# list -> array
|
||||
array = a: "[${concatStringsSep "," a}]";
|
||||
# attrset -> hashmap
|
||||
_assoc = a: mapAttrsToList (name: val: "${name}: ${val}") a;
|
||||
assoc = a: '' {
|
||||
${concatStringsSep ",\n" (concatLists (map _assoc a))}
|
||||
}'';
|
||||
# attrset -> struct
|
||||
_struct_kv = k: v:
|
||||
if v == null
|
||||
then ""
|
||||
else (concatStringsSep ":" [k (serialise.${typeOf v} v)]);
|
||||
_struct_concat = s:
|
||||
foldlAttrs (
|
||||
acc: k: v:
|
||||
if stringLength acc > 0
|
||||
then concatStringsSep ", " [acc (_struct_kv k v)]
|
||||
else _struct_kv k v
|
||||
) ""
|
||||
s;
|
||||
_struct_filt = s:
|
||||
_struct_concat (filterAttrs (k: v: v != null) s);
|
||||
struct = s: "(${_struct_filt s})";
|
||||
toQuotedString = s: ''"${toString s}"'';
|
||||
|
||||
# make an attrset for struct serialisation
|
||||
serialise = {
|
||||
int = toString;
|
||||
float = toString;
|
||||
bool = boolToString;
|
||||
string = toString;
|
||||
path = toString;
|
||||
null = toString;
|
||||
set = struct;
|
||||
list = array;
|
||||
};
|
||||
|
||||
# define the key for a keybind
|
||||
defineBinding = binding:
|
||||
struct {
|
||||
inherit (binding) modifiers;
|
||||
key =
|
||||
if isNull binding.key
|
||||
then null
|
||||
else toQuotedString binding.key;
|
||||
};
|
||||
|
||||
# map keybinding from list of attrset to hashmap of (mod,key): action
|
||||
_mapBindings = bindings:
|
||||
map (
|
||||
inner: {"${defineBinding inner}" = maybeToString (checkAction inner.action);}
|
||||
)
|
||||
bindings;
|
||||
mapBindings = bindings:
|
||||
assoc (_mapBindings bindings);
|
||||
|
||||
# check a keybinding's action
|
||||
# escape with quotes if it's a Spawn action
|
||||
checkAction = a:
|
||||
if typeOf a == "set" && a.type == "Spawn"
|
||||
then {
|
||||
inherit (a) type;
|
||||
data = toQuotedString a.data;
|
||||
}
|
||||
else a;
|
||||
|
||||
maybeToString = s:
|
||||
if typeOf s == "set"
|
||||
then concatStrings [s.type "(" (toString s.data) ")"]
|
||||
else s;
|
||||
|
||||
mapCosmicSettings = application: options:
|
||||
mapAttrs' (k: v:
|
||||
nameValuePair "cosmic/${application}/v${options.version}/${k}" {
|
||||
enable = true;
|
||||
text = serialise.${typeOf v} v;
|
||||
})
|
||||
options.option;
|
||||
# deconstructKeybindAttr = attr: mapAttrs' (
|
||||
# name: value:
|
||||
# ) attr;
|
||||
# rec_attr = lib.types.attrsOf (lib.types.oneOf lib.types.string rec_attr);
|
||||
in {
|
||||
options.cosmic = {
|
||||
enable = with lib;
|
||||
mkOption {
|
||||
default = false;
|
||||
type = types.bool;
|
||||
};
|
||||
|
||||
defaultKeybindings = with lib;
|
||||
mkOption {
|
||||
default = true;
|
||||
type = types.bool;
|
||||
};
|
||||
|
||||
# binds = with lib;
|
||||
# mkOption {
|
||||
# default = {};
|
||||
# type = with types; rec_attr;
|
||||
# };
|
||||
|
||||
keybindings = with lib;
|
||||
mkOption {
|
||||
default = [];
|
||||
type = with types;
|
||||
listOf (submodule {
|
||||
options = {
|
||||
modifiers = mkOption {
|
||||
type = listOf str;
|
||||
default = [];
|
||||
};
|
||||
key = mkOption {
|
||||
type = nullOr str;
|
||||
default = null;
|
||||
};
|
||||
action = mkOption {
|
||||
type = either str (submodule {
|
||||
options = {
|
||||
type = mkOption {
|
||||
type = str;
|
||||
};
|
||||
data = mkOption {
|
||||
type = oneOf [str int];
|
||||
default = "";
|
||||
};
|
||||
};
|
||||
});
|
||||
};
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
otherSettings = with lib;
|
||||
mkOption {
|
||||
default = {};
|
||||
type = with types;
|
||||
attrsOf (submodule {
|
||||
options = {
|
||||
version = mkOption {
|
||||
type = str;
|
||||
default = "1";
|
||||
};
|
||||
option = mkOption {
|
||||
type = attrsOf anything;
|
||||
};
|
||||
};
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
# TODO we need to split between system_actions, workspaces, custom
|
||||
config.xdg.configFile =
|
||||
{
|
||||
"cosmic/com.system76.CosmicSettings.Shortcuts/v1/custom".text = mapBindings config.cosmic.keybindings;
|
||||
"cosmic/com.system76.CosmicSettings.Shortcuts/v1/defaults" = {
|
||||
text = "{}";
|
||||
enable = !config.cosmic.defaultKeybindings;
|
||||
};
|
||||
}
|
||||
// concatMapAttrs (
|
||||
application: options:
|
||||
mapCosmicSettings application options
|
||||
)
|
||||
config.cosmic.otherSettings;
|
||||
}
|
|
@ -4,15 +4,22 @@
|
|||
...
|
||||
}: let
|
||||
inherit (builtins) getAttr stringLength substring;
|
||||
inherit (lib) mkOption getExe;
|
||||
inherit (lib) mkOption getExe listToAttrs attrsToList imap;
|
||||
inherit (lib.attrsets) filterAttrs mapAttrs mapAttrs' nameValuePair;
|
||||
inherit (lib.strings) concatStringsSep toUpper;
|
||||
|
||||
enumerate = a:
|
||||
listToAttrs (imap (id: {
|
||||
name,
|
||||
value,
|
||||
}: {
|
||||
inherit name;
|
||||
value = value // {inherit id;};
|
||||
}) (attrsToList a));
|
||||
make-app-profiles = cfg:
|
||||
mapAttrs' (name: cfg:
|
||||
nameValuePair "home-manager-webapp-${name}" {
|
||||
inherit (cfg) id;
|
||||
|
||||
userChrome = ''
|
||||
@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
|
||||
|
||||
|
@ -82,10 +89,10 @@ in {
|
|||
description = "The URL of the webapp to launch.";
|
||||
};
|
||||
|
||||
id = mkOption {
|
||||
type = int;
|
||||
description = "The Firefox profile ID to set.";
|
||||
};
|
||||
# id = mkOption {
|
||||
# type = int;
|
||||
# description = "The Firefox profile ID to set.";
|
||||
# };
|
||||
|
||||
hidden = mkOption {
|
||||
type = bool;
|
||||
|
@ -174,7 +181,7 @@ in {
|
|||
};
|
||||
|
||||
config = {
|
||||
programs.firefox.profiles = make-app-profiles config.programs.firefox.webapps;
|
||||
programs.firefox.profiles = make-app-profiles (enumerate config.programs.firefox.webapps);
|
||||
|
||||
xdg.desktopEntries =
|
||||
mapAttrs (name: cfg: {
|
||||
|
|
|
@ -7,32 +7,84 @@
|
|||
inherit (lib) mkOption;
|
||||
inherit (lib.attrsets) mapAttrs mapAttrs' nameValuePair;
|
||||
inherit (lib) getExe getExe';
|
||||
# make a firefox webapp entry for the client app
|
||||
# make a firefox webapp + hidden .desktop entry for the client app
|
||||
make-firefox = cfg:
|
||||
mapAttrs' (
|
||||
name: cfg:
|
||||
nameValuePair "${name}-client"
|
||||
{
|
||||
inherit (cfg) name id;
|
||||
inherit (cfg) name;
|
||||
url = "http://127.0.0.1:${builtins.toString cfg.port}";
|
||||
extraSettings = config.programs.firefox.profiles.default.settings;
|
||||
hidden = true;
|
||||
}
|
||||
)
|
||||
cfg;
|
||||
# make a systemd service for running the backend
|
||||
make-systemd = cfg:
|
||||
# make a systemd service for running the frontend
|
||||
make-systemd-service = cfg:
|
||||
mapAttrs' (
|
||||
name: cfg:
|
||||
if
|
||||
(cfg.service
|
||||
!= null)
|
||||
then
|
||||
nameValuePair "${cfg.name}-frontend" {
|
||||
Unit = {
|
||||
Description = "${cfg.name} Frontend";
|
||||
WantedBy = lib.mkForce [];
|
||||
};
|
||||
|
||||
Service = cfg.service;
|
||||
}
|
||||
else nameValuePair "" {}
|
||||
)
|
||||
cfg;
|
||||
# modify systemd units to be PartOf this target
|
||||
modify-systemd-services = cfg:
|
||||
lib.listToAttrs (lib.flatten (lib.mapAttrsToList (
|
||||
name: cfg: (map (
|
||||
req: {
|
||||
name = "${req}";
|
||||
value = {
|
||||
Unit = {
|
||||
PartOf = "${lib.toLower cfg.name}.target";
|
||||
};
|
||||
};
|
||||
}
|
||||
)
|
||||
cfg.requires.services)
|
||||
)
|
||||
cfg));
|
||||
modify-quadlets = cfg:
|
||||
lib.listToAttrs (lib.flatten (lib.mapAttrsToList (
|
||||
name: cfg: (map (
|
||||
req: {
|
||||
name = "${req}";
|
||||
value = {
|
||||
unitConfig = {
|
||||
PartOf = "${lib.toLower cfg.name}.target";
|
||||
};
|
||||
};
|
||||
}
|
||||
)
|
||||
cfg.requires.containers)
|
||||
)
|
||||
cfg));
|
||||
# make a systemd target to collate dependencies
|
||||
make-systemd-target = cfg:
|
||||
mapAttrs (
|
||||
name: cfg: {
|
||||
Unit = {
|
||||
Description = "${cfg.name} Backend";
|
||||
Description = "${cfg.name} Target";
|
||||
WantedBy = lib.mkForce [];
|
||||
Requires =
|
||||
(map (req: req + ".service") cfg.requires.services)
|
||||
++ (map (req: "podman-" + req + ".service") cfg.requires.containers);
|
||||
};
|
||||
Service = cfg.service;
|
||||
}
|
||||
)
|
||||
cfg;
|
||||
# make desktop shortcuts and a script which will handle starting both the above
|
||||
# make desktop shortcuts and a script which will handle starting everything
|
||||
make-xdg = cfg:
|
||||
mapAttrs (
|
||||
name: cfg: {
|
||||
|
@ -42,45 +94,72 @@
|
|||
notify-send = "${getExe' pkgs.libnotify "notify-send"} -a \"${cfg.name}\"";
|
||||
systemctl = "${getExe' pkgs.systemd "systemctl"}";
|
||||
dex = "${getExe pkgs.dex}";
|
||||
curl = "${getExe pkgs.curl}";
|
||||
podman = "${getExe pkgs.podman}";
|
||||
makeContainerCheck = container: ''[ "$(${podman} inspect -f {{.State.Health.Status}} ${container})" == "healthy" ]'';
|
||||
# makeContainerCheck = container: ''
|
||||
# [ ${podman} inspect -f {{.State.Status}} ${container})" != "running" ]
|
||||
# '';
|
||||
containerChecks =
|
||||
if (cfg.requires.containers != [])
|
||||
then
|
||||
''
|
||||
container_checks() {
|
||||
if ''
|
||||
+ (lib.concatMapStringsSep " && "
|
||||
(container: makeContainerCheck container)
|
||||
cfg.requires.containers)
|
||||
+ ''
|
||||
; then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
''
|
||||
else ''
|
||||
container_checks() {
|
||||
return 0
|
||||
}
|
||||
'';
|
||||
in
|
||||
pkgs.writeShellScript "${name}"
|
||||
''
|
||||
set -euo pipefail
|
||||
${notify-send} "Launching backend.." "Please be patient."
|
||||
${systemctl} --user start ${name}
|
||||
attempts=0
|
||||
success=false
|
||||
|
||||
while [[ $attempts -lt $((20*9)) ]]; do
|
||||
if [[ $(${curl} -sf http://127.0.0.1:${builtins.toString cfg.port} --output /dev/null; printf $?) -eq 0 ]]; then
|
||||
${notify-send} "Backend up." "Launching client.."
|
||||
success=true
|
||||
break
|
||||
else
|
||||
attempts=$((attempts+1))
|
||||
if [[ $(($attempts % 20)) -eq 0 ]]; then
|
||||
${notify-send} "Launching backend.." "Still launching.. ($((attempts/2))s)"
|
||||
fi
|
||||
exit_error() {
|
||||
${notify-send} -w "Failure" $1
|
||||
exit 1
|
||||
}
|
||||
|
||||
${containerChecks}
|
||||
|
||||
${notify-send} "Launching ${name} backend.." "Please be patient."
|
||||
${systemctl} --user start ${name}.target || exit_error "Failed to launch!"
|
||||
|
||||
checks=0
|
||||
until container_checks; do
|
||||
sleep 2
|
||||
checks=$((checks+1))
|
||||
if [ $((checks%10)) -eq 0 ]; then
|
||||
${notify-send} "Waiting for backend."
|
||||
fi
|
||||
if [ $checks -ge 60 ]; then
|
||||
${systemctl} --no-block --user stop ${name}.target
|
||||
exit_error "Failed to launch!"
|
||||
fi
|
||||
sleep 0.5
|
||||
done
|
||||
|
||||
if [[ ! $success ]]; then
|
||||
${notify-send} "Failure" "Failed to launch backend!"
|
||||
${systemctl} --user kill ${name}
|
||||
exit 1
|
||||
fi
|
||||
|
||||
${notify-send} "Launching ${name}.."
|
||||
${dex} -w ~/.nix-profile/share/applications/${name}-client.desktop
|
||||
|
||||
${notify-send} "Goodbye" "Shutting down."
|
||||
${systemctl} --user stop ${name}
|
||||
${systemctl} --user stop ${name}.target
|
||||
exit 0
|
||||
''}";
|
||||
}
|
||||
)
|
||||
cfg;
|
||||
cfg = config.localWebApps;
|
||||
in {
|
||||
options.localWebApps = mkOption {
|
||||
default = {};
|
||||
|
@ -104,29 +183,59 @@ in {
|
|||
default = null;
|
||||
};
|
||||
|
||||
id = mkOption {
|
||||
type = int;
|
||||
description = "Firefox profile ID for the webapp's client";
|
||||
requires = mkOption {
|
||||
type = nullOr (submodule {
|
||||
options = {
|
||||
containers = mkOption {
|
||||
type = listOf str;
|
||||
default = [];
|
||||
};
|
||||
services = mkOption {
|
||||
type = listOf str;
|
||||
default = [];
|
||||
};
|
||||
};
|
||||
});
|
||||
default = null;
|
||||
description = "Containers or services this app requires.";
|
||||
};
|
||||
|
||||
service = mkOption {
|
||||
type = nullOr (submodule {
|
||||
options = {
|
||||
execStartPre = mkOption {
|
||||
type = nullOr str;
|
||||
default = null;
|
||||
};
|
||||
execStart = mkOption {
|
||||
type = nullOr str;
|
||||
default = null;
|
||||
};
|
||||
execStop = mkOption {
|
||||
type = nullOr str;
|
||||
default = null;
|
||||
};
|
||||
};
|
||||
});
|
||||
default = null;
|
||||
description = "Submodule containing exec[StartPre/Start/Stop] commands for any required systemd service";
|
||||
};
|
||||
|
||||
port = mkOption {
|
||||
type = int;
|
||||
description = "Local port the webapp should host on.";
|
||||
};
|
||||
|
||||
service = mkOption {
|
||||
type = attrsOf str;
|
||||
description = "The service settings for systemd user service. Requires at least ExecStart.";
|
||||
};
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
config = {
|
||||
programs.firefox.webapps = make-firefox config.localWebApps;
|
||||
programs.firefox.webapps = make-firefox cfg;
|
||||
|
||||
systemd.user.services = make-systemd config.localWebApps;
|
||||
systemd.user.targets = make-systemd-target cfg;
|
||||
systemd.user.services = (make-systemd-service cfg) // (modify-systemd-services cfg);
|
||||
services.podman.containers = modify-quadlets cfg;
|
||||
|
||||
xdg.desktopEntries = make-xdg config.localWebApps;
|
||||
xdg.desktopEntries = make-xdg cfg;
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue