Skip to content

Commit 0608feb

Browse files
committed
implement support for --sync-pr-with-develop
1 parent ed15ef0 commit 0608feb

File tree

4 files changed

+90
-8
lines changed

4 files changed

+90
-8
lines changed

easybuild/main.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@
5858
from easybuild.tools.docs import list_software
5959
from easybuild.tools.filetools import adjust_permissions, cleanup, write_file
6060
from easybuild.tools.github import check_github, find_easybuild_easyconfig, install_github_token
61-
from easybuild.tools.github import close_pr, list_prs, new_pr, merge_pr, update_pr
61+
from easybuild.tools.github import close_pr, list_prs, new_pr, merge_pr, sync_pr_with_develop, update_pr
6262
from easybuild.tools.hooks import START, END, load_hooks, run_hook
6363
from easybuild.tools.modules import modules_tool
6464
from easybuild.tools.options import set_up_configuration, use_color
@@ -293,8 +293,8 @@ def main(args=None, logfile=None, do_build=None, testing=False, modtool=None):
293293
categorized_paths = categorize_files_by_type(orig_paths)
294294

295295
# command line options that do not require any easyconfigs to be specified
296-
new_update_preview_pr = options.new_pr or options.update_pr or options.preview_pr
297-
no_ec_opts = [options.aggregate_regtest, options.regtest, search_query, new_update_preview_pr]
296+
pr_options = options.new_pr or options.preview_pr or options.sync_pr_with_develop or options.update_pr
297+
no_ec_opts = [options.aggregate_regtest, options.regtest, pr_options, search_query]
298298

299299
# determine paths to easyconfigs
300300
determined_paths = det_easyconfig_paths(categorized_paths['easyconfigs'])
@@ -355,7 +355,7 @@ def main(args=None, logfile=None, do_build=None, testing=False, modtool=None):
355355
dry_run_mode = options.dry_run or options.dry_run_short or options.missing_modules
356356

357357
# skip modules that are already installed unless forced, or unless an option is used that warrants not skipping
358-
if not (forced or dry_run_mode or options.extended_dry_run or new_update_preview_pr or options.inject_checksums):
358+
if not (forced or dry_run_mode or options.extended_dry_run or pr_options or options.inject_checksums):
359359
retained_ecs = skip_available(easyconfigs, modtool)
360360
if not testing:
361361
for skipped_ec in [ec for ec in easyconfigs if ec not in retained_ecs]:
@@ -366,26 +366,30 @@ def main(args=None, logfile=None, do_build=None, testing=False, modtool=None):
366366
if len(easyconfigs) > 0:
367367
# resolve dependencies if robot is enabled, except in dry run mode
368368
# one exception: deps *are* resolved with --new-pr or --update-pr when dry run mode is enabled
369-
if options.robot and (not dry_run_mode or new_update_preview_pr):
369+
if options.robot and (not dry_run_mode or pr_options):
370370
print_msg("resolving dependencies ...", log=_log, silent=testing)
371371
ordered_ecs = resolve_dependencies(easyconfigs, modtool)
372372
else:
373373
ordered_ecs = easyconfigs
374-
elif new_update_preview_pr:
374+
elif pr_options:
375375
ordered_ecs = None
376376
else:
377377
print_msg("No easyconfigs left to be built.", log=_log, silent=testing)
378378
ordered_ecs = []
379379

380380
# creating/updating PRs
381-
if new_update_preview_pr:
381+
if pr_options:
382382
if options.new_pr:
383383
new_pr(categorized_paths, ordered_ecs, title=options.pr_title, descr=options.pr_descr,
384384
commit_msg=options.pr_commit_msg)
385385
elif options.preview_pr:
386386
print(review_pr(paths=determined_paths, colored=use_color(options.color)))
387-
else:
387+
elif options.sync_pr_with_develop:
388+
sync_pr_with_develop(options.sync_pr_with_develop)
389+
elif options.update_pr:
388390
update_pr(options.update_pr, categorized_paths, ordered_ecs, commit_msg=options.pr_commit_msg)
391+
else:
392+
raise EasyBuildError("Unknown PR option!")
389393

390394
# dry_run: print all easyconfigs and dependencies, and whether they are already built
391395
elif dry_run_mode:

easybuild/tools/github.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@
8585
GITHUB_DIR_TYPE = u'dir'
8686
GITHUB_EB_MAIN = 'easybuilders'
8787
GITHUB_EASYCONFIGS_REPO = 'easybuild-easyconfigs'
88+
GITHUB_DEVELOP_BRANCH = 'develop'
8889
GITHUB_FILE_TYPE = u'file'
8990
GITHUB_PR_STATE_OPEN = 'open'
9091
GITHUB_PR_STATES = [GITHUB_PR_STATE_OPEN, 'closed', 'all']
@@ -1811,3 +1812,48 @@ def reviews_url(gh):
18111812
pr_data['reviews'] = reviews_data
18121813

18131814
return pr_data, pr_url
1815+
1816+
1817+
def sync_pr_with_develop(pr_id):
1818+
"""Sync pull request with current develop branch."""
1819+
github_user = build_option('github_user')
1820+
if github_user is None:
1821+
raise EasyBuildError("GitHub user must be specified to use --sync-pr-with-develop")
1822+
1823+
target_account = build_option('pr_target_account')
1824+
target_repo = build_option('pr_target_repo')
1825+
1826+
pr_account, pr_branch = det_account_branch_for_pr(pr_id)
1827+
1828+
# initialize repository
1829+
git_working_dir = tempfile.mkdtemp(prefix='git-working-dir')
1830+
git_repo = init_repo(git_working_dir, target_repo)
1831+
repo_path = os.path.join(git_working_dir, target_repo)
1832+
1833+
pr_remote_name = setup_repo(git_repo, pr_account, target_repo, pr_branch)
1834+
1835+
# pull in latest version of 'develop' branch from central repository
1836+
msg = "pulling latest version of '%s' branch from %s/%s..." % (target_account, target_repo, GITHUB_DEVELOP_BRANCH)
1837+
print_msg(msg, log=_log)
1838+
easybuilders_remote = create_remote(git_repo, target_account, target_repo)
1839+
pull_out = git_repo.git.pull(easybuilders_remote.name, GITHUB_DEVELOP_BRANCH)
1840+
_log.debug("Output of 'git pull %s %s': %s", easybuilders_remote.name, GITHUB_DEVELOP_BRANCH, pull_out)
1841+
1842+
# create 'develop' branch (with force if one already exists),
1843+
# and check it out to check git log
1844+
git_repo.create_head(GITHUB_DEVELOP_BRANCH, force=True).checkout()
1845+
git_log_develop = git_repo.git.log('-n 3')
1846+
_log.debug("Top of 'git log' for %s branch:\n%s", GITHUB_DEVELOP_BRANCH, git_log_develop)
1847+
1848+
# checkout PR branch, and merge develop branch in it (which will create a merge commit)
1849+
print_msg("merging '%s' branch into PR branch '%s'..." % (GITHUB_DEVELOP_BRANCH, pr_branch), log=_log)
1850+
git_repo.git.checkout(pr_branch)
1851+
merge_out = git_repo.git.merge(GITHUB_DEVELOP_BRANCH)
1852+
_log.debug("Output of 'git merge %s':\n%s", GITHUB_DEVELOP_BRANCH, merge_out)
1853+
1854+
# check git log, should show merge commit on top
1855+
post_merge_log = git_repo.git.log('-n 3')
1856+
_log.debug("Top of 'git log' after 'git merge %s':\n%s", GITHUB_DEVELOP_BRANCH, post_merge_log)
1857+
1858+
# push updated branch back to GitHub (unless we're doing a dry run)
1859+
return push_branch_to_github(git_repo, pr_account, target_repo, pr_branch)

easybuild/tools/options.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -605,6 +605,8 @@ def github_options(self):
605605
'pr-target-repo': ("Target repository for new/updating PRs", str, 'store', GITHUB_EASYCONFIGS_REPO),
606606
'pr-title': ("Title for new pull request created with --new-pr", str, 'store', None),
607607
'preview-pr': ("Preview a new pull request", None, 'store_true', False),
608+
'sync-pr-with-develop': ("Sync pull request with current 'develop' branch",
609+
int, 'store', None, {'metavar': 'PR#'}),
608610
'review-pr': ("Review specified pull request", int, 'store', None, {'metavar': 'PR#'}),
609611
'test-report-env-filter': ("Regex used to filter out variables in environment dump of test report",
610612
None, 'regex', None),

test/framework/options.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3030,6 +3030,36 @@ def test_new_update_pr(self):
30303030
]
30313031
self._assert_regexs(regexs, txt, assert_true=False)
30323032

3033+
def test_sync_pr_with_develop(self):
3034+
"""Test use of --sync-pr-with-develop (dry run only)."""
3035+
if self.github_token is None:
3036+
print("Skipping test_sync_pr_with_develop, no GitHub token available?")
3037+
return
3038+
3039+
# use https://github.com/easybuilders/easybuild-easyconfigs/pull/9150,
3040+
# which is a PR from boegel:develop to easybuilders:develop
3041+
# (to sync 'develop' branch in boegel's fork with central develop branch);
3042+
# we need to test with a branch that is guaranteed to stay in place for the test to work,
3043+
# since it will actually be downloaded (only the final push to update the branch is skipped under --dry-run)
3044+
args = [
3045+
'--github-user=%s' % GITHUB_TEST_ACCOUNT,
3046+
'--sync-pr-with-develop=9150',
3047+
'--dry-run',
3048+
]
3049+
txt, _ = self._run_mock_eb(args, do_build=True, raise_error=True, testing=False)
3050+
3051+
github_path = r"boegel/easybuild-easyconfigs\.git"
3052+
pattern = '\n'.join([
3053+
r"== temporary log file in case of crash .*",
3054+
r"== Determined branch name corresponding to easybuilders/easybuild-easyconfigs PR #9150: develop",
3055+
r"== fetching branch 'develop' from https://github\.com/%s\.\.\." % github_path,
3056+
r"== pulling latest version of 'easybuilders' branch from easybuild-easyconfigs/develop\.\.\.",
3057+
r"== merging 'develop' branch into PR branch 'develop'\.\.\.",
3058+
r"== pushing branch 'develop' to remote '.*' \(git@github\.com:%s\) \[DRY RUN\]" % github_path,
3059+
])
3060+
regex = re.compile(pattern)
3061+
self.assertTrue(regex.match(txt), "Pattern '%s' doesn't match: %s" % (regex.pattern, txt))
3062+
30333063
def test_new_pr_python(self):
30343064
"""Check generated PR title for --new-pr on easyconfig that includes Python dependency."""
30353065
if self.github_token is None:

0 commit comments

Comments
 (0)