From 099e1a08fd5141de032b7edc284ece9d348394d3 Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Wed, 21 Jun 2023 18:49:10 +0300 Subject: [PATCH] nix: rewrite nix flake from ground up and introduce home-manager module (#44) * rewrite nix flake and move default package * revert: remove .envrc * feat: create home-manager module big W * fix: properly parse null for config.plugins * build plugins from template derivation * build plugins from template derivation * cache each plugin individually * pass name and derivation to template derivation * fix: rework the devShell * fix: add missing flake-parts input * feat: make anyrun not build plugins it only builds plugins on the anyrun-with-all-plugins package * chore: write documentation about HM module * fix: specify pname for plugins * nix: flake-parts follows nixpkgs * nix: formatting via alejandra --------- Co-authored-by: n3oney --- .github/workflows/cachix.yml | 9 +- README.md | 62 ++++++++- flake.lock | 29 +++- flake.nix | 185 +++++++++++++++++--------- default.nix => nix/default.nix | 28 ++-- nix/hm-module.nix | 234 +++++++++++++++++++++++++++++++++ nix/plugins/default.nix | 49 +++++++ 7 files changed, 512 insertions(+), 84 deletions(-) rename default.nix => nix/default.nix (73%) create mode 100644 nix/hm-module.nix create mode 100644 nix/plugins/default.nix diff --git a/.github/workflows/cachix.yml b/.github/workflows/cachix.yml index b357cd9..ff05189 100644 --- a/.github/workflows/cachix.yml +++ b/.github/workflows/cachix.yml @@ -17,7 +17,14 @@ jobs: matrix: package: - anyrun - - defaultPackage + - applications + - dictionary + - kidex + - randr + - rink + - shell + - stdin + - symbols steps: - uses: easimon/maximize-build-space@v6 with: diff --git a/README.md b/README.md index e13c323..60fff88 100644 --- a/README.md +++ b/README.md @@ -50,16 +50,11 @@ You can use the flake: }; outputs = { self, nixpkgs, anyrun }: let - pkgs = import nixpkgs { - system = system; - overlays = [anyrun.overlay]; - allowUnfree = true; - }; in { nixosConfigurations.HOSTNAME = nixpkgs.lib.nixosSystem { # ... - system.packages = [ pkgs.anyrun ]; + system.packages = [ anyrun.packages.${system}.anyrun ]; # ... }; @@ -67,6 +62,61 @@ You can use the flake: } ``` +The flake provides multiple packages: + - anyrun (default) - just the anyrun binary + - anyrun-with-all-plugins - anyrun and all builtin plugins + - applications - the applications plugin + - dictionary - the dictionary plugin + - kidex - the kidex plugin + - randr - the randr plugin + - rink - the rink plugin + - shell - the shell plugin + - stdin - the stdin plugin + - symbols - the symbols plugin + - translate - the translate plugin + +#### home-manager module +We have a home-manager module available at `homeManagerModules.default`. You use it like this: + +```nix +{ + programs.anyrun = { + enable = true; + config = { + plugins = [ + # An array of all the plugins you want, which either can be paths to the .so files, or their packages + inputs.anyrun.packages.${pkgs.system}.applications + ./some_plugin.so + "${inputs.anyrun.packages.${pkgs.system}.anyrun-with-all-plugins}/lib/kidex" + ]; + width = { fraction = 0.3; }; + position = "top"; + verticalOffset = { absolute = 0; }; + hideIcons = false; + ignoreExclusiveZones = false; + layer = "overlay"; + hidePluginInfo = false; + closeOnClick = false; + showResultsImmediately = false; + maxEntries = null; + }; + extraCss = '' + .some_class { + background: red; + } + ''; + + extraConfigFiles."some-plugin.ron".text = '' + Config( + // for any other plugin + // this file will be put in ~/.config/anyrun/some-plugin.ron + // refer to docs of xdg.configFile for available options + ) + ''; + }; +} +``` + You might also want to use the binary cache to avoid building locally. ```nix diff --git a/flake.lock b/flake.lock index 7ddc2bc..8c1f725 100644 --- a/flake.lock +++ b/flake.lock @@ -1,23 +1,44 @@ { "nodes": { + "flake-parts": { + "inputs": { + "nixpkgs-lib": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1685662779, + "narHash": "sha256-cKDDciXGpMEjP1n6HlzKinN0H+oLmNpgeCTzYnsA2po=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "71fb97f0d875fd4de4994dfb849f2c75e17eb6c3", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, "nixpkgs": { "locked": { - "lastModified": 1682960002, - "narHash": "sha256-5Zjh4pT3lAjFGN1gVrjqj1LLJHKCAlGdLD8raU7oEMc=", + "lastModified": 1685655444, + "narHash": "sha256-6EujQNAeaUkWvpEZZcVF8qSfQrNVWFNNGbUJxv/A5a8=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "8670e496ffd093b60e74e7fa53526aa5920d09eb", + "rev": "e635192892f5abbc2289eaac3a73cdb249abaefd", "type": "github" }, "original": { "owner": "NixOS", - "ref": "nixpkgs-unstable", + "ref": "nixos-unstable", "repo": "nixpkgs", "type": "github" } }, "root": { "inputs": { + "flake-parts": "flake-parts", "nixpkgs": "nixpkgs" } } diff --git a/flake.nix b/flake.nix index 882ee1d..7690549 100644 --- a/flake.nix +++ b/flake.nix @@ -2,71 +2,126 @@ description = "A wayland native, highly customizable runner."; inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; - }; - - outputs = { - self, - nixpkgs, - }: let - cargoToml = builtins.fromTOML (builtins.readFile ./anyrun/Cargo.toml); - supportedSystems = ["x86_64-linux" "aarch64-linux"]; - forAllSystems = f: nixpkgs.lib.genAttrs supportedSystems (system: f system); - in { - overlay = final: prev: { - "${cargoToml.package.name}" = final.callPackage ./. {}; + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + flake-parts = { + url = "github:hercules-ci/flake-parts"; + inputs.nixpkgs-lib.follows = "nixpkgs"; }; - - packages = forAllSystems (system: let - pkgs = import nixpkgs { - inherit system; - overlays = [self.overlay]; - }; - in { - "${cargoToml.package.name}" = pkgs."${cargoToml.package.name}"; - }); - - defaultPackage = forAllSystems (system: - (import nixpkgs { - inherit system; - overlays = [self.overlay]; - }) - ."${cargoToml.package.name}"); - - checks = forAllSystems (system: let - pkgs = import nixpkgs { - inherit system; - overlays = [ - self.overlay - ]; - }; - in { - format = - pkgs.runCommand "check-format" - { - buildInputs = with pkgs; [rustfmt cargo]; - } '' - ${pkgs.rustfmt}/bin/cargo-fmt fmt --manifest-path ${./anyrun}/Cargo.toml -- --check - ${pkgs.nixpkgs-fmt}/bin/nixpkgs-fmt --check ${./anyrun} - touch $out # it worked! - ''; - "${cargoToml.package.name}" = pkgs."${cargoToml.package.name}"; - }); - devShell = forAllSystems (system: let - pkgs = import nixpkgs { - inherit system; - overlays = [self.overlay]; - }; - in - pkgs.mkShell { - inputsFrom = [ - pkgs."${cargoToml.package.name}" - ]; - buildInputs = with pkgs; [ - rustfmt - nixpkgs-fmt - ]; - LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib"; - }); }; + + outputs = inputs @ {flake-parts, ...}: + flake-parts.lib.mkFlake {inherit inputs;} { + systems = ["x86_64-linux" "aarch64-linux"]; + + perSystem = { + config, + self', + inputs', + pkgs, + system, + ... + }: let + inherit (inputs.nixpkgs) lib; + inherit (lib) getExe; + in { + # provide the formatter for nix fmt + formatter = pkgs.alejandra; + + devShells.default = pkgs.mkShell { + inputsFrom = builtins.attrValues self'.packages; + + packages = with pkgs; [ + alejandra # nix formatter + rustfmt # rust formatter + statix # lints and suggestions + deadnix # clean up unused nix code + rustc # rust compiler + gcc + cargo # rust package manager + clippy # opinionated rust formatter + ]; + }; + + packages = let + lockFile = ./Cargo.lock; + in rec { + anyrun = pkgs.callPackage ./nix/default.nix {inherit inputs lockFile;}; + # alias nix build .# to anyrun + default = anyrun; + + anyrun-with-all-plugins = pkgs.callPackage ./nix/default.nix { + inherit inputs lockFile; + dontBuildPlugins = false; + }; + + # expose each plugin as a package + applications = pkgs.callPackage ./nix/plugins/default.nix { + inherit inputs lockFile; + name = "applications"; + }; + + dictionary = pkgs.callPackage ./nix/plugins/default.nix { + inherit inputs lockFile; + name = "dictionary"; + }; + + kidex = pkgs.callPackage ./nix/plugins/default.nix { + inherit inputs lockFile; + name = "kidex"; + }; + + randr = pkgs.callPackage ./nix/plugins/default.nix { + inherit inputs lockFile; + name = "randr"; + }; + + rink = pkgs.callPackage ./nix/plugins/default.nix { + inherit inputs lockFile; + name = "rink"; + }; + + shell = pkgs.callPackage ./nix/plugins/default.nix { + inherit inputs lockFile; + name = "shell"; + }; + + stdin = pkgs.callPackage ./nix/plugins/default.nix { + inherit inputs lockFile; + name = "stdin"; + }; + + symbols = pkgs.callPackage ./nix/plugins/default.nix { + inherit inputs lockFile; + name = "symbols"; + }; + + translate = pkgs.callPackage ./nix/plugins/default.nix { + inherit inputs lockFile; + name = "translate"; + }; + }; + + checks = { + format = + pkgs.runCommand "check-format" { + buildInputs = with pkgs; [ + rustfmt + cargo + ]; + } '' + ${pkgs.rustfmt}/bin/cargo-fmt fmt --manifest-path ./anyrun/Cargo.toml -- --check + ${getExe pkgs.alejandra} --check ./ + touch $out # it worked! + ''; + "anyrun-format-check" = self'.packages.anyrun; + }; + }; + + flake = _: { + # TODO: Make a NixOS module + nixosModules.default = null; + + homeManagerModules.default = import ./nix/hm-module.nix inputs.self; + }; + }; } diff --git a/default.nix b/nix/default.nix similarity index 73% rename from default.nix rename to nix/default.nix index 7fa392f..1bb4ae8 100644 --- a/default.nix +++ b/nix/default.nix @@ -1,4 +1,3 @@ -# default.nix { lib, glib, @@ -12,11 +11,18 @@ rustfmt, cargo, rustc, + lockFile, + dontBuildPlugins ? true, + ... }: let - cargoToml = builtins.fromTOML (builtins.readFile ./anyrun/Cargo.toml); + cargoToml = builtins.fromTOML (builtins.readFile ../anyrun/Cargo.toml); in - rustPlatform.buildRustPackage { - src = ./.; + rustPlatform.buildRustPackage rec { + name = cargoToml.package.name; + pname = "anyrun"; + #inherit version; + + src = ../.; buildInputs = [ pkg-config @@ -28,7 +34,7 @@ in ]; cargoLock = { - lockFile = ./Cargo.lock; + lockFile = lockFile; }; checkInputs = [cargo rustc]; @@ -41,14 +47,16 @@ in cargo ]; + cargoBuildFlags = + if dontBuildPlugins + then ["-p ${name}"] + else []; + doCheck = true; CARGO_BUILD_INCREMENTAL = "false"; RUST_BACKTRACE = "full"; copyLibs = true; - name = cargoToml.package.name; - version = cargoToml.package.version; - postInstall = '' wrapProgram $out/bin/anyrun \ --set GDK_PIXBUF_MODULE_FILE "$(echo ${librsvg.out}/lib/gdk-pixbuf-2.0/*/loaders.cache)" \ @@ -66,6 +74,10 @@ in githubId = 30625554; name = "MichaƂ Minarowski"; } + { + email = "raf@notashelf.dev"; + github = "NotAShelf"; + } ]; }; } diff --git a/nix/hm-module.nix b/nix/hm-module.nix new file mode 100644 index 0000000..e33a854 --- /dev/null +++ b/nix/hm-module.nix @@ -0,0 +1,234 @@ +self: { + config, + pkgs, + lib, + hm, + ... +}: let + cfg = config.programs.anyrun; + + defaultPackage = self.packages.${pkgs.stdenv.hostPlatform.system}.default; +in { + meta.maintainers = with lib.maintainers; [n3oney NotAShelf]; + + options.programs.anyrun = with lib; { + enable = mkEnableOption "anyrun"; + + package = mkOption { + type = with types; nullOr package; + default = defaultPackage; + defaultText = lib.literalExpression '' + anyrun.packages.''${pkgs.stdenv.hostPlatform.system}.default + ''; + description = mdDoc '' + Anyrun package to use. Defaults to the one provided by the flake. + ''; + }; + config = let + mkNumericOption = { + default, + description, + ... + }: + with types; + mkOption { + inherit default description; + example = '' + { absolute = 200; }; + or + { fraction = 0.4; }; + ''; + type = submodule { + options = { + absolute = mkOption { + type = nullOr int; + default = null; + }; + fraction = mkOption { + type = nullOr float; + default = null; + }; + }; + }; + }; + + numericInfo = '' + This is a numeric option - pass either `{ absolute = int; };` or `{ fraction = float; };`. + when using `absolute` it sets the absolute value in pixels, + when using `fraction`, it sets a fraction of the width or height of the full screen (depends on exclusive zones and the settings related to them) window + ''; + in { + plugins = mkOption { + type = with types; nullOr (listOf (either package str)); + default = null; + description = mdDoc '' + List of anyrun plugins to use. Can either be packages, absolute plugin paths, or strings. + ''; + }; + + width = mkNumericOption { + default.absolute = 800; + description = mdDoc '' + How wide the input box and results are. + + ${numericInfo} + ''; + }; + + position = mkOption { + type = with types; enum ["top" "center"]; + default = "top"; + description = "Where Anyrun is located on the screen (top or center)"; + }; + + verticalOffset = mkNumericOption { + default.absolute = 0; + description = '' + How much the runner is shifted vertically. + + ${numericInfo} + ''; + }; + + hideIcons = mkOption { + type = types.bool; + default = false; + description = "Hide match and plugin info icons"; + }; + + ignoreExclusiveZones = mkOption { + type = types.bool; + default = false; + description = "ignore exclusive zones, eg. Waybar"; + }; + + layer = mkOption { + type = with types; enum ["background" "bottom" "top" "overlay"]; + default = "overlay"; + description = "Layer shell layer (background, bottom, top or overlay)"; + }; + + hidePluginInfo = mkOption { + type = types.bool; + default = false; + description = "Hide the plugin info panel"; + }; + + closeOnClick = mkOption { + type = types.bool; + default = false; + description = "Close window when a click outside the main box is received"; + }; + + showResultsImmediately = mkOption { + type = types.bool; + default = false; + description = "Show search results immediately when Anyrun starts"; + }; + + maxEntries = mkOption { + type = with types; nullOr int; + default = null; + description = "Limit amount of entries shown in total"; + }; + }; + + extraCss = lib.mkOption { + type = lib.types.nullOr lib.types.lines; + default = ""; + description = mdDoc '' + Extra CSS lines to add to {file}`~/.config/anyrun/style.css`. + ''; + }; + + extraConfigFiles = lib.mkOption { + # unfortunately HM doesn't really export the type for files, but hopefully + # hm will throw errors if the options are wrong here, so I'm being *very* loose + type = lib.types.attrs; + default = {}; + description = mdDoc '' + Extra files to put in `~/.config/anyrun`, a wrapper over `xdg.configFile`. + ''; + example = '' + programs.anyrun.extraConfigFiles."plugin-name.ron".text = ''' + Config( + some_option: true, + ) + ''' + ''; + }; + }; + + config = lib.mkIf cfg.enable (let + assertNumeric = numeric: { + assertion = !((numeric ? absolute && numeric.absolute != null) && (numeric ? fraction && numeric.fraction != null)); + message = "Invalid numeric definition, you can only specify one of absolute or fraction."; + }; + + stringifyNumeric = numeric: + if (numeric ? absolute && numeric.absolute != null) + then "Absolute(${builtins.toString numeric.absolute})" + else "Fraction(${builtins.toString numeric.fraction})"; + + capitalize = string: + lib.toUpper (builtins.substring 0 1 string) + lib.toLower (builtins.substring 1 ((builtins.stringLength string) - 1) string); + + parsedPlugins = + if cfg.config.plugins == null + then [] + else + builtins.map (entry: + if lib.types.package.check entry + then "${entry}/lib/lib${lib.replaceStrings ["-"] ["_"] entry.pname}.so" + else entry) + cfg.config.plugins; + in { + assertions = [(assertNumeric cfg.config.width) (assertNumeric cfg.config.verticalOffset)]; + + warnings = + if cfg.config.plugins == null + then [ + '' + You haven't enabled any plugins. Anyrun will not show any results, unless you specify plugins with the --override-plugins flag. + Add plugins to programs.anyrun.config.plugins, or set it to [] to silence the warning. + '' + ] + else []; + + home.packages = lib.optional (cfg.package != null) cfg.package; + + xdg.configFile = lib.mkMerge [ + (lib.mapAttrs' + (name: value: lib.nameValuePair ("anyrun/" + name) value) + cfg.extraConfigFiles) + + { + "anyrun/config.ron".text = '' + Config( + width: ${stringifyNumeric cfg.config.width}, + position: ${capitalize cfg.config.position}, + vertical_offset: ${stringifyNumeric cfg.config.verticalOffset}, + hide_icons: ${lib.boolToString cfg.config.hideIcons}, + ignore_exclusive_zones: ${lib.boolToString cfg.config.ignoreExclusiveZones}, + layer: ${capitalize cfg.config.layer}, + hide_plugin_info: ${lib.boolToString cfg.config.hidePluginInfo}, + close_on_click: ${lib.boolToString cfg.config.closeOnClick}, + show_results_immediately: ${lib.boolToString cfg.config.showResultsImmediately}, + max_entries: ${ + if cfg.config.maxEntries == null + then "None" + else builtins.toString cfg.config.maxEntries + }, + plugins: ${builtins.toJSON parsedPlugins}, + ) + ''; + } + + { + "anyrun/style.css" = lib.mkIf (cfg.extraCss != null) { + text = cfg.extraCss; + }; + } + ]; + }); +} diff --git a/nix/plugins/default.nix b/nix/plugins/default.nix new file mode 100644 index 0000000..85a4167 --- /dev/null +++ b/nix/plugins/default.nix @@ -0,0 +1,49 @@ +{ + lib, + glib, + makeWrapper, + rustPlatform, + atk, + gtk3, + gtk-layer-shell, + pkg-config, + librsvg, + inputs, + name, + lockFile, + ... +}: let + cargoToml = builtins.fromTOML (builtins.readFile ../../plugins/${name}/Cargo.toml); +in + rustPlatform.buildRustPackage { + pname = name; + name = cargoToml.package.name; + + src = "${inputs.self}"; + cargoLock.lockFile = lockFile; + + buildInputs = [ + glib + atk + gtk3 + librsvg + gtk-layer-shell + ]; + + nativeBuildInputs = [ + pkg-config + makeWrapper + ]; + + doCheck = true; + CARGO_BUILD_INCREMENTAL = "false"; + RUST_BACKTRACE = "full"; + copyLibs = true; + cargoBuildFlags = ["-p ${name}"]; + + meta = with lib; { + description = "The ${name} plugin for Anyrun"; + homepage = "https://github.com/Kirottu/anyrun"; + license = with licenses; [gpl3]; + }; + }