Repository: easybuilders/easybuild-framework

Issue:
include-easyblocks-from-pr only supports a single PR for additional easyblocks
Currently `--include-easyblocks-from-pr` only supports a single PR to obtain easyblocks from, if the option is specified multiple times the last one wins.

I was building https://github.com/easybuilders/easybuild-easyconfigs/pull/11489 and wanted to have easyblocks from both https://github.com/easybuilders/easybuild-easyblocks/pull/2191 and https://github.com/easybuilders/easybuild-easyblocks/pull/2205 to complete a full test report for the distinct pieces of software included in the pull request.

It would be great if the feature supported multiple PRs in a form like `--include-easyblocks-from-pr 2191,2205`.


Discussion:

Resolving patch:
diff --git a/easybuild/tools/options.py b/easybuild/tools/options.py
--- a/easybuild/tools/options.py
+++ b/easybuild/tools/options.py
@@ -624,7 +624,7 @@ def github_options(self):
             'git-working-dirs-path': ("Path to Git working directories for EasyBuild repositories", str, 'store', None),
             'github-user': ("GitHub username", str, 'store', None),
             'github-org': ("GitHub organization", str, 'store', None),
-            'include-easyblocks-from-pr': ("Include easyblocks from specified PR", int, 'store', None,
+            'include-easyblocks-from-pr': ("Include easyblocks from specified PR", 'strlist', 'store', [],
                                            {'metavar': 'PR#'}),
             'install-github-token': ("Install GitHub token (requires --github-user)", None, 'store_true', False),
             'close-pr': ("Close pull request", int, 'store', None, {'metavar': 'PR#'}),
@@ -1454,25 +1454,33 @@ def set_up_configuration(args=None, logfile=None, testing=False, silent=False):
     init_build_options(build_options=build_options, cmdline_options=options)
 
     # done here instead of in _postprocess_include because github integration requires build_options to be initialized
-    pr_easyblocks = eb_go.options.include_easyblocks_from_pr
-    if pr_easyblocks:
-        easyblocks_from_pr = fetch_easyblocks_from_pr(pr_easyblocks)
-        included_from_pr = set([os.path.basename(eb) for eb in easyblocks_from_pr])
+    try:
+        easyblock_prs = map(int, eb_go.options.include_easyblocks_from_pr)
+    except ValueError:
+        raise EasyBuildError("Argument to --include-easyblocks-from-pr must be a comma separated list of PR numbers.")
+    if easyblock_prs:
 
         if eb_go.options.include_easyblocks:
             # check if you are including the same easyblock twice
             included_paths = expand_glob_paths(eb_go.options.include_easyblocks)
             included_from_file = set([os.path.basename(eb) for eb in included_paths])
-            included_twice = included_from_pr & included_from_file
-            if included_twice:
-                warning_msg = "One or more easyblocks included from multiple locations: %s " % ', '.join(included_twice)
-                warning_msg += "(the one(s) from PR #%s will be used)" % pr_easyblocks
-                print_warning(warning_msg)
 
-        for easyblock in included_from_pr:
-            print_msg("easyblock %s included from PR #%s" % (easyblock, pr_easyblocks), log=log)
+        for easyblock_pr in easyblock_prs:
+            easyblocks_from_pr = fetch_easyblocks_from_pr(easyblock_pr)
+            included_from_pr = set([os.path.basename(eb) for eb in easyblocks_from_pr])
+
+            if eb_go.options.include_easyblocks:
+                included_twice = included_from_pr & included_from_file
+                if included_twice:
+                    warning_msg = "One or more easyblocks included from multiple locations: %s " \
+                                  % ', '.join(included_twice)
+                    warning_msg += "(the one(s) from PR #%s will be used)" % easyblock_pr
+                    print_warning(warning_msg)
+
+            for easyblock in included_from_pr:
+                print_msg("easyblock %s included from PR #%s" % (easyblock, easyblock_pr), log=log)
 
-        include_easyblocks(eb_go.options.tmpdir, easyblocks_from_pr)
+            include_easyblocks(eb_go.options.tmpdir, easyblocks_from_pr)
 
         if eb_go.options.list_easyblocks:
             msg = list_easyblocks(eb_go.options.list_easyblocks, eb_go.options.output_format)


Test patch:
diff --git a/test/framework/options.py b/test/framework/options.py
--- a/test/framework/options.py
+++ b/test/framework/options.py
@@ -2971,7 +2971,7 @@ def test_xxx_include_easyblocks_from_pr(self):
 
         args = [
             '--from-pr=10487',  # PR for CMake easyconfig
-            '--include-easyblocks-from-pr=1936',  # PR for EB_CMake easyblock
+            '--include-easyblocks-from-pr=1936,2204',  # PRs for EB_CMake and Siesta easyblock
             '--unittest-file=%s' % self.logfile,
             '--github-user=%s' % GITHUB_TEST_ACCOUNT,
             '--extended-dry-run',
@@ -2985,7 +2985,8 @@ def test_xxx_include_easyblocks_from_pr(self):
         logtxt = read_file(self.logfile)
 
         self.assertFalse(stderr)
-        self.assertEqual(stdout, "== easyblock cmake.py included from PR #1936\n")
+        self.assertEqual(stdout, "== easyblock cmake.py included from PR #1936\n" +
+                         "== easyblock siesta.py included from PR #2204\n")
 
         # easyconfig from pr is found
         ec_pattern = os.path.join(self.test_prefix, '.*', 'files_pr10487', 'c', 'CMake',