nix: add module + VM configs

This commit is contained in:
2026-01-12 03:31:38 +09:00
parent f48dc97b09
commit 5cf5b8fbd2
6 changed files with 333 additions and 6 deletions

4
.gitignore vendored
View File

@@ -22,4 +22,6 @@ dist/
result
config.toml
config.toml
*.qcow2

View File

@@ -44,3 +44,29 @@ Unless provided through the `--config` flag, program will automatically look for
- `/var/lib/worblehat/config.toml`
Run `uv run worblehat --help` for more info
## Development with nix
> [!NOTE]
> We have created some nix code to generate a QEMU VM with a setup similar to a production deployment
> There is not necessarily any VMs running in a production setup, and if so then at least not this VM.
> It is mainly there for easy access to interactive testing, as well as for testing the NixOS module.
You can easily start developing this with nix, by running the test VM:
```console
nix run .#vm
# Or if you need access to a proper shell in the VM as well:
nix run .#vm-non-kiosk
```
You can also build the nix package, or run the executable directly:
```
# Build package
nix build .#
# Run the executable (after building package)
nix run .#
```

View File

@@ -47,14 +47,35 @@
in {
apps = forAllSystems (system: pkgs: let
mkApp = package: {
mkApp = program: description: {
type = "app";
program = lib.getExe package;
program = toString program;
meta = {
inherit description;
};
};
mkVm = name: mkApp "${self.nixosConfigurations.${name}.config.system.build.vm}/bin/run-nixos-vm";
in {
default = mkApp self.packages.${system}.default;
default = self.apps.${system}.worblehat;
worblehat = mkApp (lib.getExe self.packages.${system}.worblehat) "Run worblehat without any setup";
vm = mkVm "vm" "Start a NixOS VM with worblehat installed in kiosk-mode";
vm-non-kiosk = mkVm "vm-non-kiosk" "Start a NixOS VM with worblehat installed in nonkiosk-mode";
});
nixosModules.default = import ./nix/module.nix;
nixosConfigurations = {
vm = import ./nix/nixos-configurations/vm.nix { inherit self nixpkgs; };
vm-non-kiosk = import ./nix/nixos-configurations/vm-non-kiosk.nix { inherit self nixpkgs; };
};
overlays = {
default = self.overlays.worblehat;
worblehat = final: prev: {
inherit (self.packages.${prev.stdenv.hostPlatform.system}) worblehat;
};
};
devShells = forAllSystems (_: pkgs: {
default = pkgs.mkShell {
packages = with pkgs; [
@@ -66,8 +87,6 @@
};
});
overlays.default = final: prev: self.packages.${final.system};
packages = forAllSystems (system: pkgs: {
default = self.packages.${system}.worblehat;
worblehat = let

191
nix/module.nix Normal file
View File

@@ -0,0 +1,191 @@
{ config, pkgs, lib, ... }: let
cfg = config.services.worblehat;
format = pkgs.formats.toml { };
in {
options.services.worblehat = {
enable = lib.mkEnableOption "worblehat, the little kiosk library";
package = lib.mkPackageOption pkgs "worblehat" { };
screenPackage = lib.mkPackageOption pkgs "screen" { };
createLocalDatabase = lib.mkEnableOption "" // {
description = ''
Whether to set up a local postgres database automatically.
::: {.note}
You must set up postgres manually before enabling this option.
:::
'';
};
kioskMode = lib.mkEnableOption "" // {
description = ''
Whether to let worblehat take over the entire machine.
This will restrict the machine to a single TTY and make the program unquittable.
You can still get access to PTYs via SSH and similar, if enabled.
'';
};
limitScreenHeight = lib.mkOption {
type = with lib.types; nullOr ints.unsigned;
default = null;
example = 42;
description = ''
If set, limits the height of the screen worblehat uses to the given number of lines.
'';
};
limitScreenWidth = lib.mkOption {
type = with lib.types; nullOr ints.unsigned;
default = null;
example = 80;
description = ''
If set, limits the width of the screen worblehat uses to the given number of columns.
'';
};
settings = lib.mkOption {
description = "Configuration for worblehat";
default = { };
type = lib.types.submodule {
freeformType = format.type;
};
};
};
config = lib.mkIf cfg.enable (lib.mkMerge [
{
services.worblehat.settings = lib.pipe ../config-template.toml [
builtins.readFile
builtins.fromTOML
(x: lib.recursiveUpdate x {
flask = {
TESTING = false;
DEBUG = false;
};
})
(lib.mapAttrsRecursive (_: lib.mkDefault))
];
}
{
environment.systemPackages = [ cfg.package ];
environment.etc."worblehat/config.toml".source = format.generate "worblehat-config.toml" cfg.settings;
users = {
users.worblehat = {
group = "worblehat";
isNormalUser = true;
};
groups.worblehat = { };
};
services.worblehat.settings.database.type = "postgresql";
services.worblehat.settings.database.postgresql = {
host = "/run/postgresql";
};
services.postgresql = lib.mkIf cfg.createLocalDatabase {
ensureDatabases = [ "worblehat" ];
ensureUsers = [{
name = "worblehat";
ensureDBOwnership = true;
ensureClauses.login = true;
}];
};
systemd.services.worblehat-setup-database = lib.mkIf cfg.createLocalDatabase {
description = "Dibbler database setup";
wantedBy = [ "default.target" ];
after = [ "postgresql.service" ];
unitConfig = {
ConditionPathExists = "!/var/lib/worblehat/.db-setup-done";
};
serviceConfig = {
Type = "oneshot";
ExecStart = "${lib.getExe cfg.package} --config /etc/worblehat/config.toml create-db";
ExecStartPost = "${lib.getExe' pkgs.coreutils "touch"} /var/lib/worblehat/.db-setup-done";
StateDirectory = "worblehat";
User = "worblehat";
Group = "worblehat";
};
};
}
(lib.mkIf cfg.kioskMode {
boot.kernelParams = [
"console=tty1"
];
users.users.worblehat = {
extraGroups = [ "lp" ];
shell = (pkgs.writeShellScriptBin "login-shell" "${lib.getExe cfg.screenPackage} -x worblehat") // {
shellPath = "/bin/login-shell";
};
};
services.worblehat.settings.general = {
quit_allowed = false;
stop_allowed = false;
};
systemd.services.worblehat-screen-session = {
description = "Worblehat Screen Session";
wantedBy = [
"default.target"
];
after = if cfg.createLocalDatabase then [
"postgresql.service"
"worblehat-setup-database.service"
] else [
"network.target"
];
serviceConfig = {
Type = "forking";
RemainAfterExit = false;
Restart = "always";
RestartSec = "5s";
SuccessExitStatus = 1;
User = "worblehat";
Group = "worblehat";
ExecStartPre = "-${lib.getExe cfg.screenPackage} -X -S worblehat kill";
ExecStart = let
screenArgs = lib.escapeShellArgs [
# -dm creates the screen in detached mode without accessing it
"-dm"
# Session name
"-S"
"worblehat"
# Set optimal output mode instead of VT100 emulation
"-O"
# Enable login mode, updates utmp entries
"-l"
];
worblehatArgs = lib.cli.toCommandLineShellGNU { } {
config = "/etc/worblehat/config.toml";
};
in "${lib.getExe cfg.screenPackage} ${screenArgs} ${lib.getExe cfg.package} ${worblehatArgs} cli";
ExecStartPost =
lib.optionals (cfg.limitScreenWidth != null) [
"${lib.getExe cfg.screenPackage} -X -S worblehat width ${toString cfg.limitScreenWidth}"
]
++ lib.optionals (cfg.limitScreenHeight != null) [
"${lib.getExe cfg.screenPackage} -X -S worblehat height ${toString cfg.limitScreenHeight}"
];
};
};
services.getty.autologinUser = "worblehat";
})
]);
}

View File

@@ -0,0 +1,54 @@
{ self, nixpkgs, ... }:
nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
pkgs = import nixpkgs {
system = "x86_64-linux";
overlays = [
self.overlays.worblehat
];
};
modules = [
"${nixpkgs}/nixos/modules/virtualisation/qemu-vm.nix"
"${nixpkgs}/nixos/tests/common/user-account.nix"
self.nixosModules.default
({ config, ... }: {
system.stateVersion = config.system.nixos.release;
virtualisation.graphics = false;
users.motd = ''
=================================
Welcome to the worblehat non-kiosk vm!
Try running:
${config.services.worblehat.package.meta.mainProgram} cli
Password for worblehat is 'worblehat'
To exit, press Ctrl+A, then X
=================================
'';
users.users.worblehat = {
isNormalUser = true;
password = "worblehat";
extraGroups = [ "wheel" ];
};
services.getty.autologinUser = "worblehat";
programs.vim = {
enable = true;
defaultEditor = true;
};
services.postgresql.enable = true;
services.worblehat = {
enable = true;
createLocalDatabase = true;
};
})
];
}

View File

@@ -0,0 +1,35 @@
{ self, nixpkgs, ... }:
nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
pkgs = import nixpkgs {
system = "x86_64-linux";
overlays = [
self.overlays.default
];
};
modules = [
"${nixpkgs}/nixos/modules/virtualisation/qemu-vm.nix"
"${nixpkgs}/nixos/tests/common/user-account.nix"
self.nixosModules.default
({ config, ... }: {
system.stateVersion = config.system.nixos.release;
virtualisation.graphics = false;
services.postgresql.enable = true;
services.worblehat = {
enable = true;
createLocalDatabase = true;
kioskMode = true;
settings = {
flask = {
TESTING = true;
DEBUG = true;
};
};
};
})
];
}