Unverified Commit 994080ec authored by Paul Haerle's avatar Paul Haerle Committed by GitHub
Browse files

nixos/atuin: add system-wide atuin program option (#498887)

parents 4ff9b9f3 3770285f
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -57,6 +57,8 @@

- [OpenThread Border Router](https://openthread.io/), a Thread border router for POSIX-based platforms that bridges Thread mesh networks to IP networks. Available as [services.openthread-border-router](#opt-services.openthread-border-router.enable).

- [Atuin](https://atuin.sh), magical shell history — sync, search and backup your terminal history. Available as [programs.atuin](#opt-programs.atuin.enable).

- [Meshtastic](https://meshtastic.org), an open-source, off-grid, decentralised mesh network
  designed to run on affordable, low-power devices. Available as [services.meshtasticd]
  (#opt-services.meshtasticd.enable).
+1 −0
Original line number Diff line number Diff line
@@ -168,6 +168,7 @@
  ./programs/appimage.nix
  ./programs/arp-scan.nix
  ./programs/atop.nix
  ./programs/atuin.nix
  ./programs/ausweisapp.nix
  ./programs/autoenv.nix
  ./programs/autojump.nix
+195 −0
Original line number Diff line number Diff line
{
  config,
  lib,
  pkgs,
  ...
}:
let
  inherit (lib) escapeShellArgs;

  cfg = config.programs.atuin;

  tomlFormat = pkgs.formats.toml { };

  settingsFile = tomlFormat.generate "atuin-config" cfg.settings;
in
{
  options.programs.atuin = {
    enable = lib.mkEnableOption "atuin";

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

    enableBashIntegration = lib.mkEnableOption "Bash integration" // {
      default = config.programs.bash.enable;
      defaultText = lib.literalExpression "config.programs.bash.enable";
    };

    enableZshIntegration = lib.mkEnableOption "Zsh integration" // {
      default = config.programs.zsh.enable;
      defaultText = lib.literalExpression "config.programs.zsh.enable";
    };

    enableFishIntegration = lib.mkEnableOption "Fish integration" // {
      default = config.programs.fish.enable;
      defaultText = lib.literalExpression "config.programs.fish.enable";
    };

    flags = lib.mkOption {
      type = lib.types.listOf lib.types.str;
      default = [ ];
      example = [
        "--disable-up-arrow"
        "--disable-ctrl-r"
      ];
      description = ''
        Flags to append to the shell hook.
      '';
    };

    settings = lib.mkOption {
      type = tomlFormat.type;
      default = { };
      example = lib.literalExpression ''
        {
          auto_sync = true;
          sync_frequency = "5m";
          sync_address = "https://api.atuin.sh";
          search_mode = "prefix";
        }
      '';
      description = ''
        Configuration written to {file}`/etc/atuin/config.toml`.

        See <https://docs.atuin.sh/configuration/config/> for the full list
        of options.
      '';
    };

    daemon = {
      enable = lib.mkEnableOption "the Atuin daemon" // {
        default = pkgs.stdenv.hostPlatform.isLinux;
        defaultText = lib.literalExpression "pkgs.stdenv.hostPlatform.isLinux";
      };

      logLevel = lib.mkOption {
        type = lib.types.enum [
          "trace"
          "debug"
          "info"
          "warn"
          "error"
        ];
        default = "info";
        description = ''
          Log level for the Atuin daemon.
        '';
      };
    };

    themes = lib.mkOption {
      type = lib.types.attrsOf (
        lib.types.oneOf [
          tomlFormat.type
          lib.types.path
          lib.types.lines
        ]
      );
      description = ''
        Each theme is written to
        {file}`/etc/atuin/themes/theme-name.toml`
        where the name of each attribute is the theme-name

        See <https://docs.atuin.sh/guide/theming/> for the full list
        of options.
      '';
      default = { };
      example = lib.literalExpression ''
        {
          "my-theme" = {
            theme.name = "My Theme";
            colors = {
              Base = "#000000";
              Title = "#FFFFFF";
            };
          };
        }
      '';
    };
  };

  config = lib.mkIf cfg.enable {
    environment.systemPackages = [ cfg.package ];

    # Atuin only reads from ATUIN_CONFIG_DIR or XDG_CONFIG_HOME, not XDG_CONFIG_DIRS,
    # so we must set ATUIN_CONFIG_DIR to point to the system-wide config location.
    environment.variables.ATUIN_CONFIG_DIR = "/etc/atuin";

    environment.etc = lib.mkMerge [
      (lib.mkIf (cfg.settings != { }) {
        "atuin/config.toml".source = settingsFile;
      })

      (lib.mkIf (cfg.themes != { }) (
        builtins.mapAttrs' (
          name: theme:
          lib.nameValuePair "atuin/themes/${name}.toml" {
            source =
              if builtins.isString theme then
                pkgs.writeText "atuin-theme-${name}" theme
              else if builtins.isPath theme || lib.isStorePath theme then
                theme
              else
                tomlFormat.generate "atuin-theme-${name}" theme;
          }
        ) cfg.themes
      ))
    ];

    programs.bash.interactiveShellInit = lib.mkIf cfg.enableBashIntegration ''
      if [[ :$SHELLOPTS: =~ :(vi|emacs): ]]; then
        eval "$(${lib.getExe cfg.package} init bash ${escapeShellArgs cfg.flags})"
      fi
    '';

    programs.zsh.interactiveShellInit = lib.mkIf cfg.enableZshIntegration ''
      if [[ $options[zle] = on ]]; then
        eval "$(${lib.getExe cfg.package} init zsh ${escapeShellArgs cfg.flags})"
      fi
    '';

    programs.fish.interactiveShellInit = lib.mkIf cfg.enableFishIntegration ''
      ${lib.getExe cfg.package} init fish ${escapeShellArgs cfg.flags} | source
    '';

    systemd = lib.mkIf (cfg.daemon.enable && pkgs.stdenv.hostPlatform.isLinux) {
      user.services.atuin-daemon = {
        unitConfig = {
          Description = "Atuin daemon";
          Requires = [ "atuin-daemon.socket" ];
        };
        serviceConfig = {
          ExecStart = "${lib.getExe cfg.package} daemon start";
          Environment = [ "ATUIN_LOG=${cfg.daemon.logLevel}" ];
          Restart = "on-failure";
          RestartSteps = 3;
          RestartMaxDelaySec = 6;
        };
      };

      user.sockets.atuin-daemon = {
        unitConfig = {
          Description = "Atuin daemon socket";
        };
        socketConfig = {
          ListenStream = "%t/atuin/atuin.sock";
          SocketMode = "0640";
          DirectoryMode = "0740";
          RemoveOnStop = true;
        };
        wantedBy = [ "sockets.target" ];
      };
    };
  };

  meta.maintainers = cfg.package.meta.maintainers;
}
+1 −0
Original line number Diff line number Diff line
@@ -236,6 +236,7 @@ in
  atticd = runTest ./atticd.nix;
  attr = pkgs.callPackage ./attr.nix { };
  atuin = runTest ./atuin.nix;
  atuin-programs = runTest ./atuin-programs.nix;
  audiobookshelf = runTest ./audiobookshelf.nix;
  audit = runTest ./audit.nix;
  audit-testsuite = runTest ./audit-testsuite.nix;
+39 −0
Original line number Diff line number Diff line
{ pkgs, ... }:
{
  name = "atuin";
  meta.maintainers = pkgs.atuin.meta.maintainers;

  nodes.machine = {
    programs = {
      bash.enable = true;
      fish.enable = true;
      zsh.enable = true;

      atuin = {
        enable = true;
        settings = {
          auto_sync = false;
        };
      };
    };
  };

  testScript = ''
    start_all()
    machine.wait_for_unit("default.target")

    # Check atuin is installed
    machine.succeed("atuin --version")

    # Check shell integration - verify the init scripts can be sourced without error
    machine.succeed("bash -c 'eval \"$(atuin init bash)\"'")
    machine.succeed("zsh -c 'eval \"$(atuin init zsh)\"'")
    machine.succeed("fish -c 'atuin init fish | source'")

    # Verify config file was created
    machine.succeed("grep -q 'auto_sync = false' /etc/atuin/config.toml")

    # Verify daemon socket unit is enabled
    machine.succeed("systemctl --user --machine=root@ is-enabled atuin-daemon.socket")
  '';
}
Loading