diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 947ae4a..0476802 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,6 +4,7 @@ on: workflow_run: workflows: [Build] types: [completed] + branches: [main] concurrency: group: release diff --git a/README.md b/README.md index e2c14c7..dd453b7 100644 --- a/README.md +++ b/README.md @@ -127,6 +127,14 @@ devShells.default = pkgs.mkShell { `packagesFromVersionsFile` is of the form `{ “” = ; "" = ; ... }`. +## Version Files + +As of version `2.1.0`, asdf2nix now supports legacy version files in the same +way that +[asdf](https://asdf-vm.com/manage/configuration.html#legacy-version-file) does. +Check the version files [test suite](tests/version_files_test.nix) for more +information on how this feature works. + ## Plugins In an asdf2nix context, a plugin’s primary goal is to determine whether a diff --git a/flake.nix b/flake.nix index 3480daa..f254e44 100644 --- a/flake.nix +++ b/flake.nix @@ -7,7 +7,8 @@ in { lib.packagesFromVersionsFile = - { versionsFile + { versionsFile ? null + , legacyVersionFiles ? { } , system ? builtins.currentSystem , plugins ? { } , skipMissingPlugins ? false @@ -22,10 +23,46 @@ in { name = builtins.elemAt pluginAndVersion 0; - version = builtins.elemAt pluginAndVersion 1; + value = builtins.elemAt pluginAndVersion 1; }); - filterPlugins = builtins.filter - ({ name, ... }: + toolVersions = + if versionsFile == null + then { } + else + builtins.listToAttrs + (parseVersions + (removeComments + (fileLines versionsFile))); + legacyVersions = + builtins.mapAttrs + (_: file: lib.fileContents file) + legacyVersionFiles; + versions = + lib.throwIf (versionsFile == null && legacyVersionFiles == { }) + '' + No version files provided. Try with `versionsFile`: + + ``` + packagesFromVersionsFile { + versionsFile = ./.tool-versions; + ... + } + ``` + + Or `legacyVersionFiles`: + + ``` + packagesFromVersionsFile { + legacyVersionFiles = { + python = ./.python-version; + }; + ... + } + ``` + '' + toolVersions // legacyVersions; + filterPlugins = lib.filterAttrs + (name: _: let hasPlugin = builtins.hasAttr name plugins; in @@ -56,8 +93,8 @@ lib.warnIf (!hasPlugin) "Skipping \"${name}\" plugin" hasPlugin); - findPackages = builtins.map - ({ name, version }: + findPackages = builtins.mapAttrs + (name: version: let plugin = plugins.${name}; in @@ -70,18 +107,13 @@ > nix flake lock --update-input ``` '' + plugin.packageFromVersion { - inherit name; - value = plugin.packageFromVersion { inherit system version; }; + inherit system version; } ); in - builtins.listToAttrs - (findPackages - (filterPlugins - (parseVersions - (removeComments - (fileLines versionsFile))))); + findPackages (filterPlugins versions); templates = { default = { diff --git a/tests/lib_test.nix b/tests/lib_test.nix index de3c685..c1f0332 100644 --- a/tests/lib_test.nix +++ b/tests/lib_test.nix @@ -2,30 +2,12 @@ let lib = (builtins.getFlake (builtins.toString ./..)).lib; in [ + # comments { - name = "Ignores comment lines"; - actual = lib.packagesFromVersionsFile { - versionsFile = builtins.toFile ".tool-versions" '' - # This is a comment - python 3.12.0 - # This is another comment - terraform 1.6.3 - ''; - plugins = { - python = { - hasVersion = _: true; - packageFromVersion = { version, ... }: version; - }; - terraform = { - hasVersion = _: true; - packageFromVersion = { version, ... }: version; - }; - }; - }; - expected = { python = "3.12.0"; terraform = "1.6.3"; }; - } - { - name = "Ignores inline comments"; + name = '' + When versionsFile contain comments, then it ignores those during the + parsing + ''; actual = lib.packagesFromVersionsFile { versionsFile = builtins.toFile ".tool-versions" '' # This is a comment @@ -48,14 +30,8 @@ in # skipMissingPlugins = false { name = '' - Given: - - A versions file referencing some plugins - - A set of plugins matching only one of them - - A feature flag to skip missing plugins - When: - - The matching plugin provides a package for the requested version - Then: - - Returns a set with the retrieved package + When skipMissingPlugins is enabled and some plugins are missing, then + returns packages only for the ones matching ''; actual = lib.packagesFromVersionsFile { versionsFile = builtins.toFile ".tool-versions" '' @@ -74,15 +50,8 @@ in } { name = '' - Given: - - A versions file referencing some plugins - - A set of plugins matching only one of them - - A feature flag to skip missing plugins - When: - - The matching plugin does not provide a package for the requested - version - Then: - - Throws an error + When skipMissingPlugins is enabled and all plugins are provided, then it + returns all the packages ''; actual = builtins.tryEval ( (lib.packagesFromVersionsFile { @@ -96,20 +65,14 @@ in }; }; skipMissingPlugins = true; - }) + }).python ); expected = { success = false; value = false; }; } { name = '' - Given: - - A versions file referencing some plugins - - A set of empty plugins - - A feature flag to skip missing plugins - When: - - There are no plugins available - Then: - - Returns an empty set + When skipMissingPlugins is enabled and no plugins are provided, then + returns an empty set ''; actual = lib.packagesFromVersionsFile { versionsFile = builtins.toFile ".tool-versions" '' @@ -123,13 +86,8 @@ in # skipMissingPlugins = false { name = '' - Given: - - A versions file referencing some plugins - - A set of plugins matching all of them - When: - - The matching plugins provide packages for the requested versions - Then: - - Returns a set with the retrieved packages + When skipMissingPlugins is disabled and all plugins are provided, then + returns all packages ''; actual = lib.packagesFromVersionsFile { versionsFile = builtins.toFile ".tool-versions" '' @@ -149,39 +107,30 @@ in }; expected = { python = "3.12.0"; terraform = "1.6.3"; }; } + # hasVersions { name = '' - Given: - - A versions file referencing some plugins - - A set of plugins matching all of them - When: - - The matching plugin does not provide a package for the requested - version - Then: - - Throws an error + When the provided plugin does not contain the requested version, then + throws an error ''; - actual = builtins.tryEval (lib.packagesFromVersionsFile { - versionsFile = builtins.toFile ".tool-versions" '' - python 3.12.0 - ''; - plugins = { - python = { - hasVersion = _: false; - packageFromVersion = { version, ... }: version; + actual = builtins.tryEval ( + (lib.packagesFromVersionsFile { + versionsFile = builtins.toFile ".tool-versions" '' + python 3.12.0 + ''; + plugins = { + python = { + hasVersion = _: false; + packageFromVersion = { version, ... }: version; + }; }; - }; - }); + }).python + ); expected = { success = false; value = false; }; } { name = '' - Given: - - A versions file referencing some plugins - - A set of empty plugins - When: - - There are no plugins available - Then: - - Throws an error + When no plugins are provided, then throws an error ''; actual = builtins.tryEval (lib.packagesFromVersionsFile { versionsFile = builtins.toFile ".tool-versions" '' diff --git a/tests/version_files_test.nix b/tests/version_files_test.nix new file mode 100644 index 0000000..99748ee --- /dev/null +++ b/tests/version_files_test.nix @@ -0,0 +1,89 @@ +let + lib = (builtins.getFlake (builtins.toString ./..)).lib; +in +[ + { + name = '' + When neither versionsFile nor legacyVersionFiles are provided, then + throws an error + ''; + actual = builtins.tryEval (lib.packagesFromVersionsFile { }); + expected = { success = false; value = false; }; + } + { + name = '' + When only legacyVersionFiles is provided, then returns the package + matching the requested version + ''; + actual = lib.packagesFromVersionsFile { + legacyVersionFiles = { + python = builtins.toFile ".python-version" '' + 3.12.0 + ''; + }; + plugins = { + python = { + hasVersion = _: true; + packageFromVersion = { version, ... }: version; + }; + }; + }; + expected = { python = "3.12.0"; }; + } + { + name = '' + When both versionsFile and legacyVersionFiles are provided and the + plugins does not overlap, then returns packages specified in both files + ''; + actual = lib.packagesFromVersionsFile { + versionsFile = builtins.toFile ".tool-versions" '' + terraform 1.6.3 + ''; + legacyVersionFiles = { + python = builtins.toFile ".python-version" '' + 3.12.0 + ''; + }; + plugins = { + python = { + hasVersion = _: true; + packageFromVersion = { version, ... }: version; + }; + terraform = { + hasVersion = _: true; + packageFromVersion = { version, ... }: version; + }; + }; + }; + expected = { python = "3.12.0"; terraform = "1.6.3"; }; + } + { + name = '' + When both versionsFile and legacyVersionFiles are provided and the + plugins overlap, then the version of the tool coming from the legacy file + takes more precedence + ''; + actual = lib.packagesFromVersionsFile { + versionsFile = builtins.toFile ".tool-versions" '' + python 2.7.6 + terraform 1.6.3 + ''; + legacyVersionFiles = { + python = builtins.toFile ".python-version" '' + 3.12.0 + ''; + }; + plugins = { + python = { + hasVersion = _: true; + packageFromVersion = { version, ... }: version; + }; + terraform = { + hasVersion = _: true; + packageFromVersion = { version, ... }: version; + }; + }; + }; + expected = { python = "3.12.0"; terraform = "1.6.3"; }; + } +]