about summary refs log tree commit diff
path: root/nixos
diff options
context:
space:
mode:
authorgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>2024-06-12 06:01:24 +0000
committerGitHub <noreply@github.com>2024-06-12 06:01:24 +0000
commitd4adfab6df6bdffe5608cd34708462bd4421bf67 (patch)
tree5a20402c230d1507e5a1f7dbaf356eef8da64de2 /nixos
parentad67554387c7816e13c8003e7d5820e0f47e8220 (diff)
parente145b7fb56806bd90267401d3b7bdc18189a792a (diff)
Merge staging-next into staging
Diffstat (limited to 'nixos')
-rw-r--r--nixos/doc/manual/release-notes/rl-2411.section.md2
-rw-r--r--nixos/modules/module-list.nix1
-rw-r--r--nixos/modules/services/misc/renovate.nix153
-rw-r--r--nixos/tests/all-tests.nix1
-rw-r--r--nixos/tests/renovate.nix69
5 files changed, 226 insertions, 0 deletions
diff --git a/nixos/doc/manual/release-notes/rl-2411.section.md b/nixos/doc/manual/release-notes/rl-2411.section.md
index cd080879a567f..499380a835bf8 100644
--- a/nixos/doc/manual/release-notes/rl-2411.section.md
+++ b/nixos/doc/manual/release-notes/rl-2411.section.md
@@ -14,6 +14,8 @@
 
 - [Quickwit](https://quickwit.io), sub-second search & analytics engine on cloud storage. Available as [services.quickwit](options.html#opt-services.quickwit).
 
+- [Renovate](https://github.com/renovatebot/renovate), a dependency updating tool for various git forges and language ecosystems. Available as [services.renovate](#opt-services.renovate.enable).
+
 ## Backward Incompatibilities {#sec-release-24.11-incompatibilities}
 
 - `nginx` package no longer includes `gd` and `geoip` dependencies. For enabling it, override `nginx` package with the optionals `withImageFilter` and `withGeoIP`.
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index 1bdb3aa414e29..61b95134637e5 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -794,6 +794,7 @@
   ./services/misc/radarr.nix
   ./services/misc/readarr.nix
   ./services/misc/redmine.nix
+  ./services/misc/renovate.nix
   ./services/misc/ripple-data-api.nix
   ./services/misc/rippled.nix
   ./services/misc/rmfakecloud.nix
diff --git a/nixos/modules/services/misc/renovate.nix b/nixos/modules/services/misc/renovate.nix
new file mode 100644
index 0000000000000..25a719c91cbd8
--- /dev/null
+++ b/nixos/modules/services/misc/renovate.nix
@@ -0,0 +1,153 @@
+{
+  config,
+  lib,
+  pkgs,
+  ...
+}:
+let
+  inherit (lib)
+    mkEnableOption
+    mkPackageOption
+    mkOption
+    types
+    mkIf
+    ;
+  json = pkgs.formats.json { };
+  cfg = config.services.renovate;
+  generateValidatedConfig =
+    name: value:
+    pkgs.callPackage (
+      { runCommand, jq }:
+      runCommand name
+        {
+          nativeBuildInputs = [
+            jq
+            cfg.package
+          ];
+          value = builtins.toJSON value;
+          passAsFile = [ "value" ];
+          preferLocalBuild = true;
+        }
+        ''
+          jq . "$valuePath"> $out
+          renovate-config-validator $out
+        ''
+    ) { };
+  generateConfig = if cfg.validateSettings then generateValidatedConfig else json.generate;
+in
+{
+  meta.maintainers = with lib.maintainers; [ marie natsukium ];
+
+  options.services.renovate = {
+    enable = mkEnableOption "renovate";
+    package = mkPackageOption pkgs "renovate" { };
+    schedule = mkOption {
+      type = with types; nullOr str;
+      description = "How often to run renovate. See {manpage}`systemd.time(7)` for the format.";
+      example = "*:0/10";
+      default = null;
+    };
+    credentials = mkOption {
+      type = with types; attrsOf path;
+      description = ''
+        Allows configuring environment variable credentials for renovate, read from files.
+        This should always be used for passing confidential data to renovate.
+      '';
+      example = {
+        RENOVATE_TOKEN = "/etc/renovate/token";
+      };
+      default = { };
+    };
+    runtimePackages = mkOption {
+      type = with types; listOf package;
+      description = "Packages available to renovate.";
+      default = [ ];
+    };
+    validateSettings = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Weither to run renovate's config validator on the built configuration.";
+    };
+    settings = mkOption {
+      type = json.type;
+      default = { };
+      example = {
+        platform = "gitea";
+        endpoint = "https://git.example.com";
+        gitAuthor = "Renovate <renovate@example.com>";
+      };
+      description = ''
+        Renovate's global configuration.
+        If you want to pass secrets to renovate, please use {option}`services.renovate.credentials` for that.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    services.renovate.settings = {
+      cacheDir = "/var/cache/renovate";
+      baseDir = "/var/lib/renovate";
+    };
+
+    systemd.services.renovate = {
+      description = "Renovate dependency updater";
+      documentation = [ "https://docs.renovatebot.com/" ];
+      after = [ "network.target" ];
+      startAt = lib.optional (cfg.schedule != null) cfg.schedule;
+      path = [
+        config.systemd.package
+        pkgs.git
+      ] ++ cfg.runtimePackages;
+
+      serviceConfig = {
+        Type = "oneshot";
+        User = "renovate";
+        Group = "renovate";
+        DynamicUser = true;
+        LoadCredential = lib.mapAttrsToList (name: value: "SECRET-${name}:${value}") cfg.credentials;
+        RemainAfterExit = false;
+        Restart = "on-failure";
+        CacheDirectory = "renovate";
+        StateDirectory = "renovate";
+
+        # Hardening
+        CapabilityBoundingSet = [ "" ];
+        DeviceAllow = [ "" ];
+        LockPersonality = true;
+        PrivateDevices = true;
+        PrivateUsers = true;
+        ProcSubset = "pid";
+        ProtectClock = true;
+        ProtectControlGroups = true;
+        ProtectHome = true;
+        ProtectHostname = true;
+        ProtectKernelLogs = true;
+        ProtectKernelModules = true;
+        ProtectKernelTunables = true;
+        ProtectProc = "invisible";
+        RestrictAddressFamilies = [
+          "AF_INET"
+          "AF_INET6"
+        ];
+        RestrictNamespaces = true;
+        RestrictRealtime = true;
+        SystemCallArchitectures = "native";
+        UMask = "0077";
+      };
+
+      script = ''
+        ${lib.concatStringsSep "\n" (
+          builtins.map (name: "export ${name}=$(systemd-creds cat 'SECRET-${name}')") (
+            lib.attrNames cfg.credentials
+          )
+        )}
+        exec ${lib.escapeShellArg (lib.getExe cfg.package)}
+      '';
+
+      environment = {
+        RENOVATE_CONFIG_FILE = generateConfig "renovate-config.json" cfg.settings;
+        HOME = "/var/lib/renovate";
+      };
+    };
+  };
+}
diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix
index 92238016af569..3052c1ddfe0da 100644
--- a/nixos/tests/all-tests.nix
+++ b/nixos/tests/all-tests.nix
@@ -803,6 +803,7 @@ in {
   redis = handleTest ./redis.nix {};
   redlib = handleTest ./redlib.nix {};
   redmine = handleTest ./redmine.nix {};
+  renovate = handleTest ./renovate.nix {};
   restartByActivationScript = handleTest ./restart-by-activation-script.nix {};
   restic-rest-server = handleTest ./restic-rest-server.nix {};
   restic = handleTest ./restic.nix {};
diff --git a/nixos/tests/renovate.nix b/nixos/tests/renovate.nix
new file mode 100644
index 0000000000000..a30b5b3d60b9c
--- /dev/null
+++ b/nixos/tests/renovate.nix
@@ -0,0 +1,69 @@
+import ./make-test-python.nix (
+  { pkgs, ... }:
+  {
+    name = "renovate";
+    meta.maintainers = with pkgs.lib.maintainers; [ marie natsukium ];
+
+    nodes.machine =
+      { config, ... }:
+      {
+        services.renovate = {
+          enable = true;
+          settings = {
+            platform = "gitea";
+            endpoint = "http://localhost:3000";
+            autodiscover = true;
+            gitAuthor = "Renovate <renovate@example.com>";
+          };
+          credentials = {
+            RENOVATE_TOKEN = "/etc/renovate-token";
+          };
+        };
+        environment.systemPackages = [
+          config.services.forgejo.package
+          pkgs.tea
+          pkgs.git
+        ];
+        services.forgejo = {
+          enable = true;
+          settings.server.HTTP_PORT = 3000;
+        };
+      };
+
+    testScript = ''
+      def gitea(command):
+        return machine.succeed(f"cd /var/lib/forgejo && sudo --user=forgejo GITEA_WORK_DIR=/var/lib/forgejo GITEA_CUSTOM=/var/lib/forgejo/custom gitea {command}")
+
+      machine.wait_for_unit("forgejo.service")
+      machine.wait_for_open_port(3000)
+
+      machine.systemctl("stop forgejo.service")
+
+      gitea("admin user create --username meow --email meow@example.com --password meow")
+
+      machine.systemctl("start forgejo.service")
+      machine.wait_for_unit("forgejo.service")
+      machine.wait_for_open_port(3000)
+
+      accessToken = gitea("admin user generate-access-token --raw --username meow --scopes all | tr -d '\n'")
+
+      machine.succeed(f"tea login add --name default --user meow --token '{accessToken}' --password meow --url http://localhost:3000")
+      machine.succeed("tea repo create --name kitty --init")
+      machine.succeed("git config --global user.name Meow")
+      machine.succeed("git config --global user.email meow@example.com")
+      machine.succeed(f"git clone http://meow:{accessToken}@localhost:3000/meow/kitty.git /tmp/kitty")
+      machine.succeed("echo '{ \"name\": \"meow\", \"version\": \"0.1.0\" }' > /tmp/kitty/package.json")
+      machine.succeed("git -C /tmp/kitty add /tmp/kitty/package.json")
+      machine.succeed("git -C /tmp/kitty commit -m 'add package.json'")
+      machine.succeed("git -C /tmp/kitty push origin")
+
+      machine.succeed(f"echo '{accessToken}' > /etc/renovate-token")
+      machine.systemctl("start renovate.service")
+
+      machine.succeed("tea pulls list --repo meow/kitty | grep 'Configure Renovate'")
+      machine.succeed("tea pulls merge --repo meow/kitty 1")
+
+      machine.systemctl("start renovate.service")
+    '';
+  }
+)