Unverified Commit 172dae70 authored by Sandro Jäckel's avatar Sandro Jäckel Committed by GitHub
Browse files

olivetin: init at 2025.4.22 (#395047)

parents 34b016fd 37914c29
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -162,6 +162,8 @@

- [`yarr`](https://github.com/nkanaev/yarr), a small, web-based feed aggregator and RSS reader. Available as [services.yarr](#opt-services.yarr.enable).

- [OliveTin](https://www.olivetin.app/), gives safe and simple access to predefined shell commands from a web interface. Available as [services.olivetin](#opt-services.olivetin.enable).

- [Stash](https://github.com/stashapp/stash), An organizer for your adult videos/images, written in Go. Available as [services.stash](#opt-services.stash.enable).

- [vsmartcard-vpcd](https://frankmorgner.github.io/vsmartcard/virtualsmartcard/README.html), a virtual smart card driver. Available as [services.vsmartcard-vpcd](#opt-services.vsmartcard-vpcd.enable).
+1 −0
Original line number Diff line number Diff line
@@ -1604,6 +1604,7 @@
  ./services/web-apps/mediagoblin.nix
  ./services/web-apps/open-web-calendar.nix
  ./services/web-apps/mobilizon.nix
  ./services/web-apps/olivetin.nix
  ./services/web-apps/openwebrx.nix
  ./services/web-apps/outline.nix
  ./services/web-apps/pds.nix
+152 −0
Original line number Diff line number Diff line
{
  config,
  pkgs,
  lib,
  ...
}:

let
  cfg = config.services.olivetin;

  settingsFormat = pkgs.formats.yaml { };
in

{
  meta.maintainers = with lib.maintainers; [ defelo ];

  options.services.olivetin = {
    enable = lib.mkEnableOption "OliveTin";

    package = lib.mkPackageOption pkgs "olivetin" { };

    user = lib.mkOption {
      type = lib.types.str;
      description = "The user account under which OliveTin runs.";
      default = "olivetin";
    };

    group = lib.mkOption {
      type = lib.types.str;
      description = "The group under which OliveTin runs.";
      default = "olivetin";
    };

    path = lib.mkOption {
      type =
        with lib.types;
        listOf (oneOf [
          package
          str
        ]);
      description = ''
        Packages added to the service's {env}`PATH`.
      '';
      defaultText = lib.literalExpression ''
        with pkgs; [ bash ]
      '';
    };

    settings = lib.mkOption {
      description = ''
        Configuration of OliveTin. See <https://docs.olivetin.app/config.html> for more information.
      '';
      default = { };

      type = lib.types.submodule {
        freeformType = settingsFormat.type;

        options = {
          ListenAddressSingleHTTPFrontend = lib.mkOption {
            type = lib.types.str;
            description = ''
              The address to listen on for the internal "microproxy" frontend.
            '';
            default = "127.0.0.1:8000";
            example = "0.0.0.0:8000";
          };
        };
      };
    };

    extraConfigFiles = lib.mkOption {
      type = lib.types.listOf lib.types.path;
      default = [ ];
      example = [ "/run/secrets/olivetin.yaml" ];
      description = ''
        Config files to merge into the settings defined in [](#opt-services.olivetin.settings).
        This is useful to avoid putting secrets into the nix store.
        See <https://docs.olivetin.app/config.html> for more information.
      '';
    };
  };

  config = lib.mkIf cfg.enable {
    services.olivetin = {
      path = with pkgs; [ bash ];
    };

    systemd.services.olivetin = {
      description = "OliveTin";

      wantedBy = [ "multi-user.target" ];

      wants = [
        "network-online.target"
        "local-fs.target"
      ];
      after = [
        "network-online.target"
        "local-fs.target"
      ];

      inherit (cfg) path;

      preStart = ''
        tmp="$(mktemp -d)"
        trap 'rm -rf "$tmp"' EXIT
        cd "$tmp"

        cp ${settingsFormat.generate "olivetin-config.yaml" cfg.settings} config.yaml
        chmod +w config.yaml
        for ((i=0; i < ${toString (lib.length cfg.extraConfigFiles)}; i++)); do
          ${lib.getExe pkgs.yq} -yi '
            def merge($y):
              . as $x |
              if ($x | type == "object") and ($y | type == "object") then
                $x + $y + with_entries(select(.key | in($y)) | .key as $key | .value |= merge($y[$key]))
              elif ($x | type == "array") and ($y | type == "array") then
                $x + $y
              else
                $y
              end;
            merge($f | fromjson)
          ' config.yaml --rawfile f <(${lib.getExe pkgs.yq} -c . "$CREDENTIALS_DIRECTORY/config-$i.yaml")
        done
        chmod -w config.yaml

        mkdir -p /run/olivetin/config
        mv config.yaml /run/olivetin/config/config.yaml
      '';

      serviceConfig = {
        User = cfg.user;
        Group = cfg.group;
        RuntimeDirectory = "olivetin";
        Restart = "always";

        LoadCredential = lib.imap0 (i: path: "config-${toString i}.yaml:${path}") cfg.extraConfigFiles;

        ExecStart = "${lib.getExe cfg.package} -configdir /run/olivetin/config";
      };
    };

    users.users = lib.mkIf (cfg.user == "olivetin") {
      olivetin = {
        group = cfg.group;
        isSystemUser = true;
      };
    };

    users.groups = lib.mkIf (cfg.group == "olivetin") { olivetin = { }; };
  };
}
+1 −0
Original line number Diff line number Diff line
@@ -966,6 +966,7 @@ in
  oddjobd = handleTestOn [ "x86_64-linux" "aarch64-linux" ] ./oddjobd.nix { };
  obs-studio = runTest ./obs-studio.nix;
  oh-my-zsh = handleTest ./oh-my-zsh.nix { };
  olivetin = runTest ./olivetin.nix;
  ollama = runTest ./ollama.nix;
  ollama-cuda = runTestOn [ "x86_64-linux" "aarch64-linux" ] ./ollama-cuda.nix;
  ollama-rocm = runTestOn [ "x86_64-linux" "aarch64-linux" ] ./ollama-rocm.nix;
+57 −0
Original line number Diff line number Diff line
{ lib, ... }:

{
  name = "olivetin";
  meta.maintainers = with lib.maintainers; [ defelo ];

  nodes.machine = {
    services.olivetin = {
      enable = true;
      settings = {
        actions = [
          {
            id = "hello_world";
            title = "Say Hello";
            shell = "echo -n 'Hello World!' | tee /tmp/result";
          }
        ];
      };
      extraConfigFiles = [
        (builtins.toFile "secrets.yaml" ''
          actions:
            - id: secret
              title: Secret Action
              shell: echo -n secret > /tmp/result2
        '')
      ];
    };
  };

  interactive.nodes.machine = {
    services.olivetin.settings.ListenAddressSingleHTTPFrontend = "0.0.0.0:8000";
    networking.firewall.allowedTCPPorts = [ 8000 ];
    virtualisation.forwardPorts = [
      {
        from = "host";
        host.port = 8000;
        guest.port = 8000;
      }
    ];
  };

  testScript = ''
    import json

    machine.wait_for_unit("olivetin.service")
    machine.wait_for_open_port(8000)

    response = json.loads(machine.succeed("curl http://localhost:8000/api/StartActionByGetAndWait/hello_world"))
    assert response["logEntry"]["exitCode"] == 0
    assert response["logEntry"]["output"] == "Hello World!"
    assert machine.succeed("cat /tmp/result") == "Hello World!"

    response = json.loads(machine.succeed("curl http://localhost:8000/api/StartActionByGetAndWait/secret"))
    assert response["logEntry"]["exitCode"] == 0
    assert machine.succeed("cat /tmp/result2") == "secret"
  '';
}
Loading