This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

kannix: run from git worktree with user systemd service

- User systemd with linger, uvicorn --reload from bare repo worktree
- Vendor files (diff2html) fetched and symlinked at startup
- kannix-ctl wrapper script in user packages

+138 -116
+6 -87
flake.lock
··· 132 132 "url": "https://git.clan.lol/clan/data-mesher/archive/main.tar.gz" 133 133 } 134 134 }, 135 - "diff2html-css": { 136 - "flake": false, 137 - "locked": { 138 - "narHash": "sha256-4laymVEnJy8ZQBBsJv5XXRg/6sA/gO1FpY6bBRm9z34=", 139 - "type": "file", 140 - "url": "https://cdn.jsdelivr.net/npm/diff2html@3.4.56/bundles/css/diff2html.min.css" 141 - }, 142 - "original": { 143 - "type": "file", 144 - "url": "https://cdn.jsdelivr.net/npm/diff2html@3.4.56/bundles/css/diff2html.min.css" 145 - } 146 - }, 147 - "diff2html-js": { 148 - "flake": false, 149 - "locked": { 150 - "narHash": "sha256-Rc/n1Z3lrdOYh83ZxtQzy5YD1VhuB9m+YOBkQ8a9OTA=", 151 - "type": "file", 152 - "url": "https://cdn.jsdelivr.net/npm/diff2html@3.4.56/bundles/js/diff2html-ui.min.js" 153 - }, 154 - "original": { 155 - "type": "file", 156 - "url": "https://cdn.jsdelivr.net/npm/diff2html@3.4.56/bundles/js/diff2html-ui.min.js" 157 - } 158 - }, 159 135 "disko": { 160 136 "inputs": { 161 137 "nixpkgs": [ ··· 337 313 } 338 314 }, 339 315 "flake-utils_2": { 340 - "inputs": { 341 - "systems": "systems_3" 342 - }, 343 - "locked": { 344 - "lastModified": 1731533236, 345 - "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", 346 - "owner": "numtide", 347 - "repo": "flake-utils", 348 - "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", 349 - "type": "github" 350 - }, 351 - "original": { 352 - "owner": "numtide", 353 - "repo": "flake-utils", 354 - "type": "github" 355 - } 356 - }, 357 - "flake-utils_3": { 358 316 "locked": { 359 317 "lastModified": 1676283394, 360 318 "narHash": "sha256-XX2f9c3iySLCw54rJ/CZs+ZK6IQy7GXNY4nSOyu2QG4=", ··· 432 390 "type": "github" 433 391 } 434 392 }, 435 - "kannix": { 436 - "inputs": { 437 - "diff2html-css": "diff2html-css", 438 - "diff2html-js": "diff2html-js", 439 - "flake-utils": "flake-utils_2", 440 - "nixpkgs": [ 441 - "nixpkgs" 442 - ] 443 - }, 444 - "locked": { 445 - "lastModified": 1773427235, 446 - "narHash": "sha256-IT1SDGP5fRxrWj0zpuSSClzyqH8HsPezNaGpA0NmC9Q=", 447 - "ref": "refs/heads/master", 448 - "rev": "c2c64242355a354fd2c0146a436bcc91a0a208ab", 449 - "revCount": 41, 450 - "type": "git", 451 - "url": "file:/home/lass/src/kannix" 452 - }, 453 - "original": { 454 - "type": "git", 455 - "url": "file:/home/lass/src/kannix" 456 - } 457 - }, 458 393 "le_menu": { 459 394 "inputs": { 460 395 "flake-parts": "flake-parts_2", ··· 485 420 "nixpkgs": [ 486 421 "nixpkgs" 487 422 ], 488 - "systems": "systems_4", 423 + "systems": "systems_3", 489 424 "treefmt-nix": "treefmt-nix_3" 490 425 }, 491 426 "locked": { ··· 624 559 }, 625 560 "nix-writers": { 626 561 "inputs": { 627 - "flake-utils": "flake-utils_3", 562 + "flake-utils": "flake-utils_2", 628 563 "nixpkgs": [ 629 564 "stockholm", 630 565 "nixpkgs" ··· 700 635 }, 701 636 "nixpkgs": { 702 637 "locked": { 703 - "lastModified": 1773122722, 704 - "narHash": "sha256-FIqHByVqxCprNjor1NqF80F2QQoiiyqanNNefdlvOg4=", 638 + "lastModified": 1773282481, 639 + "narHash": "sha256-b/GV2ysM8mKHhinse2wz+uP37epUrSE+sAKXy/xvBY4=", 705 640 "owner": "nixos", 706 641 "repo": "nixpkgs", 707 - "rev": "62dc67aa6a52b4364dd75994ec00b51fbf474e50", 642 + "rev": "fe416aaedd397cacb33a610b33d60ff2b431b127", 708 643 "type": "github" 709 644 }, 710 645 "original": { ··· 720 655 "nixpkgs": [ 721 656 "nixpkgs" 722 657 ], 723 - "systems": "systems_5" 658 + "systems": "systems_4" 724 659 }, 725 660 "locked": { 726 661 "lastModified": 1772402258, ··· 783 718 "clan-core": "clan-core", 784 719 "disko": "disko", 785 720 "extra-container": "extra-container", 786 - "kannix": "kannix", 787 721 "le_menu": "le_menu", 788 722 "llm-agents": "llm-agents", 789 723 "nether": "nether", ··· 914 848 } 915 849 }, 916 850 "systems_4": { 917 - "locked": { 918 - "lastModified": 1681028828, 919 - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 920 - "owner": "nix-systems", 921 - "repo": "default", 922 - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 923 - "type": "github" 924 - }, 925 - "original": { 926 - "owner": "nix-systems", 927 - "repo": "default", 928 - "type": "github" 929 - } 930 - }, 931 - "systems_5": { 932 851 "locked": { 933 852 "lastModified": 1681028828, 934 853 "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
-2
flake.nix
··· 76 76 nixos-mailserver.url = "gitlab:simple-nixos-mailserver/nixos-mailserver/master"; 77 77 nixos-mailserver.inputs.nixpkgs.follows = "nixpkgs"; 78 78 79 - kannix.url = "git+file:/home/lass/src/kannix"; 80 - kannix.inputs.nixpkgs.follows = "nixpkgs"; 81 79 }; 82 80 83 81 outputs =
+132 -27
2configs/kannix.nix
··· 1 1 { 2 2 self, 3 - config, 4 3 pkgs, 5 4 lib, 6 5 ... ··· 8 7 let 9 8 port = 4243; 10 9 stateDir = "/var/lib/kannix"; 10 + bareRepo = "${stateDir}/repos/kannix.git"; 11 + srcDir = "${stateDir}/checkout/kannix"; 12 + 13 + pythonEnv = pkgs.python3.withPackages ( 14 + ps: with ps; [ 15 + fastapi 16 + uvicorn 17 + jinja2 18 + python-multipart 19 + bcrypt 20 + websockets 21 + ] 22 + ); 23 + 24 + configFile = pkgs.writeText "kannix.json" ( 25 + builtins.toJSON { 26 + columns = [ 27 + "backlog" 28 + "planning" 29 + "doing" 30 + "review" 31 + "done" 32 + ]; 33 + server = { 34 + host = "127.0.0.1"; 35 + inherit port; 36 + }; 37 + repos_dir = "${stateDir}/repos"; 38 + worktree_dir = "${stateDir}/worktrees"; 39 + } 40 + ); 41 + 42 + vendorDir = pkgs.runCommand "kannix-vendor" { } '' 43 + mkdir -p $out 44 + cp ${pkgs.fetchurl { 45 + url = "https://cdn.jsdelivr.net/npm/diff2html@3.4.56/bundles/css/diff2html.min.css"; 46 + hash = "sha256-0+zA6bKx5chGbBneKb7QUv0IY0ddJYKezIWERu/e03I="; 47 + }} $out/diff2html.min.css 48 + cp ${pkgs.fetchurl { 49 + url = "https://cdn.jsdelivr.net/npm/diff2html@3.4.56/bundles/js/diff2html-ui.min.js"; 50 + hash = "sha256-mXoOqduEwKvM41xkFZr+RtMNz9/SQ0FWGdmz6BAXE4s="; 51 + }} $out/diff2html-ui.min.js 52 + ''; 53 + 54 + kannixDevScript = pkgs.writeShellScript "kannix-dev" '' 55 + export KANNIX_CONFIG=${configFile} 56 + export KANNIX_STATE_DIR=${stateDir} 57 + export PYTHONPATH=${srcDir}/src 58 + export PATH=${ 59 + lib.makeBinPath [ 60 + pkgs.tmux 61 + pkgs.git 62 + ] 63 + }:$PATH 64 + cd ${srcDir} 65 + 66 + # Symlink vendor files 67 + mkdir -p src/kannix/static/vendor 68 + ln -sf ${vendorDir}/diff2html.min.css src/kannix/static/vendor/diff2html.min.css 69 + ln -sf ${vendorDir}/diff2html-ui.min.js src/kannix/static/vendor/diff2html-ui.min.js 70 + 71 + exec ${pythonEnv}/bin/python -m uvicorn \ 72 + kannix.main:create_dev_app \ 73 + --factory \ 74 + --host 127.0.0.1 \ 75 + --port ${toString port} \ 76 + --reload \ 77 + --reload-dir ${srcDir}/src 78 + ''; 11 79 in 12 80 { 13 - imports = [ 14 - self.inputs.kannix.nixosModules.default 15 - ]; 16 - 17 - services.kannix = { 18 - enable = true; 19 - package = self.inputs.kannix.packages.${pkgs.system}.default; 20 - host = "127.0.0.1"; 21 - inherit port; 22 - inherit stateDir; 23 - columns = [ 24 - "Backlog" 25 - "In Progress" 26 - "Review" 27 - "Done" 81 + users.users.kannix = { 82 + isNormalUser = true; 83 + home = stateDir; 84 + createHome = true; 85 + group = "users"; 86 + shell = pkgs.bashInteractive; 87 + linger = true; 88 + openssh.authorizedKeys.keys = [ 89 + self.keys.ssh.barnacle.public 90 + self.keys.ssh.yubi_pgp.public 91 + self.keys.ssh.yubi1.public 92 + self.keys.ssh.yubi2.public 93 + self.keys.ssh.solo2.public 94 + self.keys.ssh.xerxes.public 95 + self.keys.ssh.massulus.public 28 96 ]; 29 - reposDir = "${stateDir}/repos"; 30 - worktreeDir = "${stateDir}/worktrees"; 97 + packages = [ 98 + pythonEnv 99 + pkgs.tmux 100 + pkgs.git 101 + self.packages.${pkgs.system}.s 102 + (pkgs.writeShellScriptBin "kannix-ctl" '' 103 + export PYTHONPATH=${srcDir}/src 104 + exec ${pythonEnv}/bin/python -m kannix.ctl "$@" 105 + '') 106 + ]; 31 107 }; 32 108 33 - # SSH access for the kannix user 34 - users.users.kannix.openssh.authorizedKeys.keys = [ 35 - self.keys.ssh.barnacle.public 36 - self.keys.ssh.yubi_pgp.public 37 - self.keys.ssh.yubi1.public 38 - self.keys.ssh.yubi2.public 39 - self.keys.ssh.solo2.public 40 - self.keys.ssh.xerxes.public 41 - self.keys.ssh.massulus.public 109 + systemd.tmpfiles.rules = [ 110 + "d ${stateDir}/repos 0755 kannix users -" 111 + "d ${stateDir}/worktrees 0755 kannix users -" 112 + "d ${stateDir}/checkout 0755 kannix users -" 42 113 ]; 114 + 115 + systemd.services.kannix-checkout = { 116 + description = "Create kannix worktree from bare repo"; 117 + wantedBy = [ "multi-user.target" ]; 118 + unitConfig.ConditionPathExists = "!${srcDir}/.git"; 119 + serviceConfig = { 120 + Type = "oneshot"; 121 + User = "kannix"; 122 + Group = "users"; 123 + ExecStart = toString ( 124 + pkgs.writeShellScript "kannix-checkout" '' 125 + ${pkgs.git}/bin/git --git-dir=${bareRepo} worktree add ${srcDir} main 126 + '' 127 + ); 128 + }; 129 + }; 130 + 131 + systemd.user.services.kannix = { 132 + description = "Kannix dev server"; 133 + wantedBy = [ "default.target" ]; 134 + after = [ "network.target" ]; 135 + path = [ 136 + pkgs.tmux 137 + pkgs.git 138 + pythonEnv 139 + ]; 140 + serviceConfig = { 141 + Type = "simple"; 142 + ExecStart = toString kannixDevScript; 143 + Restart = "on-failure"; 144 + RestartSec = 5; 145 + WorkingDirectory = srcDir; 146 + }; 147 + }; 43 148 44 149 services.nginx.virtualHosts."kannix.lassul.us" = { 45 150 enableACME = true;