fix: this should fix the cyclical reference now.

This commit is contained in:
Lewis Wynne 2026-04-07 16:15:05 +01:00
parent 69b35ba3c2
commit 9b3c3e8f8c

View file

@ -1,8 +1,9 @@
{ config, lib, pkgs, ... }: { config, lib, pkgs, ... }:
let let
inherit (lib) mkOption types mkIf mkMerge mapAttrsToList optional; inherit (lib) mkOption types mkIf mkMerge mapAttrsToList mapAttrs' nameValuePair
concatLists optional;
siteModule = types.submodule ({ name, config, ... }: { siteModule = types.submodule ({ name, ... }: {
options = { options = {
domain = mkOption { domain = mkOption {
type = types.str; type = types.str;
@ -74,9 +75,10 @@ let
cfg = config.services.site; cfg = config.services.site;
makeSiteConfig = name: site: webhookPort = 4323;
siteHelpers = name: site:
let let
dataDir = site.dataDir;
pmBin = pmBin =
if site.packageManager == "pnpm" if site.packageManager == "pnpm"
then "${pkgs.pnpm}/bin/pnpm" then "${pkgs.pnpm}/bin/pnpm"
@ -86,22 +88,34 @@ let
then "${pmBin} install --frozen-lockfile" then "${pmBin} install --frozen-lockfile"
else "${pmBin} ci"; else "${pmBin} ci";
in in
{ inherit pmBin installCmd; dataDir = site.dataDir; };
in
{
options.services.site = mkOption {
type = types.attrsOf siteModule;
default = {};
description = "Node.js web site services with git clone, build, and webhook support.";
};
config = {
services.caddy.virtualHosts = mkMerge (mapAttrsToList (name: site:
{ {
services.caddy.virtualHosts = { ${site.domain}.extraConfig = ''
${site.domain} = {
extraConfig = ''
reverse_proxy localhost:${toString site.port} reverse_proxy localhost:${toString site.port}
encode zstd gzip encode zstd gzip
''; '';
};
} // builtins.listToAttrs (map (d: { } // builtins.listToAttrs (map (d: {
name = d; name = d;
value.extraConfig = '' value.extraConfig = ''
redir https://${site.domain}{uri} permanent redir https://${site.domain}{uri} permanent
''; '';
}) site.redirectDomains); }) site.redirectDomains)
) cfg);
systemd.services.${name} = { systemd.services = mkMerge ((mapAttrsToList (name: site:
let h = siteHelpers name site; in {
${name} = {
description = site.domain; description = site.domain;
environment = { environment = {
HOST = "127.0.0.1"; HOST = "127.0.0.1";
@ -109,7 +123,7 @@ let
} // site.environment; } // site.environment;
serviceConfig = { serviceConfig = {
Type = "simple"; Type = "simple";
WorkingDirectory = "${dataDir}/repo"; WorkingDirectory = "${h.dataDir}/repo";
ExecStart = "${pkgs.nodejs}/bin/node ${site.entryPoint}"; ExecStart = "${pkgs.nodejs}/bin/node ${site.entryPoint}";
Restart = "on-failure"; Restart = "on-failure";
User = name; User = name;
@ -118,7 +132,7 @@ let
}; };
}; };
systemd.services."${name}-rebuild" = { "${name}-rebuild" = {
description = "Clone/pull and build ${site.domain}"; description = "Clone/pull and build ${site.domain}";
after = [ "network-online.target" ] ++ site.afterServices; after = [ "network-online.target" ] ++ site.afterServices;
path = [ pkgs.nodejs pkgs.bash ] path = [ pkgs.nodejs pkgs.bash ]
@ -130,60 +144,34 @@ let
Type = "oneshot"; Type = "oneshot";
RemainAfterExit = false; RemainAfterExit = false;
ExecStartPre = "+${pkgs.writeShellScript "prepare-${name}" '' ExecStartPre = "+${pkgs.writeShellScript "prepare-${name}" ''
mkdir -p ${dataDir} mkdir -p ${h.dataDir}
chown -R ${name}:${name} ${dataDir} chown -R ${name}:${name} ${h.dataDir}
''}"; ''}";
ExecStart = pkgs.writeShellScript "rebuild-${name}" '' ExecStart = pkgs.writeShellScript "rebuild-${name}" ''
set -euo pipefail set -euo pipefail
if [ ! -d ${dataDir}/repo/.git ]; then if [ ! -d ${h.dataDir}/repo/.git ]; then
${pkgs.git}/bin/git clone ${site.repo} ${dataDir}/repo ${pkgs.git}/bin/git clone ${site.repo} ${h.dataDir}/repo
fi fi
cd ${dataDir}/repo cd ${h.dataDir}/repo
${pkgs.git}/bin/git fetch origin ${pkgs.git}/bin/git fetch origin
${pkgs.git}/bin/git reset --hard origin/${site.branch} ${pkgs.git}/bin/git reset --hard origin/${site.branch}
${installCmd} ${h.installCmd}
${pmBin} run build ${h.pmBin} run build
''; '';
ExecStartPost = "+/run/current-system/sw/bin/systemctl restart ${name}"; ExecStartPost = "+/run/current-system/sw/bin/systemctl restart ${name}";
User = name; User = name;
Group = name; Group = name;
}; };
}; };
}
systemd.paths."${name}-rebuild-trigger" = { ) cfg) ++ [{
description = "Watch for ${name} rebuild trigger"; site-webhook = mkIf (cfg != {}) {
wantedBy = [ "multi-user.target" ];
pathConfig = {
PathModified = "${dataDir}/trigger";
Unit = "${name}-rebuild.service";
};
};
users.users.${name} = {
isSystemUser = true;
group = name;
home = dataDir;
};
users.groups.${name} = {};
};
in
{
options.services.site = mkOption {
type = types.attrsOf siteModule;
default = {};
description = "Node.js web site services with git clone, build, and webhook support.";
};
config = mkMerge ((mapAttrsToList makeSiteConfig cfg) ++ [{
systemd.services.site-webhook = mkIf (cfg != {}) {
description = "Webhook listener for site rebuilds"; description = "Webhook listener for site rebuilds";
after = [ "network.target" ]; after = [ "network.target" ];
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
serviceConfig = { serviceConfig = {
Type = "simple"; Type = "simple";
ExecStart = let ExecStart = let
webhookPort = 4323;
allHooks = mapAttrsToList (name: site: { allHooks = mapAttrsToList (name: site: {
id = "${name}-rebuild"; id = "${name}-rebuild";
execute-command = "/run/current-system/sw/bin/touch"; execute-command = "/run/current-system/sw/bin/touch";
@ -198,4 +186,28 @@ in
}; };
}; };
}]); }]);
systemd.paths = mkMerge (mapAttrsToList (name: site: {
"${name}-rebuild-trigger" = {
description = "Watch for ${name} rebuild trigger";
wantedBy = [ "multi-user.target" ];
pathConfig = {
PathModified = "${site.dataDir}/trigger";
Unit = "${name}-rebuild.service";
};
};
}) cfg);
users.users = mkMerge (mapAttrsToList (name: site: {
${name} = {
isSystemUser = true;
group = name;
home = site.dataDir;
};
}) cfg);
users.groups = mkMerge (mapAttrsToList (name: _: {
${name} = {};
}) cfg);
};
} }