Skip to content
Merged
19 changes: 15 additions & 4 deletions easybuild/framework/easyconfig/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -474,24 +474,35 @@ def find_related_easyconfigs(path, ec):
return sorted(res)


def review_pr(pr, colored=True, branch='develop'):
def review_pr(paths=None, pr=None, colored=True, branch='develop'):
"""
Print multi-diff overview between easyconfigs in specified PR and specified branch.
Print multi-diff overview between specified easyconfigs or PR and specified branch.
:param pr: pull request number in easybuild-easyconfigs repo to review
:param paths: path tuples (path, generated) of easyconfigs to review
:param colored: boolean indicating whether a colored multi-diff should be generated
:param branch: easybuild-easyconfigs branch to compare with
"""
tmpdir = tempfile.mkdtemp()

download_repo_path = download_repo(branch=branch, path=tmpdir)
repo_path = os.path.join(download_repo_path, 'easybuild', 'easyconfigs')
pr_files = [path for path in fetch_easyconfigs_from_pr(pr) if path.endswith('.eb')]

if pr:
pr_files = [path for path in fetch_easyconfigs_from_pr(pr) if path.endswith('.eb')]
elif paths:
pr_files = paths
else:
raise EasyBuildError("No PR # or easyconfig path specified")

lines = []
ecs, _ = parse_easyconfigs([(fp, False) for fp in pr_files], validate=False)
for ec in ecs:
files = find_related_easyconfigs(repo_path, ec['ec'])
_log.debug("File in PR#%s %s has these related easyconfigs: %s" % (pr, ec['spec'], files))
if pr:
pr_msg = "PR#%s" % pr
else:
pr_msg = "new PR"
_log.debug("File in %s %s has these related easyconfigs: %s" % (pr_msg, ec['spec'], files))
if files:
lines.append(multidiff(ec['spec'], files, colored=colored))
else:
Expand Down
25 changes: 14 additions & 11 deletions easybuild/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ def main(args=None, logfile=None, do_build=None, testing=False, modtool=None):
merge_pr(options.merge_pr)

elif options.review_pr:
print review_pr(options.review_pr, colored=use_color(options.color))
print review_pr(pr=options.review_pr, colored=use_color(options.color))

elif options.list_installed_software:
detailed = options.list_installed_software == 'detailed'
Expand Down Expand Up @@ -323,13 +323,13 @@ def main(args=None, logfile=None, do_build=None, testing=False, modtool=None):
categorized_paths = categorize_files_by_type(orig_paths)

# command line options that do not require any easyconfigs to be specified
no_ec_opts = [options.aggregate_regtest, options.new_pr, options.regtest, options.update_pr, search_query]
no_ec_opts = [options.aggregate_regtest, options.regtest, search_query]

# determine paths to easyconfigs
paths = det_easyconfig_paths(categorized_paths['easyconfigs'])
if paths:
determined_paths = det_easyconfig_paths(categorized_paths['easyconfigs'])
if determined_paths:
# transform paths into tuples, use 'False' to indicate the corresponding easyconfig files were not generated
paths = [(p, False) for p in paths]
paths = [(p, False) for p in determined_paths]
else:
if 'name' in build_specs:
# try to obtain or generate an easyconfig file via build specifications if a software name is provided
Expand Down Expand Up @@ -375,10 +375,10 @@ def main(args=None, logfile=None, do_build=None, testing=False, modtool=None):

forced = options.force or options.rebuild
dry_run_mode = options.dry_run or options.dry_run_short
new_update_pr = options.new_pr or options.update_pr
new_update_preview_pr = options.new_pr or options.update_pr or options.preview_pr

# skip modules that are already installed unless forced, or unless an option is used that warrants not skipping
if not (forced or dry_run_mode or options.extended_dry_run or new_update_pr or options.inject_checksums):
if not (forced or dry_run_mode or options.extended_dry_run or new_update_preview_pr or options.inject_checksums):
retained_ecs = skip_available(easyconfigs, modtool)
if not testing:
for skipped_ec in [ec for ec in easyconfigs if ec not in retained_ecs]:
Expand All @@ -389,22 +389,24 @@ def main(args=None, logfile=None, do_build=None, testing=False, modtool=None):
if len(easyconfigs) > 0:
# resolve dependencies if robot is enabled, except in dry run mode
# one exception: deps *are* resolved with --new-pr or --update-pr when dry run mode is enabled
if options.robot and (not dry_run_mode or new_update_pr):
if options.robot and (not dry_run_mode or new_update_preview_pr):
print_msg("resolving dependencies ...", log=_log, silent=testing)
ordered_ecs = resolve_dependencies(easyconfigs, modtool)
else:
ordered_ecs = easyconfigs
elif new_update_pr:
elif new_update_preview_pr:
ordered_ecs = None
else:
print_msg("No easyconfigs left to be built.", log=_log, silent=testing)
ordered_ecs = []

# creating/updating PRs
if new_update_pr:
if new_update_preview_pr:
if options.new_pr:
new_pr(categorized_paths, ordered_ecs, title=options.pr_title, descr=options.pr_descr,
commit_msg=options.pr_commit_msg)
elif options.preview_pr:
print review_pr(paths=determined_paths, colored=use_color(options.color))
else:
update_pr(options.update_pr, categorized_paths, ordered_ecs, commit_msg=options.pr_commit_msg)

Expand All @@ -428,7 +430,8 @@ def main(args=None, logfile=None, do_build=None, testing=False, modtool=None):
inject_checksums(ordered_ecs, options.inject_checksums)

# cleanup and exit after dry run, searching easyconfigs or submitting regression test
if any(no_ec_opts + [options.check_conflicts, dry_run_mode, options.dump_env_script, options.inject_checksums]):
stop_options = [options.check_conflicts, dry_run_mode, options.dump_env_script, options.inject_checksums]
if any(no_ec_opts) or new_update_preview_pr or any(stop_options):
cleanup(logfile, eb_tmpdir, testing)
sys.exit(0)

Expand Down
1 change: 1 addition & 0 deletions easybuild/tools/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,7 @@ def github_options(self):
'pr-target-branch': ("Target branch for new PRs", str, 'store', 'develop'),
'pr-target-repo': ("Target repository for new/updating PRs", str, 'store', GITHUB_EASYCONFIGS_REPO),
'pr-title': ("Title for new pull request created with --new-pr", str, 'store', None),
'preview-pr': ("Preview a new pull request", None, 'store_true', False),
'review-pr': ("Review specified pull request", int, 'store', None, {'metavar': 'PR#'}),
'test-report-env-filter': ("Regex used to filter out variables in environment dump of test report",
None, 'regex', None),
Expand Down
18 changes: 18 additions & 0 deletions test/framework/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -2185,6 +2185,24 @@ def test_cleanup_tmpdir(self):
tweaked_dir = os.path.join(tmpdir, tmpdir_files[0], 'tweaked_easyconfigs')
self.assertTrue(os.path.exists(os.path.join(tweaked_dir, 'toy-1.0.eb')))

def test_preview_pr(self):
"""Test --preview-pr."""

self.mock_stdout(True)

test_ecs_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'easyconfigs', 'test_ecs')
eb_file = os.path.join(test_ecs_path, 'b', 'bzip2', 'bzip2-1.0.6-GCC-4.9.2.eb')
args = [
'--color=never',
'--preview-pr',
eb_file,
]
self.eb_main(args, raise_error=True)
txt = self.get_stdout()
self.mock_stdout(False)
regex = re.compile(r"^Comparing bzip2-1.0.6\S* with bzip2-1.0.6")
self.assertTrue(regex.search(txt), "Pattern '%s' not found in: %s" % (regex.pattern, txt))

def test_review_pr(self):
"""Test --review-pr."""
if self.github_token is None:
Expand Down