From 4ed68ff05c4b2cff8cc5ab51e6e4dc618c347361 Mon Sep 17 00:00:00 2001 From: h7x4 Date: Tue, 6 Jan 2026 14:01:36 +0900 Subject: [PATCH] nix: yeet skrott, massive module modifications tm, wrap package and more Sorry for the kinda big commit that does everything at once This change does the following: - yeets skrott and skrot-specific settings from the NixOS module, - adds a bunch more settings and generalizations to the NixOS module, - adds two VM NixOS configurations for interactive testing - wraps the nix package so that `less` is always present in `$PATH` - yeah, that's about it kthxbye --- .gitignore | 2 + README.md | 30 ++-- flake.nix | 43 ++--- nix/module.nix | 204 ++++++++++++++++------ nix/nixos-configurations/vm-non-kiosk.nix | 54 ++++++ nix/nixos-configurations/vm.nix | 29 +++ nix/{dibbler.nix => package.nix} | 18 +- nix/skrott.nix | 27 --- 8 files changed, 292 insertions(+), 115 deletions(-) create mode 100644 nix/nixos-configurations/vm-non-kiosk.nix create mode 100644 nix/nixos-configurations/vm.nix rename nix/{dibbler.nix => package.nix} (62%) delete mode 100644 nix/skrott.nix diff --git a/.gitignore b/.gitignore index 26bf605..4e4e580 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,5 @@ dist test.db .ruff_cache + +*.qcow2 diff --git a/README.md b/README.md index 55f7063..ac44620 100644 --- a/README.md +++ b/README.md @@ -21,29 +21,27 @@ Deretter kan du kjøre programmet med ```console python -m dibbler -c example-config.ini create-db +python -m dibbler -c example-config.ini seed-data python -m dibbler -c example-config.ini loop ``` ## Nix -### Bygge nytt image +Du kan enklest komme i gang med nix-utvikling ved å kjøre VM-en: -For å bygge et image trenger du en builder som takler å bygge for arkitekturen du skal lage et image for. +```console +nix run .#vm -(Eller be til gudene om at cross compile funker) +# Eller hvis du trenger tilgang til terminalen i VM-en også: +nix run .#vm-non-kiosk +``` -Flaket exposer en modul som autologger inn med en bruker som automatisk kjører dibbler, og setter opp et minimalistisk miljø. +Du kan også bygge pakken manuelt, eller kjøre den direkte: -Før du bygger imaget burde du kopiere og endre `example-config.ini` lokalt til å inneholde instillingene dine. **NB: Denne kommer til å ligge i nix storen, ikke si noe her som du ikke vil at moren din skal høre.** +```console +nix build .#dibbler -Du kan også endre hvilken config-fil som blir brukt direkte i pakken eller i modulen. - -Se eksempelet for hvordan skrot er satt opp i `flake.nix` og `nix/skrott.nix` - -### Bygge image for skrot -Skrot har et image definert i flake.nix: - -1. endre `example-config.ini` -2. `nix build .#images.skrot` -3. ??? -4. non-profit +nix run .# -- --config example-config.ini create-db +nix run .# -- --config example-config.ini seed-data +nix run .# -- --config example-config.ini loop +``` diff --git a/flake.nix b/flake.nix index 4410f60..d525fd9 100644 --- a/flake.nix +++ b/flake.nix @@ -17,21 +17,31 @@ pkgs = nixpkgs.legacyPackages.${system}; in f system pkgs); in { - packages = forAllSystems (system: pkgs: { - default = self.packages.${system}.dibbler; - dibbler = pkgs.callPackage ./nix/dibbler.nix { - python3Packages = pkgs.python312Packages; + apps = let + mkApp = program: description: { + type = "app"; + program = toString program; + meta = { + inherit description; + }; }; - skrot = self.nixosConfigurations.skrot.config.system.build.sdImage; - }); - - apps = forAllSystems (system: pkgs: { + mkVm = name: mkApp "${self.nixosConfigurations.${name}.config.system.build.vm}/bin/run-nixos-vm"; + in forAllSystems (system: pkgs: { default = self.apps.${system}.dibbler; dibbler = flake-utils.lib.mkApp { drv = self.packages.${system}.dibbler; }; + vm = mkVm "vm" "Start a NixOS VM with dibbler installed in kiosk-mode"; + vm-non-kiosk = mkVm "vm-non-kiosk" "Start a NixOS VM with dibbler 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.dibbler; dibbler = final: prev: { @@ -46,20 +56,11 @@ }; }); - # Note: using the module requires that you have applied the overlay first - nixosModules.default = import ./nix/module.nix; - - nixosConfigurations.skrot = nixpkgs.lib.nixosSystem (rec { - system = "aarch64-linux"; - pkgs = import nixpkgs { - inherit system; - overlays = [ self.overlays.dibbler ]; + packages = forAllSystems (system: pkgs: { + default = self.packages.${system}.dibbler; + dibbler = pkgs.callPackage ./nix/package.nix { + python3Packages = pkgs.python312Packages; }; - modules = [ - (nixpkgs + "/nixos/modules/installer/sd-card/sd-image-aarch64.nix") - self.nixosModules.default - ./nix/skrott.nix - ]; }); }; } diff --git a/nix/module.nix b/nix/module.nix index 5f6dce2..4eb6726 100644 --- a/nix/module.nix +++ b/nix/module.nix @@ -8,6 +8,45 @@ in { package = lib.mkPackageOption pkgs "dibbler" { }; + 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 dibbler 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 dibbler 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 dibbler uses to the given number of columns. + ''; + }; + settings = lib.mkOption { description = "Configuration for dibbler"; default = { }; @@ -17,61 +56,128 @@ in { }; }; - config = let - screen = "${pkgs.screen}/bin/screen"; - in lib.mkIf cfg.enable { - services.dibbler.settings = lib.pipe ../example-config.ini [ - builtins.readFile - builtins.fromTOML - (lib.mapAttrsRecursive (_: lib.mkDefault)) - ]; + config = lib.mkIf cfg.enable (lib.mkMerge [ + { + services.dibbler.settings = lib.pipe ../example-config.ini [ + builtins.readFile + builtins.fromTOML + (lib.mapAttrsRecursive (_: lib.mkDefault)) + ]; + } + { + environment.systemPackages = [ cfg.package ]; - boot = { - consoleLogLevel = 0; - enableContainers = false; - loader.grub.enable = false; - }; + environment.etc."dibbler/dibbler.conf".source = format.generate "dibbler.conf" cfg.settings; - users = { - groups.dibbler = { }; - users.dibbler = { - group = "dibbler"; + users = { + users.dibbler = { + group = "dibbler"; + isNormalUser = true; + }; + groups.dibbler = { }; + }; + + services.dibbler.settings.database.url = lib.mkIf cfg.createLocalDatabase "postgresql://dibbler?host=/run/postgresql"; + + services.postgresql = lib.mkIf cfg.createLocalDatabase { + ensureDatabases = [ "dibbler" ]; + ensureUsers = [{ + name = "dibbler"; + ensureDBOwnership = true; + ensureClauses.login = true; + }]; + }; + + systemd.services.dibbler-setup-database = lib.mkIf cfg.createLocalDatabase { + description = "Dibbler database setup"; + wantedBy = [ "default.target" ]; + after = [ "postgresql.service" ]; + unitConfig = { + ConditionPathExists = "!/var/lib/dibbler/.db-setup-done"; + }; + serviceConfig = { + Type = "oneshot"; + ExecStart = "${lib.getExe cfg.package} --config /etc/dibbler/dibbler.conf create-db"; + ExecStartPost = "${lib.getExe' pkgs.coreutils "touch"} /var/lib/dibbler/.db-setup-done"; + StateDirectory = "dibbler"; + + User = "dibbler"; + Group = "dibbler"; + }; + }; + } + (lib.mkIf cfg.kioskMode { + boot.kernelParams = [ + "console=tty1" + ]; + + + users.users.dibbler = { extraGroups = [ "lp" ]; - isNormalUser = true; - shell = (pkgs.writeShellScriptBin "login-shell" "${screen} -x dibbler") // {shellPath = "/bin/login-shell";}; + shell = (pkgs.writeShellScriptBin "login-shell" "${lib.getExe cfg.screenPackage} -x dibbler") // { + shellPath = "/bin/login-shell"; + }; }; - }; - systemd.services.screen-daemon = { - description = "Dibbler service screen"; - wantedBy = [ "default.target" ]; - serviceConfig = { - ExecStartPre = "-${screen} -X -S dibbler kill"; - ExecStart = let - config = format.generate "dibbler-config.ini" cfg.settings; - in "${screen} -dmS dibbler -O -l ${cfg.package}/bin/dibbler --config ${config} loop"; - ExecStartPost = "${screen} -X -S dibbler width 42 80"; - User = "dibbler"; - Group = "dibbler"; - Type = "forking"; - RemainAfterExit = false; - Restart = "always"; - RestartSec = "5s"; - SuccessExitStatus = 1; + services.dibbler.settings.general = { + quit_allowed = false; + stop_allowed = false; }; - }; - # https://github.com/NixOS/nixpkgs/issues/84105 - boot.kernelParams = [ - "console=ttyUSB0,9600" - "console=tty1" - ]; - systemd.services."serial-getty@ttyUSB0" = { - enable = true; - wantedBy = [ "getty.target" ]; # to start at boot - serviceConfig.Restart = "always"; # restart when session is closed - }; + systemd.services.dibbler-screen-session = { + description = "Dibbler Screen Session"; + wantedBy = [ + "default.target" + ]; + after = if cfg.createLocalDatabase then [ + "postgresql.service" + "dibbler-setup-database.service" + ] else [ + "network.target" + ]; + serviceConfig = { + Type = "forking"; + RemainAfterExit = false; + Restart = "always"; + RestartSec = "5s"; + SuccessExitStatus = 1; - services.getty.autologinUser = lib.mkForce "dibbler"; - }; + User = "dibbler"; + Group = "dibbler"; + + ExecStartPre = "-${lib.getExe cfg.screenPackage} -X -S dibbler kill"; + ExecStart = let + screenArgs = lib.escapeShellArgs [ + # -dm creates the screen in detached mode without accessing it + "-dm" + + # Session name + "-S" + "dibbler" + + # Set optimal output mode instead of VT100 emulation + "-O" + + # Enable login mode, updates utmp entries + "-l" + ]; + + dibblerArgs = lib.cli.toCommandLineShellGNU { } { + config = "/etc/dibbler/dibbler.conf"; + }; + + in "${lib.getExe cfg.screenPackage} ${screenArgs} ${lib.getExe cfg.package} ${dibblerArgs} loop"; + ExecStartPost = + lib.optionals (cfg.limitScreenWidth != null) [ + "${lib.getExe cfg.screenPackage} -X -S dibbler width ${toString cfg.limitScreenWidth}" + ] + ++ lib.optionals (cfg.limitScreenHeight != null) [ + "${lib.getExe cfg.screenPackage} -X -S dibbler height ${toString cfg.limitScreenHeight}" + ]; + }; + }; + + services.getty.autologinUser = "dibbler"; + }) + ]); } diff --git a/nix/nixos-configurations/vm-non-kiosk.nix b/nix/nixos-configurations/vm-non-kiosk.nix new file mode 100644 index 0000000..b55efe5 --- /dev/null +++ b/nix/nixos-configurations/vm-non-kiosk.nix @@ -0,0 +1,54 @@ +{ self, nixpkgs, ... }: +nixpkgs.lib.nixosSystem { + system = "x86_64-linux"; + pkgs = import nixpkgs { + system = "x86_64-linux"; + overlays = [ + self.overlays.dibbler + ]; + }; + 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 dibbler non-kiosk vm! + + Try running: + ${config.services.dibbler.package.meta.mainProgram} loop + + Password for dibbler is 'dibbler' + + To exit, press Ctrl+A, then X + ================================= + ''; + + users.users.dibbler = { + isNormalUser = true; + password = "dibbler"; + extraGroups = [ "wheel" ]; + }; + + services.getty.autologinUser = "dibbler"; + + programs.vim = { + enable = true; + defaultEditor = true; + }; + + services.postgresql.enable = true; + + services.dibbler = { + enable = true; + createLocalDatabase = true; + }; + }) + ]; +} diff --git a/nix/nixos-configurations/vm.nix b/nix/nixos-configurations/vm.nix new file mode 100644 index 0000000..3ff2e75 --- /dev/null +++ b/nix/nixos-configurations/vm.nix @@ -0,0 +1,29 @@ +{ 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.dibbler = { + enable = true; + createLocalDatabase = true; + kioskMode = true; + }; + }) + ]; +} diff --git a/nix/dibbler.nix b/nix/package.nix similarity index 62% rename from nix/dibbler.nix rename to nix/package.nix index 40f4aff..96c8018 100644 --- a/nix/dibbler.nix +++ b/nix/package.nix @@ -1,6 +1,7 @@ { lib , python3Packages -, fetchFromGitHub +, makeWrapper +, less }: let pyproject = builtins.fromTOML (builtins.readFile ../pyproject.toml); @@ -16,7 +17,10 @@ python3Packages.buildPythonApplication { # https://github.com/NixOS/nixpkgs/issues/285234 dontCheckRuntimeDeps = true; - nativeBuildInputs = with python3Packages; [ setuptools ]; + nativeBuildInputs = with python3Packages; [ + setuptools + makeWrapper + ]; propagatedBuildInputs = with python3Packages; [ brother-ql matplotlib @@ -24,4 +28,14 @@ python3Packages.buildPythonApplication { python-barcode sqlalchemy ]; + + postInstall = '' + wrapProgram $out/bin/dibbler \ + --prefix PATH : "${lib.makeBinPath [ less ]}" + ''; + + meta = { + description = "The little kiosk that could"; + mainProgram = "dibbler"; + }; } diff --git a/nix/skrott.nix b/nix/skrott.nix deleted file mode 100644 index 516d718..0000000 --- a/nix/skrott.nix +++ /dev/null @@ -1,27 +0,0 @@ -{...}: { - system.stateVersion = "25.05"; - - services.dibbler.enable = true; - - networking = { - hostName = "skrot"; - domain = "pvv.ntnu.no"; - nameservers = [ "129.241.0.200" "129.241.0.201" ]; - defaultGateway = "129.241.210.129"; - interfaces.eth0 = { - useDHCP = false; - ipv4.addresses = [{ - address = "129.241.210.235"; - prefixLength = 25; - }]; - }; - }; - # services.resolved.enable = true; - # systemd.network.enable = true; - # systemd.network.networks."30-network" = { - # matchConfig.Name = "*"; - # DHCP = "no"; - # address = [ "129.241.210.235/25" ]; - # gateway = [ "129.241.210.129" ]; - # }; -}