|
85 | 85 | GITHUB_DIR_TYPE = u'dir' |
86 | 86 | GITHUB_EB_MAIN = 'easybuilders' |
87 | 87 | GITHUB_EASYCONFIGS_REPO = 'easybuild-easyconfigs' |
| 88 | +GITHUB_DEVELOP_BRANCH = 'develop' |
88 | 89 | GITHUB_FILE_TYPE = u'file' |
89 | 90 | GITHUB_PR_STATE_OPEN = 'open' |
90 | 91 | GITHUB_PR_STATES = [GITHUB_PR_STATE_OPEN, 'closed', 'all'] |
@@ -808,35 +809,73 @@ def _easyconfigs_pr_common(paths, ecs, start_branch=None, pr_branch=None, start_ |
808 | 809 | # commit |
809 | 810 | git_repo.index.commit(commit_msg) |
810 | 811 |
|
811 | | - # push to GitHub |
812 | | - github_url = '[email protected]:%s/%s.git' % ( target_account, pr_target_repo) |
| 812 | + push_branch_to_github(git_repo, target_account, pr_target_repo, pr_branch) |
| 813 | + |
| 814 | + return file_info, deleted_paths, git_repo, pr_branch, diff_stat |
| 815 | + |
| 816 | + |
| 817 | +def create_remote(git_repo, account, repo, https=False): |
| 818 | + """ |
| 819 | + Create remote in specified git working directory for specified account & repository. |
| 820 | +
|
| 821 | + :param git_repo: git.Repo instance to use (after init_repo & setup_repo) |
| 822 | + :param account: GitHub account name |
| 823 | + :param repo: repository name |
| 824 | + :param https: use https:// URL rather than git@ |
| 825 | + """ |
| 826 | + |
| 827 | + if https: |
| 828 | + github_url = 'https://github.com/%s/%s.git' % (account, repo) |
| 829 | + else: |
| 830 | + github_url = '[email protected]:%s/%s.git' % ( account, repo) |
| 831 | + |
813 | 832 | salt = ''.join(random.choice(ascii_letters) for _ in range(5)) |
814 | | - remote_name = 'github_%s_%s' % (target_account, salt) |
| 833 | + remote_name = 'github_%s_%s' % (account, salt) |
| 834 | + |
| 835 | + try: |
| 836 | + remote = git_repo.create_remote(remote_name, github_url) |
| 837 | + except GitCommandError as err: |
| 838 | + raise EasyBuildError("Failed to create remote %s for %s: %s", remote_name, github_url, err) |
| 839 | + |
| 840 | + return remote |
| 841 | + |
| 842 | + |
| 843 | +def push_branch_to_github(git_repo, target_account, target_repo, branch): |
| 844 | + """ |
| 845 | + Push specified branch to GitHub from specified git repository. |
| 846 | +
|
| 847 | + :param git_repo: git.Repo instance to use (after init_repo & setup_repo) |
| 848 | + :param target_account: GitHub account name |
| 849 | + :param target_repo: repository name |
| 850 | + :param branch: name of branch to push |
| 851 | + """ |
| 852 | + |
| 853 | + # push to GitHub |
| 854 | + remote = create_remote(git_repo, target_account, target_repo) |
815 | 855 |
|
816 | 856 | dry_run = build_option('dry_run') or build_option('extended_dry_run') |
817 | 857 |
|
818 | | - push_branch_msg = "pushing branch '%s' to remote '%s' (%s)" % (pr_branch, remote_name, github_url) |
| 858 | + github_url = '[email protected]:%s/%s.git' % ( target_account, target_repo) |
| 859 | + |
| 860 | + push_branch_msg = "pushing branch '%s' to remote '%s' (%s)" % (branch, remote.name, github_url) |
819 | 861 | if dry_run: |
820 | 862 | print_msg(push_branch_msg + ' [DRY RUN]', log=_log) |
821 | 863 | else: |
822 | 864 | print_msg(push_branch_msg, log=_log) |
823 | 865 | try: |
824 | | - my_remote = git_repo.create_remote(remote_name, github_url) |
825 | | - res = my_remote.push(pr_branch) |
| 866 | + res = remote.push(branch) |
826 | 867 | except GitCommandError as err: |
827 | | - raise EasyBuildError("Failed to push branch '%s' to GitHub (%s): %s", pr_branch, github_url, err) |
| 868 | + raise EasyBuildError("Failed to push branch '%s' to GitHub (%s): %s", branch, github_url, err) |
828 | 869 |
|
829 | 870 | if res: |
830 | 871 | if res[0].ERROR & res[0].flags: |
831 | 872 | raise EasyBuildError("Pushing branch '%s' to remote %s (%s) failed: %s", |
832 | | - pr_branch, my_remote, github_url, res[0].summary) |
| 873 | + branch, remote, github_url, res[0].summary) |
833 | 874 | else: |
834 | | - _log.debug("Pushed branch %s to remote %s (%s): %s", pr_branch, my_remote, github_url, res[0].summary) |
| 875 | + _log.debug("Pushed branch %s to remote %s (%s): %s", branch, remote, github_url, res[0].summary) |
835 | 876 | else: |
836 | 877 | raise EasyBuildError("Pushing branch '%s' to remote %s (%s) failed: empty result", |
837 | | - pr_branch, my_remote, github_url) |
838 | | - |
839 | | - return file_info, deleted_paths, git_repo, pr_branch, diff_stat |
| 878 | + branch, remote, github_url) |
840 | 879 |
|
841 | 880 |
|
842 | 881 | def is_patch_for(patch_name, ec): |
@@ -1359,41 +1398,55 @@ def new_pr(paths, ecs, title=None, descr=None, commit_msg=None): |
1359 | 1398 | _log.info("Failed to add labels to PR# %s: %s." % (pr, err)) |
1360 | 1399 |
|
1361 | 1400 |
|
| 1401 | +def det_account_branch_for_pr(pr_id, github_user=None): |
| 1402 | + """Determine account & branch corresponding to pull request with specified id.""" |
| 1403 | + |
| 1404 | + if github_user is None: |
| 1405 | + github_user = build_option('github_user') |
| 1406 | + |
| 1407 | + if github_user is None: |
| 1408 | + raise EasyBuildError("GitHub username (--github-user) must be specified!") |
| 1409 | + |
| 1410 | + pr_target_account = build_option('pr_target_account') |
| 1411 | + pr_target_repo = build_option('pr_target_repo') |
| 1412 | + |
| 1413 | + pr_data, _ = fetch_pr_data(pr_id, pr_target_account, pr_target_repo, github_user) |
| 1414 | + |
| 1415 | + # branch that corresponds with PR is supplied in form <account>:<branch_label> |
| 1416 | + account = pr_data['head']['label'].split(':')[0] |
| 1417 | + branch = ':'.join(pr_data['head']['label'].split(':')[1:]) |
| 1418 | + github_target = '%s/%s' % (pr_target_account, pr_target_repo) |
| 1419 | + print_msg("Determined branch name corresponding to %s PR #%s: %s" % (github_target, pr_id, branch), log=_log) |
| 1420 | + |
| 1421 | + return account, branch |
| 1422 | + |
| 1423 | + |
1362 | 1424 | @only_if_module_is_available('git', pkgname='GitPython') |
1363 | | -def update_pr(pr, paths, ecs, commit_msg=None): |
| 1425 | +def update_pr(pr_id, paths, ecs, commit_msg=None): |
1364 | 1426 | """ |
1365 | 1427 | Update specified pull request using specified files |
1366 | 1428 |
|
1367 | | - :param pr: ID of pull request to update |
| 1429 | + :param pr_id: ID of pull request to update |
1368 | 1430 | :param paths: paths to categorized lists of files (easyconfigs, files to delete, patches) |
1369 | 1431 | :param ecs: list of parsed easyconfigs, incl. for dependencies (if robot is enabled) |
1370 | 1432 | :param commit_msg: commit message to use |
1371 | 1433 | """ |
1372 | | - github_user = build_option('github_user') |
1373 | | - if github_user is None: |
1374 | | - raise EasyBuildError("GitHub user must be specified to use --update-pr") |
1375 | 1434 |
|
1376 | 1435 | if commit_msg is None: |
1377 | 1436 | raise EasyBuildError("A meaningful commit message must be specified via --pr-commit-msg when using --update-pr") |
1378 | 1437 |
|
1379 | 1438 | pr_target_account = build_option('pr_target_account') |
1380 | 1439 | pr_target_repo = build_option('pr_target_repo') |
1381 | 1440 |
|
1382 | | - pr_data, _ = fetch_pr_data(pr, pr_target_account, pr_target_repo, github_user) |
1383 | | - |
1384 | | - # branch that corresponds with PR is supplied in form <account>:<branch_label> |
1385 | | - account = pr_data['head']['label'].split(':')[0] |
1386 | | - branch = ':'.join(pr_data['head']['label'].split(':')[1:]) |
1387 | | - github_target = '%s/%s' % (pr_target_account, pr_target_repo) |
1388 | | - print_msg("Determined branch name corresponding to %s PR #%s: %s" % (github_target, pr, branch), log=_log) |
| 1441 | + account, branch = det_account_branch_for_pr(pr_id) |
1389 | 1442 |
|
1390 | 1443 | _, _, _, _, diff_stat = _easyconfigs_pr_common(paths, ecs, start_branch=branch, pr_branch=branch, |
1391 | 1444 | start_account=account, commit_msg=commit_msg) |
1392 | 1445 |
|
1393 | 1446 | print_msg("Overview of changes:\n%s\n" % diff_stat, log=_log, prefix=False) |
1394 | 1447 |
|
1395 | 1448 | full_repo = '%s/%s' % (pr_target_account, pr_target_repo) |
1396 | | - msg = "Updated %s PR #%s by pushing to branch %s/%s" % (full_repo, pr, account, branch) |
| 1449 | + msg = "Updated %s PR #%s by pushing to branch %s/%s" % (full_repo, pr_id, account, branch) |
1397 | 1450 | if build_option('dry_run') or build_option('extended_dry_run'): |
1398 | 1451 | msg += " [DRY RUN]" |
1399 | 1452 | print_msg(msg, log=_log, prefix=False) |
@@ -1777,3 +1830,47 @@ def reviews_url(gh): |
1777 | 1830 | pr_data['reviews'] = reviews_data |
1778 | 1831 |
|
1779 | 1832 | return pr_data, pr_url |
| 1833 | + |
| 1834 | + |
| 1835 | +def sync_pr_with_develop(pr_id): |
| 1836 | + """Sync pull request with specified ID with current develop branch.""" |
| 1837 | + github_user = build_option('github_user') |
| 1838 | + if github_user is None: |
| 1839 | + raise EasyBuildError("GitHub user must be specified to use --sync-pr-with-develop") |
| 1840 | + |
| 1841 | + target_account = build_option('pr_target_account') |
| 1842 | + target_repo = build_option('pr_target_repo') |
| 1843 | + |
| 1844 | + pr_account, pr_branch = det_account_branch_for_pr(pr_id) |
| 1845 | + |
| 1846 | + # initialize repository |
| 1847 | + git_working_dir = tempfile.mkdtemp(prefix='git-working-dir') |
| 1848 | + git_repo = init_repo(git_working_dir, target_repo) |
| 1849 | + |
| 1850 | + setup_repo(git_repo, pr_account, target_repo, pr_branch) |
| 1851 | + |
| 1852 | + # pull in latest version of 'develop' branch from central repository |
| 1853 | + msg = "pulling latest version of '%s' branch from %s/%s..." % (target_account, target_repo, GITHUB_DEVELOP_BRANCH) |
| 1854 | + print_msg(msg, log=_log) |
| 1855 | + easybuilders_remote = create_remote(git_repo, target_account, target_repo, https=True) |
| 1856 | + pull_out = git_repo.git.pull(easybuilders_remote.name, GITHUB_DEVELOP_BRANCH) |
| 1857 | + _log.debug("Output of 'git pull %s %s': %s", easybuilders_remote.name, GITHUB_DEVELOP_BRANCH, pull_out) |
| 1858 | + |
| 1859 | + # create 'develop' branch (with force if one already exists), |
| 1860 | + # and check it out to check git log |
| 1861 | + git_repo.create_head(GITHUB_DEVELOP_BRANCH, force=True).checkout() |
| 1862 | + git_log_develop = git_repo.git.log('-n 3') |
| 1863 | + _log.debug("Top of 'git log' for %s branch:\n%s", GITHUB_DEVELOP_BRANCH, git_log_develop) |
| 1864 | + |
| 1865 | + # checkout PR branch, and merge develop branch in it (which will create a merge commit) |
| 1866 | + print_msg("merging '%s' branch into PR branch '%s'..." % (GITHUB_DEVELOP_BRANCH, pr_branch), log=_log) |
| 1867 | + git_repo.git.checkout(pr_branch) |
| 1868 | + merge_out = git_repo.git.merge(GITHUB_DEVELOP_BRANCH) |
| 1869 | + _log.debug("Output of 'git merge %s':\n%s", GITHUB_DEVELOP_BRANCH, merge_out) |
| 1870 | + |
| 1871 | + # check git log, should show merge commit on top |
| 1872 | + post_merge_log = git_repo.git.log('-n 3') |
| 1873 | + _log.debug("Top of 'git log' after 'git merge %s':\n%s", GITHUB_DEVELOP_BRANCH, post_merge_log) |
| 1874 | + |
| 1875 | + # push updated branch back to GitHub (unless we're doing a dry run) |
| 1876 | + return push_branch_to_github(git_repo, pr_account, target_repo, pr_branch) |
0 commit comments