Skip to content
Permalink
Browse files
git-webkit land should apply the merge-queue label
https://bugs.webkit.org/show_bug.cgi?id=240308
<rdar://problem/93501363>

Reviewed by Ryan Haddad.

* Tools/Scripts/libraries/webkitcorepy/setup.py: Bump version.
* Tools/Scripts/libraries/webkitcorepy/webkitcorepy/__init__.py: Ditto.
* Tools/Scripts/libraries/webkitcorepy/webkitcorepy/arguments.py:
(NoAction.__call__): Support --un along with --no- to invert option.
* Tools/Scripts/libraries/webkitscmpy/setup.py: Bump version.
* Tools/Scripts/libraries/webkitscmpy/webkitscmpy/__init__.py: Ditto.
* Tools/Scripts/libraries/webkitscmpy/webkitscmpy/program/land.py:
(Land.parser): Invoke PullRequest's parser.
(Land.merge_queue): Update (or create) pull-request and add merge-queue label to.
(Land.main): Support PullRequest options including automatic commit creation, squashing and PR creation.
* Tools/Scripts/libraries/webkitscmpy/webkitscmpy/program/pull_request.py:
(PullRequest): Add merge-queue and unsafe-merge-queue labels.
(PullRequest.parser): Add --no-squash option.
(PullRequest.create_pull_request): Remove merge-queue labels, allow caller to specify a callback.
(PullRequest.main): Create commit even when squashing.
* Tools/Scripts/libraries/webkitscmpy/webkitscmpy/test/land_unittest.py:
(repository):
(TestLand.test_with_oops):
(TestLand.test_default):
(TestLand.test_canonicalize):
(TestLand.test_svn):
(TestLand.test_default_with_radar):
(TestLand.test_canonicalize_with_bugzilla):
(TestLand.test_svn_with_bugzilla):
(TestLandGitHub.webserver):
(TestLandGitHub):
* Tools/Scripts/libraries/webkitscmpy/webkitscmpy/test/pull_request_unittest.py:
* Tools/Scripts/libraries/webkitscmpy/webkitscmpy/test/revert_unittest.py:
(test_update):
* Tools/Scripts/libraries/webkitscmpy/webkitscmpy/test/squash_unittest.py:
(TestSquash.test_github_with_previous_history):
(TestSquash.test_github_without_previous_history):

Canonical link: https://commits.webkit.org/251766@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@295761 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
JonWBedard committed Jun 23, 2022
1 parent 8ecc226 commit fbe4e7a9e3a227ea99a5d8927b26d5e5f659c402
Showing 11 changed files with 175 additions and 36 deletions.
@@ -30,7 +30,7 @@ def readme():

setup(
name='webkitcorepy',
version='0.13.9',
version='0.13.10',
description='Library containing various Python support classes and functions.',
long_description=readme(),
classifiers=[
@@ -44,7 +44,7 @@
from webkitcorepy.editor import Editor
from webkitcorepy.file_lock import FileLock

version = Version(0, 13, 9)
version = Version(0, 13, 10)

from webkitcorepy.autoinstall import Package, AutoInstall
if sys.version_info > (3, 0):
@@ -31,7 +31,7 @@ def __init__(self, option_strings, dest, **kwargs):
super(NoAction, self).__init__(option_strings, dest, nargs=0, **kwargs)

def __call__(self, parser, namespace, values, option_string=None):
setattr(namespace, self.dest, False if option_string.startswith('--no') else True)
setattr(namespace, self.dest, False if option_string.startswith('--no') or option_string.startswith('--un') else True)


def CountAction(value=1):
@@ -29,7 +29,7 @@ def readme():

setup(
name='webkitscmpy',
version='5.1.1',
version='5.2.0',
description='Library designed to interact with git and svn repositories.',
long_description=readme(),
classifiers=[
@@ -46,7 +46,7 @@ def _maybe_add_webkitcorepy_path():
"Please install webkitcorepy with `pip install webkitcorepy --extra-index-url <package index URL>`"
)

version = Version(5, 1, 1)
version = Version(5, 2, 0)

AutoInstall.register(Package('fasteners', Version(0, 15, 0)))
AutoInstall.register(Package('jinja2', Version(2, 11, 3)))
@@ -57,6 +57,7 @@ def revert_branch(cls, repository, remote, branch):

@classmethod
def parser(cls, parser, loggers=None):
PullRequest.parser(parser, loggers=loggers)
parser.add_argument(
'--no-force-review', '--force-review', '--no-review',
dest='review', default=True,
@@ -70,8 +71,48 @@ def parser(cls, parser, loggers=None):
action=arguments.NoAction,
)
parser.add_argument(
'--defaults', '--no-defaults', action=arguments.NoAction, default=None,
help='Do not prompt the user for defaults, always use (or do not use) them',
'--safe', '--unsafe',
dest='safe', default=None,
help='Land change via safe (or unsafe) merge queue, if available.',
action=arguments.NoAction,
)

@classmethod
def merge_queue(cls, args, repository, branch_point, merge_labels=None):
log.info('Detected merging automation, using that instead of local git tooling')
merge_type = {
True: 'safe',
False: 'unsafe',
}.get(args.safe, sorted(merge_labels.keys())[0])
merge_label = merge_labels.get(merge_type)
if not merge_label:
sys.stderr.write("No {} merge-queue available for this repository\n".format(merge_type))
return 1

def callback(pr):
pr_issue = pr._metadata.get('issue')
if not pr_issue:
sys.stderr.write("Cannot set any labels on '{}' because the service doesn't support labels\n".format(pr))
return 1

labels = pr_issue.labels
if PullRequest.BLOCKED_LABEL in labels and merge_type == 'unsafe':
log.info("Removing '{}' from PR {}...".format(cls.BLOCKED_LABEL, existing_pr.number))
labels.remove(PullRequest.BLOCKED_LABEL)
log.info("Adding '{}' to '{}'".format(merge_label, pr))
labels.append(merge_label)
if pr_issue.set_labels(labels):
print("Added '{}' to '{}', change is in the queue to be landed".format(merge_label, pr))
return 0
sys.stderr.write("Failed to add '{}' to '{}', change is not landing\n".format(merge_label, pr))
if pr.url:
sys.stderr.write("See if you can add the label manually on '{}'".format(pr.url))
return 1

return PullRequest.create_pull_request(
repository, args, branch_point,
callback=callback,
unblock=True if merge_type == 'unsafe' else False,
)

@classmethod
@@ -92,18 +133,55 @@ def main(cls, args, repository, identifier_template=None, canonical_svn=False, *
sys.stderr.write("Cannot 'land' on a canonical SVN repository that is not configured as git-svn\n")
return 1

if not PullRequest.check_pull_request_args(repository, args):
return 1

modified_files = [] if args.will_add is False else repository.modified()
if args.will_add:
modified_files = list(set(modified_files).union(set(repository.modified(staged=False))))
if not Branch.editable(repository.branch, repository=repository) and not modified_files:
sys.stderr.write("Can only 'land' editable branches\n")
return 1

branch_point = PullRequest.pull_request_branch_point(repository, args, **kwargs)
if not branch_point:
return 1
source_branch = repository.branch
if not Branch.editable(source_branch, repository=repository):
sys.stderr.write("Can only 'land' editable branches\n")
return 1
branch_point = Branch.branch_point(repository)

result = PullRequest.create_commit(args, repository, **kwargs)
if result:
return result

commits = list(repository.commits(begin=dict(hash=branch_point.hash), end=dict(branch=source_branch)))
if not commits:
sys.stderr.write('Failed to find commits to land\n')
return 1

pull_request = None
if args.squash or (args.squash is None and len(commits) > 1):
result = Squash.squash_commit(args, repository, branch_point, **kwargs)
if result:
return result
commits = list(repository.commits(begin=dict(hash=branch_point.hash), end=dict(branch=source_branch)))

rmt = repository.remote()
if rmt and isinstance(rmt, remote.GitHub):
merge_labels = dict()
for name in rmt.tracker.labels.keys():
if name in PullRequest.MERGE_LABELS:
merge_labels['safe'] = name
if name in PullRequest.UNSAFE_MERGE_LABELS:
merge_labels['unsafe'] = name
if merge_labels:
return cls.merge_queue(args, repository, branch_point, merge_labels=merge_labels)

if args.safe is not None:
sys.stderr.write("No merge-queue available for this repository\n")
return 1

pull_request = None
if rmt and rmt.pull_requests:
candidates = list(rmt.pull_requests.find(opened=True, head=source_branch))
if len(candidates) == 1:
@@ -148,7 +226,7 @@ def main(cls, args, repository, identifier_template=None, canonical_svn=False, *
elif not pull_request:
sys.stderr.write("Failed to find pull-request associated with '{}'\n".format(source_branch))

if not args.oops and any([cls.OOPS_RE.search(commit.message) for commit in commits]):
if not args.oops and any([cls.OOPS_RE.search(commit.message) for commit in commits if commit.message]):
sys.stderr.write("Found '(OOPS!)' message in commit messages, please resolve before committing\n")
return 1

@@ -38,6 +38,8 @@ class PullRequest(Command):
aliases = ['pr', 'pfr', 'upload']
help = 'Push the current checkout state as a pull-request'
BLOCKED_LABEL = 'merging-blocked'
MERGE_LABELS = ['merge-queue']
UNSAFE_MERGE_LABELS = ['unsafe-merge-queue']

@classmethod
def parser(cls, parser, loggers=None):
@@ -56,10 +58,10 @@ def parser(cls, parser, loggers=None):
action=arguments.NoAction,
)
parser.add_argument(
'--squash',
dest='squash', default=False,
action='store_true',
'--squash', '--no-squash',
dest='squash', default=None,
help='Combine all commits on the current development branch into a single commit before pushing',
action=arguments.NoAction,
)
parser.add_argument(
'--defaults', '--no-defaults', action=arguments.NoAction, default=None,
@@ -260,7 +262,7 @@ def add_comment_to_reverted_commit_bug_tracker(cls, repository, args, pr, commit
return 0

@classmethod
def create_pull_request(cls, repository, args, branch_point):
def create_pull_request(cls, repository, args, branch_point, callback=None, unblock=True):
# FIXME: We can do better by inferring the remote from the branch point, if it's not specified
source_remote = args.remote or 'origin'
if not repository.config().get('remote.{}.url'.format(source_remote)):
@@ -279,8 +281,6 @@ def create_pull_request(cls, repository, args, branch_point):
return 1
log.info("Rebased '{}' on '{}!'".format(repository.branch, branch_point.branch))
branch_point = Branch.branch_point(repository)
else:
branch_point = Branch.branch_point(repository)

if args.checks is None:
args.checks = repository.config().get('webkitscmpy.auto-check', 'false') == 'true'
@@ -303,15 +303,19 @@ def create_pull_request(cls, repository, args, branch_point):
) == 'Yes'):
existing_pr = None

# Remove "merging-blocked" label
# Remove any active labels
if existing_pr and existing_pr._metadata and existing_pr._metadata.get('issue'):
log.info("Checking PR labels for '{}'...".format(cls.BLOCKED_LABEL))
log.info("Checking PR labels for active labels...")
pr_issue = existing_pr._metadata['issue']
labels = pr_issue.labels
if cls.BLOCKED_LABEL in labels:
log.info("Removing '{}' from PR {}...".format(cls.BLOCKED_LABEL, existing_pr.number))
labels.remove(cls.BLOCKED_LABEL)
pr_issue.set_labels([])
did_remove = False
for to_remove in cls.MERGE_LABELS + cls.UNSAFE_MERGE_LABELS + ([cls.BLOCKED_LABEL] if unblock else []):
if to_remove in labels:
log.info("Removing '{}' from PR {}...".format(to_remove, existing_pr.number))
labels.remove(to_remove)
did_remove = True
if did_remove:
pr_issue.set_labels(labels)

if isinstance(remote_repo, remote.GitHub):
target = 'fork' if source_remote == 'origin' else '{}-fork'.format(source_remote)
@@ -429,6 +433,8 @@ def create_pull_request(cls, repository, args, branch_point):
if pr.url:
print(pr.url)

if callback:
return callback(pr)
return 0

@classmethod
@@ -443,10 +449,11 @@ def main(cls, args, repository, **kwargs):
if not branch_point:
return 1

result = cls.create_commit(args, repository, **kwargs)
if result:
return result
if args.squash:
result = Squash.squash_commit(args, repository, branch_point, **kwargs)
else:
result = cls.create_commit(args, repository, **kwargs)
if result:
return result

0 comments on commit fbe4e7a

Please sign in to comment.