diff --git a/modules/modules.nix b/modules/modules.nix index bd88f8ada..ea2e70c5a 100644 --- a/modules/modules.nix +++ b/modules/modules.nix @@ -268,6 +268,7 @@ let ./programs/sesh.nix ./programs/sftpman.nix ./programs/sioyek.nix + ./programs/sketchybar.nix ./programs/skim.nix ./programs/sm64ex.nix ./programs/smug.nix diff --git a/modules/programs/sketchybar.nix b/modules/programs/sketchybar.nix new file mode 100644 index 000000000..fb046cfc8 --- /dev/null +++ b/modules/programs/sketchybar.nix @@ -0,0 +1,195 @@ +{ + config, + lib, + pkgs, + ... +}: + +let + inherit (lib) + literalExpression + mkEnableOption + mkOption + types + ; + + cfg = config.programs.sketchybar; +in +{ + meta.maintainers = [ lib.maintainers.khaneliman ]; + + options.programs.sketchybar = { + enable = mkEnableOption "sketchybar"; + + package = lib.mkPackageOption pkgs "sketchybar" { }; + + configType = mkOption { + type = types.enum [ + "bash" + "lua" + ]; + default = "bash"; + description = '' + The type of configuration to generate. + + Set to "bash" to use the standard bash configuration. + Set to "lua" to use the Lua configuration via SbarLua. + ''; + }; + + config = mkOption { + type = with types; nullOr (either lines path); + default = ""; + example = literalExpression '' + # Bash example + #!/usr/bin/env bash + + # Define colors + export COLOR_BLACK="0xff181926" + export COLOR_WHITE="0xffcad3f5" + + # Configure bar + sketchybar --bar height=32 \ + position=top \ + padding_left=10 \ + padding_right=10 \ + color=$COLOR_BLACK + + # Configure default values + sketchybar --default icon.font="SF Pro:Bold:14.0" \ + icon.color=$COLOR_WHITE \ + label.font="SF Pro:Bold:14.0" \ + label.color=$COLOR_WHITE + + # Add items to the bar + sketchybar --add item clock right \ + --set clock script="date '+%H:%M'" \ + update_freq=10 + + # Update the bar + sketchybar --update + ''; + description = '' + The complete sketchybar configuration content. + This should be written in the language specified by configType (bash or lua). + + The appropriate shebang will be automatically added. + ''; + }; + + service = { + enable = mkEnableOption "sketchybar service" // { + default = true; + }; + + errorLogFile = mkOption { + type = with lib.types; nullOr (either path str); + default = "${config.home.homeDirectory}/Library/Logs/sketchybar/sketchybar.err.log"; + defaultText = lib.literalExpression "\${config.home.homeDirectory}/Library/Logs/sketchybar/sketchybar.err.log"; + example = "/Users/khaneliman/Library/Logs/sketchybar.log"; + description = "Absolute path to log all stderr output."; + }; + + outLogFile = mkOption { + type = with lib.types; nullOr (either path str); + default = "${config.home.homeDirectory}/Library/Logs/sketchybar/sketchybar.out.log"; + defaultText = lib.literalExpression "\${config.home.homeDirectory}/Library/Logs/sketchybar/sketchybar.out.log"; + example = "/Users/khaneliman/Library/Logs/sketchybar.log"; + description = "Absolute path to log all stdout output."; + }; + + sbarLuaPackage = lib.mkPackageOption pkgs "sbarlua" { + nullable = true; + extraDescription = "Required when using a lua configuration."; + }; + + extraLuaPackages = mkOption { + type = with types; functionTo (listOf package); + default = _: [ ]; + defaultText = literalExpression "ps: [ ]"; + example = literalExpression "luaPkgs: with luaPkgs; [ luautf8 ]"; + description = '' + The extra Lua packages required for your plugins to work. + This option accepts a function that takes a Lua package set as an argument, + and selects the required Lua packages from this package set. + See the example for more info. + ''; + }; + + extraPackages = mkOption { + type = with lib.types; listOf package; + default = [ ]; + example = literalExpression "[ pkgs.jq ]"; + description = '' + Extra packages to add to PATH for the sketchybar service. + ''; + }; + }; + }; + + config = lib.mkMerge [ + (lib.mkIf cfg.enable { + assertions = [ + { + assertion = !(cfg.configType == "lua" && cfg.sbarLuaPackage == null); + message = "When configType is set to \"lua\", sbarLuaPackage must be specified"; + } + ]; + + home.packages = [ cfg.package ]; + + xdg.configFile."sketchybar/sketchybarrc".source = lib.mkIf (cfg.config != "") ( + pkgs.writeTextFile { + name = "sketchybarrc"; + text = + if cfg.configType == "lua" then + '' + #!/usr/bin/env lua + -- Generated by home-manager + ${cfg.config} + '' + else + '' + #!/usr/bin/env bash + # Generated by home-manager + ${cfg.config} + ''; + executable = true; + } + ); + }) + + (lib.mkIf cfg.service.enable { + assertions = [ + (lib.hm.assertions.assertPlatform "programs.sketchybar" pkgs lib.platforms.darwin) + ]; + + launchd.agents.sketchybar = + let + resolvedExtraLuaPackages = cfg.service.extraLuaPackages pkgs.lua54Packages; + in + { + enable = true; + config = { + Program = lib.getExe cfg.package; + ProcessType = "Interactive"; + KeepAlive = true; + RunAtLoad = true; + StandardErrorPath = cfg.service.errorLogFile; + StandardOutPath = cfg.service.outLogFile; + EnvironmentVariables = + { + PATH = (lib.concatMapStringsSep ":" (p: "${p}/bin") ([ cfg.package ] ++ cfg.service.extraPackages)); + } + // lib.optionalAttrs + (cfg.configType == "lua" && (cfg.sbarLuaPackage != null || cfg.extraLuaPackages != (_: [ ]))) + { + LUA_CPATH = "${ + lib.concatMapStringsSep ";" pkgs.lua54Packages.getLuaCPath resolvedExtraLuaPackages + };${cfg.sbarLuaPackage}/lib/lua/${pkgs.lua.luaversion}/?.so"; + }; + }; + }; + }) + ]; +} diff --git a/tests/default.nix b/tests/default.nix index bfe9ff4c8..44c7da96f 100644 --- a/tests/default.nix +++ b/tests/default.nix @@ -334,6 +334,7 @@ import nmtSrc { ./modules/launchd ./modules/programs/aerospace ./modules/programs/element-desktop/darwin.nix + ./modules/programs/sketchybar ./modules/services/borgmatic-darwin ./modules/services/emacs-darwin ./modules/services/espanso-darwin diff --git a/tests/modules/programs/sketchybar/default.nix b/tests/modules/programs/sketchybar/default.nix new file mode 100644 index 000000000..b158ac10c --- /dev/null +++ b/tests/modules/programs/sketchybar/default.nix @@ -0,0 +1,7 @@ +{ + # Combined tests with built-in validation + sketchybar = ./sketchybar.nix; # Bash configuration with validation + sketchybar-lua-config = ./sketchybar-lua-config.nix; # Lua configuration with validation + sketchybar-invalid-lua-config = ./sketchybar-invalid-lua-config.nix; # Tests error on missing sbarLua + sketchybar-service-integration = ./sketchybar-service-integration.nix; # Service integration with validation +} diff --git a/tests/modules/programs/sketchybar/sketchybar-invalid-lua-config.nix b/tests/modules/programs/sketchybar/sketchybar-invalid-lua-config.nix new file mode 100644 index 000000000..dadc55c6b --- /dev/null +++ b/tests/modules/programs/sketchybar/sketchybar-invalid-lua-config.nix @@ -0,0 +1,23 @@ +{ config, ... }: +{ + programs.sketchybar = { + enable = true; + package = config.lib.test.mkStubPackage { }; + + # Test case for lua configuration without sbarLuaPackage + configType = "lua"; + # sbarLuaPackage intentionally not set + + variables = { + PADDING = 3; + }; + + config.bar = { + height = 30; + }; + }; + + test.asserts.assertions.expected = [ + "When configType is set to \"lua\", sbarLuaPackage must be specified" + ]; +} diff --git a/tests/modules/programs/sketchybar/sketchybar-lua-config.nix b/tests/modules/programs/sketchybar/sketchybar-lua-config.nix new file mode 100644 index 000000000..e96bc32a9 --- /dev/null +++ b/tests/modules/programs/sketchybar/sketchybar-lua-config.nix @@ -0,0 +1,77 @@ +{ config, pkgs, ... }: + +let + pkgsSbarlua = pkgs.writeTextFile { + name = "sbarlua"; + destination = "/bin/sbarlua"; + executable = true; + text = '' + #!/bin/sh + echo "SbarLua mock" + ''; + }; +in +{ + programs.sketchybar = { + enable = true; + package = config.lib.test.mkStubPackage { }; + + configType = "lua"; + sbarLuaPackage = pkgsSbarlua; + + variables = { + PADDING = 3; + FONT = "SF Pro"; + COLOR = "0xff0000ff"; + # Test more complex values + ITEMS = [ + "calendar" + "cpu" + "memory" + ]; + SETTINGS = { + refresh_freq = 1; + enable_logging = true; + }; + }; + + config = { + bar = { + height = 30; + position = "top"; + padding_left = 10; + padding_right = 10; + blur_radius = 20; + corner_radius = 9; + }; + + defaults = { + "icon.font" = "$FONT"; + "icon.color" = "$COLOR"; + "background.height" = 24; + "label.padding" = "$PADDING"; + "popup.background.border_width" = 2; + "popup.background.corner_radius" = 9; + }; + }; + + extraConfig = '' + -- This is a test Lua configuration + sbar:add("item", "cpu", { + position = "right", + update_freq = 1, + script = "./scripts/cpu.lua" + }) + + -- Subscribe to events + sbar:subscribe("cpu", "system_woke") + ''; + }; + + # Validate the generated Lua configuration file + nmt.script = '' + assertFileContent \ + home-files/.config/sketchybar/sketchybarrc \ + ${./sketchybarrc.lua} + ''; +} diff --git a/tests/modules/programs/sketchybar/sketchybar-service-expected.plist b/tests/modules/programs/sketchybar/sketchybar-service-expected.plist new file mode 100644 index 000000000..a01c60434 --- /dev/null +++ b/tests/modules/programs/sketchybar/sketchybar-service-expected.plist @@ -0,0 +1,25 @@ + + + + + EnvironmentVariables + + PATH + /@sketchybar@/bin:/@jq@/bin + + KeepAlive + + Label + org.nix-community.home.sketchybar + ProcessType + Interactive + Program + /@sketchybar@/bin/sketchybar + RunAtLoad + + StandardErrorPath + /home/hm-user/Library/Logs/sketchybar/sketchybar.err.log + StandardOutPath + /home/hm-user/Library/Logs/sketchybar/sketchybar.out.log + + \ No newline at end of file diff --git a/tests/modules/programs/sketchybar/sketchybar-service-integration.nix b/tests/modules/programs/sketchybar/sketchybar-service-integration.nix new file mode 100644 index 000000000..2fecd69ba --- /dev/null +++ b/tests/modules/programs/sketchybar/sketchybar-service-integration.nix @@ -0,0 +1,59 @@ +{ + config, + pkgs, + ... +}: + +let + hmPkgs = pkgs.extend ( + self: super: { + sketchybar = config.lib.test.mkStubPackage { + name = "sketchybar"; + outPath = "/@sketchybar@"; + }; + jq = config.lib.test.mkStubPackage { outPath = "/@jq@"; }; + } + ); +in +{ + programs.sketchybar = { + enable = true; + package = hmPkgs.sketchybar; + configType = "bash"; + + variables = { + PADDING = "3"; + FONT = "SF Pro"; + COLOR = "0xff0000ff"; + }; + + config.bar = { + height = 30; + position = "top"; + padding_left = 10; + padding_right = 10; + }; + + # Enable the integrated service + service = { + enable = true; + extraPackages = [ hmPkgs.jq ]; + errorLogFile = "/home/hm-user/Library/Logs/sketchybar/sketchybar.err.log"; + outLogFile = "/home/hm-user/Library/Logs/sketchybar/sketchybar.out.log"; + }; + }; + + # Change home directory for the test + home.homeDirectory = "/home/hm-user"; + + # Validate the generated config files + nmt.script = '' + # Verify config file exists + assertFileExists home-files/.config/sketchybar/sketchybarrc + + # Verify service file exists and matches expected content + serviceFile=LaunchAgents/org.nix-community.home.sketchybar.plist + assertFileExists "$serviceFile" + assertFileContent "$serviceFile" ${./sketchybar-service-expected.plist} + ''; +} diff --git a/tests/modules/programs/sketchybar/sketchybar.nix b/tests/modules/programs/sketchybar/sketchybar.nix new file mode 100644 index 000000000..e67d0f307 --- /dev/null +++ b/tests/modules/programs/sketchybar/sketchybar.nix @@ -0,0 +1,53 @@ +{ config, ... }: + +{ + programs.sketchybar = { + enable = true; + package = config.lib.test.mkStubPackage { }; + + configType = "bash"; + + variables = { + PADDING = "3"; + FONT = "SF Pro"; + COLOR = "0xff0000ff"; + }; + + config = { + bar = { + height = 30; + position = "top"; + padding_left = 10; + padding_right = 10; + }; + + defaults = { + "icon.font" = "$FONT"; + "icon.color" = "$COLOR"; + "background.height" = 24; + }; + }; + + plugins = [ + { + name = "clock"; + placement = "right"; + script = "./scripts/clock.sh"; + update_freq = 1; + } + ]; + + extraConfig = '' + # This is a test configuration + sketchybar --add item cpu right \ + --set cpu script="$PLUGIN_DIR/cpu.sh" \ + --subscribe cpu system_woke + ''; + }; + + nmt.script = '' + assertFileContent \ + home-files/.config/sketchybar/sketchybarrc \ + ${./sketchybarrc.bash} + ''; +} diff --git a/tests/modules/programs/sketchybar/sketchybarrc.bash b/tests/modules/programs/sketchybar/sketchybarrc.bash new file mode 100644 index 000000000..8cc3b48f0 --- /dev/null +++ b/tests/modules/programs/sketchybar/sketchybarrc.bash @@ -0,0 +1,26 @@ +export COLOR=0xff0000ff +export FONT=SF Pro +export PADDING=3 + +sketchybar --bar \ +height=30 \ +padding_left=10 \ +padding_right=10 \ +position=top + +sketchybar --default \ +background.height=24 \ +icon.color=$COLOR \ +icon.font=$FONT + + +sketchybar --add item clock right --set clock script=./scripts/clock.sh update_freq=1 + + +# This is a test configuration +sketchybar --add item cpu right \ + --set cpu script="$PLUGIN_DIR/cpu.sh" \ + --subscribe cpu system_woke + + +sketchybar --update \ No newline at end of file diff --git a/tests/modules/programs/sketchybar/sketchybarrc.lua b/tests/modules/programs/sketchybar/sketchybarrc.lua new file mode 100644 index 000000000..77c363417 --- /dev/null +++ b/tests/modules/programs/sketchybar/sketchybarrc.lua @@ -0,0 +1,52 @@ +#!/usr/bin/env lua +-- Generated by home-manager +local sbar = require("sbarlua") + +-- Set variables +sbar.variables["COLOR"] = "0xff0000ff" +sbar.variables["FONT"] = "SF Pro" +sbar.variables["ITEMS"] = { + "calendar", + "cpu", + "memory" +} +sbar.variables["PADDING"] = 3 +sbar.variables["SETTINGS"] = { + ["enable_logging"] = true, + ["refresh_freq"] = 1 +} + +-- Configure bar +sbar.bar:set({ + ["blur_radius"] = 20, + ["corner_radius"] = 9, + ["height"] = 30, + ["padding_left"] = 10, + ["padding_right"] = 10, + ["position"] = "top" +}) + +-- Configure defaults +sbar.defaults:set({ + ["background.height"] = 24, + ["icon.color"] = "$COLOR", + ["icon.font"] = "$FONT", + ["label.padding"] = "$PADDING", + ["popup.background.border_width"] = 2, + ["popup.background.corner_radius"] = 9 +}) + +-- Extra configuration +-- This is a test Lua configuration +sbar:add("item", "cpu", { + position = "right", + update_freq = 1, + script = "./scripts/cpu.lua" +}) + +-- Subscribe to events +sbar:subscribe("cpu", "system_woke") + + +-- Update the bar +sbar:update()