Repository: easybuilders/easybuild-framework

Issue:
Misleading --accept-eula configuration option and a wildcard proposal
Hi guys,

I think the '--accept-eula' configuration option is misleading.
There is already an easyconfig parameter of a very similar name, 'accept_eula', and of different data types, string vs bool.
Using the name '--accept-eula-for' instead would be more self-explanatory. The use case would be: '--accept-eula-for=AOCC,Intel-OneAPI'. I noticed that Kenneth already used such a name in PR #3535 but renamed it to --accept-eula.

To me, the configuration option naming, '--accept-eula', implies boolean type. The first time I saw the option, I thought I need to specify True to accept it. I did it and noticed that it didn't work. Checking Github and the documentation, I figured out that I need to specify the package for which I accept EULA.

On top of that, one should specify for which packages he accepts EULA. In most cases, one doesn't care and thus wants to specify that he accepts them all but in the current state cannot. Therefore, I propose to add a wildcard '*' which means that he accepts EULA for all packages he installs.

Best regards,
Robert


Discussion:

Resolving patch:
diff --git a/easybuild/framework/easyblock.py b/easybuild/framework/easyblock.py
--- a/easybuild/framework/easyblock.py
+++ b/easybuild/framework/easyblock.py
@@ -1655,8 +1655,8 @@ def check_accepted_eula(self, name=None, more_info=None):
         if name is None:
             name = self.name
 
-        accepted_eulas = build_option('accept_eula') or []
-        if self.cfg['accept_eula'] or name in accepted_eulas:
+        accepted_eulas = build_option('accept_eula_for') or []
+        if self.cfg['accept_eula'] or name in accepted_eulas or any(re.match(x, name) for x in accepted_eulas):
             self.log.info("EULA for %s is accepted", name)
         else:
             error_lines = [
@@ -1667,7 +1667,7 @@ def check_accepted_eula(self, name=None, more_info=None):
 
             error_lines.extend([
                 "You should either:",
-                "- add --accept-eula=%(name)s to the 'eb' command;",
+                "- add --accept-eula-for=%(name)s to the 'eb' command;",
                 "- update your EasyBuild configuration to always accept the EULA for %(name)s;",
                 "- add 'accept_eula = True' to the easyconfig file you are using;",
                 '',
diff --git a/easybuild/tools/config.py b/easybuild/tools/config.py
--- a/easybuild/tools/config.py
+++ b/easybuild/tools/config.py
@@ -163,7 +163,7 @@ def mk_full_default_path(name, prefix=DEFAULT_PREFIX):
 # build options that have a perfectly matching command line option, listed by default value
 BUILD_OPTIONS_CMDLINE = {
     None: [
-        'accept_eula',
+        'accept_eula_for',
         'aggregate_regtest',
         'backup_modules',
         'container_config',
@@ -504,6 +504,10 @@ def init_build_options(build_options=None, cmdline_options=None):
             _log.info("Auto-enabling ignoring of OS dependencies")
             cmdline_options.ignore_osdeps = True
 
+        if not cmdline_options.accept_eula_for and cmdline_options.accept_eula:
+            _log.deprecated("Use accept-eula-for configuration setting rather than accept-eula.", '5.0')
+            cmdline_options.accept_eula_for = cmdline_options.accept_eula
+
         cmdline_build_option_names = [k for ks in BUILD_OPTIONS_CMDLINE.values() for k in ks]
         active_build_options.update(dict([(key, getattr(cmdline_options, key)) for key in cmdline_build_option_names]))
         # other options which can be derived but have no perfectly matching cmdline option
@@ -537,6 +541,9 @@ def build_option(key, **kwargs):
     build_options = BuildOptions()
     if key in build_options:
         return build_options[key]
+    elif key == 'accept_eula':
+        _log.deprecated("Use accept_eula_for build option rather than accept_eula.", '5.0')
+        return build_options['accept_eula_for']
     elif 'default' in kwargs:
         return kwargs['default']
     else:
diff --git a/easybuild/tools/options.py b/easybuild/tools/options.py
--- a/easybuild/tools/options.py
+++ b/easybuild/tools/options.py
@@ -334,7 +334,9 @@ def override_options(self):
         descr = ("Override options", "Override default EasyBuild behavior.")
 
         opts = OrderedDict({
-            'accept-eula': ("Accept EULA for specified software", 'strlist', 'store', []),
+            'accept-eula': ("Accept EULA for specified software [DEPRECATED, use --accept-eula-for instead!]",
+                            'strlist', 'store', []),
+            'accept-eula-for': ("Accept EULA for specified software", 'strlist', 'store', []),
             'add-dummy-to-minimal-toolchains': ("Include dummy toolchain in minimal toolchain searches "
                                                 "[DEPRECATED, use --add-system-to-minimal-toolchains instead!)",
                                                 None, 'store_true', False),


Test patch:
diff --git a/test/framework/options.py b/test/framework/options.py
--- a/test/framework/options.py
+++ b/test/framework/options.py
@@ -5894,7 +5894,7 @@ def test_sysroot(self):
         self.assertErrorRegex(EasyBuildError, error_pattern, self._run_mock_eb, ['--show-config'], raise_error=True)
 
     def test_accept_eula_for(self):
-        """Test --accept-eula configuration option."""
+        """Test --accept-eula-for configuration option."""
 
         # use toy-0.0.eb easyconfig file that comes with the tests
         topdir = os.path.abspath(os.path.dirname(__file__))
@@ -5911,27 +5911,56 @@ def test_accept_eula_for(self):
         args = [test_ec, '--force']
         error_pattern = r"The End User License Argreement \(EULA\) for toy is currently not accepted!"
         self.assertErrorRegex(EasyBuildError, error_pattern, self.eb_main, args, do_build=True, raise_error=True)
-
-        # installation proceeds if EasyBuild is configured to accept EULA for specified software via --accept-eula
-        self.eb_main(args + ['--accept-eula=foo,toy,bar'], do_build=True, raise_error=True)
-
         toy_modfile = os.path.join(self.test_installpath, 'modules', 'all', 'toy', '0.0')
         if get_module_syntax() == 'Lua':
             toy_modfile += '.lua'
-        self.assertTrue(os.path.exists(toy_modfile))
+
+        # installation proceeds if EasyBuild is configured to accept EULA for specified software via --accept-eula-for
+        for val in ('foo,toy,bar', '.*', 't.y'):
+            self.eb_main(args + ['--accept-eula-for=' + val], do_build=True, raise_error=True)
+
+            self.assertTrue(os.path.exists(toy_modfile))
+
+            remove_dir(self.test_installpath)
+            self.assertFalse(os.path.exists(toy_modfile))
+
+            # also check use of $EASYBUILD_ACCEPT_EULA to accept EULA for specified software
+            os.environ['EASYBUILD_ACCEPT_EULA_FOR'] = val
+            self.eb_main(args, do_build=True, raise_error=True)
+            self.assertTrue(os.path.exists(toy_modfile))
+
+            remove_dir(self.test_installpath)
+            self.assertFalse(os.path.exists(toy_modfile))
+
+            del os.environ['EASYBUILD_ACCEPT_EULA_FOR']
+
+        # also check deprecated --accept-eula configuration option
+        self.allow_deprecated_behaviour()
+
+        self.mock_stderr(True)
+        self.eb_main(args + ['--accept-eula=foo,toy,bar'], do_build=True, raise_error=True)
+        stderr = self.get_stderr()
+        self.mock_stderr(False)
+        self.assertTrue("Use accept-eula-for configuration setting rather than accept-eula" in stderr)
 
         remove_dir(self.test_installpath)
         self.assertFalse(os.path.exists(toy_modfile))
 
-        # also check use of $EASYBUILD_ACCEPT_EULA to accept EULA for specified software
+        # also via $EASYBUILD_ACCEPT_EULA
+        self.mock_stderr(True)
         os.environ['EASYBUILD_ACCEPT_EULA'] = 'toy'
         self.eb_main(args, do_build=True, raise_error=True)
+        stderr = self.get_stderr()
+        self.mock_stderr(False)
+
         self.assertTrue(os.path.exists(toy_modfile))
+        self.assertTrue("Use accept-eula-for configuration setting rather than accept-eula" in stderr)
 
         remove_dir(self.test_installpath)
         self.assertFalse(os.path.exists(toy_modfile))
 
         # also check accepting EULA via 'accept_eula = True' in easyconfig file
+        self.disallow_deprecated_behaviour()
         del os.environ['EASYBUILD_ACCEPT_EULA']
         write_file(test_ec, test_ec_txt + '\naccept_eula = True')
         self.eb_main(args, do_build=True, raise_error=True)