{ inputs.flake-utils.url = "github:numtide/flake-utils"; inputs.nixpkgs.url = "github:NixOS/nixpkgs"; description = "Pastebin-like service"; outputs = { self, flake-utils, nixpkgs }: flake-utils.lib.eachDefaultSystem (system: let pkgs = import nixpkgs { inherit system; overlays = []; }; in rec { hydraJobs = checks; checks = pkgs.lib.optionalAttrs (builtins.elem system pkgs.lib.systems.doubles.linux) { test = let testing = import (nixpkgs + "/nixos/lib/testing-python.nix") { inherit system; }; in testing.makeTest { nodes = { server = { pkgs, ... }: { imports = [ self.nixosModule ]; config = { environment.systemPackages = [ pkgs.curl ]; services.httpd = { enable = true; adminAddr = "foo@example.org"; extraConfig = '' ProxyPass / unix:///run/paste/gunicorn.sock|http://localhost/ ProxyPassReverse / unix:///run/paste/gunicorn.sock|http://localhost/ ''; }; services.paste.enable = true; }; }; }; testScript = '' start_all() server.wait_for_unit("httpd.service") server.wait_for_unit("paste.service") server.wait_until_succeeds("[ -S /run/paste/gunicorn.sock ]", 10) server.succeed("curl -f http://localhost/") server.succeed("curl -f http://localhost/ | grep -q 'Get the source'") ''; }; }; }) // rec { nixosModule = { config, lib, pkgs, ... }: let cfg = config.services.paste; in { # Necessary for situations where flake gets included multiple times key = builtins.hashString "sha256" (builtins.path { path = self.sourceInfo.outPath; name = "source"; }); options = { services.paste = { enable = lib.mkOption { type = lib.types.bool; default = false; description = "Whether to enable the pastebin service"; }; dataDir = lib.mkOption { type = lib.types.path; default = "/var/lib/paste"; description = '' The directory where Paste stores its data. ''; }; socketsDir = lib.mkOption { type = lib.types.str; default = "/run/paste"; description = "Socket which is used for communication with Paste."; }; webDirectory = lib.mkOption { type = lib.types.nullOr lib.types.str; default = null; description = "Subdirectory url to which the app will be served"; }; # Output variables systemdStateDirectory = lib.mkOption { type = lib.types.str; # Use ReadWritePaths= instead if varDir is outside of /var/lib default = assert lib.strings.hasPrefix "/var/lib/" cfg.dataDir; lib.strings.removePrefix "/var/lib/" cfg.dataDir; description = '' Adjusted paste data directory for systemd ''; readOnly = true; }; systemdRuntimeDirectory = lib.mkOption { type = lib.types.str; # Use ReadWritePaths= instead if socketsDir is outside of /run default = assert lib.strings.hasPrefix "/run/" cfg.socketsDir; lib.strings.removePrefix "/run/" cfg.socketsDir; description = '' Adjusted paste sockets directory for systemd ''; readOnly = true; }; sockets = lib.mkOption { type = lib.types.attrsOf lib.types.path; default = { pid = "${cfg.socketsDir}/gunicorn.pid"; gunicorn = "${cfg.socketsDir}/gunicorn.sock"; }; readOnly = true; description = '' Paste sockets ''; }; }; }; config = lib.mkIf cfg.enable { systemd.services.paste = { description = "Pastebin like service"; after = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; serviceConfig = { Environment = pkgs.lib.optionals (cfg.webDirectory != null) [ "SCRIPT_NAME=${cfg.webDirectory}" ]; Type = "simple"; User = config.services.httpd.user; ExecStart = let python = pkgs.python3.withPackages (p: [p.gunicorn p.flask p.pygments p.python_magic ]); in "${python}/bin/gunicorn -w4 -p ${cfg.sockets.pid} -e PASTE_DIRECTORY=${cfg.dataDir} --bind unix:${cfg.sockets.gunicorn} --chdir ${./paste} paste:app"; Restart = "always"; RestartSec = "5s"; PIDFile = cfg.sockets.pid; RuntimeDirectory = cfg.systemdRuntimeDirectory; StateDirectory = cfg.systemdStateDirectory; StandardOutput = "journal"; StandardError = "inherit"; }; }; }; }; }; }