fixxxxxxxxxxxxxxxx

This commit is contained in:
2026-02-19 21:04:10 +02:00
parent 5b4f973d53
commit 22772a1d3d
2 changed files with 124 additions and 35 deletions

View File

@@ -1,7 +1,5 @@
# wafrn-nix # wafrn-nix
By default this module uses a flake-pinned Wafrn source, so you do not need to clone Wafrn manually. You can still override `services.wafrn.source` if you want a local checkout.
Example: Example:
```nix ```nix
@@ -18,6 +16,8 @@ Example:
services.wafrn = { services.wafrn = {
enable = true; enable = true;
# package = my-wafrn-src-package;
# optional: override source checkout path # optional: override source checkout path
# source = "/srv/wafrn"; # source = "/srv/wafrn";
stateDir = "/var/lib/wafrn"; stateDir = "/var/lib/wafrn";

View File

@@ -12,6 +12,23 @@ let
cfg = config.services.wafrn; cfg = config.services.wafrn;
defaultWafrnPackage =
if wafrnSrc != null then
pkgs.stdenvNoCC.mkDerivation {
pname = "wafrn-source";
version = "unstable";
src = wafrnSrc;
dontConfigure = true;
dontBuild = true;
installPhase = ''
mkdir -p "$out"
cp -a ./. "$out/"
chmod -R u+w "$out"
'';
}
else
null;
toEnvString = value: toEnvString = value:
if builtins.isBool value then if builtins.isBool value then
(if value then "true" else "false") (if value then "true" else "false")
@@ -105,14 +122,17 @@ let
serviceEnvFile = "${cfg.stateDir}/.env"; serviceEnvFile = "${cfg.stateDir}/.env";
composeFile = "${cfg.stateDir}/docker-compose.yml"; composeFile = "${cfg.stateDir}/docker-compose.yml";
sourcePath = packageSourcePath = toString cfg.package;
if cfg.source != null then sourcePath = if cfg.source != null then cfg.source else "";
cfg.source
else if wafrnSrc != null then
toString wafrnSrc
else
"";
preparedSourcePath = "${cfg.stateDir}/source"; preparedSourcePath = "${cfg.stateDir}/source";
usingPrebuiltImages = cfg.images.backend != null && cfg.images.frontend != null;
effectiveCaddyConfigDir =
if cfg.caddyConfigDir != null then
cfg.caddyConfigDir
else if usingPrebuiltImages then
null
else
"${preparedSourcePath}/packages/caddy";
publishedPorts = publishedPorts =
lib.optionals (cfg.httpPort != null) [ "${toString cfg.httpPort}:80" ] lib.optionals (cfg.httpPort != null) [ "${toString cfg.httpPort}:80" ]
@@ -132,8 +152,24 @@ let
dockerfile = "packages/backend/Dockerfile"; dockerfile = "packages/backend/Dockerfile";
}; };
backendContainerSpec =
if usingPrebuiltImages then
{ image = cfg.images.backend; }
else
{ build = backendBuild; };
frontendContainerSpec =
if usingPrebuiltImages then
{ image = cfg.images.frontend; }
else
{
build = {
context = preparedSourcePath;
dockerfile = "packages/frontend/Dockerfile";
};
};
backendService = { backendService = {
build = backendBuild;
depends_on = { depends_on = {
db = { condition = "service_started"; }; db = { condition = "service_started"; };
redis = { condition = "service_started"; }; redis = { condition = "service_started"; };
@@ -153,9 +189,9 @@ let
composeConfig = { composeConfig = {
services = { services = {
backend = serviceCommon // backendService; backend = serviceCommon // backendService // backendContainerSpec;
migration = serviceCommon // backendService // { migration = serviceCommon // backendService // backendContainerSpec // {
restart = "no"; restart = "no";
depends_on = { depends_on = {
db = { condition = "service_started"; }; db = { condition = "service_started"; };
@@ -165,11 +201,7 @@ let
command = "npm exec tsx migrate.ts init-container"; command = "npm exec tsx migrate.ts init-container";
}; };
frontend = serviceCommon // { frontend = serviceCommon // frontendContainerSpec // {
build = {
context = preparedSourcePath;
dockerfile = "packages/frontend/Dockerfile";
};
restart = "unless-stopped"; restart = "unless-stopped";
ports = publishedPorts; ports = publishedPorts;
extra_hosts = [ "host.docker.internal:host-gateway" ]; extra_hosts = [ "host.docker.internal:host-gateway" ];
@@ -177,7 +209,8 @@ let
"${cfg.stateDir}/caddy:/data" "${cfg.stateDir}/caddy:/data"
"${cfg.stateDir}/frontend:/var/www/html/frontend" "${cfg.stateDir}/frontend:/var/www/html/frontend"
"${cfg.stateDir}/uploads:/var/www/html/uploads" "${cfg.stateDir}/uploads:/var/www/html/uploads"
"${preparedSourcePath}/packages/caddy:/etc/caddy/config" ] ++ lib.optionals (effectiveCaddyConfigDir != null) [
"${effectiveCaddyConfigDir}:/etc/caddy/config"
]; ];
}; };
@@ -205,8 +238,7 @@ let
}; };
} }
// optionalAttrs cfg.bluesky.enable { // optionalAttrs cfg.bluesky.enable {
pds_worker = serviceCommon // { pds_worker = serviceCommon // backendContainerSpec // {
build = backendBuild;
command = "npm exec tsx atproto.ts"; command = "npm exec tsx atproto.ts";
depends_on = { depends_on = {
db = { condition = "service_started"; }; db = { condition = "service_started"; };
@@ -266,7 +298,41 @@ in
type = types.nullOr types.str; type = types.nullOr types.str;
default = null; default = null;
example = "/srv/wafrn"; example = "/srv/wafrn";
description = "Optional path to a Wafrn source checkout. If null, the module uses the pinned source from this flake input."; description = "Optional path to a Wafrn source checkout. If null, the service uses services.wafrn.package as the source.";
};
package = mkOption {
type = types.package;
default =
if defaultWafrnPackage != null then
defaultWafrnPackage
else
throw "services.wafrn.package must be set when no flake-pinned wafrn source is available";
defaultText = lib.literalExpression "<wafrn source package>";
description = "Package containing Wafrn source tree used to build backend/frontend Docker images.";
};
caddyConfigDir = mkOption {
type = types.nullOr types.str;
default = null;
example = "/srv/wafrn/packages/caddy";
description = "Optional host path mounted into frontend container as /etc/caddy/config for Caddy hooks/overrides.";
};
images = {
backend = mkOption {
type = types.nullOr types.str;
default = null;
example = "ghcr.io/your-org/wafrn-backend:2026.02.01";
description = "Prebuilt backend image. Set together with images.frontend to skip local source/Docker builds.";
};
frontend = mkOption {
type = types.nullOr types.str;
default = null;
example = "ghcr.io/your-org/wafrn-frontend:2026.02.01";
description = "Prebuilt frontend image. Set together with images.backend to skip local source/Docker builds.";
};
}; };
stateDir = mkOption { stateDir = mkOption {
@@ -352,8 +418,12 @@ in
message = "services.wafrn requires virtualisation.docker.enable = true;"; message = "services.wafrn requires virtualisation.docker.enable = true;";
} }
{ {
assertion = cfg.source != null || wafrnSrc != null; assertion = usingPrebuiltImages || cfg.source != null || cfg.package != null;
message = "services.wafrn.source is null and no flake-pinned wafrn source is available."; message = "Provide services.wafrn.images.backend+frontend, set services.wafrn.source, or set services.wafrn.package.";
}
{
assertion = (cfg.images.backend == null) == (cfg.images.frontend == null);
message = "Set both services.wafrn.images.backend and services.wafrn.images.frontend, or neither.";
} }
{ {
assertion = cfg.httpPort != null || cfg.httpsPort != null; assertion = cfg.httpPort != null || cfg.httpsPort != null;
@@ -369,6 +439,7 @@ in
"d ${cfg.stateDir}/cache 0750 root root -" "d ${cfg.stateDir}/cache 0750 root root -"
"d ${cfg.stateDir}/caddy 0750 root root -" "d ${cfg.stateDir}/caddy 0750 root root -"
"d ${cfg.stateDir}/frontend 0750 root root -" "d ${cfg.stateDir}/frontend 0750 root root -"
] ++ lib.optionals (!usingPrebuiltImages) [
"d ${cfg.stateDir}/source 0750 root root -" "d ${cfg.stateDir}/source 0750 root root -"
] ++ lib.optionals (cfg.bluesky.enable && cfg.bluesky.useBundledPds) [ ] ++ lib.optionals (cfg.bluesky.enable && cfg.bluesky.useBundledPds) [
"d ${cfg.stateDir}/pds 0750 root root -" "d ${cfg.stateDir}/pds 0750 root root -"
@@ -389,20 +460,38 @@ in
script = '' script = ''
set -euo pipefail set -euo pipefail
if [ ! -d "${sourcePath}" ]; then ${optionalString (!usingPrebuiltImages) ''
echo "wafrn-nix: source directory does not exist: ${sourcePath}" >&2 selected_source=""
exit 1
fi
rm -rf "${preparedSourcePath}" if [ -n "${sourcePath}" ] && [ -d "${sourcePath}" ]; then
mkdir -p "${preparedSourcePath}" selected_source="${sourcePath}"
cp -a "${sourcePath}/." "${preparedSourcePath}/" elif [ -n "${sourcePath}" ]; then
chmod -R u+w "${preparedSourcePath}" echo "wafrn-nix: configured source does not exist (${sourcePath}), falling back to services.wafrn.package" >&2
fi
if [ ! -f "${preparedSourcePath}/package-lock.json" ]; then if [ -z "$selected_source" ] && [ -d "${packageSourcePath}" ]; then
echo "wafrn-nix: package-lock.json missing, generating with npm" >&2 selected_source="${packageSourcePath}"
(cd "${preparedSourcePath}" && npm install --package-lock-only --ignore-scripts) fi
fi
if [ -z "$selected_source" ]; then
echo "wafrn-nix: no usable source directory found" >&2
exit 1
fi
rm -rf "${preparedSourcePath}"
mkdir -p "${preparedSourcePath}"
cp -a "$selected_source/." "${preparedSourcePath}/"
chmod -R u+w "${preparedSourcePath}"
if [ ! -e "${preparedSourcePath}/.git" ]; then
mkdir -p "${preparedSourcePath}/.git"
fi
if [ ! -f "${preparedSourcePath}/package-lock.json" ]; then
echo "wafrn-nix: package-lock.json missing, generating with npm" >&2
(cd "${preparedSourcePath}" && npm install --package-lock-only --ignore-scripts)
fi
''}
install -m 0600 ${envTemplate} ${serviceEnvFile} install -m 0600 ${envTemplate} ${serviceEnvFile}
${optionalString (cfg.secretsFile != null) '' ${optionalString (cfg.secretsFile != null) ''