Title: [292485] trunk/Tools
Revision
292485
Author
jbed...@apple.com
Date
2022-04-06 11:51:37 -0700 (Wed, 06 Apr 2022)

Log Message

[git-webkit] Add automation to assist in branch cleanup
https://bugs.webkit.org/show_bug.cgi?id=238637
<rdar://problem/91127828>

Reviewed by Stephanie Lewis and Dewei Zhu.

* Tools/Scripts/libraries/webkitscmpy/setup.py: Bump version.
* Tools/Scripts/libraries/webkitscmpy/webkitscmpy/__init__.py: Ditto.
* Tools/Scripts/libraries/webkitscmpy/webkitscmpy/mocks/local/git.py: Add branch deletion and reset mocks.
* Tools/Scripts/libraries/webkitscmpy/webkitscmpy/mocks/local/svn.py:
(Svn.__init__): Add `svn revert` mock.
* Tools/Scripts/libraries/webkitscmpy/webkitscmpy/program/clean.py:
(Clean):
(Clean.parser): Allow caller to specify branch or pull request.
(Clean.cleanup): Delete a branch or branches associated with a pull request.
(Clean.main): Differentiate calls with branches and pull requests specified from those without.
* Tools/Scripts/libraries/webkitscmpy/webkitscmpy/test/clean_unittest.py: Added.
(TestClean):
(TestClean.setUp):
(TestClean.test_checkout_none):
(TestClean.test_clean_git):
(TestClean.test_clean_svn):
(TestClean.test_clean_branch):
(TestClean.test_clean_pr):

Canonical link: https://commits.webkit.org/249336@main

Modified Paths

Added Paths

Diff

Modified: trunk/Tools/ChangeLog (292484 => 292485)


--- trunk/Tools/ChangeLog	2022-04-06 18:48:46 UTC (rev 292484)
+++ trunk/Tools/ChangeLog	2022-04-06 18:51:37 UTC (rev 292485)
@@ -1,5 +1,32 @@
 2022-04-06  Jonathan Bedard  <jbed...@apple.com>
 
+        [git-webkit] Add automation to assist in branch cleanup
+        https://bugs.webkit.org/show_bug.cgi?id=238637
+        <rdar://problem/91127828>
+
+        Reviewed by Stephanie Lewis and Dewei Zhu.
+
+        * Scripts/libraries/webkitscmpy/setup.py: Bump version.
+        * Scripts/libraries/webkitscmpy/webkitscmpy/__init__.py: Ditto.
+        * Scripts/libraries/webkitscmpy/webkitscmpy/mocks/local/git.py: Add branch deletion and reset mocks.
+        * Scripts/libraries/webkitscmpy/webkitscmpy/mocks/local/svn.py:
+        (Svn.__init__): Add `svn revert` mock.
+        * Scripts/libraries/webkitscmpy/webkitscmpy/program/clean.py:
+        (Clean):
+        (Clean.parser): Allow caller to specify branch or pull request.
+        (Clean.cleanup): Delete a branch or branches associated with a pull request.
+        (Clean.main): Differentiate calls with branches and pull requests specified from those without.
+        * Scripts/libraries/webkitscmpy/webkitscmpy/test/clean_unittest.py: Added.
+        (TestClean):
+        (TestClean.setUp):
+        (TestClean.test_checkout_none):
+        (TestClean.test_clean_git):
+        (TestClean.test_clean_svn):
+        (TestClean.test_clean_branch):
+        (TestClean.test_clean_pr):
+
+2022-04-06  Jonathan Bedard  <jbed...@apple.com>
+
         [git-webkit] Apply labels based on tracker
         https://bugs.webkit.org/show_bug.cgi?id=238640
         <rdar://problem/91135356>

Modified: trunk/Tools/Scripts/libraries/webkitscmpy/setup.py (292484 => 292485)


--- trunk/Tools/Scripts/libraries/webkitscmpy/setup.py	2022-04-06 18:48:46 UTC (rev 292484)
+++ trunk/Tools/Scripts/libraries/webkitscmpy/setup.py	2022-04-06 18:51:37 UTC (rev 292485)
@@ -29,7 +29,7 @@
 
 setup(
     name='webkitscmpy',
-    version='4.7.0',
+    version='4.8.0',
     description='Library designed to interact with git and svn repositories.',
     long_description=readme(),
     classifiers=[

Modified: trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/__init__.py (292484 => 292485)


--- trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/__init__.py	2022-04-06 18:48:46 UTC (rev 292484)
+++ trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/__init__.py	2022-04-06 18:51:37 UTC (rev 292485)
@@ -46,7 +46,7 @@
         "Please install webkitcorepy with `pip install webkitcorepy --extra-index-url <package index URL>`"
     )
 
-version = Version(4, 7, 0)
+version = Version(4, 8, 0)
 
 AutoInstall.register(Package('fasteners', Version(0, 15, 0)))
 AutoInstall.register(Package('jinja2', Version(2, 11, 3)))

Modified: trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/mocks/local/git.py (292484 => 292485)


--- trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/mocks/local/git.py	2022-04-06 18:48:46 UTC (rev 292484)
+++ trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/mocks/local/git.py	2022-04-06 18:51:37 UTC (rev 292485)
@@ -493,6 +493,10 @@
                 cwd=self.path,
                 generator=lambda *args, **kwargs: self.move_branch(args[3], args[4]),
             ), mocks.Subprocess.Route(
+                self.executable, 'branch', '-D', re.compile(r'.+'),
+                cwd=self.path,
+                generator=lambda *args, **kwargs: self.delete_branch(args[3]),
+            ), mocks.Subprocess.Route(
                 self.executable, 'push', re.compile(r'.+'), re.compile(r'.+'),
                 cwd=self.path,
                 generator=lambda *args, **kwargs: self.push(args[2], args[3].split(':')[0]),
@@ -511,9 +515,9 @@
                     ])
                 )
             ), mocks.Subprocess.Route(
-                self.executable, 'reset', re.compile(r'HEAD~\d+'),
+                self.executable, 'reset', 'HEAD',
                 cwd=self.path,
-                generator=lambda *args, **kwargs: self.reset(int(args[2].split('~')[-1])),
+                generator=lambda *args, **kwargs: self.reset(int(args[2].split('~')[-1]) if '~' in args[2] else None),
             ), mocks.Subprocess.Route(
                 self.executable,
                 cwd=self.path,
@@ -930,6 +934,15 @@
         self.head = self.commits[to_be_moved][-1]
         return mocks.ProcessCompletion(returncode=0)
 
+    def delete_branch(self, branch):
+        if branch in self.commits:
+            del self.commits[branch]
+            return mocks.ProcessCompletion(returncode=0)
+        return mocks.ProcessCompletion(
+            returncode=1,
+            stdout="error: branch '{}' not found.\n".format(branch),
+        )
+
     def push(self, remote, branch):
         self.remotes['{}/{}'.format(remote, branch)] = self.commits[branch][-1]
         return mocks.ProcessCompletion(returncode=0)
@@ -943,6 +956,11 @@
         )
 
     def reset(self, index):
+        if index is None:
+            self.modified = {}
+            self.staged = {}
+            return mocks.ProcessCompletion(returncode=0)
+
         self.head = self.commits[self.head.branch][-(index + 1)]
         return mocks.ProcessCompletion(returncode=0)
 

Modified: trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/mocks/local/svn.py (292484 => 292485)


--- trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/mocks/local/svn.py	2022-04-06 18:48:46 UTC (rev 292484)
+++ trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/mocks/local/svn.py	2022-04-06 18:51:37 UTC (rev 292485)
@@ -150,6 +150,10 @@
                 cwd=self.path,
                 completion=mocks.ProcessCompletion(returncode=0),
             ), mocks.Subprocess.Route(
+                self.executable, 'revert', '-R',
+                cwd=self.path,
+                completion=mocks.ProcessCompletion(returncode=0),
+            ), mocks.Subprocess.Route(
                 self.executable,
                 cwd=self.path,
                 completion=mocks.ProcessCompletion(

Modified: trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/program/clean.py (292484 => 292485)


--- trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/program/clean.py	2022-04-06 18:48:46 UTC (rev 292484)
+++ trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/program/clean.py	2022-04-06 18:51:37 UTC (rev 292485)
@@ -1,4 +1,4 @@
-# Copyright (C) 2021 Apple Inc. All rights reserved.
+# Copyright (C) 2021-2022 Apple Inc. All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions
@@ -20,21 +20,88 @@
 # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+import re
 import sys
 
 from .command import Command
-from webkitcorepy import arguments
-from webkitscmpy import local
+from webkitcorepy import arguments, run, Terminal
+from webkitscmpy import local, remote
 
 
 class Clean(Command):
     name = 'clean'
-    help = 'Remove all local changes from current checkout'
+    help = 'Remove all local changes from current checkout or delete local and remote branches associated with a pull-request'
 
+    PR_RE = re.compile(r'^\[?[Pp][Rr][ -](?P<number>\d+)]?$')
+
     @classmethod
+    def parser(cls, parser, loggers=None):
+        parser.add_argument(
+            'arguments', nargs='*',
+            type=str, default=None,
+            help='String representation(s) of a commit or branch to be cleaned',
+        )
+
+    @classmethod
+    def cleanup(cls, repository, argument):
+        if not repository.is_git:
+            sys.stderr('Can only cleanup branches on git repositories\n')
+            return 1
+
+        rmt = repository.remote()
+        target = 'fork' if isinstance(rmt, remote.GitHub) else 'origin'
+
+        match = cls.PR_RE.match(argument)
+        if match:
+            if not rmt:
+                sys.stderr.write("'{}' doesn't have a recognized remote\n".format(repository.root_path))
+                return 1
+            if not rmt.pull_requests:
+                sys.stderr.write("'{}' cannot generate pull-requests\n".format(rmt.url))
+                return 1
+
+            pr = rmt.pull_requests.get(int(match.group('number')))
+            if not pr:
+                sys.stderr.write("No pull request found for '{}'\n".format(argument))
+                return 1
+
+            if pr.opened and Terminal.choose("'{arg}' is open, cleaning it up will close the pull request.\nAre you sure you want to close {arg}?".format(arg=argument), default='No') != 'Yes':
+                sys.stderr.write("Keeping '{}' because it is still open\n".format(argument))
+                return 0
+
+            argument = pr.head
+
+        did_delete = False
+        code = 0
+        regex = re.compile(r'^{}-(?P<count>\d+)$'.format(argument))
+        for to_delete in repository.branches_for(remote=False):
+            if to_delete == argument or regex.match(to_delete):
+                code += run([repository.executable(), 'branch', '-D', to_delete], cwd=repository.root_path).returncode
+                did_delete = True
+        for to_delete in repository.branches_for(remote=target):
+            if to_delete == argument or regex.match(to_delete) and target == 'fork':
+                code += run([repository.executable(), 'push', target, '--delete', to_delete], cwd=repository.root_path).returncode
+                did_delete = True
+
+        if not did_delete:
+            sys.stderr.write("No branches matching '{}' were found\n".format(argument))
+            return 1
+
+        return code
+
+    @classmethod
     def main(cls, args, repository, **kwargs):
+        if not repository:
+            sys.stderr.write('No repository provided\n')
+            return 1
         if not repository.path:
             sys.stderr.write('Cannot clean on remote repository\n')
             return 1
 
-        return repository.clean()
+        if not args.arguments:
+            return repository.clean()
+
+        result = 0
+        for argument in args.arguments:
+            result += cls.cleanup(repository, argument)
+        return result

Added: trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/test/clean_unittest.py (0 => 292485)


--- trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/test/clean_unittest.py	                        (rev 0)
+++ trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/test/clean_unittest.py	2022-04-06 18:51:37 UTC (rev 292485)
@@ -0,0 +1,86 @@
+# Copyright (C) 2020-2022 Apple Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1.  Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+# 2.  Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS "AS IS" AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR
+# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import os
+
+from webkitcorepy import OutputCapture, testing
+from webkitcorepy.mocks import Time as MockTime, Terminal as MockTerminal
+from webkitscmpy import program, mocks
+
+
+class TestClean(testing.PathTestCase):
+    basepath = 'mock/repository'
+
+    def setUp(self):
+        super(TestClean, self).setUp()
+        os.mkdir(os.path.join(self.path, '.git'))
+        os.mkdir(os.path.join(self.path, '.svn'))
+
+    def test_checkout_none(self):
+        with OutputCapture() as captured, mocks.local.Git(), mocks.local.Svn(), MockTime:
+            self.assertEqual(1, program.main(
+                args=('clean',),
+                path=self.path,
+            ))
+
+        self.assertEqual(captured.stderr.getvalue(), 'No repository provided\n')
+
+    def test_clean_git(self):
+        with OutputCapture(), mocks.local.Git(self.path) as repo, mocks.local.Svn(), MockTime:
+            repo.modified['some/file'] = 'diff contnet'
+            self.assertEqual(0, program.main(
+                args=('clean',),
+                path=self.path,
+            ))
+            self.assertDictEqual({}, repo.modified)
+
+    def test_clean_svn(self):
+        with OutputCapture(), mocks.local.Git(), mocks.local.Svn(self.path), MockTime:
+            self.assertEqual(0, program.main(
+                args=('clean',),
+                path=self.path,
+            ))
+
+    def test_clean_branch(self):
+        with OutputCapture(), mocks.local.Git(self.path) as repo, mocks.local.Svn(), MockTime:
+            self.assertIn('branch-a', repo.commits)
+            self.assertEqual(0, program.main(
+                args=('clean', 'branch-a'),
+                path=self.path,
+            ))
+            self.assertNotIn('branch-a', repo.commits)
+
+    def test_clean_pr(self):
+        with OutputCapture(), mocks.remote.GitHub() as remote, mocks.local.Git(self.path, remote='https://{}'.format(remote.remote)) as repo, mocks.local.Svn():
+            repo.staged['added.txt'] = 'added'
+            self.assertEqual(0, program.main(
+                args=('pull-request', '-i', 'pr-branch'),
+                path=self.path,
+            ))
+
+            self.assertIn('eng/pr-branch', repo.commits)
+            with MockTerminal.input('y'):
+                self.assertEqual(0, program.main(
+                    args=('clean', 'pr-1'),
+                    path=self.path,
+                ))
+            self.assertNotIn('eng/pr-branch', repo.commits)
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to