diff options
Diffstat (limited to 'flake.nix')
-rw-r--r-- | flake.nix | 279 |
1 files changed, 12 insertions, 267 deletions
diff --git a/flake.nix b/flake.nix index 5eae2da..d437310 100644 --- a/flake.nix +++ b/flake.nix @@ -18,285 +18,30 @@ supportedSystems = ["aarch64-linux" "x86_64-linux"]; forAllSystems = f: flake-utils.lib.eachSystem supportedSystems f; in { - nixosModule = { config, pkgs, lib, ... }: with lib; let - cfg = config.services.kittybox; - in { - options = { - services.kittybox = { - enable = mkOption { - type = types.bool; - default = false; - description = '' - Whether to enable Kittybox, the IndieWeb blogging solution. - ''; - }; - package = mkOption { - type = types.package; - default = self.packages.${config.nixpkgs.localSystem.system}.kittybox; - defaultText = "<kittybox package from the upstream flake>"; - description = "Which Kittybox derivation to use."; - }; - - bind = mkOption { - type = types.nullOr types.str; - default = "127.0.0.1"; - description = "The host for Kittybox to bind to."; - example = "192.168.1.100"; - }; - port = mkOption { - type = types.int; - default = 8080; - description = "The port for Kittybox to listen at."; - example = 16420; - }; - logLevel = mkOption { - type = types.str; - default = "warn"; - example = "info"; - description = "Specify the server verbosity level. Uses RUST_LOG environment variable internally."; - }; - backendUri = mkOption { - type = types.str; - default = "file:///var/lib/kittybox/data"; - example = "redis://192.168.1.200:6379"; - description = '' - Set the backend used for storing data. Available backends are: - - file:// - static folder backend (recommended) - - redis:// - Redis backend - - Make sure that if you are using the file backend, the state - directory is accessible by Kittybox. By default, the unit config - uses DynamicUser=true, which prevents the unit from accessing - data outside of its directory. It is recommended to use a - bind-mount to /var/lib/private/kittybox if you require the state - directory to reside elsewhere. - ''; - }; - tokenEndpoint = mkOption { - type = types.str; - example = "https://tokens.indieauth.com/token"; - description = "Token endpoint to use for authenticating Micropub requests. Use the example if you are unsure."; - }; - authorizationEndpoint = mkOption { - type = types.str; - example = "https://indieauth.com/auth"; - description = "Authorization endpoint to use to authenticate the user. You can use the default if you are unsure."; - }; - mediaEndpoint = mkOption { - type = types.nullOr types.str; - default = null; - description = "The URL of a media endpoint to announce when asked by a Micropub client. Strongly recommended if you plan to upload images."; - }; - microsubServer = mkOption { - type = types.nullOr types.str; - default = null; - example = "https://aperture.p3k.io/microsub/69420"; - description = '' - The URL of your Microsub server, which saves feeds for you - and allows you to browse Web content from one place. Try - https://aperture.p3k.io/ if you don't have one yet! - ''; - }; - webmentionEndpoint = mkOption { - type = types.nullOr types.str; - default = null; - example = "https://webmention.io/example.com/webmention"; - description = '' - The URL of your webmention endpoint, which allows you to - receive notifications about your site's content being featured - or interacted with elsewhere on the IndieWeb. - - By default Kittybox expects the Webmention endpoint to post - updates using an internal token. kittybox-webmention is an - endpoint capable of that. - ''; - }; - internalTokenFile = mkOption { - type = types.nullOr types.str; - default = null; - example = "/run/secrets/kittybox-shared-secret"; - description = "A shared secret that will, when passed, allow unlimited editing access to database. Keep it safe."; - }; - cookieSecretFile = mkOption { - type = types.str; - default = "/var/lib/kittybox/cookie_secret_key"; - example = "/run/secrets/kittybox-cookie-secret"; - description = "A secret file to encrypt cookies with the contents of. Should be at least 32 bytes in length. A random persistent file will be generated if this variable is left untouched."; - }; - }; - }; - config = lib.mkIf cfg.enable { - systemd.services.kittybox = { - description = "An IndieWeb-enabled blog engine"; - - wantedBy = [ "multi-user.target" ]; - after = [ "network.target" ]; - - restartTriggers = [ - cfg.package - cfg.backendUri cfg.tokenEndpoint - cfg.authorizationEndpoint - cfg.internalTokenFile - cfg.bind cfg.port - cfg.cookieSecretFile - ]; - - environment = { - SERVE_AT = "${cfg.bind}:${builtins.toString cfg.port}"; - AUTHORIZATION_ENDPOINT = cfg.authorizationEndpoint; - TOKEN_ENDPOINT = cfg.tokenEndpoint; - MEDIA_ENDPOINT = cfg.mediaEndpoint; - MICROSUB_ENDPOINT = cfg.microsubServer; - WEBMENTION_ENDPOINT = cfg.webmentionEndpoint; - #REDIS_URI = if (cfg.redisUri == null) then "redis://127.0.0.1:6379/" else cfg.redisUri; - BACKEND_URI = cfg.backendUri; - RUST_LOG = "${cfg.logLevel}"; - COOKIE_SECRET_FILE = "${cfg.cookieSecretFile}"; - }; - - script = '' - ${lib.optionalString (cfg.internalTokenFile != null) '' - if [[ -f ${cfg.internalTokenFile} ]]; then - export KITTYBOX_INTERNAL_TOKEN=$(${pkgs.coreutils}/bin/cat ${cfg.internalTokenFile}) - fi - ''} - if [[ ${cfg.cookieSecretFile} == /var/lib/kittybox/cookie_secret_key && ! -f /var/lib/kittybox/cookie_secret_key ]]; then - cat /dev/urandom | tr -Cd '[:alnum:]' | head -c 128 > /var/lib/kittybox/cookie_secret_key - fi - exec ${cfg.package}/bin/kittybox - ''; - - serviceConfig = { - DynamicUser = true; - StateDirectory = "kittybox"; - }; - }; - }; - }; + nixosModules.default = import ./configuration.nix self; } // forAllSystems (system: let pkgs = nixpkgs.legacyPackages.${system}; - packages = { - kittybox = { stdenv, lib, runCommandNoCC, openssl, zlib, pkg-config, protobuf, naersk-lib, lld, mold }: - naersk-lib.buildPackage { - pname = "kittybox"; - version = "0.1.0"; - - src = runCommandNoCC "kittybox-src" {} '' - mkdir -p $out - cp -r ${./Cargo.toml} $out/Cargo.toml - cp -r ${./Cargo.lock} $out/Cargo.lock - cp -r ${./src} $out/src - cp -r ${./templates} $out/templates - cp -r ${./util} $out/util - cp -r ${./fonts} $out/fonts - ''; - - checkInputs = [ openssl.dev zlib ]; - nativeBuildInputs = [ pkg-config protobuf ]; - nativeCheckInputs = [ pkg-config ]; - - doCheck = stdenv.hostPlatform == stdenv.targetPlatform; - - meta = with lib.meta; { - maintainers = with lib.maintainers; [ vikanezrimaya ]; - platforms = supportedSystems; - mainProgram = "kittybox"; - }; - }; + naersk-lib = naersk.lib.${system}.override { + inherit (pkgs) rustc cargo; }; in { - packages = let - naersk-lib = naersk.lib.${system}.override { - inherit (pkgs) rustc cargo; - }; - in { - kittybox = pkgs.callPackage packages.kittybox { inherit naersk-lib; }; + packages = { + kittybox = pkgs.callPackage ./kittybox.nix { inherit naersk-lib; }; + default = self.packages.${system}.kittybox; }; - defaultPackage = self.packages.${system}.kittybox; checks = { kittybox = self.packages.${system}.kittybox; distributed-test = pkgs.nixosTest (import ./distributed-test.nix self); - nixos-test = (pkgs.nixosTest ({ lib }: { - name = "nixos-kittybox"; - - nodes = { - kittybox = { config, pkgs, lib, ... }: { - imports = [ self.nixosModule ]; - - services.kittybox = { - enable = true; - # It never actually contacts those endpoints anyway unless we use Micropub so it's fine! - # TODO: Once we have self-hosted software for those endpoints, - # make an e2e test for common workflows (e.g. making a post) - tokenEndpoint = "https://example.com"; - authorizationEndpoint = "https://example.com"; - logLevel = "info,kittybox=debug,retainer::cache=warn,h2=warn,rustls=warn"; - }; - - environment.systemPackages = with pkgs; [ - curl - ]; - }; - }; - - testScript = '' - with subtest("Verify that Kittybox started correctly..."): - kittybox.wait_for_open_port(8080) - kittybox.succeed("curl --silent http://localhost:8080/micropub") - - with subtest("Onboarding should correctly work..."): - kittybox.copy_from_host("${./onboarding.json}", "/root/onboarding.json") - kittybox.succeed("curl -vvv http://localhost:8080/onboarding -d@/root/onboarding.json -H 'Content-Type: application/json'") - # Testing for a known string is the easiest way to determine that the onboarding worked - kittybox.succeed("curl --silent http://localhost:8080/ | grep 'vestige of the past long gone'") - ''; - })); - dockerContainer = pkgs.dockerTools.buildImage { - name = "kittybox"; - tag = self.rev or "development"; - created = let - date = self.lastModifiedDate; - in builtins.concatStringsSep "" [ - (builtins.substring 0 4 date) - "-" - (builtins.substring 4 2 date) - "-" - (builtins.substring 6 2 date) - "T" - (builtins.substring 8 2 date) - ":" - (builtins.substring 10 2 date) - ":" - (builtins.substring 12 2 date) - "Z" - ]; - - runAsRoot = '' - #!${pkgs.runtimeShell} - ${pkgs.dockerTools.shadowSetup} - groupadd -r kittybox - useradd -r -g kittybox kittybox - mkdir -p /data - chown kittybox:kittybox /data - ''; - - config = { - Cmd = [ "${self.packages.${system}.kittybox}/bin/kittybox" ]; - Env = [ - "SERVE_AT=0.0.0.0:8080" - "BACKEND_URI=file:///data" - "RUST_LOG=info" - ]; - WorkingDir = "/data"; - Volumes = { "/data" = {}; }; - User = "kittybox"; - ExposedPorts = { "8080" = {}; }; - }; + nixos-test = pkgs.nixosTest (import ./smoke-test.nix self); + dockerContainer = pkgs.callPackage ./docker.nix { + inherit (self.packages.${system}) kittybox; + rev = self.rev or "development"; + inherit (self) lastModifiedDate; }; }; - devShell = pkgs.mkShell { + devShells.default = pkgs.mkShell { name = "rust-dev-shell"; nativeBuildInputs = with pkgs; [ |