Merge pull request #296641 from toastal/movim-service
nixos/movim: init, movim: additions + patches
This commit is contained in:
commit
4cde9116a1
|
@ -1365,6 +1365,7 @@
|
||||||
./services/web-apps/miniflux.nix
|
./services/web-apps/miniflux.nix
|
||||||
./services/web-apps/monica.nix
|
./services/web-apps/monica.nix
|
||||||
./services/web-apps/moodle.nix
|
./services/web-apps/moodle.nix
|
||||||
|
./services/web-apps/movim.nix
|
||||||
./services/web-apps/netbox.nix
|
./services/web-apps/netbox.nix
|
||||||
./services/web-apps/nextcloud.nix
|
./services/web-apps/nextcloud.nix
|
||||||
./services/web-apps/nextcloud-notify_push.nix
|
./services/web-apps/nextcloud-notify_push.nix
|
||||||
|
|
711
nixos/modules/services/web-apps/movim.nix
Normal file
711
nixos/modules/services/web-apps/movim.nix
Normal file
|
@ -0,0 +1,711 @@
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
inherit (lib)
|
||||||
|
filterAttrsRecursive
|
||||||
|
generators
|
||||||
|
literalExpression
|
||||||
|
mkDefault
|
||||||
|
mkIf
|
||||||
|
mkOption
|
||||||
|
mkEnableOption
|
||||||
|
mkPackageOption
|
||||||
|
mkMerge
|
||||||
|
pipe
|
||||||
|
types
|
||||||
|
;
|
||||||
|
|
||||||
|
cfg = config.services.movim;
|
||||||
|
|
||||||
|
defaultPHPCfg = {
|
||||||
|
"output_buffering" = 0;
|
||||||
|
"error_reporting" = "E_ALL & ~E_DEPRECATED & ~E_STRICT";
|
||||||
|
"opcache.enable_cli" = 1;
|
||||||
|
"opcache.interned_strings_buffer" = 8;
|
||||||
|
"opcache.max_accelerated_files" = 6144;
|
||||||
|
"opcache.memory_consumption" = 128;
|
||||||
|
"opcache.revalidate_freq" = 2;
|
||||||
|
"opcache.fast_shutdown" = 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
phpCfg = generators.toKeyValue
|
||||||
|
{ mkKeyValue = generators.mkKeyValueDefault { } " = "; }
|
||||||
|
(defaultPHPCfg // cfg.phpCfg);
|
||||||
|
|
||||||
|
podConfigFlags =
|
||||||
|
let
|
||||||
|
bevalue = a: lib.escapeShellArg (generators.mkValueStringDefault { } a);
|
||||||
|
in
|
||||||
|
lib.concatStringsSep " "
|
||||||
|
(lib.attrsets.foldlAttrs
|
||||||
|
(acc: k: v: acc ++ lib.optional (v != null) "--${k}=${bevalue v}")
|
||||||
|
[ ]
|
||||||
|
cfg.podConfig);
|
||||||
|
|
||||||
|
package =
|
||||||
|
let
|
||||||
|
p = cfg.package.override
|
||||||
|
({
|
||||||
|
inherit phpCfg;
|
||||||
|
withPgsql = cfg.database.type == "pgsql";
|
||||||
|
withMysql = cfg.database.type == "mysql";
|
||||||
|
inherit (cfg) minifyStaticFiles;
|
||||||
|
} // lib.optionalAttrs (lib.isAttrs cfg.minifyStaticFiles) (with cfg.minifyStaticFiles; {
|
||||||
|
esbuild = esbuild.package;
|
||||||
|
lightningcss = lightningcss.package;
|
||||||
|
scour = scour.package;
|
||||||
|
}));
|
||||||
|
in
|
||||||
|
p.overrideAttrs (finalAttrs: prevAttrs:
|
||||||
|
let
|
||||||
|
appDir = "$out/share/php/${finalAttrs.pname}";
|
||||||
|
|
||||||
|
stateDirectories = ''
|
||||||
|
# Symlinking in our state directories
|
||||||
|
rm -rf $out/.env $out/cache ${appDir}/public/cache
|
||||||
|
ln -s ${cfg.dataDir}/.env ${appDir}/.env
|
||||||
|
ln -s ${cfg.dataDir}/public/cache ${appDir}/public/cache
|
||||||
|
ln -s ${cfg.logDir} ${appDir}/log
|
||||||
|
ln -s ${cfg.runtimeDir}/cache ${appDir}/cache
|
||||||
|
'';
|
||||||
|
|
||||||
|
exposeComposer = ''
|
||||||
|
# Expose PHP Composer for scripts
|
||||||
|
mkdir -p $out/bin
|
||||||
|
echo "#!${lib.getExe pkgs.dash}" > $out/bin/movim-composer
|
||||||
|
echo "${finalAttrs.php.packages.composer}/bin/composer --working-dir="${appDir}" \"\$@\"" >> $out/bin/movim-composer
|
||||||
|
chmod +x $out/bin/movim-composer
|
||||||
|
'';
|
||||||
|
|
||||||
|
podConfigInputDisableReplace = lib.optionalString (podConfigFlags != "")
|
||||||
|
(lib.concatStringsSep "\n"
|
||||||
|
(lib.attrsets.foldlAttrs
|
||||||
|
(acc: k: v:
|
||||||
|
acc ++ lib.optional (v != null)
|
||||||
|
# Disable all Admin panel options that were set in the
|
||||||
|
# `cfg.podConfig` to prevent confusing situtions where the
|
||||||
|
# values are rewritten on server reboot
|
||||||
|
''
|
||||||
|
substituteInPlace ${appDir}/app/widgets/AdminMain/adminmain.tpl \
|
||||||
|
--replace-warn 'name="${k}"' 'name="${k}" disabled'
|
||||||
|
'')
|
||||||
|
[ ]
|
||||||
|
cfg.podConfig));
|
||||||
|
|
||||||
|
precompressStaticFilesJobs =
|
||||||
|
let
|
||||||
|
inherit (cfg.precompressStaticFiles) brotli gzip;
|
||||||
|
|
||||||
|
findTextFileNames = lib.concatStringsSep " -o "
|
||||||
|
(builtins.map (n: ''-iname "*.${n}"'')
|
||||||
|
[ "css" "ini" "js" "json" "manifest" "mjs" "svg" "webmanifest" ]);
|
||||||
|
in
|
||||||
|
lib.concatStringsSep "\n" [
|
||||||
|
(lib.optionalString brotli.enable ''
|
||||||
|
echo -n "Precompressing static files with Brotli …"
|
||||||
|
find ${appDir}/public -type f ${findTextFileNames} \
|
||||||
|
| ${lib.getExe pkgs.parallel} ${lib.escapeShellArgs [
|
||||||
|
"--will-cite"
|
||||||
|
"-j $NIX_BUILD_CORES"
|
||||||
|
"${lib.getExe brotli.package} --keep --quality=${builtins.toString brotli.compressionLevel} --output={}.br {}"
|
||||||
|
]}
|
||||||
|
echo " done."
|
||||||
|
'')
|
||||||
|
(lib.optionalString gzip.enable ''
|
||||||
|
echo -n "Precompressing static files with Gzip …"
|
||||||
|
find ${appDir}/public -type f ${findTextFileNames} \
|
||||||
|
| ${lib.getExe pkgs.parallel} ${lib.escapeShellArgs [
|
||||||
|
"--will-cite"
|
||||||
|
"-j $NIX_BUILD_CORES"
|
||||||
|
"${lib.getExe gzip.package} -c -${builtins.toString gzip.compressionLevel} {} > {}.gz"
|
||||||
|
]}
|
||||||
|
echo " done."
|
||||||
|
'')
|
||||||
|
];
|
||||||
|
in
|
||||||
|
{
|
||||||
|
postInstall = lib.concatStringsSep "\n\n" [
|
||||||
|
prevAttrs.postInstall
|
||||||
|
stateDirectories
|
||||||
|
exposeComposer
|
||||||
|
podConfigInputDisableReplace
|
||||||
|
precompressStaticFilesJobs
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
configFile = pipe cfg.settings [
|
||||||
|
(filterAttrsRecursive (_: v: v != null))
|
||||||
|
(generators.toKeyValue { })
|
||||||
|
(pkgs.writeText "movim-env")
|
||||||
|
];
|
||||||
|
|
||||||
|
pool = "movim";
|
||||||
|
fpm = config.services.phpfpm.pools.${pool};
|
||||||
|
phpExecutionUnit = "phpfpm-${pool}";
|
||||||
|
|
||||||
|
dbService = {
|
||||||
|
"postgresql" = "postgresql.service";
|
||||||
|
"mysql" = "mysql.service";
|
||||||
|
}.${cfg.database.type};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.services = {
|
||||||
|
movim = {
|
||||||
|
enable = mkEnableOption "a Movim instance";
|
||||||
|
package = mkPackageOption pkgs "movim" { };
|
||||||
|
phpPackage = mkPackageOption pkgs "php" { };
|
||||||
|
|
||||||
|
phpCfg = mkOption {
|
||||||
|
type = with types; attrsOf (oneOf [ int str bool ]);
|
||||||
|
defaultText = literalExpression (generators.toPretty { } defaultPHPCfg);
|
||||||
|
default = { };
|
||||||
|
description = "Extra PHP INI options such as `memory_limit`, `max_execution_time`, etc.";
|
||||||
|
};
|
||||||
|
|
||||||
|
user = mkOption {
|
||||||
|
type = types.nonEmptyStr;
|
||||||
|
default = "movim";
|
||||||
|
description = "User running Movim service";
|
||||||
|
};
|
||||||
|
|
||||||
|
group = mkOption {
|
||||||
|
type = types.nonEmptyStr;
|
||||||
|
default = "movim";
|
||||||
|
description = "Group running Movim service";
|
||||||
|
};
|
||||||
|
|
||||||
|
dataDir = mkOption {
|
||||||
|
type = types.nonEmptyStr;
|
||||||
|
default = "/var/lib/movim";
|
||||||
|
description = "State directory of the `movim` user which holds the application’s state & data.";
|
||||||
|
};
|
||||||
|
|
||||||
|
logDir = mkOption {
|
||||||
|
type = types.nonEmptyStr;
|
||||||
|
default = "/var/log/movim";
|
||||||
|
description = "Log directory of the `movim` user which holds the application’s logs.";
|
||||||
|
};
|
||||||
|
|
||||||
|
runtimeDir = mkOption {
|
||||||
|
type = types.nonEmptyStr;
|
||||||
|
default = "/run/movim";
|
||||||
|
description = "Runtime directory of the `movim` user which holds the application’s caches & temporary files.";
|
||||||
|
};
|
||||||
|
|
||||||
|
domain = mkOption {
|
||||||
|
type = types.nonEmptyStr;
|
||||||
|
description = "Fully-qualified domain name (FQDN) for the Movim instance.";
|
||||||
|
};
|
||||||
|
|
||||||
|
port = mkOption {
|
||||||
|
type = types.port;
|
||||||
|
default = 8080;
|
||||||
|
description = "Movim daemon port.";
|
||||||
|
};
|
||||||
|
|
||||||
|
debug = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
description = "Debugging logs.";
|
||||||
|
};
|
||||||
|
|
||||||
|
verbose = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
description = "Verbose logs.";
|
||||||
|
};
|
||||||
|
|
||||||
|
minifyStaticFiles = mkOption {
|
||||||
|
type = with types; either bool (submodule {
|
||||||
|
options = {
|
||||||
|
script = mkOption {
|
||||||
|
type = types.submodule {
|
||||||
|
options = {
|
||||||
|
enable = mkEnableOption "Script minification";
|
||||||
|
package = mkPackageOption pkgs "esbuild" { };
|
||||||
|
target = mkOption {
|
||||||
|
type = with types; nullOr nonEmptyStr;
|
||||||
|
default = null;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
style = mkOption {
|
||||||
|
type = types.submodule {
|
||||||
|
options = {
|
||||||
|
enable = mkEnableOption "Script minification";
|
||||||
|
package = mkPackageOption pkgs "lightningcss" { };
|
||||||
|
target = mkOption {
|
||||||
|
type = with types; nullOr nonEmptyStr;
|
||||||
|
default = null;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
svg = mkOption {
|
||||||
|
type = types.submodule {
|
||||||
|
options = {
|
||||||
|
enable = mkEnableOption "SVG minification";
|
||||||
|
package = mkPackageOption pkgs "scour" { };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
});
|
||||||
|
default = true;
|
||||||
|
description = "Do minification on public static files";
|
||||||
|
};
|
||||||
|
|
||||||
|
precompressStaticFiles = mkOption {
|
||||||
|
type = with types; submodule {
|
||||||
|
options = {
|
||||||
|
brotli = {
|
||||||
|
enable = mkEnableOption "Brotli precompression";
|
||||||
|
package = mkPackageOption pkgs "brotli" { };
|
||||||
|
compressionLevel = mkOption {
|
||||||
|
type = types.ints.between 0 11;
|
||||||
|
default = 11;
|
||||||
|
description = "Brotli compression level";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
gzip = {
|
||||||
|
enable = mkEnableOption "Gzip precompression";
|
||||||
|
package = mkPackageOption pkgs "gzip" { };
|
||||||
|
compressionLevel = mkOption {
|
||||||
|
type = types.ints.between 1 9;
|
||||||
|
default = 9;
|
||||||
|
description = "Gzip compression level";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
default = {
|
||||||
|
brotli.enable = true;
|
||||||
|
gzip.enable = false;
|
||||||
|
};
|
||||||
|
description = "Aggressively precompress static files";
|
||||||
|
};
|
||||||
|
|
||||||
|
podConfig = mkOption {
|
||||||
|
type = types.submodule {
|
||||||
|
options = {
|
||||||
|
info = mkOption {
|
||||||
|
type = with types; nullOr str;
|
||||||
|
default = null;
|
||||||
|
description = "Content of the info box on the login page";
|
||||||
|
};
|
||||||
|
|
||||||
|
description = mkOption {
|
||||||
|
type = with types; nullOr str;
|
||||||
|
default = null;
|
||||||
|
description = "General description of the instance";
|
||||||
|
};
|
||||||
|
|
||||||
|
timezone = mkOption {
|
||||||
|
type = with types; nullOr str;
|
||||||
|
default = null;
|
||||||
|
description = "The server timezone";
|
||||||
|
};
|
||||||
|
|
||||||
|
restrictsuggestions = mkOption {
|
||||||
|
type = with types; nullOr bool;
|
||||||
|
default = null;
|
||||||
|
description = "Only suggest chatrooms, Communities and other contents that are available on the user XMPP server and related services";
|
||||||
|
};
|
||||||
|
|
||||||
|
chatonly = mkOption {
|
||||||
|
type = with types; nullOr bool;
|
||||||
|
default = null;
|
||||||
|
description = "Disable all the social feature (Communities, Blog…) and keep only the chat ones";
|
||||||
|
};
|
||||||
|
|
||||||
|
disableregistration = mkOption {
|
||||||
|
type = with types; nullOr bool;
|
||||||
|
default = null;
|
||||||
|
description = "Remove the XMPP registration flow and buttons from the interface";
|
||||||
|
};
|
||||||
|
|
||||||
|
loglevel = mkOption {
|
||||||
|
type = with types; nullOr (ints.between 0 3);
|
||||||
|
default = null;
|
||||||
|
description = "The server loglevel";
|
||||||
|
};
|
||||||
|
|
||||||
|
locale = mkOption {
|
||||||
|
type = with types; nullOr str;
|
||||||
|
default = null;
|
||||||
|
description = "The server main locale";
|
||||||
|
};
|
||||||
|
|
||||||
|
xmppdomain = mkOption {
|
||||||
|
type = with types; nullOr str;
|
||||||
|
default = null;
|
||||||
|
description = "The default XMPP server domain";
|
||||||
|
};
|
||||||
|
|
||||||
|
xmppdescription = mkOption {
|
||||||
|
type = with types; nullOr str;
|
||||||
|
default = null;
|
||||||
|
description = "The default XMPP server description";
|
||||||
|
};
|
||||||
|
|
||||||
|
xmppwhitelist = mkOption {
|
||||||
|
type = with types; nullOr str;
|
||||||
|
default = null;
|
||||||
|
description = "The allowlisted XMPP servers";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
default = { };
|
||||||
|
description = ''
|
||||||
|
Pod configuration (values from `php daemon.php config --help`).
|
||||||
|
Note that these values will now be disabled in the admin panel.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
settings = mkOption {
|
||||||
|
type = with types; attrsOf (nullOr (oneOf [ int str bool ]));
|
||||||
|
default = { };
|
||||||
|
description = ".env settings for Movim. Secrets should use `secretFile` option instead. `null`s will be culled.";
|
||||||
|
};
|
||||||
|
|
||||||
|
secretFile = mkOption {
|
||||||
|
type = with types; nullOr path;
|
||||||
|
default = null;
|
||||||
|
description = "The secret file to be sourced for the .env settings.";
|
||||||
|
};
|
||||||
|
|
||||||
|
database = {
|
||||||
|
type = mkOption {
|
||||||
|
type = types.enum [ "mysql" "postgresql" ];
|
||||||
|
example = "mysql";
|
||||||
|
default = "postgresql";
|
||||||
|
description = "Database engine to use.";
|
||||||
|
};
|
||||||
|
|
||||||
|
name = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "movim";
|
||||||
|
description = "Database name.";
|
||||||
|
};
|
||||||
|
|
||||||
|
user = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "movim";
|
||||||
|
description = "Database username.";
|
||||||
|
};
|
||||||
|
|
||||||
|
createLocally = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = true;
|
||||||
|
description = "local database using UNIX socket authentication";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
nginx = mkOption {
|
||||||
|
type = with types; nullOr (submodule
|
||||||
|
(import ../web-servers/nginx/vhost-options.nix {
|
||||||
|
inherit config lib;
|
||||||
|
}));
|
||||||
|
default = null;
|
||||||
|
example = lib.literalExpression /* nginx */ ''
|
||||||
|
{
|
||||||
|
serverAliases = [
|
||||||
|
"pics.''${config.networking.domain}"
|
||||||
|
];
|
||||||
|
enableACME = true;
|
||||||
|
forceHttps = true;
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
description = ''
|
||||||
|
With this option, you can customize an nginx virtual host which already has sensible defaults for Movim.
|
||||||
|
Set to `{ }` if you do not need any customization to the virtual host.
|
||||||
|
If enabled, then by default, the {option}`serverName` is `''${domain}`,
|
||||||
|
If this is set to null (the default), no nginx virtualHost will be configured.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
poolConfig = mkOption {
|
||||||
|
type = with types; attrsOf (oneOf [ int str bool ]);
|
||||||
|
default = { };
|
||||||
|
description = "Options for Movim’s PHP-FPM pool.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkIf cfg.enable {
|
||||||
|
environment.systemPackages = [ cfg.package ];
|
||||||
|
|
||||||
|
users = {
|
||||||
|
users = {
|
||||||
|
movim = mkIf (cfg.user == "movim") {
|
||||||
|
isSystemUser = true;
|
||||||
|
group = cfg.group;
|
||||||
|
};
|
||||||
|
"${config.services.nginx.user}".extraGroups = [ cfg.group ];
|
||||||
|
};
|
||||||
|
groups = {
|
||||||
|
${cfg.group} = { };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
services = {
|
||||||
|
movim = {
|
||||||
|
settings = mkMerge [
|
||||||
|
{
|
||||||
|
DAEMON_URL = "//${cfg.domain}";
|
||||||
|
DAEMON_PORT = cfg.port;
|
||||||
|
DAEMON_INTERFACE = "127.0.0.1";
|
||||||
|
DAEMON_DEBUG = cfg.debug;
|
||||||
|
DAEMON_VERBOSE = cfg.verbose;
|
||||||
|
}
|
||||||
|
(mkIf cfg.database.createLocally {
|
||||||
|
DB_DRIVER = {
|
||||||
|
"postgresql" = "pgsql";
|
||||||
|
"mysql" = "mysql";
|
||||||
|
}.${cfg.database.type};
|
||||||
|
DB_HOST = "localhost";
|
||||||
|
DB_PORT = config.services.${cfg.database.type}.settings.port;
|
||||||
|
DB_DATABASE = cfg.database.name;
|
||||||
|
DB_USERNAME = cfg.database.user;
|
||||||
|
DB_PASSWORD = "";
|
||||||
|
})
|
||||||
|
];
|
||||||
|
|
||||||
|
poolConfig = lib.mapAttrs' (n: v: lib.nameValuePair n (lib.mkDefault v)) {
|
||||||
|
"pm" = "dynamic";
|
||||||
|
"php_admin_value[error_log]" = "stderr";
|
||||||
|
"php_admin_flag[log_errors]" = true;
|
||||||
|
"catch_workers_output" = true;
|
||||||
|
"pm.max_children" = 32;
|
||||||
|
"pm.start_servers" = 2;
|
||||||
|
"pm.min_spare_servers" = 2;
|
||||||
|
"pm.max_spare_servers" = 8;
|
||||||
|
"pm.max_requests" = 500;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
nginx = mkIf (cfg.nginx != null) {
|
||||||
|
enable = true;
|
||||||
|
recommendedOptimisation = true;
|
||||||
|
recommendedGzipSettings = true;
|
||||||
|
recommendedBrotliSettings = true;
|
||||||
|
recommendedProxySettings = true;
|
||||||
|
# TODO: recommended cache options already in Nginx⁇
|
||||||
|
appendHttpConfig = /* nginx */ ''
|
||||||
|
fastcgi_cache_path /tmp/nginx_cache levels=1:2 keys_zone=nginx_cache:100m inactive=60m;
|
||||||
|
fastcgi_cache_key "$scheme$request_method$host$request_uri";
|
||||||
|
'';
|
||||||
|
virtualHosts."${cfg.domain}" = mkMerge [
|
||||||
|
cfg.nginx
|
||||||
|
{
|
||||||
|
root = lib.mkForce "${package}/share/php/movim/public";
|
||||||
|
locations = {
|
||||||
|
"/favicon.ico" = {
|
||||||
|
priority = 100;
|
||||||
|
extraConfig = /* nginx */ ''
|
||||||
|
access_log off;
|
||||||
|
log_not_found off;
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
"/robots.txt" = {
|
||||||
|
priority = 100;
|
||||||
|
extraConfig = /* nginx */ ''
|
||||||
|
access_log off;
|
||||||
|
log_not_found off;
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
"~ /\\.(?!well-known).*" = {
|
||||||
|
priority = 210;
|
||||||
|
extraConfig = /* nginx */ ''
|
||||||
|
deny all;
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
# Ask nginx to cache every URL starting with "/picture"
|
||||||
|
"/picture" = {
|
||||||
|
priority = 400;
|
||||||
|
tryFiles = "$uri $uri/ /index.php$is_args$args";
|
||||||
|
extraConfig = /* nginx */ ''
|
||||||
|
set $no_cache 0; # Enable cache only there
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
"/" = {
|
||||||
|
priority = 490;
|
||||||
|
tryFiles = "$uri $uri/ /index.php$is_args$args";
|
||||||
|
extraConfig = /* nginx */ ''
|
||||||
|
# https://github.com/movim/movim/issues/314
|
||||||
|
add_header Content-Security-Policy "default-src 'self'; img-src 'self' aesgcm: https:; media-src 'self' aesgcm: https:; script-src 'self' 'unsafe-eval' 'unsafe-inline'; style-src 'self' 'unsafe-inline';";
|
||||||
|
set $no_cache 1;
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
"~ \\.php$" = {
|
||||||
|
priority = 500;
|
||||||
|
tryFiles = "$uri =404";
|
||||||
|
extraConfig = /* nginx */ ''
|
||||||
|
include ${config.services.nginx.package}/conf/fastcgi.conf;
|
||||||
|
add_header X-Cache $upstream_cache_status;
|
||||||
|
fastcgi_ignore_headers "Cache-Control" "Expires" "Set-Cookie";
|
||||||
|
fastcgi_cache nginx_cache;
|
||||||
|
fastcgi_cache_valid any 7d;
|
||||||
|
fastcgi_cache_bypass $no_cache;
|
||||||
|
fastcgi_no_cache $no_cache;
|
||||||
|
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||||
|
fastcgi_index index.php;
|
||||||
|
fastcgi_pass unix:${fpm.socket};
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
"/ws/" = {
|
||||||
|
priority = 900;
|
||||||
|
proxyPass = "http://${cfg.settings.DAEMON_INTERFACE}:${builtins.toString cfg.port}/";
|
||||||
|
proxyWebsockets = true;
|
||||||
|
recommendedProxySettings = true;
|
||||||
|
extraConfig = /* nginx */ ''
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_redirect off;
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
extraConfig = /* ngnix */ ''
|
||||||
|
index index.php;
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
mysql = mkIf (cfg.database.createLocally && cfg.database.type == "mysql") {
|
||||||
|
enable = mkDefault true;
|
||||||
|
package = mkDefault pkgs.mariadb;
|
||||||
|
ensureDatabases = [ cfg.database.name ];
|
||||||
|
ensureUsers = [{
|
||||||
|
name = cfg.user;
|
||||||
|
ensureDBOwnership = true;
|
||||||
|
}];
|
||||||
|
};
|
||||||
|
|
||||||
|
postgresql = mkIf (cfg.database.createLocally && cfg.database.type == "postgresql") {
|
||||||
|
enable = mkDefault true;
|
||||||
|
ensureDatabases = [ cfg.database.name ];
|
||||||
|
ensureUsers = [{
|
||||||
|
name = cfg.user;
|
||||||
|
ensureDBOwnership = true;
|
||||||
|
}];
|
||||||
|
authentication = ''
|
||||||
|
host ${cfg.database.name} ${cfg.database.user} localhost trust
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
phpfpm.pools.${pool} =
|
||||||
|
let
|
||||||
|
socketOwner =
|
||||||
|
if (cfg.nginx != null)
|
||||||
|
then config.services.nginx.user
|
||||||
|
else cfg.user;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
phpPackage = package.php;
|
||||||
|
user = cfg.user;
|
||||||
|
group = cfg.group;
|
||||||
|
|
||||||
|
phpOptions = ''
|
||||||
|
error_log = 'stderr'
|
||||||
|
log_errors = on
|
||||||
|
'';
|
||||||
|
|
||||||
|
settings = {
|
||||||
|
"listen.owner" = socketOwner;
|
||||||
|
"listen.group" = cfg.group;
|
||||||
|
"listen.mode" = "0660";
|
||||||
|
"catch_workers_output" = true;
|
||||||
|
} // cfg.poolConfig;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd = {
|
||||||
|
services.movim-data-setup = {
|
||||||
|
description = "Movim setup: .env file, databases init, cache reload";
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
requiredBy = [ "${phpExecutionUnit}.service" ];
|
||||||
|
before = [ "${phpExecutionUnit}.service" ];
|
||||||
|
after = lib.optional cfg.database.createLocally dbService;
|
||||||
|
requires = lib.optional cfg.database.createLocally dbService;
|
||||||
|
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
User = cfg.user;
|
||||||
|
Group = cfg.group;
|
||||||
|
UMask = "077";
|
||||||
|
} // lib.optionalAttrs (cfg.secretFile != null) {
|
||||||
|
LoadCredential = "env-secrets:${cfg.secretFile}";
|
||||||
|
};
|
||||||
|
|
||||||
|
script = ''
|
||||||
|
# Env vars
|
||||||
|
rm -f ${cfg.dataDir}/.env
|
||||||
|
cp --no-preserve=all ${configFile} ${cfg.dataDir}/.env
|
||||||
|
echo -e '\n' >> ${cfg.dataDir}/.env
|
||||||
|
if [[ -f "$CREDENTIALS_DIRECTORY/env-secrets" ]]; then
|
||||||
|
cat "$CREDENTIALS_DIRECTORY/env-secrets" >> ${cfg.dataDir}/.env
|
||||||
|
echo -e '\n' >> ${cfg.dataDir}/.env
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Caches, logs
|
||||||
|
mkdir -p ${cfg.dataDir}/public/cache ${cfg.logDir} ${cfg.runtimeDir}/cache
|
||||||
|
chmod -R ug+rw ${cfg.dataDir}/public/cache
|
||||||
|
chmod -R ug+rw ${cfg.logDir}
|
||||||
|
chmod -R ug+rwx ${cfg.runtimeDir}/cache
|
||||||
|
|
||||||
|
# Migrations
|
||||||
|
MOVIM_VERSION="${package.version}"
|
||||||
|
if [[ ! -f "${cfg.dataDir}/.migration-version" ]] || [[ "$MOVIM_VERSION" != "$(<${cfg.dataDir}/.migration-version)" ]]; then
|
||||||
|
${package}/bin/movim-composer movim:migrate && echo $MOVIM_VERSION > ${cfg.dataDir}/.migration-version
|
||||||
|
fi
|
||||||
|
''
|
||||||
|
+ lib.optionalString (podConfigFlags != "") (
|
||||||
|
let
|
||||||
|
flags = lib.concatStringsSep " "
|
||||||
|
([ "--no-interaction" ]
|
||||||
|
++ lib.optional cfg.debug "-vvv"
|
||||||
|
++ lib.optional (!cfg.debug && cfg.verbose) "-v");
|
||||||
|
in
|
||||||
|
''
|
||||||
|
${lib.getExe package} config ${podConfigFlags}
|
||||||
|
''
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
services.movim = {
|
||||||
|
description = "Movim daemon";
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
after = [ "movim-data-setup.service" ];
|
||||||
|
requires = [ "movim-data-setup.service" ]
|
||||||
|
++ lib.optional cfg.database.createLocally dbService;
|
||||||
|
environment = {
|
||||||
|
PUBLIC_URL = "//${cfg.domain}";
|
||||||
|
WS_PORT = builtins.toString cfg.port;
|
||||||
|
};
|
||||||
|
|
||||||
|
serviceConfig = {
|
||||||
|
User = cfg.user;
|
||||||
|
Group = cfg.group;
|
||||||
|
WorkingDirectory = "${package}/share/php/movim";
|
||||||
|
ExecStart = "${lib.getExe package} start";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
services.${phpExecutionUnit} = {
|
||||||
|
after = [ "movim-data-setup.service" ];
|
||||||
|
requires = [ "movim-data-setup.service" ]
|
||||||
|
++ lib.optional cfg.database.createLocally dbService;
|
||||||
|
};
|
||||||
|
|
||||||
|
tmpfiles.settings."10-movim" = with cfg; {
|
||||||
|
"${dataDir}".d = { inherit user group; mode = "0710"; };
|
||||||
|
"${dataDir}/public".d = { inherit user group; mode = "0750"; };
|
||||||
|
"${dataDir}/public/cache".d = { inherit user group; mode = "0750"; };
|
||||||
|
"${runtimeDir}".d = { inherit user group; mode = "0700"; };
|
||||||
|
"${runtimeDir}/cache".d = { inherit user group; mode = "0700"; };
|
||||||
|
"${logDir}".d = { inherit user group; mode = "0700"; };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
|
@ -558,6 +558,7 @@ in {
|
||||||
morty = handleTest ./morty.nix {};
|
morty = handleTest ./morty.nix {};
|
||||||
mosquitto = handleTest ./mosquitto.nix {};
|
mosquitto = handleTest ./mosquitto.nix {};
|
||||||
moosefs = handleTest ./moosefs.nix {};
|
moosefs = handleTest ./moosefs.nix {};
|
||||||
|
movim = discoverTests (import ./web-apps/movim { inherit handleTestOn; });
|
||||||
mpd = handleTest ./mpd.nix {};
|
mpd = handleTest ./mpd.nix {};
|
||||||
mpv = handleTest ./mpv.nix {};
|
mpv = handleTest ./mpv.nix {};
|
||||||
mtp = handleTest ./mtp.nix {};
|
mtp = handleTest ./mtp.nix {};
|
||||||
|
|
8
nixos/tests/web-apps/movim/default.nix
Normal file
8
nixos/tests/web-apps/movim/default.nix
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
{ system ? builtins.currentSystem, handleTestOn }:
|
||||||
|
|
||||||
|
let
|
||||||
|
supportedSystems = [ "x86_64-linux" "i686-linux" ];
|
||||||
|
in
|
||||||
|
{
|
||||||
|
standard = handleTestOn supportedSystems ./standard.nix { inherit system; };
|
||||||
|
}
|
102
nixos/tests/web-apps/movim/standard.nix
Normal file
102
nixos/tests/web-apps/movim/standard.nix
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
import ../../make-test-python.nix ({ lib, pkgs, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
movim = {
|
||||||
|
domain = "movim.local";
|
||||||
|
info = "No ToS in tests";
|
||||||
|
description = "NixOS testing server";
|
||||||
|
};
|
||||||
|
xmpp = {
|
||||||
|
domain = "xmpp.local";
|
||||||
|
admin = rec {
|
||||||
|
JID = "${username}@${xmpp.domain}";
|
||||||
|
username = "romeo";
|
||||||
|
password = "juliet";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
name = "movim-standard";
|
||||||
|
|
||||||
|
meta = {
|
||||||
|
maintainers = with pkgs.lib.maintainers; [ toastal ];
|
||||||
|
};
|
||||||
|
|
||||||
|
nodes = {
|
||||||
|
server = { pkgs, ... }: {
|
||||||
|
services.movim = {
|
||||||
|
inherit (movim) domain;
|
||||||
|
enable = true;
|
||||||
|
verbose = true;
|
||||||
|
podConfig = {
|
||||||
|
inherit (movim) description info;
|
||||||
|
xmppdomain = xmpp.domain;
|
||||||
|
};
|
||||||
|
nginx = { };
|
||||||
|
};
|
||||||
|
|
||||||
|
services.prosody = {
|
||||||
|
enable = true;
|
||||||
|
xmppComplianceSuite = false;
|
||||||
|
disco_items = [
|
||||||
|
{ url = "upload.${xmpp.domain}"; description = "File Uploads"; }
|
||||||
|
];
|
||||||
|
virtualHosts."${xmpp.domain}" = {
|
||||||
|
inherit (xmpp) domain;
|
||||||
|
enabled = true;
|
||||||
|
extraConfig = ''
|
||||||
|
Component "pubsub.${xmpp.domain}" "pubsub"
|
||||||
|
pubsub_max_items = 10000
|
||||||
|
expose_publisher = true
|
||||||
|
|
||||||
|
Component "upload.${xmpp.domain}" "http_file_share"
|
||||||
|
http_external_url = "http://upload.${xmpp.domain}"
|
||||||
|
http_file_share_expires_after = 300 * 24 * 60 * 60
|
||||||
|
http_file_share_size_limit = 1024 * 1024 * 1024
|
||||||
|
http_file_share_daily_quota = 4 * 1024 * 1024 * 1024
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
extraConfig = ''
|
||||||
|
pep_max_items = 10000
|
||||||
|
|
||||||
|
http_paths = {
|
||||||
|
file_share = "/";
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
networking.extraHosts = ''
|
||||||
|
127.0.0.1 ${movim.domain}
|
||||||
|
127.0.0.1 ${xmpp.domain}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
testScript = /* python */ ''
|
||||||
|
server.wait_for_unit("phpfpm-movim.service")
|
||||||
|
server.wait_for_unit("nginx.service")
|
||||||
|
server.wait_for_open_port(80)
|
||||||
|
|
||||||
|
server.wait_for_unit("prosody.service")
|
||||||
|
server.succeed('prosodyctl status | grep "Prosody is running"')
|
||||||
|
server.succeed("prosodyctl register ${xmpp.admin.username} ${xmpp.domain} ${xmpp.admin.password}")
|
||||||
|
|
||||||
|
server.wait_for_unit("movim.service")
|
||||||
|
|
||||||
|
# Test unauthenticated
|
||||||
|
server.fail("curl -L --fail-with-body --max-redirs 0 http://${movim.domain}/chat")
|
||||||
|
|
||||||
|
# Test basic Websocket
|
||||||
|
server.succeed("echo \"\" | ${lib.getExe pkgs.websocat} 'ws://${movim.domain}/ws/?path=login&offset=0' --origin 'http://${movim.domain}'")
|
||||||
|
|
||||||
|
# Test login + create cookiejar
|
||||||
|
login_html = server.succeed("curl --fail-with-body -c /tmp/cookies http://${movim.domain}/login")
|
||||||
|
assert "${movim.description}" in login_html
|
||||||
|
assert "${movim.info}" in login_html
|
||||||
|
|
||||||
|
# Test authentication POST
|
||||||
|
server.succeed("curl --fail-with-body -b /tmp/cookies -X POST --data-urlencode 'username=${xmpp.admin.JID}' --data-urlencode 'password=${xmpp.admin.password}' http://${movim.domain}/login")
|
||||||
|
|
||||||
|
server.succeed("curl -L --fail-with-body --max-redirs 1 -b /tmp/cookies http://${movim.domain}/chat")
|
||||||
|
'';
|
||||||
|
})
|
|
@ -1,20 +1,51 @@
|
||||||
{ lib
|
{ lib
|
||||||
|
, fetchpatch
|
||||||
, fetchFromGitHub
|
, fetchFromGitHub
|
||||||
|
, dash
|
||||||
, php
|
, php
|
||||||
, phpCfg ? null
|
, phpCfg ? null
|
||||||
, withPgsql ? true # “strongly recommended” according to docs
|
, withPgsql ? true # “strongly recommended” according to docs
|
||||||
, withMysql ? false
|
, withMysql ? false
|
||||||
|
, minifyStaticFiles ? false # default files are often not minified
|
||||||
|
, parallel
|
||||||
|
, esbuild
|
||||||
|
, lightningcss
|
||||||
|
, scour
|
||||||
|
, nixosTests
|
||||||
}:
|
}:
|
||||||
|
|
||||||
|
let
|
||||||
|
defaultMinifyOpts = {
|
||||||
|
script = {
|
||||||
|
enable = false;
|
||||||
|
target = "es2021";
|
||||||
|
};
|
||||||
|
style = {
|
||||||
|
enable = false;
|
||||||
|
browserslist = "defaults, Firefox ESR, last 20 Firefox major versions, last 20 Chrome major versions, last 3 Safari major versions, last 1 KaiOS version, and supports css-variables";
|
||||||
|
};
|
||||||
|
svg = {
|
||||||
|
enable = false;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
minify = lib.recursiveUpdate defaultMinifyOpts
|
||||||
|
(if lib.isBool minifyStaticFiles && minifyStaticFiles then
|
||||||
|
{ script.enable = true; style.enable = true; svg.enable = true; }
|
||||||
|
else if lib.isAttrs minifyStaticFiles then
|
||||||
|
lib.filterAttrsRecursive (_: v: v != null) minifyStaticFiles
|
||||||
|
else
|
||||||
|
{ });
|
||||||
|
in
|
||||||
php.buildComposerProject (finalAttrs: {
|
php.buildComposerProject (finalAttrs: {
|
||||||
pname = "movim";
|
pname = "movim";
|
||||||
version = "0.23";
|
version = "0.23.0.20240328";
|
||||||
|
|
||||||
src = fetchFromGitHub {
|
src = fetchFromGitHub {
|
||||||
owner = "movim";
|
owner = "movim";
|
||||||
repo = "movim";
|
repo = "movim";
|
||||||
rev = "v${finalAttrs.version}";
|
rev = "c3a43cd7e3a1a3a6efd595470e6a85b2ec578cba";
|
||||||
hash = "sha256-9MBe2IRYxvUuCc5m7ajvIlBU7YVm4A3RABlOOIjpKoM=";
|
hash = "sha256-x0C4w3SRP3NMOhGSZOQALk6PNWUre4MvFW5cESr8Wvk=";
|
||||||
};
|
};
|
||||||
|
|
||||||
php = php.buildEnv ({
|
php = php.buildEnv ({
|
||||||
|
@ -28,16 +59,98 @@ php.buildComposerProject (finalAttrs: {
|
||||||
extraConfig = phpCfg;
|
extraConfig = phpCfg;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
nativeBuildInputs =
|
||||||
|
lib.optional (lib.any (x: x.enable) (lib.attrValues minify)) parallel
|
||||||
|
++ lib.optional minify.script.enable esbuild
|
||||||
|
++ lib.optional minify.style.enable lightningcss
|
||||||
|
++ lib.optional minify.svg.enable scour;
|
||||||
|
|
||||||
# no listed license
|
# no listed license
|
||||||
# pinned commonmark
|
# pinned commonmark
|
||||||
composerStrictValidation = false;
|
composerStrictValidation = false;
|
||||||
|
|
||||||
vendorHash = "sha256-PBoJbVuF0Qy7nNlL4yx446ivlZpPYNIai78yC0wWkCM=";
|
vendorHash = "sha256-RFIi1I+gcagRgkDpgQeR1oGJeBGA7z9q3DCfW+ZDr2Y=";
|
||||||
|
|
||||||
|
postPatch = ''
|
||||||
|
# Our modules are already wrapped, removes missing *.so warnings;
|
||||||
|
# replacing `$configuration` with actually-used flags.
|
||||||
|
substituteInPlace src/Movim/Daemon/Session.php \
|
||||||
|
--replace-fail "exec php ' . \$configuration " "exec php -dopcache.enable=1 -dopcache.enable_cli=1 ' "
|
||||||
|
|
||||||
|
# Point to PHP + PHP INI in the Nix store
|
||||||
|
substituteInPlace src/Movim/{Console/DaemonCommand.php,Daemon/Session.php} \
|
||||||
|
--replace-fail "exec php " "exec ${lib.getExe finalAttrs.php} "
|
||||||
|
substituteInPlace src/Movim/Console/DaemonCommand.php \
|
||||||
|
--replace-fail "<info>php vendor/bin/phinx migrate</info>" \
|
||||||
|
"<info>${lib.getBin finalAttrs.php} vendor/bin/phinx migrate</info>" \
|
||||||
|
--replace-fail "<info>php daemon.php setAdmin {jid}</info>" \
|
||||||
|
"<info>${finalAttrs.meta.mainProgram} setAdmin {jid}</info>"
|
||||||
|
|
||||||
|
# BUGFIX: Imagick API Changes for 7.x+
|
||||||
|
# See additionally: https://github.com/movim/movim/pull/1122
|
||||||
|
substituteInPlace src/Movim/Image.php \
|
||||||
|
--replace-fail "Imagick::ALPHACHANNEL_REMOVE" "Imagick::ALPHACHANNEL_OFF" \
|
||||||
|
--replace-fail "Imagick::ALPHACHANNEL_ACTIVATE" "Imagick::ALPHACHANNEL_ON"
|
||||||
|
'';
|
||||||
|
|
||||||
|
preBuild = lib.optionalString minify.script.enable ''
|
||||||
|
find ./public -type f -iname "*.js" \
|
||||||
|
| parallel ${lib.escapeShellArgs [
|
||||||
|
"--will-cite"
|
||||||
|
"-j $NIX_BUILD_CORES"
|
||||||
|
''
|
||||||
|
tmp="$(mktemp)"
|
||||||
|
esbuild {} --minify --target=${lib.escapeShellArg minify.script.target} --outfile=$tmp
|
||||||
|
[[ "$(stat -c %s $tmp)" -lt "$(stat -c %s {})" ]] && mv $tmp {}
|
||||||
|
''
|
||||||
|
]}
|
||||||
|
'' + lib.optionalString minify.style.enable ''
|
||||||
|
export BROWSERLIST=${lib.escapeShellArg minify.style.browserslist}
|
||||||
|
find ./public -type f -iname "*.css" \
|
||||||
|
| parallel ${lib.escapeShellArgs [
|
||||||
|
"--will-cite"
|
||||||
|
"-j $NIX_BUILD_CORES"
|
||||||
|
''
|
||||||
|
tmp="$(mktemp)"
|
||||||
|
lightningcss {} --minify --browserslist --output-file=$tmp
|
||||||
|
[[ "$(stat -c %s $tmp)" -lt "$(stat -c %s {})" ]] && mv $tmp {}
|
||||||
|
''
|
||||||
|
]}
|
||||||
|
'' + lib.optionalString minify.svg.enable ''
|
||||||
|
find ./public -type f -iname "*.svg" -a -not -path "*/emojis/*" \
|
||||||
|
| parallel ${lib.escapeShellArgs [
|
||||||
|
"--will-cite"
|
||||||
|
"-j $NIX_BUILD_CORES"
|
||||||
|
''
|
||||||
|
tmp="$(mktemp)"
|
||||||
|
scour -i {} -o $tmp --disable-style-to-xml --enable-comment-stripping --enable-viewboxing --indent=tab
|
||||||
|
[[ "$(stat -c %s $tmp)" -lt "$(stat -c %s {})" ]] && mv $tmp {}
|
||||||
|
''
|
||||||
|
]}
|
||||||
|
'';
|
||||||
|
|
||||||
|
postInstall = ''
|
||||||
|
mkdir -p $out/bin
|
||||||
|
echo "#!${lib.getExe dash}" > $out/bin/movim
|
||||||
|
echo "${lib.getExe finalAttrs.php} $out/share/php/${finalAttrs.pname}/daemon.php \"\$@\"" >> $out/bin/movim
|
||||||
|
chmod +x $out/bin/movim
|
||||||
|
|
||||||
|
mkdir -p $out/share/{bash-completion/completion,fish/vendor_completions.d,zsh/site-functions}
|
||||||
|
$out/bin/movim completion bash | sed "s/daemon.php/movim/g" > $out/share/bash-completion/completion/movim.bash
|
||||||
|
$out/bin/movim completion fish | sed "s/daemon.php/movim/g" > $out/share/fish/vendor_completions.d/movim.fish
|
||||||
|
$out/bin/movim completion zsh | sed "s/daemon.php/movim/g" > $out/share/zsh/site-functions/_movim
|
||||||
|
chmod +x $out/share/{bash-completion/completion/movim.bash,fish/vendor_completions.d/movim.fish,zsh/site-functions/_movim}
|
||||||
|
'';
|
||||||
|
|
||||||
|
passthru = {
|
||||||
|
tests = { inherit (nixosTests) movim; };
|
||||||
|
};
|
||||||
|
|
||||||
meta = {
|
meta = {
|
||||||
description = "a federated blogging & chat platform that acts as a web front end for the XMPP protocol";
|
description = "a federated blogging & chat platform that acts as a web front end for the XMPP protocol";
|
||||||
homepage = "https://movim.eu";
|
homepage = "https://movim.eu";
|
||||||
license = lib.licenses.agpl3Plus;
|
license = lib.licenses.agpl3Plus;
|
||||||
maintainers = with lib.maintainers; [ toastal ];
|
maintainers = with lib.maintainers; [ toastal ];
|
||||||
|
mainProgram = "movim";
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in New Issue
Block a user