Title: [280739] trunk/Tools
Revision
280739
Author
jbed...@apple.com
Date
2021-08-06 15:16:34 -0700 (Fri, 06 Aug 2021)

Log Message

[git-webkit] Add setup function
https://bugs.webkit.org/show_bug.cgi?id=225985
<rdar://problem/78226729>

Reviewed by Dewei Zhu.

* Scripts/libraries/webkitcorepy/setup.py: Bump version.
* Scripts/libraries/webkitcorepy/webkitcorepy/__init__.py: Ditto.
* Scripts/libraries/webkitcorepy/webkitcorepy/credentials.py:
(credentials): Key should be pulled from environment variable with key_name in it.
* Scripts/libraries/webkitscmpy/setup.py: Bump version.
* Scripts/libraries/webkitscmpy/webkitscmpy/__init__.py: Ditto.
* Scripts/libraries/webkitscmpy/webkitscmpy/mocks/local/git.py:
(Git): Move config regexes.
(Git.__init__): Add `git remote add`, `git config` and `git fetch fork`.
(Git.config): Add user.email to default configuration.
(Git.edit_config): Modify configuration file.
* Scripts/libraries/webkitscmpy/webkitscmpy/mocks/remote/git_hub.py:
(GitHub): Add list of forks.
(GitHub.__enter__): Correctly mock token.
(GitHub.request): Add fork check and fork addition.
* Scripts/libraries/webkitscmpy/webkitscmpy/program/__init__.py:
(main): Add setup function.
* Scripts/libraries/webkitscmpy/webkitscmpy/remote/git_hub.py:
(GitHub.credentials): Strip email domain, guide user on token permissions.
* Scripts/libraries/webkitscmpy/webkitscmpy/program/setup.py: Added.
(Setup.github): Ensure that GitHub credentials are in the keychain.
(Setup.git): Set the user for the repository, along with merge strategy.
(Setup.parser):
(Setup.main):
* Scripts/libraries/webkitscmpy/webkitscmpy/test/git_unittest.py:
(test_config): Change user name.
(test_global_config): Ditto.
* Scripts/libraries/webkitscmpy/webkitscmpy/test/setup_unittest.py: Added.
(TestSetup):
(TestSetup.setUp):
(TestSetup.test_svn):
(TestSetup.test_github):
(TestSetup.test_git):
(TestSetup.test_github_checkout):

Modified Paths

Added Paths

Diff

Modified: trunk/Tools/ChangeLog (280738 => 280739)


--- trunk/Tools/ChangeLog	2021-08-06 22:13:32 UTC (rev 280738)
+++ trunk/Tools/ChangeLog	2021-08-06 22:16:34 UTC (rev 280739)
@@ -1,3 +1,46 @@
+2021-08-06  Jonathan Bedard  <jbed...@apple.com>
+
+        [git-webkit] Add setup function
+        https://bugs.webkit.org/show_bug.cgi?id=225985
+        <rdar://problem/78226729>
+
+        Reviewed by Dewei Zhu.
+
+        * Scripts/libraries/webkitcorepy/setup.py: Bump version.
+        * Scripts/libraries/webkitcorepy/webkitcorepy/__init__.py: Ditto.
+        * Scripts/libraries/webkitcorepy/webkitcorepy/credentials.py:
+        (credentials): Key should be pulled from environment variable with key_name in it.
+        * Scripts/libraries/webkitscmpy/setup.py: Bump version.
+        * Scripts/libraries/webkitscmpy/webkitscmpy/__init__.py: Ditto.
+        * Scripts/libraries/webkitscmpy/webkitscmpy/mocks/local/git.py:
+        (Git): Move config regexes.
+        (Git.__init__): Add `git remote add`, `git config` and `git fetch fork`.
+        (Git.config): Add user.email to default configuration.
+        (Git.edit_config): Modify configuration file.
+        * Scripts/libraries/webkitscmpy/webkitscmpy/mocks/remote/git_hub.py:
+        (GitHub): Add list of forks.
+        (GitHub.__enter__): Correctly mock token.
+        (GitHub.request): Add fork check and fork addition.
+        * Scripts/libraries/webkitscmpy/webkitscmpy/program/__init__.py:
+        (main): Add setup function.
+        * Scripts/libraries/webkitscmpy/webkitscmpy/remote/git_hub.py:
+        (GitHub.credentials): Strip email domain, guide user on token permissions.
+        * Scripts/libraries/webkitscmpy/webkitscmpy/program/setup.py: Added.
+        (Setup.github): Ensure that GitHub credentials are in the keychain.
+        (Setup.git): Set the user for the repository, along with merge strategy.
+        (Setup.parser):
+        (Setup.main):
+        * Scripts/libraries/webkitscmpy/webkitscmpy/test/git_unittest.py:
+        (test_config): Change user name.
+        (test_global_config): Ditto.
+        * Scripts/libraries/webkitscmpy/webkitscmpy/test/setup_unittest.py: Added.
+        (TestSetup):
+        (TestSetup.setUp):
+        (TestSetup.test_svn):
+        (TestSetup.test_github):
+        (TestSetup.test_git):
+        (TestSetup.test_github_checkout):
+
 2021-08-06  Fujii Hironori  <hironori.fu...@sony.com>
 
         [Win][WebKitTestRunner] WTR::PlatformWebView leaks m_view (WKViewRef)

Modified: trunk/Tools/Scripts/libraries/webkitcorepy/setup.py (280738 => 280739)


--- trunk/Tools/Scripts/libraries/webkitcorepy/setup.py	2021-08-06 22:13:32 UTC (rev 280738)
+++ trunk/Tools/Scripts/libraries/webkitcorepy/setup.py	2021-08-06 22:16:34 UTC (rev 280739)
@@ -30,7 +30,7 @@
 
 setup(
     name='webkitcorepy',
-    version='0.8.0',
+    version='0.8.1',
     description='Library containing various Python support classes and functions.',
     long_description=readme(),
     classifiers=[

Modified: trunk/Tools/Scripts/libraries/webkitcorepy/webkitcorepy/__init__.py (280738 => 280739)


--- trunk/Tools/Scripts/libraries/webkitcorepy/webkitcorepy/__init__.py	2021-08-06 22:13:32 UTC (rev 280738)
+++ trunk/Tools/Scripts/libraries/webkitcorepy/webkitcorepy/__init__.py	2021-08-06 22:16:34 UTC (rev 280739)
@@ -40,7 +40,7 @@
 from webkitcorepy.measure_time import MeasureTime
 from webkitcorepy.nested_fuzzy_dict import NestedFuzzyDict
 
-version = Version(0, 8, 0)
+version = Version(0, 8, 1)
 
 from webkitcorepy.autoinstall import Package, AutoInstall
 if sys.version_info > (3, 0):

Modified: trunk/Tools/Scripts/libraries/webkitcorepy/webkitcorepy/credentials.py (280738 => 280739)


--- trunk/Tools/Scripts/libraries/webkitcorepy/webkitcorepy/credentials.py	2021-08-06 22:13:32 UTC (rev 280738)
+++ trunk/Tools/Scripts/libraries/webkitcorepy/webkitcorepy/credentials.py	2021-08-06 22:16:34 UTC (rev 280739)
@@ -38,7 +38,7 @@
         return _cache.get(name)
 
     username = os.environ.get('{}_USERNAME'.format(name.upper()))
-    key = os.environ.get('{}_{}'.format(name.upper(), name.upper()))
+    key = os.environ.get('{}_{}'.format(name.upper(), key_name.upper()))
 
     if username and key:
         _cache[name] = (username, key)

Modified: trunk/Tools/Scripts/libraries/webkitscmpy/setup.py (280738 => 280739)


--- trunk/Tools/Scripts/libraries/webkitscmpy/setup.py	2021-08-06 22:13:32 UTC (rev 280738)
+++ trunk/Tools/Scripts/libraries/webkitscmpy/setup.py	2021-08-06 22:16:34 UTC (rev 280739)
@@ -29,7 +29,7 @@
 
 setup(
     name='webkitscmpy',
-    version='1.0.6',
+    version='1.1.0',
     description='Library designed to interact with git and svn repositories.',
     long_description=readme(),
     classifiers=[

Modified: trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/__init__.py (280738 => 280739)


--- trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/__init__.py	2021-08-06 22:13:32 UTC (rev 280738)
+++ trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/__init__.py	2021-08-06 22:16:34 UTC (rev 280739)
@@ -46,7 +46,7 @@
         "Please install webkitcorepy with `pip install webkitcorepy --extra-index-url <package index URL>`"
     )
 
-version = Version(1, 0, 6)
+version = Version(1, 1, 0)
 
 AutoInstall.register(Package('fasteners', Version(0, 15, 0)))
 AutoInstall.register(Package('monotonic', Version(1, 5)))

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


--- trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/mocks/local/git.py	2021-08-06 22:13:32 UTC (rev 280738)
+++ trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/mocks/local/git.py	2021-08-06 22:16:34 UTC (rev 280739)
@@ -35,6 +35,15 @@
 
 
 class Git(mocks.Subprocess):
+    # Parse a .git/config that looks like this
+    # [core]
+    #     repositoryformatversion = 0
+    # [branch "main"]
+    #     remote = origin
+    # 	  merge = refs/heads/main
+    RE_SINGLE_TOP = re.compile(r'^\[\s*(?P<key>\S+)\s*\]')
+    RE_MULTI_TOP = re.compile(r'^\[\s*(?P<keya>\S+) "(?P<keyb>\S+)"\s*\]')
+    RE_ELEMENT = re.compile(r'^\s+(?P<key>\S+)\s*=\s*(?P<value>\S+)')
 
     def __init__(
         self, path='/.invalid-git', datafile=None,
@@ -189,6 +198,12 @@
                     stderr="fatal: No such remote '{}'\n".format(args[3]),
                 ),
             ), mocks.Subprocess.Route(
+                self.executable, 'remote', 'add', re.compile(r'.+'),
+                cwd=self.path,
+                completion=mocks.ProcessCompletion(
+                    returncode=0,
+                ),
+            ), mocks.Subprocess.Route(
                 self.executable, 'branch', '-a',
                 cwd=self.path,
                 generator=lambda *args, **kwargs: mocks.ProcessCompletion(
@@ -355,6 +370,17 @@
                         stdout='\n'.join(['{}={}'.format(key, value) for key, value in Git.config().items()])
                     ),
             ), mocks.Subprocess.Route(
+                self.executable, 'config', re.compile(r'.+'), re.compile(r'.+'),
+                cwd=self.path,
+                generator=lambda *args, **kwargs:
+                    self.edit_config(args[2], args[3]),
+            ), mocks.Subprocess.Route(
+                self.executable, 'fetch', 'fork',
+                cwd=self.path,
+                completion=mocks.ProcessCompletion(
+                    returncode=0,
+                ),
+            ), mocks.Subprocess.Route(
                 self.executable,
                 cwd=self.path,
                 completion=mocks.ProcessCompletion(
@@ -590,34 +616,50 @@
     def config(context):
         if isinstance(context, type):
             return {
-                'user.name': 'tap...@webkit.org',
+                'user.name': 'Tim Apple',
+                'user.email': 'tap...@webkit.org',
                 'sendemail.transferencoding': 'base64',
             }
 
-        # Parse a .git/config that looks like this
-        # [core]
-        #     repositoryformatversion = 0
-        # [branch "main"]
-        #     remote = origin
-        # 	  merge = refs/heads/main
-        RE_SINGLE_TOP = re.compile(r'^\[\s*(?P<key>\S+)\s*\]')
-        RE_MULTI_TOP = re.compile(r'^\[\s*(?P<keya>\S+) "(?P<keyb>\S+)"\s*\]')
-        RE_ELEMENT = re.compile(r'^\s+(?P<key>\S+)\s*=\s*(?P<value>\S+)')
-
         top = None
         result = Git.config()
         with open(os.path.join(context.path, '.git', 'config'), 'r') as configfile:
             for line in configfile.readlines():
-                match = RE_MULTI_TOP.match(line)
+                match = context.RE_MULTI_TOP.match(line)
                 if match:
                     top = '{}.{}'.format(match.group('keya'), match.group('keyb'))
                     continue
-                match = RE_SINGLE_TOP.match(line)
+                match = context.RE_SINGLE_TOP.match(line)
                 if match:
                     top = match.group('key')
                     continue
 
-                match = RE_ELEMENT.match(line)
+                match = context.RE_ELEMENT.match(line)
                 if top and match:
                     result['{}.{}'.format(top, match.group('key'))] = match.group('value')
         return result
+
+    def edit_config(self, key, value):
+        with open(os.path.join(self.path, '.git', 'config'), 'r') as configfile:
+            lines = [line for line in configfile.readlines()]
+
+        key_a = key.split('.')[0]
+        key_b = '.'.join(key.split('.')[1:])
+
+        did_print = False
+        with open(os.path.join(self.path, '.git', 'config'), 'w') as configfile:
+            for line in lines:
+                match = self.RE_ELEMENT.match(line)
+                if not match or match.group('key') != key_b:
+                    configfile.write(line)
+                match = self.RE_MULTI_TOP.match(line)
+                if not match or '{}.{}'.format(match.group('keya'), match.group('keyb')) != key_a:
+                    continue
+                configfile.write('\t{}={}\n'.format(key_b, value))
+                did_print = True
+
+            if not did_print:
+                configfile.write('[{}]\n'.format(key_a))
+                configfile.write('\t{}={}\n'.format(key_b, value))
+
+        return mocks.ProcessCompletion(returncode=0)

Modified: trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/mocks/remote/git_hub.py (280738 => 280739)


--- trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/mocks/remote/git_hub.py	2021-08-06 22:13:32 UTC (rev 280738)
+++ trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/mocks/remote/git_hub.py	2021-08-06 22:16:34 UTC (rev 280739)
@@ -39,6 +39,7 @@
 
         self.default_branch = default_branch
         self.remote = remote
+        self.forks = []
         hostname = self.remote.split('/')[0]
         self.api_remote = 'api.{hostname}/repos/{repo}'.format(
             hostname=hostname,
@@ -62,7 +63,7 @@
     def __enter__(self):
         prefix = self.remote.split('/')[0].replace('.', '_').upper()
         username_key = '{}_USERNAME'.format(prefix)
-        token_key = '{}_ACCESS_TOKEN'.format(prefix)
+        token_key = '{}_TOKEN'.format(prefix)
         self._environment = {
             username_key: os.environ.get(username_key),
             token_key: os.environ.get(token_key),
@@ -315,4 +316,24 @@
         if stripped_url.startswith('{}/tree/'.format(self.remote)):
             return self._parents_of_request(url="" ref=stripped_url.split('/')[-1])
 
+        # Check for existance of forked repo
+        if stripped_url.startswith('{}/repos'.format(self.api_remote.split('/')[0])) and stripped_url.split('/')[-1] == self.remote.split('/')[-1]:
+            username = stripped_url.split('/')[-2]
+            if username in self.forks or username == self.remote.split('/')[-2]:
+                return mocks.Response.fromJson(dict(
+                    owmer=dict(
+                        login=username,
+                    ), fork=username in self.forks,
+                    name=stripped_url.split('/')[-1],
+                    full_name='/'.join(stripped_url.split('/')[-2:]),
+                ), url=""
+            return mocks.Response.create404(url)
+
+        # Add fork
+        if stripped_url.startswith('{}/forks'.format(self.api_remote)) and method == 'POST':
+            username = kwargs.get('json', {}).get('owner', None)
+            if username:
+                self.forks.append(username)
+            return mocks.Response.fromJson({}) if username else mocks.Response.create404(url)
+
         return mocks.Response.create404(url)

Modified: trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/program/__init__.py (280738 => 280739)


--- trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/program/__init__.py	2021-08-06 22:13:32 UTC (rev 280738)
+++ trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/program/__init__.py	2021-08-06 22:16:34 UTC (rev 280739)
@@ -33,6 +33,7 @@
 from .log import Log
 from .pull import Pull
 from .setup_git_svn import SetupGitSvn
+from .setup import Setup
 
 from webkitcorepy import arguments, log as webkitcorepy_log
 from webkitscmpy import local, log, remote
@@ -63,7 +64,7 @@
 
     subparsers = parser.add_subparsers(help='sub-command help')
 
-    programs = [Find, Info, Checkout, Canonicalize, Pull, Clean, Log, Blame]
+    programs = [Find, Info, Checkout, Canonicalize, Pull, Clean, Log, Blame, Setup]
     if subversion:
         programs.append(SetupGitSvn)
 

Added: trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/program/setup.py (0 => 280739)


--- trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/program/setup.py	                        (rev 0)
+++ trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/program/setup.py	2021-08-06 22:16:34 UTC (rev 280739)
@@ -0,0 +1,213 @@
+# Copyright (C) 2021 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 requests
+import sys
+
+from .command import Command
+from requests.auth import HTTPBasicAuth
+from webkitcorepy import arguments, run, Terminal
+from webkitscmpy import log, local, remote
+
+
+class Setup(Command):
+    name = 'setup'
+    help = 'Configure local settings for the current repository'
+
+    @classmethod
+    def github(cls, args, repository, **kwargs):
+        log.warning('Saving GitHub credentials in system credential store...')
+        username, access_token = repository.credentials(required=True)
+        log.warning('GitHub credentials saved via Keyring!')
+
+        log.warning('Verifying user owned fork...')
+        auth = HTTPBasicAuth(username, access_token)
+        response = requests.get('{}/repos/{}/{}'.format(
+            repository.api_url,
+            username,
+            repository.name,
+        ), auth=auth, headers=dict(Accept='application/vnd.github.v3+json'))
+        if response.status_code == 200:
+            log.warning("User already owns a fork of '{}'!".format(repository.name))
+            return 0
+
+        if repository.owner == username or args.defaults or Terminal.choose(
+            "Create a private fork of '{}' belonging to '{}'".format(repository.name, username),
+            default='No',
+        ) == 'No':
+            log.warning("Continuing without forking '{}'".format(repository.name))
+            return 1
+
+        response = requests.post('{}/repos/{}/{}/forks'.format(
+            repository.api_url,
+            repository.owner,
+            repository.name,
+        ), json=dict(
+            owner=username,
+            description="{}'s fork of {}".format(username, repository.name),
+            private=True,
+        ), auth=auth, headers=dict(Accept='application/vnd.github.v3+json'))
+        if response.status_code not in (200, 202):
+            sys.stderr.write("Failed to create a fork of '{}' belonging to '{}'\n".format(repository.name, username))
+            return 1
+        log.warning("Created a private fork of '{}' belonging to '{}'!".format(repository.name, username))
+        return 0
+
+    @classmethod
+    def git(cls, args, repository, **kwargs):
+        global_config = local.Git.config()
+        result = 0
+
+        email = global_config.get('user.email')
+        log.warning('Setting git user email for {}...'.format(repository.root_path))
+        if not email or args.defaults is False or (not args.defaults and Terminal.choose(
+            "Set '{}' as the git user email".format(email),
+            default='Yes',
+        ) == 'No'):
+            email = Terminal.input('Git user email: ')
+
+        if run(
+            [local.Git.executable(), 'config', 'user.email', email], capture_output=True, cwd=repository.root_path,
+        ).returncode:
+            sys.stderr.write('Failed to set the git user email to {}\n'.format(email))
+            result += 1
+        else:
+            log.warning("Set git user email to '{}'".format(email))
+
+        name = repository.contributors.get(email)
+        if name:
+            name = name.name
+        else:
+            name = global_config.get('user.name')
+        log.warning('Setting git user name for {}...'.format(repository.root_path))
+        if not name or args.defaults is False or (not args.defaults and Terminal.choose(
+            "Set '{}' as the git user name".format(name),
+            default='Yes',
+        ) == 'No'):
+            name = Terminal.input('Git user name: ')
+        if run(
+            [local.Git.executable(), 'config', 'user.name', name], capture_output=True, cwd=repository.root_path,
+        ).returncode:
+            sys.stderr.write('Failed to set the git user name to {}\n'.format(name))
+            result += 1
+        else:
+            log.warning("Set git user name to '{}'".format(name))
+
+        log.warning('Setting better Objective-C diffing behavior...')
+        result += run(
+            [local.Git.executable(), 'config', 'diff.objcpp.xfuncname', '^[-+@a-zA-Z_].*$'],
+            capture_output=True, cwd=repository.root_path,
+        ).returncode
+        result += run(
+            [local.Git.executable(), 'config', 'diff.objcppheader.xfuncname', '^[@a-zA-Z_].*$'],
+            capture_output=True, cwd=repository.root_path,
+        ).returncode
+        log.warning('Set better Objective-C diffing behavior!')
+
+        if args.defaults or Terminal.choose(
+            'Auto-color status, diff, and branch?'.format(email),
+            default='Yes',
+        ) == 'Yes':
+            for command in ('status', 'diff', 'branch'):
+                result += run(
+                    [local.Git.executable(), 'config', 'color.{}'.format(command), 'auto'],
+                    capture_output=True, cwd=repository.root_path,
+                ).returncode
+
+        log.warning('Using {} merge strategy'.format('merge commits as a' if args.merge else 'a rebase'))
+        if run(
+            [local.Git.executable(), 'config', 'pull.rebase', 'false' if args.merge else 'true'],
+            capture_output=True, cwd=repository.root_path,
+        ).returncode:
+            sys.stderr.write('Failed to use {} as the merge strategy\n'.format('merge commits' if args.merge else 'rebase'))
+            result += 1
+
+        # Only configure GitHub if the URL is a GitHub URL
+        rmt = repository.remote()
+        if not isinstance(rmt, remote.GitHub):
+            return result
+
+        code = cls.github(args, rmt, **kwargs)
+        result += code
+        if code:
+            return result
+
+        username, _ = rmt.credentials(required=True)
+        log.warning("Adding forked remote as '{}' and 'fork'...".format(username))
+        url = ""
+
+        if '://' in url:
+            fork_remote = '{}://{}/{}/{}.git'.format(url.split(':')[0], url.split('/')[2], username, rmt.name)
+        elif ':' in url:
+            fork_remote = '{}:{}/{}.git'.format(url.split(':')[0], username, rmt.name)
+        else:
+            return result + 1
+
+        available_remotes = []
+        for name in [username, 'fork']:
+            returncode = run(
+                [repository.executable(), 'remote', 'add', name, fork_remote],
+                capture_output=True, cwd=repository.root_path,
+            ).returncode
+            if returncode == 3:
+                returncode = run(
+                    [repository.executable(), 'remote', 'set-url', name, fork_remote],
+                    capture_output=True, cwd=repository.root_path,
+                ).returncode
+            if returncode:
+                sys.stderr.write("Failed to add remote '{}'\n".format(name))
+                result += 1
+            else:
+                available_remotes.append(name)
+                log.warning("Added remote '{}'".format(name))
+
+        if 'fork' not in available_remotes:
+            return result
+        log.warning("Fetching '{}'".format(fork_remote))
+        return run(
+            [repository.executable(), 'fetch', 'fork'],
+            capture_output=True, cwd=repository.root_path,
+        ).returncode
+
+    @classmethod
+    def parser(cls, parser, loggers=None):
+        parser.add_argument(
+            '--defaults', '--no-defaults', action="" default=None,
+            help='Do not prompt the user for defaults, always use (or do not use) them',
+        )
+        parser.add_argument(
+            '--merge', '--no-merge', action="" default=False,
+            help='Use a merge-commit workflow instead of a rebase workflow',
+        )
+
+    @classmethod
+    def main(cls, args, repository, **kwargs):
+        if isinstance(repository, local.Git):
+            return cls.git(args, repository, **kwargs)
+
+        if isinstance(repository, remote.GitHub):
+            return cls.github(args, repository, **kwargs)
+
+        sys.stderr.write('No setup required for {}\n'.format(
+            getattr(repository, 'root_path', getattr(repository, 'url', '?')),
+        ))
+        return 1

Modified: trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/remote/git_hub.py (280738 => 280739)


--- trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/remote/git_hub.py	2021-08-06 22:13:32 UTC (rev 280738)
+++ trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/remote/git_hub.py	2021-08-06 22:16:34 UTC (rev 280739)
@@ -1,4 +1,4 @@
-# Copyright (C) 2020 Apple Inc. All rights reserved.
+# Copyright (C) 2020, 2021 Apple Inc. All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions
@@ -63,13 +63,18 @@
         )
 
     def credentials(self, required=True):
-        return credentials(
+        username, token = credentials(
             url=""
             required=required,
             name=self.url.split('/')[2].replace('.', '_').upper(),
-            prompt="GitHub's API\nPlease generate a 'Personal access token' via 'Developer settings' for your user",
+            prompt='''GitHub's API
+Please generate a 'Personal access token' via 'Developer settings' with 'repo' and 'workflow' access
+for your {} user'''.format(self.url.split('/')[2]),
             key_name='token',
         )
+        if username:
+            username = username.split('@')[0]
+        return username, token
 
     @property
     def is_git(self):

Modified: trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/test/git_unittest.py (280738 => 280739)


--- trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/test/git_unittest.py	2021-08-06 22:13:32 UTC (rev 280738)
+++ trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/test/git_unittest.py	2021-08-06 22:16:34 UTC (rev 280739)
@@ -411,7 +411,7 @@
         with mocks.local.Git(self.path, git_svn=True) as m:
             repo = local.Git(self.path)
 
-            self.assertEqual(repo.config()['user.name'], 'tap...@webkit.org')
+            self.assertEqual(repo.config()['user.name'], 'Tim Apple')
             self.assertEqual(repo.config()['core.filemode'], 'true')
             self.assertEqual(repo.config()['remote.origin.url'], 'g...@example.org:/mock/repository')
             self.assertEqual(repo.config()['svn-remote.svn.url'], 'https://svn.example.org/repository/webkit')
@@ -419,7 +419,7 @@
 
     def test_global_config(self):
         with mocks.local.Git(self.path, git_svn=True), OutputCapture():
-            self.assertEqual(local.Git.config()['user.name'], 'tap...@webkit.org')
+            self.assertEqual(local.Git.config()['user.name'], 'Tim Apple')
             self.assertEqual(local.Git.config()['sendemail.transferencoding'], 'base64')
 
 

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


--- trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/test/setup_unittest.py	                        (rev 0)
+++ trunk/Tools/Scripts/libraries/webkitscmpy/webkitscmpy/test/setup_unittest.py	2021-08-06 22:16:34 UTC (rev 280739)
@@ -0,0 +1,137 @@
+# Copyright (C) 2021 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 TestSetup(testing.PathTestCase):
+    basepath = 'mock/repository'
+
+    def setUp(self):
+        super(TestSetup, self).setUp()
+        os.mkdir(os.path.join(self.path, '.git'))
+        os.mkdir(os.path.join(self.path, '.svn'))
+
+    def test_svn(self):
+        with OutputCapture() as captured, mocks.local.Git(), mocks.local.Svn(self.path):
+            self.assertEqual(1, program.main(
+                args=('setup',),
+                path=self.path,
+            ))
+        self.assertEqual(captured.stderr.getvalue(), 'No setup required for {}\n'.format(self.path))
+
+    def test_github(self):
+        with OutputCapture() as captured, MockTerminal.input('y'), mocks.remote.GitHub() as remote:
+            self.assertEqual(0, program.main(
+                args=('-C', 'https://{}'.format(remote.remote), 'setup'),
+                path=self.path,
+            ))
+
+        self.assertEqual(captured.stdout.getvalue(), "Create a private fork of 'WebKit' belonging to 'username' (Yes/No): \n")
+        self.assertEqual(captured.stderr.getvalue(), '')
+        self.assertEqual(
+            captured.root.log.getvalue(),
+            '''Saving GitHub credentials in system credential store...
+GitHub credentials saved via Keyring!
+Verifying user owned fork...
+Created a private fork of 'WebKit' belonging to 'username'!
+''',
+        )
+
+    def test_git(self):
+        with OutputCapture() as captured, mocks.local.Git(self.path) as repo, mocks.local.Svn():
+            self.assertEqual(0, program.main(
+                args=('setup', '--defaults'),
+                path=self.path,
+            ))
+
+            config = repo.config()
+            self.assertEqual('^[-+@a-zA-Z_].*$', config.get('diff.objcpp.xfuncname', ''))
+            self.assertEqual('^[@a-zA-Z_].*$', config.get('diff.objcppheader.xfuncname', ''))
+            self.assertEqual('auto', config.get('color.status', ''))
+            self.assertEqual('auto', config.get('color.diff', ''))
+            self.assertEqual('auto', config.get('color.branch', ''))
+            self.assertEqual('true', config.get('pull.rebase', ''))
+
+        self.assertEqual(captured.stdout.getvalue(), '')
+        self.assertEqual(captured.stderr.getvalue(), '')
+        self.assertEqual(
+            captured.root.log.getvalue(),
+            '''Setting git user email for {repository}...
+Set git user email to 'tap...@webkit.org'
+Setting git user name for {repository}...
+Set git user name to 'Tim Apple'
+Setting better Objective-C diffing behavior...
+Set better Objective-C diffing behavior!
+Using a rebase merge strategy
+'''.format(repository=self.path),
+        )
+
+    def test_github_checkout(self):
+        with OutputCapture() as captured, mocks.remote.GitHub() as remote, \
+            MockTerminal.input('n', 'commit...@webkit.org', 'n', 'Committer', 'n', 'y'), \
+            mocks.local.Git(self.path, remote='https://{}'.format(remote.remote)) as repo:
+
+            self.assertEqual(0, program.main(
+                args=('setup',),
+                path=self.path,
+            ))
+
+            config = repo.config()
+            self.assertNotIn('color.status', config)
+            self.assertEqual('Committer', config.get('user.name', ''))
+            self.assertEqual('commit...@webkit.org', config.get('user.email', ''))
+
+        self.assertEqual(
+            captured.stdout.getvalue(),
+            '''Set 'tap...@webkit.org' as the git user email (Yes/No): 
+Git user email: 
+Set 'Tim Apple' as the git user name (Yes/No): 
+Git user name: 
+Auto-color status, diff, and branch? (Yes/No): 
+Create a private fork of 'WebKit' belonging to 'username' (Yes/No): 
+''')
+        self.assertEqual(captured.stderr.getvalue(), '')
+        self.maxDiff = None
+        self.assertEqual(
+            captured.root.log.getvalue(),
+            '''Setting git user email for {repository}...
+Set git user email to 'commit...@webkit.org'
+Setting git user name for {repository}...
+Set git user name to 'Committer'
+Setting better Objective-C diffing behavior...
+Set better Objective-C diffing behavior!
+Using a rebase merge strategy
+Saving GitHub credentials in system credential store...
+GitHub credentials saved via Keyring!
+Verifying user owned fork...
+Created a private fork of 'WebKit' belonging to 'username'!
+Adding forked remote as 'username' and 'fork'...
+Added remote 'username'
+Added remote 'fork'
+Fetching 'https://github.example.com/username/WebKit.git'
+'''.format(repository=self.path),
+        )
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to