From 599e22b1c76dc56172c7bb97bc3cd04266869bd6 Mon Sep 17 00:00:00 2001 From: Philippe Laflamme Date: Sun, 2 Oct 2022 22:52:33 -0400 Subject: [PATCH] sbt: allow managing the `~/.sbt/repositories` file sbt allows overriding the default repositories to use to resolve dependencies. This is often used with proxies and/or private repositories to host internal packages. This change adds a `repositories` attribute to `sbt` to allow specifying the values that will go in `~/.sbt/repositories` file. To support the above change we also deprecate the `baseConfigPath` option in favour of `baseUserConfigPath` which points one level higher by default. This allows not using relative paths to refer to the top-level configuration directory. Also adds tests for the new option and the deprecation of the previous one. --- modules/programs/sbt.nix | 73 +++++++++++++++++-- tests/modules/programs/sbt/default.nix | 5 +- .../programs/sbt/deprecated-options.nix | 18 +++++ tests/modules/programs/sbt/repositories.nix | 38 ++++++++++ .../modules/programs/sbt/user-config-path.nix | 39 ++++++++++ 5 files changed, 167 insertions(+), 6 deletions(-) create mode 100644 tests/modules/programs/sbt/deprecated-options.nix create mode 100644 tests/modules/programs/sbt/repositories.nix create mode 100644 tests/modules/programs/sbt/user-config-path.nix diff --git a/modules/programs/sbt.nix b/modules/programs/sbt.nix index bf355a80b..22a32fe28 100644 --- a/modules/programs/sbt.nix +++ b/modules/programs/sbt.nix @@ -16,6 +16,17 @@ let import scala.sys.process._ ${concatStrings (map renderCredential creds)}''; + renderRepository = value: + if isString value then '' + ${value} + '' else '' + ${concatStrings (mapAttrsToList (name: value: "${name}: ${value}") value)} + ''; + + renderRepositories = repos: '' + [repositories] + ${concatStrings (map renderRepository cfg.repositories)}''; + sbtTypes = { plugin = types.submodule { options = { @@ -68,6 +79,11 @@ let cfg = config.programs.sbt; in { + imports = [ + (mkRemovedOptionModule [ "programs" "sbt" "baseConfigPath" ] + "Use programs.sbt.baseUserConfigPath instead, but note that the semantics are slightly different.") + ]; + meta.maintainers = [ maintainers.kubukoz ]; options.programs.sbt = { @@ -80,10 +96,13 @@ in { description = "The package with sbt to be installed."; }; - baseConfigPath = mkOption { + baseUserConfigPath = mkOption { type = types.str; - default = ".sbt/1.0"; - description = "Where the plugins and credentials should be located."; + default = ".sbt"; + description = '' + Where the sbt configuration files should be located, relative + HOME. + ''; }; plugins = mkOption { @@ -123,19 +142,63 @@ in { A list of credentials to define in the sbt configuration directory. ''; }; + + repositories = mkOption { + type = with types; + listOf + (either (enum [ "local" "maven-central" "maven-local" ]) (attrsOf str)); + default = [ ]; + example = literalExpression '' + [ + "local" + { my-ivy-proxy-releases = "http://repo.company.com/ivy-releases/, [organization]/[module]/(scala_[scalaVersion]/)(sbt_[sbtVersion]/)[revision]/[type]s/[artifact](-[classifier]).[ext]" } + { my-maven-proxy-releases = "http://repo.company.com/maven-releases/" } + "maven-central" + ] + ''; + description = '' + A list of repositories to use when resolving dependencies. Defined as a + list of pre-defined repository or custom repository as a set of name to + URL. The list will be used populate the ~/.sbt/repositories + file in the order specified. + + + + Pre-defined repositories must be one of local, + maven-local, maven-central. + + + + Custom repositories are defined as + { name-of-repo = "https://url.to.repo.com"}. + + + + See + + about this configuration section and + + to read about proxy repositories. + ''; + }; }; config = mkIf cfg.enable (mkMerge [ { home.packages = [ cfg.package ]; } (mkIf (cfg.plugins != [ ]) { - home.file."${cfg.baseConfigPath}/plugins/plugins.sbt".text = + home.file."${cfg.baseUserConfigPath}/1.0/plugins/plugins.sbt".text = concatStrings (map renderPlugin cfg.plugins); }) (mkIf (cfg.credentials != [ ]) { - home.file."${cfg.baseConfigPath}/credentials.sbt".text = + home.file."${cfg.baseUserConfigPath}/1.0/credentials.sbt".text = renderCredentials cfg.credentials; }) + + (mkIf (cfg.repositories != [ ]) { + home.file."${cfg.baseUserConfigPath}/repositories".text = + renderRepositories cfg.repositories; + }) ]); } diff --git a/tests/modules/programs/sbt/default.nix b/tests/modules/programs/sbt/default.nix index 59ad89dae..2d506b683 100644 --- a/tests/modules/programs/sbt/default.nix +++ b/tests/modules/programs/sbt/default.nix @@ -1,4 +1,7 @@ { - sbt-plugins = ./plugins.nix; sbt-credentials = ./credentials.nix; + sbt-deprecated-options = ./deprecated-options.nix; + sbt-plugins = ./plugins.nix; + sbt-repositories = ./repositories.nix; + sbt-user-config-path = ./user-config-path.nix; } diff --git a/tests/modules/programs/sbt/deprecated-options.nix b/tests/modules/programs/sbt/deprecated-options.nix new file mode 100644 index 000000000..c760354a4 --- /dev/null +++ b/tests/modules/programs/sbt/deprecated-options.nix @@ -0,0 +1,18 @@ +{ ... }: + +{ + programs.sbt = { + enable = true; + baseConfigPath = "gone"; + }; + + test.stubs.sbt = { }; + + test.asserts.assertions.expected = [ + (let offendingFile = toString ./deprecated-options.nix; + in '' + The option definition `programs.sbt.baseConfigPath' in `${offendingFile}' no longer has any effect; please remove it. + Use programs.sbt.baseUserConfigPath instead, but note that the semantics are slightly different. + '') + ]; +} diff --git a/tests/modules/programs/sbt/repositories.nix b/tests/modules/programs/sbt/repositories.nix new file mode 100644 index 000000000..0def44f7c --- /dev/null +++ b/tests/modules/programs/sbt/repositories.nix @@ -0,0 +1,38 @@ +{ pkgs, ... }: + +let + repositories = [ + "local" + { my-maven-proxy = "http://repo.mavenproxy.io/a/b/c/d"; } + "maven-local" + { + my-ivy-proxy = + "http://repo.company.com/ivy-releases/, [organization]/[module]/(scala_[scalaVersion]/)(sbt_[sbtVersion]/)[revision]/[type]s/[artifact](-[classifier]).[ext]"; + } + "maven-central" + ]; + + expectedRepositories = builtins.toFile "repositories" '' + [repositories] + local + my-maven-proxy: http://repo.mavenproxy.io/a/b/c/d + maven-local + my-ivy-proxy: http://repo.company.com/ivy-releases/, [organization]/[module]/(scala_[scalaVersion]/)(sbt_[sbtVersion]/)[revision]/[type]s/[artifact](-[classifier]).[ext] + maven-central + ''; + + repositoriesSbtPath = ".sbt/repositories"; +in { + config = { + programs.sbt = { + enable = true; + repositories = repositories; + package = pkgs.writeScriptBin "sbt" ""; + }; + + nmt.script = '' + assertFileExists "home-files/${repositoriesSbtPath}" + assertFileContent "home-files/${repositoriesSbtPath}" "${expectedRepositories}" + ''; + }; +} diff --git a/tests/modules/programs/sbt/user-config-path.nix b/tests/modules/programs/sbt/user-config-path.nix new file mode 100644 index 000000000..ef7c2ca8a --- /dev/null +++ b/tests/modules/programs/sbt/user-config-path.nix @@ -0,0 +1,39 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + plugins = [{ + org = "a"; + artifact = "b"; + version = "c"; + }]; + + credentials = [{ + realm = "a"; + host = "b"; + user = "c"; + passwordCommand = "d"; + }]; + + repositories = [ "local" ]; + + baseSbtPath = ".config/sbt"; +in { + config = { + programs.sbt = { + enable = true; + plugins = plugins; + credentials = credentials; + repositories = repositories; + baseUserConfigPath = ".config/sbt"; + package = pkgs.writeScriptBin "sbt" ""; + }; + + nmt.script = '' + assertFileExists "home-files/${baseSbtPath}/1.0/plugins/plugins.sbt" + assertFileExists "home-files/${baseSbtPath}/1.0/credentials.sbt" + assertFileExists "home-files/${baseSbtPath}/repositories" + ''; + }; +}