about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorBenedikt Peetz <benedikt.peetz@b-peetz.de>2025-06-06 21:34:09 +0200
committerBenedikt Peetz <benedikt.peetz@b-peetz.de>2025-06-06 21:34:09 +0200
commitc5f4fd12735673831ead5faca8d9ad410d77a938 (patch)
treebbdeb8c5e328740f4ff64a001e0ce9e33f450900
parentfix(back::web::main): Pretty print error, on failed connection accept (diff)
downloadback-c5f4fd12735673831ead5faca8d9ad410d77a938.zip
feat(flake): Export the module and ensure that it works with a test
-rw-r--r--flake.nix17
-rw-r--r--nix/module.nix41
-rw-r--r--nix/package.nix1
-rw-r--r--tests/base.nix296
4 files changed, 139 insertions, 216 deletions
diff --git a/flake.nix b/flake.nix
index b6df3c8..cf3fd77 100644
--- a/flake.nix
+++ b/flake.nix
@@ -38,14 +38,19 @@
     flake-utils,
     treefmt-nix,
     ...
-  }:
-    flake-utils.lib.eachDefaultSystem (system: let
-      pkgs = nixpkgs.legacyPackages."x86_64-linux";
+  }: let
+    pkgs = nixpkgs.legacyPackages."x86_64-linux";
 
+    back = pkgs.callPackage ./nix/package.nix {};
+    module = import ./nix/module.nix {extraPackages = {inherit back;};};
+  in
+    (flake-utils.lib.eachDefaultSystem (system: let
       treefmtEval = import ./treefmt.nix {
         inherit treefmt-nix pkgs;
       };
 
+      nixos-lib = import (nixpkgs + "/nixos/lib") {};
+
       rustfmt = pkgs.writeShellScriptBin "rustfmt" ''
         # Avoid the duplicated edition flag, that rust-analyzer passes.
         if [ "$1" = "--edition" ] && [ "$2" == "2024" ]; then
@@ -57,6 +62,7 @@
     in {
       checks = {
         formatting = treefmtEval.config.build.check self;
+        nixos = pkgs.callPackage ./tests/base.nix {inherit module nixos-lib;};
       };
 
       formatter = treefmtEval.config.build.wrapper;
@@ -75,7 +81,10 @@
           pkgs.reuse
         ];
       };
-    });
+    }))
+    // {
+      nixosModules.default = module;
+    };
 }
 # vim: ts=2
 
diff --git a/nix/module.nix b/nix/module.nix
index eb1257c..82a6bd3 100644
--- a/nix/module.nix
+++ b/nix/module.nix
@@ -8,21 +8,34 @@
 #
 # You should have received a copy of the License along with this program.
 # If not, see <https://www.gnu.org/licenses/agpl.txt>.
-{
+{extraPackages}: {
   config,
   lib,
-  vhackPackages,
   pkgs,
   ...
 }: let
   cfg = config.vhack.back;
 in {
   options.vhack.back = {
-    enable = lib.mkEnableOption "Back issue tracker (inspired by TVL's panettone)";
+    enable = lib.mkEnableOption "Back";
 
-    domain = lib.mkOption {
+    package = lib.mkPackageOption extraPackages "back" {};
+
+    group = lib.mkOption {
+      type = lib.types.str;
+      description = ''
+        The group to run back under.
+
+        This group needs read and write access to the git repositories.
+      '';
+    };
+    user = lib.mkOption {
       type = lib.types.str;
-      description = "The domain to host this `back` instance on.";
+      description = ''
+        The user to run back under.
+
+        This user needs read and write access to the git repositories.
+      '';
     };
 
     settings = {
@@ -44,7 +57,6 @@ in {
       root_url = lib.mkOption {
         type = lib.types.str;
         description = "The url to this instance of back.";
-        default = "https://${cfg.domain}";
       };
     };
   };
@@ -57,19 +69,19 @@ in {
       wantedBy = ["default.target"];
 
       serviceConfig = {
-        ExecStart = "${lib.getExe vhackPackages.back} ${(pkgs.formats.json {}).generate "config.json" cfg.settings}";
+        ExecStart = "${lib.getExe cfg.package} ${(pkgs.formats.json {}).generate "config.json" cfg.settings}";
 
         # Ensure that the service can read the repository
         # FIXME(@bpeetz): This has the implied assumption, that all the exposed git
         # repositories are readable for the git group. This should not be necessary. <2024-12-23>
-        User = "git";
-        Group = "git";
+        Group = cfg.group;
+        User = cfg.user;
 
-        DynamicUser = true;
         Restart = "always";
 
         # Sandboxing
         ProtectSystem = "strict";
+        ReadWritePaths = ["${cfg.settings.scan_path}"];
         ProtectHome = true;
         PrivateTmp = true;
         PrivateDevices = true;
@@ -89,15 +101,8 @@ in {
         PrivateMounts = true;
         # System Call Filtering
         SystemCallArchitectures = "native";
-        SystemCallFilter = ["~@cpu-emulation @debug @keyring @mount @obsolete @privileged @setuid"];
+        SystemCallFilter = [];
       };
     };
-
-    services.nginx.virtualHosts."${cfg.domain}" = {
-      locations."/".proxyPass = "http://127.0.0.1:8000";
-
-      enableACME = true;
-      forceSSL = true;
-    };
   };
 }
diff --git a/nix/package.nix b/nix/package.nix
index b974ea2..bbcf981 100644
--- a/nix/package.nix
+++ b/nix/package.nix
@@ -33,5 +33,6 @@ rustPlatform.buildRustPackage {
 
   meta = {
     mainProgram = "back";
+    description = "An extremely simple git bug visualization system. Inspired by TVL's panettone";
   };
 }
diff --git a/tests/base.nix b/tests/base.nix
index 5aebe6a..4c033c1 100644
--- a/tests/base.nix
+++ b/tests/base.nix
@@ -9,208 +9,116 @@
 # You should have received a copy of the License along with this program.
 # If not, see <https://www.gnu.org/licenses/agpl.txt>.
 {
-  nixos-lib,
-  pkgsUnstable,
-  nixpkgs-unstable,
-  vhackPackages,
   pkgs,
-  extraModules,
-  nixLib,
+  module,
+  nixos-lib,
   ...
-}: let
-  domain = "server";
-
-  sshKeys =
-    import ../../gi/git-server/ssh_keys.nix {inherit pkgs;};
-
-  gitoliteAdminConfSnippet = pkgs.writeText "gitolite-admin-conf-snippet" ''
-    repo CREATOR/[a-zA-Z0-9].*
-      C     = @all
-      RW+   = CREATOR
-      RW    = WRITERS
-      R     = READERS
-      option user-configs = cgit\.owner cgit\.desc cgit\.section cgit\.homepage
-  '';
-in
-  nixos-lib.runTest {
-    hostPkgs = pkgs; # the Nixpkgs package set used outside the VMs
+}:
+nixos-lib.runTest {
+  hostPkgs = pkgs;
+
+  name = "back";
+
+  node = {
+    # Use the nixpkgs as constructed by the `nixpkgs.*` options
+    pkgs = null;
+  };
+
+  nodes = {
+    machine = {config, ...}: {
+      environment.systemPackages = [
+        pkgs.git
+        pkgs.git-bug
+        pkgs.curl
+      ];
+
+      imports = [module];
+
+      vhack = {
+        back = {
+          enable = true;
+
+          user = "root";
+          group = "root";
+
+          settings = {
+            scan_path = "/srv/git/repositories";
+            project_list = "/srv/git/projects.list";
+            root_url = "https://issues.examplec.com";
+          };
+        };
+      };
+    };
+  };
 
-    name = "back";
+  testScript = {nodes, ...}:
+  /*
+  python
+  */
+  ''
+    start_all()
 
-    node = {
-      specialArgs = {inherit pkgsUnstable vhackPackages nixpkgs-unstable nixLib;};
+    with subtest("Create git-bug issues in owner/repo"):
+      machine.succeed("${pkgs.writeShellScript "setup-git-repo" ''
+      set -ex
 
-      # Use the nixpkgs as constructed by the `nixpkgs.*` options
-      pkgs = null;
-    };
+      mkdir --parents /srv/git/repositories
+      cd /srv/git/repositories
 
-    nodes = {
-      server = {config, ...}: {
-        environment.systemPackages = [pkgs.git];
-
-        imports =
-          extraModules
-          ++ [
-            ../../../../modules
-          ];
-
-        vhack = {
-          persist.enable = true;
-          openssh.enable = true;
-          nginx = {
-            enable = true;
-            selfsign = true;
-          };
-          git-server = {
-            enable = true;
-            domain = "git.${domain}";
-            gitolite.adminPubkey = sshKeys.admin.pub;
-          };
-          back = {
-            enable = true;
-            domain = "issues.${domain}";
-
-            settings = {
-              scan_path = "${config.services.gitolite.dataDir}/repositories";
-              project_list = "${config.services.gitolite.dataDir}/projects.list";
-            };
-          };
-        };
-      };
+      echo "owner/repo" > /srv/git/projects.list
 
-      client = {nodes, ...}: {
-        environment.systemPackages = [pkgs.git pkgs.curl pkgs.git-bug pkgs.gawk];
-        programs.ssh.extraConfig = ''
-          Host *
-            UserKnownHostsFile /dev/null
-            StrictHostKeyChecking no
-            # there's nobody around that can input password
-            PreferredAuthentications publickey
-        '';
-        users.users.alice = {isNormalUser = true;};
-        networking.hosts = {
-          "${nodes.server.networking.primaryIPAddress}" = [
-            "git.${domain}"
-            "issues.${domain}"
-            "${domain}"
-          ];
-        };
-      };
-    };
+      mkdir --parents owner/repo_base
+      cd owner/repo_base
+      git init
+
+      git bug user new --avatar "" --email "alice@machine.org" --name "alice" --non-interactive
+
+      git bug bug new \
+      --title "Some bug title" \
+      --message "A long description of the bug. Probably has some code segments, maybe even *markdown* mark_up_, <html> or other things" \
+      --non-interactive
+
+      git bug bug new \
+      --title "Second bug title" \
+      --message "" \
+      --non-interactive
 
-    testScript = {nodes, ...}:
-    /*
-    python
-    */
-    ''
-      start_all()
+      git bug bug new \
+      --title "Third bug title" \
+      --message "" \
+      --non-interactive
 
-      with subtest("can setup ssh keys on client"):
-        client.succeed(
-            "mkdir -p ~root/.ssh",
-            "cp ${sshKeys.admin.priv} ~root/.ssh/id_ed25519",
-            "chmod 600 ~root/.ssh/id_ed25519",
-        )
-        client.succeed(
-            "sudo -u alice mkdir -p ~alice/.ssh",
-            "sudo -u alice cp ${sshKeys.alice.priv} ~alice/.ssh/id_ed25519",
-            "sudo -u alice chmod 600 ~alice/.ssh/id_ed25519",
-        )
-
-      with subtest("gitolite server starts"):
-        server.wait_for_unit("gitolite-init.service")
-        server.wait_for_unit("sshd.service")
-        client.succeed("ssh -n git@git.${domain} info")
-
-
-      with subtest("admin can clone and configure gitolite-admin.git"):
-        client.succeed("${pkgs.writeShellScript "setup-gitolite-admin.git" ''
-        set -xe
-
-        git clone git@git.${domain}:gitolite-admin.git
-        git config --global user.name 'System Administrator'
-        git config --global user.email root\@domain.example
-
-        cp ${sshKeys.alice.pub} gitolite-admin/keydir/alice.pub
-
-        (cd gitolite-admin && git switch -c master && git branch -D main)
-
-        (cd gitolite-admin && git add . && git commit -m 'Add keys for alice' && git push -u origin master)
-        cat ${gitoliteAdminConfSnippet} >> gitolite-admin/conf/gitolite.conf
-        (cd gitolite-admin && git add . && git commit -m 'Add support for wild repos' && git push)
-        (cd gitolite-admin && git push -d origin main)
-      ''}")
-
-      with subtest("alice can create a repo"):
-        client.succeed("sudo -u alice ${pkgs.writeShellScript "alice-create-repo" ''
-        set -xe
-
-        mkdir --parents ./alice/repo1 && cd alice/repo1;
-
-        git init --initial-branch main
-        echo "# Alice's Repo" > README.md
-        git add README.md
-        git -c user.name=Alice -c user.email=alice@domain.example commit -m 'Add readme'
-
-        git remote add origin git@git.${domain}:alice/repo1.git
-        git push --set-upstream origin main
-      ''}")
-
-      with subtest("can setup git-bug issues in alice/repo1"):
-        client.succeed("sudo -u alice ${pkgs.writeShellScript "setup-git-repo" ''
-        set -ex
-
-        cd alice/repo1
-
-        git bug user create --avatar "" --email "alice@server.org" --name "alice" --non-interactive
-
-        git bug add \
-        --title "Some bug title" \
-        --message "A long description of the bug. Probably has some code segments, maybe even *markdown* mark_up_ or other things" \
-        --non-interactive
-
-        git bug add \
-        --title "Second bug title" \
-        --message "" \
-        --non-interactive
-
-        git bug add \
-        --title "Third bug title" \
-        --message "" \
-        --non-interactive
-
-        git bug select "$(git bug ls --format plain | awk '{print $1}' | head -n 1)"
-
-        git bug comment add --message "Some comment message" --non-interactive
-        git bug comment add --message "Second comment message" --non-interactive
-        git bug label add "Test"
-
-        # TODO: This should use `git bug push`, but their ssh implementation is just
-        # too special to work in a VM test <2025-03-08>
-        git push origin +refs/bugs/*
-        git push origin +refs/identities/*
-
-        ssh git@${domain} -- config alice/repo1 --add cgit.owner Alice
-        ssh git@${domain} -- perms alice/repo1 + READERS @all
-      ''}")
-
-      with subtest("back server starts"):
-        server.wait_for_unit("back.service")
-
-      with subtest("client can access the server"):
-        client.succeed("${pkgs.writeShellScript "curl-back" ''
-        set -xe
-
-        curl --insecure --fail --show-error "https://issues.${domain}/alice/repo1.git/issues/open" --output /root/issues.html
-        grep -- 'Second bug title' /root/issues.html
+      git bug bug select "$(git bug bug --format plain | awk '{print $1}' | head -n 1)"
 
-        curl --insecure --fail --show-error "https://issues.${domain}/" --output /root/repos.html
-        grep -- 'repo' /root/repos.html
-        grep -- "&#60;No description&#62;" /root/repos.html
-        grep -- '<span class="user-name">Alice</span>' /root/repos.html
-      ''} >&2")
+      git bug bug comment new --message "Some <comment> message" --non-interactive
+      git bug bug comment new --message "Second comment message" --non-interactive
+      git bug bug label new "Test"
+      git bug bug label new "Test2"
 
-      client.copy_from_vm("/root/issues.html", "");
-      client.copy_from_vm("/root/repos.html", "");
-    '';
-  }
+      cd /srv/git/repositories
+      git clone \
+        --bare \
+        --config 'remote.origin.fetch=+refs/bugs/*:refs/bugs/*' \
+        --config 'remote.origin.fetch=+refs/identities/*:refs/identities/*' \
+        ./owner/repo_base ./owner/repo.git
+    ''}")
+
+    with subtest("back machine starts"):
+      machine.wait_for_unit("back.service")
+
+    with subtest("client can access the machine"):
+      machine.succeed("${pkgs.writeShellScript "curl-back" ''
+      set -xe
+
+      curl --insecure --fail --show-error "http://127.0.0.1:8000/owner/repo/issues/?query=status:open" --output /root/issues.html
+      grep -- 'Second bug title' /root/issues.html
+
+      curl --insecure --fail --show-error "http://127.0.0.1:8000/" --output /root/repos.html
+      grep -- 'repo' /root/repos.html
+      grep -- "Unnamed repository; edit this file 'description' to name the repository." /root/repos.html
+    ''} >&2")
+
+    machine.copy_from_vm("/root/issues.html", "");
+    machine.copy_from_vm("/root/repos.html", "");
+  '';
+}