diff --git a/lib/default.nix b/lib/default.nix index 904d5a0e..5c6ffb00 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -26,6 +26,7 @@ lib.makeExtensible ( options = call ./options.nix { }; plugins = call ./plugins { }; utils = call ./utils.nix { inherit _nixvimTests; } // call ./utils.internal.nix { }; + version = call ./version.nix { inherit flake; }; # Top-level helper aliases: # TODO: deprecate some aliases diff --git a/lib/version.nix b/lib/version.nix new file mode 100644 index 00000000..120ca3f5 --- /dev/null +++ b/lib/version.nix @@ -0,0 +1,140 @@ +{ + lib, + flake, +}: +let + inherit (builtins) + isList + mapAttrs + head + tail + ; + + lockFile = lib.importJSON ../flake.lock; + + /** + Resolve an input spec into a node name. + + # Inputs + + `inputSpec` ([String] | String) + : Either a node name, or a "follows" path relative to the root node. + + # Output + + The node name. + + # Upstream + + This is based on `resolveInput` in nix's [call-flake.nix] + + [call-flake.nix]: https://github.com/NixOS/nix/blob/46beb9af/src/libflake/call-flake.nix#L21-L25 + */ + resolveInput = + inputSpec: if isList inputSpec then getInputByPath lockFile.root inputSpec else inputSpec; + + /** + Follow an input attrpath from the root node, stepping through each node along the path. + + # Inputs + + `nodeName` + : The name of the _current_ node. + + `path` + : The input attrpath to follow. + E.g. `["dwarffs" "nixpkgs"]` + + # Outputs + + The final node name. + + # Upstream + + This is based on `getInputByPath` in nix's [call-flake.nix] + + [call-flake.nix]: https://github.com/NixOS/nix/blob/46beb9af/src/libflake/call-flake.nix#L27-L37 + */ + getInputByPath = + nodeName: path: + if path == [ ] then + nodeName + else + getInputByPath (resolveInput lockFile.nodes.${nodeName}.inputs.${head path}) (tail path); + + # Inputs specified in our flake.lock + lockedInputs = lib.pipe lockFile.nodes.${lockFile.root}.inputs [ + (mapAttrs (inputName: inputSpec: lockFile.nodes.${resolveInput inputSpec})) + ]; + + # Our actual inputs, potentially modified by end-user's "follows" + realInputs = flake.inputs; +in +{ + /** + Check whether a specific input is modified when compared to our `flake.lock` file. + + This can happen when a user specifies one of our inputs "follows" one of theirs. + */ + isInputModified = + name: + assert lib.assertMsg ( + realInputs ? ${name} + ) "isInputModified: `${lib.strings.escapeNixIdentifier name}` is not in `inputs`."; + assert lib.assertMsg ( + lockedInputs ? ${name} + ) "isInputModified: `${lib.strings.escapeNixIdentifier name}` is not in `flake.lock`."; + let + # If a non-flake input is used, `sourceInfo` will be missing + real = realInputs.${name}.sourceInfo.narHash or null; + locked = lockedInputs.${name}.locked.narHash; + in + real != locked; + + /** + Nixvim's current commit, or the specified default when nixvim's repo is dirty. + */ + revisionWithDefault = default: flake.sourceInfo.rev or default; + + /** + Nixvim's current commit, defaulting to the canonical branch name for this release. + */ + revision = with lib.nixvim.version; revisionWithDefault releaseBranch; + + /** + Nixvim's release. + + Derived from the nixpkgs branch targeted in `flake.lock`. + */ + release = + let + inherit (lockedInputs.nixpkgs.original) ref; + parts = lib.splitString "-" ref; + in + lib.last parts; + + /** + The canonical branch name associated with this release of nixvim. + */ + releaseBranch = + let + inherit (lib.nixvim.version) release; + in + if release == "unstable" then "main" else "nixos-" + release; + + /** + Whether the nixpkgs input is correct for this release of nixvim. + + Can be used to identify mismatched nixpkgs inputs, usually caused our input + following a user's input from another channel + */ + correctNixpkgsChannel = + let + nixvimRelease = lib.nixvim.version.release; + nixpkgsRelease = lib.trivial.release; + in + if nixvimRelease == "unstable" then + true # TODO: how do we know what channel a nixpkgs instance is on? + else + nixvimRelease == nixpkgsRelease; +}