nixos/firefly-iii: Changes to module and tests

Module has been fixed and now uses the maintenance service to cache
settings so as to not require environment files wherever possible.

The tests now test using mariadb and postgresql as well as sqlite to be
more complete. A test has been added for testing whether app.js has been
compiled successfully, as well as to check whether the cronjob fires
successfully.
This commit is contained in:
Savyasachee Jha 2024-05-20 17:03:51 +05:30
parent 7d516d945d
commit eee8b0bff3
2 changed files with 142 additions and 46 deletions

View File

@ -3,8 +3,8 @@
let
inherit (lib) optionalString mkDefault mkIf mkOption mkEnableOption literalExpression;
inherit (lib.types) nullOr attrsOf oneOf str int bool path package enum submodule;
inherit (lib.strings) concatMapStringsSep removePrefix toShellVars removeSuffix hasSuffix;
inherit (lib.attrsets) attrValues genAttrs filterAttrs mapAttrs' nameValuePair;
inherit (lib.strings) concatLines removePrefix toShellVars removeSuffix hasSuffix;
inherit (lib.attrsets) mapAttrsToList attrValues genAttrs filterAttrs mapAttrs' nameValuePair;
inherit (builtins) isInt isString toString typeOf;
cfg = config.services.firefly-iii;
@ -21,18 +21,10 @@ let
(filterAttrs (n: v: hasSuffix "_FILE" n) cfg.settings);
env-nonfile-values = filterAttrs (n: v: ! hasSuffix "_FILE" n) cfg.settings;
envfile = pkgs.writeText "firefly-iii-env" ''
${toShellVars env-file-values}
${toShellVars env-nonfile-values}
'';
fileenv-func = ''
cp --no-preserve=mode ${envfile} /tmp/firefly-iii-env
${concatMapStringsSep "\n"
(n: "${pkgs.replace-secret}/bin/replace-secret ${n} ${n} /tmp/firefly-iii-env")
(attrValues env-file-values)}
set -a
. /tmp/firefly-iii-env
${toShellVars env-nonfile-values}
${concatLines (mapAttrsToList (n: v: "${n}=\"$(< ${v})\"") env-file-values)}
set +a
'';
@ -41,15 +33,13 @@ let
${optionalString (cfg.settings.DB_CONNECTION == "sqlite")
"touch ${cfg.dataDir}/storage/database/database.sqlite"}
${artisan} migrate --seed --no-interaction --force
${artisan} firefly-iii:decrypt-all
${artisan} package:discover
${artisan} firefly-iii:upgrade-database
${artisan} firefly-iii:correct-database
${artisan} firefly-iii:report-integrity
${artisan} firefly-iii:laravel-passport-keys
${artisan} cache:clear
mv /tmp/firefly-iii-env /run/phpfpm/firefly-iii-env
${artisan} view:cache
${artisan} route:cache
${artisan} config:cache
'';
commonServiceConfig = {
@ -146,6 +136,7 @@ in {
virtualHost = mkOption {
type = str;
default = "localhost";
description = ''
The hostname at which you wish firefly-iii to be served. If you have
enabled nginx using `services.firefly-iii.enableNginx` then this will
@ -170,14 +161,15 @@ in {
};
settings = mkOption {
default = {};
description = ''
Options for firefly-iii configuration. Refer to
<https://github.com/firefly-iii/firefly-iii/blob/main/.env.example> for
details on supported values. All <option>_FILE values supported by
upstream are supported here.
APP_URL will be set by `services.firefly-iii.virtualHost`, do not
redefine it here.
APP_URL will be the same as `services.firefly-iii.virtualHost` if the
former is unset in `services.firefly-iii.settings`.
'';
example = literalExpression ''
{
@ -192,7 +184,6 @@ in {
DB_PASSWORD_FILE = "/var/secrets/firefly-iii-mysql-password.txt;
}
'';
default = {};
type = submodule {
freeformType = attrsOf (oneOf [str int bool]);
options = {
@ -216,9 +207,9 @@ in {
};
DB_PORT = mkOption {
type = nullOr int;
default = if cfg.settings.DB_CONNECTION == "sqlite" then null
default = if cfg.settings.DB_CONNECTION == "pgsql" then 5432
else if cfg.settings.DB_CONNECTION == "mysql" then 3306
else 5432;
else null;
defaultText = ''
`null` if DB_CONNECTION is "sqlite", `3306` if "mysql", `5432` if "pgsql"
'';
@ -227,6 +218,21 @@ in {
this value to be filled.
'';
};
DB_HOST = mkOption {
type = str;
default = if cfg.settings.DB_CONNECTION == "pgsql" then "/run/postgresql"
else "localhost";
defaultText = ''
"localhost" if DB_CONNECTION is "sqlite" or "mysql", "/run/postgresql" if "pgsql".
'';
description = ''
The machine which hosts your database. This is left at the
default value for "mysql" because we use the "DB_SOCKET" option
to connect to a unix socket instead. "pgsql" requires that the
unix socket location be specified here instead of at "DB_SOCKET".
This option does not affect "sqlite".
'';
};
APP_KEY_FILE = mkOption {
type = path;
description = ''
@ -235,6 +241,20 @@ in {
/dev/urandom | base64)" > /path/to/key-file`.
'';
};
APP_URL = mkOption {
type = str;
default = if cfg.virtualHost == "localhost" then "http://${cfg.virtualHost}"
else "https://${cfg.virtualHost}";
defaultText = ''
http(s)://''${config.services.firefly-iii.virtualHost}
'';
description = ''
The APP_URL used by firefly-iii internally. Please make sure this
URL matches the external URL of your Firefly III installation. It
is used to validate specific requests and to generate URLs in
emails.
'';
};
};
};
};
@ -242,12 +262,6 @@ in {
config = mkIf cfg.enable {
services.firefly-iii = {
settings = {
APP_URL = cfg.virtualHost;
};
};
services.phpfpm.pools.firefly-iii = {
inherit user group;
phpPackage = cfg.package.phpPackage;
@ -262,29 +276,27 @@ in {
} // cfg.poolConfig;
};
systemd.services.phpfpm-firefly-iii.serviceConfig = {
EnvironmentFile = "/run/phpfpm/firefly-iii-env";
ExecStartPost = "${pkgs.coreutils}/bin/rm /run/phpfpm/firefly-iii-env";
};
systemd.services.firefly-iii-setup = {
after = [ "postgresql.service" "mysql.service" ];
requiredBy = [ "phpfpm-firefly-iii.service" ];
before = [ "phpfpm-firefly-iii.service" ];
serviceConfig = {
ExecStart = firefly-iii-maintenance;
RuntimeDirectory = "phpfpm";
RuntimeDirectoryPreserve = true;
RemainAfterExit = true;
} // commonServiceConfig;
unitConfig.JoinsNamespaceOf = "phpfpm-firefly-iii.service";
restartTriggers = [ cfg.package ];
};
systemd.services.firefly-iii-cron = {
after = [ "firefly-iii-setup.service" "postgresql.service" "mysql.service" ];
wants = [ "firefly-iii-setup.service" ];
description = "Daily Firefly III cron job";
script = ''
${fileenv-func}
${artisan} firefly-iii:cron
'';
serviceConfig = commonServiceConfig;
serviceConfig = {
ExecStart = "${artisan} firefly-iii:cron";
} // commonServiceConfig;
};
systemd.timers.firefly-iii-cron = {
@ -295,6 +307,7 @@ in {
Persistent = true;
};
wantedBy = [ "timers.target" ];
restartTriggers = [ cfg.package ];
};
services.nginx = mkIf cfg.enableNginx {

View File

@ -1,14 +1,19 @@
import ./make-test-python.nix ({ lib, pkgs, ... }: {
import ./make-test-python.nix ({ lib, ... }:
let
db-pass = "Test2Test2";
app-key = "TestTestTestTestTestTestTestTest";
in
{
name = "firefly-iii";
meta.maintainers = [ lib.maintainers.savyajha ];
nodes.machine = { config, ... }: {
nodes.fireflySqlite = { config, ... }: {
environment.etc = {
"firefly-iii-appkey".text = "TestTestTestTestTestTestTestTest";
"firefly-iii-appkey".text = app-key;
};
services.firefly-iii = {
enable = true;
virtualHost = "http://localhost";
enableNginx = true;
settings = {
APP_KEY_FILE = "/etc/firefly-iii-appkey";
@ -18,9 +23,87 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: {
};
};
nodes.fireflyPostgresql = { config, pkgs, ... }: {
environment.etc = {
"firefly-iii-appkey".text = app-key;
"postgres-pass".text = db-pass;
};
services.firefly-iii = {
enable = true;
enableNginx = true;
settings = {
APP_KEY_FILE = "/etc/firefly-iii-appkey";
LOG_CHANNEL = "stdout";
SITE_OWNER = "mail@example.com";
DB_CONNECTION = "pgsql";
DB_DATABASE = "firefly";
DB_USERNAME = "firefly";
DB_PASSWORD_FILE = "/etc/postgres-pass";
};
};
services.postgresql = {
enable = true;
package = pkgs.postgresql_15;
authentication = ''
local all postgres peer
local firefly firefly password
'';
initialScript = pkgs.writeText "firefly-init.sql" ''
CREATE USER "firefly" WITH LOGIN PASSWORD '${db-pass}';
CREATE DATABASE "firefly" WITH OWNER "firefly";
CREATE SCHEMA AUTHORIZATION firefly;
'';
};
};
nodes.fireflyMysql = { config, pkgs, ... }: {
environment.etc = {
"firefly-iii-appkey".text = app-key;
"mysql-pass".text = db-pass;
};
services.firefly-iii = {
enable = true;
enableNginx = true;
settings = {
APP_KEY_FILE = "/etc/firefly-iii-appkey";
LOG_CHANNEL = "stdout";
SITE_OWNER = "mail@example.com";
DB_CONNECTION = "mysql";
DB_DATABASE = "firefly";
DB_USERNAME = "firefly";
DB_PASSWORD_FILE = "/etc/mysql-pass";
DB_SOCKET = "/run/mysqld/mysqld.sock";
};
};
services.mysql = {
enable = true;
package = pkgs.mariadb;
initialScript = pkgs.writeText "firefly-init.sql" ''
create database firefly DEFAULT CHARACTER SET utf8mb4;
create user 'firefly'@'localhost' identified by '${db-pass}';
grant all on firefly.* to 'firefly'@'localhost';
'';
settings.mysqld.character-set-server = "utf8mb4";
};
};
testScript = ''
machine.wait_for_unit("phpfpm-firefly-iii.service")
machine.wait_for_unit("nginx.service")
machine.succeed("curl -fvvv -Ls http://localhost/ | grep 'Firefly III'")
fireflySqlite.wait_for_unit("phpfpm-firefly-iii.service")
fireflySqlite.wait_for_unit("nginx.service")
fireflySqlite.succeed("curl -fvvv -Ls http://localhost/ | grep 'Firefly III'")
fireflySqlite.succeed("curl -fvvv -Ls http://localhost/v1/js/app.js")
fireflySqlite.succeed("systemctl start firefly-iii-cron.service")
fireflyPostgresql.wait_for_unit("phpfpm-firefly-iii.service")
fireflyPostgresql.wait_for_unit("nginx.service")
fireflyPostgresql.wait_for_unit("postgresql.service")
fireflyPostgresql.succeed("curl -fvvv -Ls http://localhost/ | grep 'Firefly III'")
fireflyPostgresql.succeed("systemctl start firefly-iii-cron.service")
fireflyMysql.wait_for_unit("phpfpm-firefly-iii.service")
fireflyMysql.wait_for_unit("nginx.service")
fireflyMysql.wait_for_unit("mysql.service")
fireflyMysql.succeed("curl -fvvv -Ls http://localhost/ | grep 'Firefly III'")
fireflyMysql.succeed("systemctl start firefly-iii-cron.service")
'';
})