|
46 | 46 | from vsc.utils.missing import nub |
47 | 47 |
|
48 | 48 | from easybuild.framework.easyconfig.easyconfig import copy_easyconfigs, copy_patch_files, process_easyconfig |
49 | | -from easybuild.framework.easyconfig.format.one import EB_FORMAT_EXTENSION |
50 | | -from easybuild.framework.easyconfig.format.yeb import YEB_FORMAT_EXTENSION |
51 | 49 | from easybuild.tools.build_log import EasyBuildError, print_msg, print_warning |
52 | 50 | from easybuild.tools.config import build_option |
53 | 51 | from easybuild.tools.filetools import apply_patch, copy_dir, det_patched_files, download_file, extract_file |
54 | | -from easybuild.tools.filetools import mkdir, read_file, which, write_file |
| 52 | +from easybuild.tools.filetools import mkdir, read_file, symlink, which, write_file |
55 | 53 | from easybuild.tools.systemtools import UNKNOWN, get_tool_version |
56 | 54 | from easybuild.tools.utilities import only_if_module_is_available |
57 | 55 |
|
@@ -368,87 +366,108 @@ def fetch_easyconfigs_from_pr(pr, path=None, github_user=None): |
368 | 366 | # make sure path exists, create it if necessary |
369 | 367 | mkdir(path, parents=True) |
370 | 368 |
|
371 | | - _log.debug("Fetching easyconfigs from PR #%s into %s" % (pr, path)) |
372 | | - pr_url = lambda g: g.repos[GITHUB_EB_MAIN][GITHUB_EASYCONFIGS_REPO].pulls[pr] |
| 369 | + github_account = build_option('pr_target_account') |
| 370 | + github_repo = GITHUB_EASYCONFIGS_REPO |
| 371 | + |
| 372 | + def pr_url(gh): |
| 373 | + """Utility function to fetch data for a specific PR.""" |
| 374 | + return gh.repos[github_account][github_repo].pulls[pr] |
| 375 | + |
| 376 | + _log.debug("Fetching easyconfigs from %s/%s PR #%s into %s", github_account, github_repo, pr, path) |
373 | 377 |
|
374 | 378 | status, pr_data = github_api_get_request(pr_url, github_user) |
375 | 379 | if status != HTTP_STATUS_OK: |
376 | 380 | raise EasyBuildError("Failed to get data for PR #%d from %s/%s (status: %d %s)", |
377 | | - pr, GITHUB_EB_MAIN, GITHUB_EASYCONFIGS_REPO, status, pr_data) |
378 | | - |
379 | | - # if PR is open and mergable, download develop and patch |
380 | | - stable = pr_data['mergeable_state'] == GITHUB_MERGEABLE_STATE_CLEAN |
381 | | - closed = pr_data['state'] == GITHUB_STATE_CLOSED and not pr_data['merged'] |
| 381 | + pr, github_account, github_repo, status, pr_data) |
382 | 382 |
|
383 | | - # 'clean' on successful (or missing) test, 'unstable' on failed tests or merge conflict |
384 | | - if not stable: |
385 | | - _log.warning("Mergeable state for PR #%d is not '%s': %s.", |
386 | | - pr, GITHUB_MERGEABLE_STATE_CLEAN, pr_data['mergeable_state']) |
| 383 | + pr_merged = pr_data['merged'] |
| 384 | + pr_closed = pr_data['state'] == GITHUB_STATE_CLOSED and not pr_merged |
387 | 385 |
|
388 | | - if (stable or pr_data['merged']) and not closed: |
389 | | - # whether merged or not, download develop |
390 | | - final_path = download_repo(repo=GITHUB_EASYCONFIGS_REPO, branch='develop', github_user=github_user) |
| 386 | + pr_target_branch = pr_data['base']['ref'] |
| 387 | + _log.info("Target branch for PR #%s: %s", pr, pr_target_branch) |
391 | 388 |
|
392 | | - else: |
393 | | - final_path = path |
| 389 | + # download target branch of PR so we can try and apply the PR patch on top of it |
| 390 | + repo_target_branch = download_repo(repo=github_repo, account=github_account, branch=pr_target_branch, |
| 391 | + github_user=github_user) |
394 | 392 |
|
395 | 393 | # determine list of changed files via diff |
396 | 394 | diff_fn = os.path.basename(pr_data['diff_url']) |
397 | | - diff_filepath = os.path.join(final_path, diff_fn) |
| 395 | + diff_filepath = os.path.join(path, diff_fn) |
398 | 396 | download_file(diff_fn, pr_data['diff_url'], diff_filepath, forced=True) |
399 | 397 | diff_txt = read_file(diff_filepath) |
400 | 398 |
|
401 | 399 | patched_files = det_patched_files(txt=diff_txt, omit_ab_prefix=True, github=True, filter_deleted=True) |
402 | 400 | _log.debug("List of patched files: %s" % patched_files) |
403 | 401 |
|
404 | 402 | for key, val in sorted(pr_data.items()): |
405 | | - _log.debug("\n%s:\n\n%s\n" % (key, val)) |
| 403 | + _log.debug("\n%s:\n\n%s\n", key, val) |
406 | 404 |
|
407 | 405 | # obtain last commit |
408 | 406 | # get all commits, increase to (max of) 100 per page |
409 | 407 | if pr_data['commits'] > GITHUB_MAX_PER_PAGE: |
410 | | - raise EasyBuildError("PR #%s contains more than %s commits, can't obtain last commit", pr, GITHUB_MAX_PER_PAGE) |
411 | | - status, commits_data = github_api_get_request(lambda g: pr_url(g).commits, github_user, |
| 408 | + raise EasyBuildError("PR #%s contains more than %s commits, can't obtain last commit", |
| 409 | + pr, GITHUB_MAX_PER_PAGE) |
| 410 | + |
| 411 | + status, commits_data = github_api_get_request(lambda gh: pr_url(gh).commits, github_user, |
412 | 412 | per_page=GITHUB_MAX_PER_PAGE) |
413 | 413 | if status != HTTP_STATUS_OK: |
414 | 414 | raise EasyBuildError("Failed to get data for PR #%d from %s/%s (status: %d %s)", |
415 | | - pr, GITHUB_EB_MAIN, GITHUB_EASYCONFIGS_REPO, status, commits_data) |
| 415 | + pr, github_account, github_repo, status, commits_data) |
416 | 416 | last_commit = commits_data[-1] |
417 | | - _log.debug("Commits: %s, last commit: %s" % (commits_data, last_commit['sha'])) |
418 | | - |
419 | | - if not(pr_data['merged']): |
420 | | - if not stable or closed: |
421 | | - state_msg = "unstable (pending/failed tests or merge conflict)" if not stable else "closed (but not merged)" |
422 | | - print "\n*** WARNING: Using easyconfigs from %s PR #%s ***\n" % (state_msg, pr) |
423 | | - # obtain most recent version of patched files |
424 | | - for patched_file in patched_files: |
425 | | - # path to patch file, incl. subdir it is in |
426 | | - fn = os.path.sep.join(patched_file.split(os.path.sep)[-3:]) |
427 | | - sha = last_commit['sha'] |
428 | | - full_url = URL_SEPARATOR.join([GITHUB_RAW, GITHUB_EB_MAIN, GITHUB_EASYCONFIGS_REPO, sha, patched_file]) |
429 | | - _log.info("Downloading %s from %s" % (fn, full_url)) |
430 | | - download_file(fn, full_url, path=os.path.join(path, fn), forced=True) |
431 | | - else: |
432 | | - apply_patch(diff_filepath, final_path, level=1) |
| 417 | + _log.debug("Commits: %s, last commit: %s", commits_data, last_commit['sha']) |
| 418 | + |
| 419 | + final_path = None |
433 | 420 |
|
| 421 | + # try to apply PR patch on top of target branch, unless the PR is closed or already merged |
| 422 | + if pr_merged: |
| 423 | + _log.info("PR is already merged, so using current version of PR target branch") |
| 424 | + final_path = repo_target_branch |
| 425 | + |
| 426 | + elif not pr_closed: |
| 427 | + try: |
| 428 | + _log.debug("Trying to apply PR patch %s to %s...", diff_filepath, repo_target_branch) |
| 429 | + apply_patch(diff_filepath, repo_target_branch, level=1) |
| 430 | + _log.info("Using %s which included PR patch to test PR #%s", repo_target_branch, pr) |
| 431 | + final_path = repo_target_branch |
| 432 | + |
| 433 | + except EasyBuildError as err: |
| 434 | + _log.warning("Ignoring problem that occured when applying PR patch: %s", err) |
| 435 | + |
| 436 | + if final_path is None: |
| 437 | + |
| 438 | + if pr_closed: |
| 439 | + print_warning("Using easyconfigs from closed PR #%s" % pr) |
| 440 | + |
| 441 | + # obtain most recent version of patched files |
| 442 | + for patched_file in patched_files: |
| 443 | + # path to patch file, incl. subdir it is in |
| 444 | + fn = os.path.sep.join(patched_file.split(os.path.sep)[-3:]) |
| 445 | + sha = last_commit['sha'] |
| 446 | + full_url = URL_SEPARATOR.join([GITHUB_RAW, github_account, github_repo, sha, patched_file]) |
| 447 | + _log.info("Downloading %s from %s", fn, full_url) |
| 448 | + download_file(fn, full_url, path=os.path.join(path, fn), forced=True) |
| 449 | + |
| 450 | + final_path = path |
| 451 | + |
| 452 | + # symlink directories into expected place if they're not there yet |
434 | 453 | if final_path != path: |
435 | 454 | dirpath = os.path.join(final_path, 'easybuild', 'easyconfigs') |
436 | 455 | for eb_dir in os.listdir(dirpath): |
437 | | - os.symlink(os.path.join(dirpath, eb_dir), os.path.join(path, os.path.basename(eb_dir))) |
| 456 | + symlink(os.path.join(dirpath, eb_dir), os.path.join(path, os.path.basename(eb_dir))) |
438 | 457 |
|
439 | 458 | # sanity check: make sure all patched files are downloaded |
440 | 459 | ec_files = [] |
441 | 460 | for patched_file in [f for f in patched_files if not f.startswith('test/')]: |
442 | 461 | fn = os.path.sep.join(patched_file.split(os.path.sep)[-3:]) |
443 | | - if os.path.exists(os.path.join(path, fn)): |
444 | | - ec_files.append(os.path.join(path, fn)) |
| 462 | + full_path = os.path.join(path, fn) |
| 463 | + if os.path.exists(full_path): |
| 464 | + ec_files.append(full_path) |
445 | 465 | else: |
446 | | - raise EasyBuildError("Couldn't find path to patched file %s", os.path.join(path, fn)) |
| 466 | + raise EasyBuildError("Couldn't find path to patched file %s", full_path) |
447 | 467 |
|
448 | 468 | return ec_files |
449 | 469 |
|
450 | 470 |
|
451 | | - |
452 | 471 | def create_gist(txt, fn, descr=None, github_user=None): |
453 | 472 | """Create a gist with the provided text.""" |
454 | 473 | if descr is None: |
|
0 commit comments