Unverified Commit fb7ea8fe authored by nixpkgs-ci[bot]'s avatar nixpkgs-ci[bot] Committed by GitHub
Browse files

Merge staging-next into staging

parents a9700212 8cb1861b
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -235,3 +235,6 @@ ef85e0daa092c9eae0d32c7ce16b889728a5fbc0
d89ad6c70e0e89aaae75e9f886878ea4e103965a
e0fe216f4912dd88a021d12a44155fd2cfeb31c8
80d5b411f6397d5c3e755a0635d95742f76f3c75

# nixos/movim: format with nixfmt-rfc-style
43c1654cae47cbf987cb63758c06245fa95c1e3b
+2 −0
Original line number Diff line number Diff line
@@ -310,6 +310,8 @@

- `hardware.pulseaudio` has been renamed to `services.pulseaudio`.  The deprecated option names will continue to work, but causes a warning.

- `services.nextcloud` now uses systemd's credential mechanism to read in secret files. The `nextcloud-occ` wrapper script implements this using `systemd-run`, as such it now also requires root privileges or `$CREDENTIALS_DIRECTORY` set where running it as user `nextcloud` was enough previously.

- `minetest` has been renamed to `luanti` to match the upstream name change but aliases have been added. The new name hasn't resulted in many changes as of yet but older references to minetest should be sunset. See the [new name announcement](https://blog.minetest.net/2024/10/13/Introducing-Our-New-Name/) for more details.

- `poac` has been renamed to `cabinpkg` to match the upstream name change but an alias has been added. See the [new name announcement](https://github.com/orgs/cabinpkg/discussions/1052) for more details.
+53 −16
Original line number Diff line number Diff line
{
  config,
  lib,
  pkgs,
  config,
  utils,
  ...
}:
let
  inherit (lib)
    getExe
    mkEnableOption
    mkIf
    mkOption
    mkPackageOption
    ;

  cfg = config.services.evcc;

  format = pkgs.formats.yaml { };
@@ -17,27 +26,40 @@ in
  meta.maintainers = with lib.maintainers; [ hexa ];

  options.services.evcc = with lib.types; {
    enable = lib.mkEnableOption "EVCC, the extensible EV Charge Controller with PV integration";
    enable = mkEnableOption "EVCC, the extensible EV Charge Controller and Home Energy Management System";

    package = mkPackageOption pkgs "evcc" { };

    extraArgs = lib.mkOption {
    extraArgs = mkOption {
      type = listOf str;
      default = [ ];
      description = ''
        Extra arguments to pass to the evcc executable.
        Extra arguments to pass to the `evcc` executable.
      '';
    };

    settings = lib.mkOption {
    environmentFile = mkOption {
      type = nullOr path;
      default = null;
      example = /run/keys/evcc;
      description = ''
        File with environment variables to pass into the runtime environment.

        Useful to pass secrets into the configuration, that get applied using `envsubst`.
      '';
    };

    settings = mkOption {
      type = format.type;
      description = ''
        evcc configuration as a Nix attribute set.
        evcc configuration as a Nix attribute set. Supports substitution of secrets using `envsubst` from the `environmentFile`.

        Check for possible options in the sample [evcc.dist.yaml](https://github.com/andig/evcc/blob/${package.version}/evcc.dist.yaml).
      '';
    };
  };

  config = lib.mkIf cfg.enable {
  config = mkIf cfg.enable {
    systemd.services.evcc = {
      wants = [ "network-online.target" ];
      after = [
@@ -52,7 +74,21 @@ in
        getent
      ];
      serviceConfig = {
        ExecStart = "${package}/bin/evcc --config ${configFile} ${lib.escapeShellArgs cfg.extraArgs}";
        EnvironmentFile = lib.optionals (cfg.environmentFile != null) [ cfg.environmentFile ];
        ExecStartPre = utils.escapeSystemdExecArgs [
          (getExe pkgs.envsubst)
          "-i"
          configFile
          "-o"
          "/run/evcc/config.yaml"
        ];
        ExecStart = utils.escapeSystemdExecArgs (
          [
            (getExe cfg.package)
            "--config=/run/evcc/config.yaml"
          ]
          ++ cfg.extraArgs
        );
        CapabilityBoundingSet = [ "" ];
        DeviceAllow = [
          "char-ttyUSB"
@@ -61,14 +97,6 @@ in
        DynamicUser = true;
        LockPersonality = true;
        MemoryDenyWriteExecute = true;
        Restart = "on-failure";
        RestrictAddressFamilies = [
          "AF_INET"
          "AF_INET6"
          "AF_UNIX"
        ];
        RestrictNamespaces = true;
        RestrictRealtime = true;
        PrivateTmp = true;
        PrivateUsers = true;
        ProcSubset = "pid";
@@ -80,6 +108,15 @@ in
        ProtectKernelModules = true;
        ProtectKernelTunables = true;
        ProtectProc = "invisible";
        Restart = "on-failure";
        RestrictAddressFamilies = [
          "AF_INET"
          "AF_INET6"
          "AF_UNIX"
        ];
        RestrictNamespaces = true;
        RestrictRealtime = true;
        RuntimeDirectory = "evcc";
        StateDirectory = "evcc";
        SystemCallArchitectures = "native";
        SystemCallFilter = [
+349 −243
Original line number Diff line number Diff line
{ config, lib, pkgs, ... }:
{
  config,
  lib,
  pkgs,
  ...
}:

let
  inherit (lib)
@@ -28,39 +33,47 @@ let
    "opcache.fast_shutdown" = 1;
  };

  phpCfg = generators.toKeyValue
    { mkKeyValue = generators.mkKeyValueDefault { } " = "; }
    (defaultPHPCfg // cfg.phpCfg);
  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);
    lib.concatStringsSep " " (
      lib.attrsets.foldlAttrs (
        acc: k: v:
        acc ++ lib.optional (v != null) "--${k}=${bevalue v}"
      ) [ ] cfg.podConfig
    );

  package =
    let
      p = cfg.package.override
        ({
      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; {
        }
        // lib.optionalAttrs (lib.isAttrs cfg.minifyStaticFiles) (
          with cfg.minifyStaticFiles;
          {
            esbuild = esbuild.package;
            lightningcss = lightningcss.package;
            scour = scour.package;
        }));
          }
        )
      );
    in
    p.overrideAttrs (finalAttrs: prevAttrs:
    p.overrideAttrs (
      finalAttrs: prevAttrs:
      let
        appDir = "$out/share/php/${finalAttrs.pname}";

        stateDirectories = /* sh */ ''
        stateDirectories = # sh
          ''
            # Symlinking in our state directories
            rm -rf $out/{.env,cache} ${appDir}/{log,public/cache}
            ln -s ${cfg.dataDir}/.env ${appDir}/.env
@@ -69,7 +82,8 @@ let
            ln -s ${cfg.runtimeDir}/cache ${appDir}/cache
          '';

        exposeComposer = /* sh */ ''
        exposeComposer = # sh
          ''
            # Expose PHP Composer for scripts
            mkdir -p $out/bin
            echo "#!${lib.getExe pkgs.dash}" > $out/bin/movim-composer
@@ -77,31 +91,45 @@ let
            chmod +x $out/bin/movim-composer
          '';

        podConfigInputDisableReplace = lib.optionalString (podConfigFlags != "")
          (lib.concatStringsSep "\n"
            (lib.attrsets.foldlAttrs
              (acc: k: v:
                acc ++ lib.optional (v != null)
        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
                  /* sh */ ''
                  # sh
                  ''
                    substituteInPlace ${appDir}/app/Widgets/AdminMain/adminmain.tpl \
                      --replace-warn 'name="${k}"' 'name="${k}" readonly'
                '')
              [ ]
              cfg.podConfig));
                  ''
            ) [ ] 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" ]);
            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 /* sh */ ''
            (lib.optionalString brotli.enable # sh
              ''
                echo -n "Precompressing static files with Brotli …"
                find ${appDir}/public -type f ${findTextFileNames} -print0 \
                  | xargs -0 -n 1 -P $NIX_BUILD_CORES ${pkgs.writeShellScript "movim_precompress_broti" ''
@@ -109,8 +137,10 @@ let
                    ${lib.getExe brotli.package} --keep --quality=${builtins.toString brotli.compressionLevel} --output=$file.br $file
                  ''}
                echo " done."
            '')
            (lib.optionalString gzip.enable /* sh */ ''
              ''
            )
            (lib.optionalString gzip.enable # sh
              ''
                echo -n "Precompressing static files with Gzip …"
                find ${appDir}/public -type f ${findTextFileNames} -print0 \
                  | xargs -0 -n 1 -P $NIX_BUILD_CORES ${pkgs.writeShellScript "movim_precompress_gzip" ''
@@ -118,7 +148,8 @@ let
                    ${lib.getExe gzip.package} -c -${builtins.toString gzip.compressionLevel} $file > $file.gz
                  ''}
                echo " done."
            '')
              ''
            )
          ];
      in
      {
@@ -129,7 +160,8 @@ let
          podConfigInputDisableReplace
          precompressStaticFilesJobs
        ];
      });
      }
    );

  configFile = pipe cfg.settings [
    (filterAttrsRecursive (_: v: v != null))
@@ -141,10 +173,12 @@ let
  fpm = config.services.phpfpm.pools.${pool};
  phpExecutionUnit = "phpfpm-${pool}";

  dbService = {
  dbService =
    {
      "postgresql" = "postgresql.service";
      "mysql" = "mysql.service";
  }.${cfg.database.type};
    }
    .${cfg.database.type};
in
{
  options.services = {
@@ -154,7 +188,13 @@ in
      phpPackage = mkPackageOption pkgs "php" { };

      phpCfg = mkOption {
        type = with types; attrsOf (oneOf [ int str bool ]);
        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.";
@@ -214,7 +254,9 @@ in
      };

      minifyStaticFiles = mkOption {
        type = with types; either bool (submodule {
        type =
          with types;
          either bool (submodule {
            options = {
              script = mkOption {
                type = types.submodule {
@@ -255,7 +297,9 @@ in
      };

      precompressStaticFiles = mkOption {
        type = with types; submodule {
        type =
          with types;
          submodule {
            options = {
              brotli = {
                enable = mkEnableOption "Brotli precompression";
@@ -362,7 +406,15 @@ in
      };

      settings = mkOption {
        type = with types; attrsOf (nullOr (oneOf [ int str bool ]));
        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.";
      };
@@ -375,7 +427,10 @@ in

      database = {
        type = mkOption {
          type = types.enum [ "mysql" "postgresql" ];
          type = types.enum [
            "mysql"
            "postgresql"
          ];
          example = "mysql";
          default = "postgresql";
          description = "Database engine to use.";
@@ -401,12 +456,19 @@ in
      };

      nginx = mkOption {
        type = with types; nullOr (submodule
          (import ../web-servers/nginx/vhost-options.nix {
        type =
          with types;
          nullOr (
            submodule (
              import ../web-servers/nginx/vhost-options.nix {
                inherit config lib;
          }));
              }
            )
          );
        default = null;
        example = lib.literalExpression /* nginx */ ''
        example =
          lib.literalExpression # nginx
            ''
              {
                serverAliases = [
                  "pics.''${config.networking.domain}"
@@ -424,7 +486,13 @@ in
      };

      poolConfig = mkOption {
        type = with types; attrsOf (oneOf [ int str bool ]);
        type =
          with types;
          attrsOf (oneOf [
            int
            str
            bool
          ]);
        default = { };
        description = "Options for Movim’s PHP-FPM pool.";
      };
@@ -435,12 +503,14 @@ in
    environment.systemPackages = [ cfg.package ];

    users = {
      users = {
      users =
        {
          movim = mkIf (cfg.user == "movim") {
            isSystemUser = true;
            group = cfg.group;
          };
      } // lib.optionalAttrs (cfg.nginx != null) {
        }
        // lib.optionalAttrs (cfg.nginx != null) {
          "${config.services.nginx.user}".extraGroups = [ cfg.group ];
        };
      groups = {
@@ -459,10 +529,12 @@ in
            DAEMON_VERBOSE = cfg.verbose;
          }
          (mkIf cfg.database.createLocally {
            DB_DRIVER = {
            DB_DRIVER =
              {
                "postgresql" = "pgsql";
                "mysql" = "mysql";
            }.${cfg.database.type};
              }
              .${cfg.database.type};
            DB_HOST = "localhost";
            DB_PORT = config.services.${cfg.database.type}.settings.port;
            DB_DATABASE = cfg.database.name;
@@ -484,13 +556,14 @@ in
        };
      };

      nginx = mkIf (cfg.nginx != null)
        {
      nginx =
        mkIf (cfg.nginx != null) {
          enable = true;
          recommendedOptimisation = mkDefault true;
          recommendedProxySettings = true;
          # TODO: recommended cache options already in Nginx⁇
          appendHttpConfig = /* 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";
            '';
@@ -501,21 +574,24 @@ in
              locations = {
                "/favicon.ico" = {
                  priority = 100;
                  extraConfig = /* nginx */ ''
                  extraConfig = # nginx
                    ''
                      access_log off;
                      log_not_found off;
                    '';
                };
                "/robots.txt" = {
                  priority = 100;
                  extraConfig = /* nginx */ ''
                  extraConfig = # nginx
                    ''
                      access_log off;
                      log_not_found off;
                    '';
                };
                "~ /\\.(?!well-known).*" = {
                  priority = 210;
                  extraConfig = /* nginx */ ''
                  extraConfig = # nginx
                    ''
                      deny all;
                    '';
                };
@@ -523,14 +599,16 @@ in
                "/picture" = {
                  priority = 400;
                  tryFiles = "$uri $uri/ /index.php$is_args$args";
                  extraConfig = /* nginx */ ''
                  extraConfig = # nginx
                    ''
                      set $no_cache 0; # Enable cache only there
                    '';
                };
                "/" = {
                  priority = 490;
                  tryFiles = "$uri $uri/ /index.php$is_args$args";
                  extraConfig = /* nginx */ ''
                  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;
@@ -539,7 +617,8 @@ in
                "~ \\.php$" = {
                  priority = 500;
                  tryFiles = "$uri =404";
                  extraConfig = /* nginx */ ''
                  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";
@@ -557,38 +636,48 @@ in
                  proxyPass = "http://${cfg.settings.DAEMON_INTERFACE}:${builtins.toString cfg.port}/";
                  proxyWebsockets = true;
                  recommendedProxySettings = true;
                  extraConfig = /* nginx */ ''
                  extraConfig = # nginx
                    ''
                      proxy_set_header X-Forwarded-Proto $scheme;
                      proxy_redirect off;
                    '';
                };
              };
              extraConfig = /* ngnix */ ''
              extraConfig = # ngnix
                ''
                  index index.php;
                '';
            }
          ];
        }
      // lib.optionalAttrs (cfg.precompressStaticFiles.gzip.enable) { recommendedGzipSettings = mkDefault true; }
      // lib.optionalAttrs (cfg.precompressStaticFiles.brotli.enable) { recommendedBrotliSettings = mkDefault true; };
        // lib.optionalAttrs (cfg.precompressStaticFiles.gzip.enable) {
          recommendedGzipSettings = mkDefault true;
        }
        // lib.optionalAttrs (cfg.precompressStaticFiles.brotli.enable) {
          recommendedBrotliSettings = mkDefault true;
        };

      mysql = mkIf (cfg.database.createLocally && cfg.database.type == "mysql") {
        enable = mkDefault true;
        package = mkDefault pkgs.mariadb;
        ensureDatabases = [ cfg.database.name ];
        ensureUsers = [{
        ensureUsers = [
          {
            name = cfg.database.user;
            ensureDBOwnership = true;
        }];
          }
        ];
      };

      postgresql = mkIf (cfg.database.createLocally && cfg.database.type == "postgresql") {
        enable = mkDefault true;
        ensureDatabases = [ cfg.database.name ];
        ensureUsers = [{
        ensureUsers = [
          {
            name = cfg.database.user;
            ensureDBOwnership = true;
        }];
          }
        ];
        authentication = ''
          host ${cfg.database.name} ${cfg.database.user} localhost trust
        '';
@@ -596,10 +685,7 @@ in

      phpfpm.pools.${pool} =
        let
          socketOwner =
            if (cfg.nginx != null)
            then config.services.nginx.user
            else cfg.user;
          socketOwner = if (cfg.nginx != null) then config.services.nginx.user else cfg.user;
        in
        {
          phpPackage = package.php;
@@ -629,16 +715,19 @@ in
        after = lib.optional cfg.database.createLocally dbService;
        requires = lib.optional cfg.database.createLocally dbService;

        serviceConfig = {
        serviceConfig =
          {
            Type = "oneshot";
            User = cfg.user;
            Group = cfg.group;
            UMask = "077";
        } // lib.optionalAttrs (cfg.secretFile != null) {
          }
          // lib.optionalAttrs (cfg.secretFile != null) {
            LoadCredential = "env-secrets:${cfg.secretFile}";
          };

        script = /* sh */ ''
        script = # sh
          ''
            # Env vars
            rm -f ${cfg.dataDir}/.env
            cp --no-preserve=all ${configFile} ${cfg.dataDir}/.env
@@ -662,10 +751,11 @@ in
          ''
          + lib.optionalString (podConfigFlags != "") (
            let
            flags = lib.concatStringsSep " "
              ([ "--no-interaction" ]
              flags = lib.concatStringsSep " " (
                [ "--no-interaction" ]
                ++ lib.optional cfg.debug "-vvv"
                ++ lib.optional (!cfg.debug && cfg.verbose) "-v");
                ++ lib.optional (!cfg.debug && cfg.verbose) "-v"
              );
            in
            ''
              ${lib.getExe package} config ${podConfigFlags}
@@ -677,8 +767,7 @@ in
        description = "Movim daemon";
        wantedBy = [ "multi-user.target" ];
        after = [ "movim-data-setup.service" ];
        requires = [ "movim-data-setup.service" ]
          ++ lib.optional cfg.database.createLocally dbService;
        requires = [ "movim-data-setup.service" ] ++ lib.optional cfg.database.createLocally dbService;
        environment = {
          PUBLIC_URL = "//${cfg.domain}";
          WS_PORT = builtins.toString cfg.port;
@@ -694,17 +783,34 @@ in

      services.${phpExecutionUnit} = {
        after = [ "movim-data-setup.service" ];
        requires = [ "movim-data-setup.service" ]
          ++ lib.optional cfg.database.createLocally dbService;
        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"; };
        "${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";
        };
      };
    };
  };
+68 −53
Original line number Diff line number Diff line
@@ -78,7 +78,8 @@ in
    );

  config = lib.mkIf cfg.enable {
    systemd.services.nextcloud-notify_push = {
    systemd.services = {
      nextcloud-notify_push = {
        description = "Push daemon for Nextcloud clients";
        documentation = [ "https://github.com/nextcloud/notify_push" ];
        after = [
@@ -92,9 +93,6 @@ in
          DATABASE_PREFIX = cfg.dbtableprefix;
          LOG = cfg.logLevel;
        };
      postStart = ''
        ${cfgN.occ}/bin/nextcloud-occ notify_push:setup ${cfg.nextcloudUrl}/push
      '';
        script =
          let
            dbType = if cfg.dbtype == "pgsql" then "postgresql" else cfg.dbtype;
@@ -118,8 +116,8 @@ in
            dbName = lib.optionalString (cfg.dbname != null) "/${cfg.dbname}";
            dbUrl = "${dbType}://${dbUser}${dbPass}${dbHost}${dbName}${dbOpts}";
          in
        lib.optionalString (dbPass != "") ''
          export DATABASE_PASSWORD="$(<"${cfg.dbpassFile}")"
          lib.optionalString (cfg.dbpassFile != null) ''
            export DATABASE_PASSWORD="$(<"$CREDENTIALS_DIRECTORY/dbpass")"
          ''
          + ''
            export DATABASE_URL="${dbUrl}"
@@ -132,6 +130,23 @@ in
          Restart = "on-failure";
          RestartSec = "5s";
          Type = "notify";
          LoadCredential = lib.optional (cfg.dbpassFile != null) "dbpass:${cfg.dbpassFile}";
        };
      };

      nextcloud-notify_push_setup = {
        wantedBy = [ "multi-user.target" ];
        requiredBy = [ "nextcloud-notify_push.service" ];
        after = [ "nextcloud-notify_push.service" ];
        serviceConfig = {
          Type = "oneshot";
          User = "nextcloud";
          Group = "nextcloud";
          ExecStart = "${lib.getExe cfgN.occ} notify_push:setup ${cfg.nextcloudUrl}/push";
          LoadCredential = config.systemd.services.nextcloud-cron.serviceConfig.LoadCredential;
          RestartMode = "direct";
          Restart = "on-failure";
        };
      };
    };

Loading