diff options
| author | Mike Frysinger <vapier@google.com> | 2019-09-30 22:39:49 -0400 |
|---|---|---|
| committer | Mike Frysinger <vapier@google.com> | 2019-10-01 05:40:28 +0000 |
| commit | 71b0f312b15b597ab54d4d3bd6629efdcf188884 (patch) | |
| tree | b02a38b231c6e7e94776706d08670bc66db14ddf | |
| parent | 369814b4a77adcc78b2549ad728e0d69175f08e8 (diff) | |
| download | git-repo-71b0f312b15b597ab54d4d3bd6629efdcf188884.tar.gz | |
git_command: refactor User-Agent settings
Convert the RepoUserAgent function into a UserAgent class. This
makes it cleaner to hold internal state, and will make it easier
to add a separate git User-Agent, although we don't do it here.
We make the RepoSourceVersion independent of GitCommand so that
it can be called by the class (later).
Bug: https://crbug.com/gerrit/11144
Change-Id: Iab4e1f974b8733a36b243b2d03f5085a96effa19
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/239232
Reviewed-by: David Pursehouse <dpursehouse@collab.net>
Tested-by: Mike Frysinger <vapier@google.com>
| -rw-r--r-- | git_command.py | 128 | ||||
| -rwxr-xr-x | main.py | 6 | ||||
| -rw-r--r-- | tests/test_git_command.py | 18 |
3 files changed, 95 insertions, 57 deletions
diff --git a/git_command.py b/git_command.py index 32dcde09..a4081f45 100644 --- a/git_command.py +++ b/git_command.py | |||
| @@ -22,6 +22,7 @@ import tempfile | |||
| 22 | from signal import SIGTERM | 22 | from signal import SIGTERM |
| 23 | 23 | ||
| 24 | from error import GitError | 24 | from error import GitError |
| 25 | from git_refs import HEAD | ||
| 25 | import platform_utils | 26 | import platform_utils |
| 26 | from repo_trace import REPO_TRACE, IsTrace, Trace | 27 | from repo_trace import REPO_TRACE, IsTrace, Trace |
| 27 | from wrapper import Wrapper | 28 | from wrapper import Wrapper |
| @@ -99,50 +100,72 @@ class _GitCall(object): | |||
| 99 | git = _GitCall() | 100 | git = _GitCall() |
| 100 | 101 | ||
| 101 | 102 | ||
| 102 | _user_agent = None | 103 | def RepoSourceVersion(): |
| 104 | """Return the version of the repo.git tree.""" | ||
| 105 | ver = getattr(RepoSourceVersion, 'version', None) | ||
| 103 | 106 | ||
| 104 | def RepoUserAgent(): | 107 | # We avoid GitCommand so we don't run into circular deps -- GitCommand needs |
| 105 | """Return a User-Agent string suitable for HTTP-like services. | 108 | # to initialize version info we provide. |
| 109 | if ver is None: | ||
| 110 | env = GitCommand._GetBasicEnv() | ||
| 106 | 111 | ||
| 107 | We follow the style as documented here: | 112 | proj = os.path.dirname(os.path.abspath(__file__)) |
| 108 | https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent | 113 | env[GIT_DIR] = os.path.join(proj, '.git') |
| 109 | """ | 114 | |
| 110 | global _user_agent | 115 | p = subprocess.Popen([GIT, 'describe', HEAD], stdout=subprocess.PIPE, |
| 111 | 116 | env=env) | |
| 112 | if _user_agent is None: | 117 | if p.wait() == 0: |
| 113 | py_version = sys.version_info | 118 | ver = p.stdout.read().strip().decode('utf-8') |
| 114 | 119 | if ver.startswith('v'): | |
| 115 | os_name = sys.platform | 120 | ver = ver[1:] |
| 116 | if os_name == 'linux2': | ||
| 117 | os_name = 'Linux' | ||
| 118 | elif os_name == 'win32': | ||
| 119 | os_name = 'Win32' | ||
| 120 | elif os_name == 'cygwin': | ||
| 121 | os_name = 'Cygwin' | ||
| 122 | elif os_name == 'darwin': | ||
| 123 | os_name = 'Darwin' | ||
| 124 | |||
| 125 | p = GitCommand( | ||
| 126 | None, ['describe', 'HEAD'], | ||
| 127 | cwd=os.path.dirname(__file__), | ||
| 128 | capture_stdout=True) | ||
| 129 | if p.Wait() == 0: | ||
| 130 | repo_version = p.stdout | ||
| 131 | if repo_version and repo_version[-1] == '\n': | ||
| 132 | repo_version = repo_version[0:-1] | ||
| 133 | if repo_version and repo_version[0] == 'v': | ||
| 134 | repo_version = repo_version[1:] | ||
| 135 | else: | 121 | else: |
| 136 | repo_version = 'unknown' | 122 | ver = 'unknown' |
| 123 | setattr(RepoSourceVersion, 'version', ver) | ||
| 137 | 124 | ||
| 138 | _user_agent = 'git-repo/%s (%s) git/%s Python/%d.%d.%d' % ( | 125 | return ver |
| 139 | repo_version, | ||
| 140 | os_name, | ||
| 141 | git.version_tuple().full, | ||
| 142 | py_version.major, py_version.minor, py_version.micro) | ||
| 143 | 126 | ||
| 144 | return _user_agent | ||
| 145 | 127 | ||
| 128 | class UserAgent(object): | ||
| 129 | """Mange User-Agent settings when talking to external services | ||
| 130 | |||
| 131 | We follow the style as documented here: | ||
| 132 | https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent | ||
| 133 | """ | ||
| 134 | |||
| 135 | _os = None | ||
| 136 | _repo_ua = None | ||
| 137 | |||
| 138 | @property | ||
| 139 | def os(self): | ||
| 140 | """The operating system name.""" | ||
| 141 | if self._os is None: | ||
| 142 | os_name = sys.platform | ||
| 143 | if os_name.lower().startswith('linux'): | ||
| 144 | os_name = 'Linux' | ||
| 145 | elif os_name == 'win32': | ||
| 146 | os_name = 'Win32' | ||
| 147 | elif os_name == 'cygwin': | ||
| 148 | os_name = 'Cygwin' | ||
| 149 | elif os_name == 'darwin': | ||
| 150 | os_name = 'Darwin' | ||
| 151 | self._os = os_name | ||
| 152 | |||
| 153 | return self._os | ||
| 154 | |||
| 155 | @property | ||
| 156 | def repo(self): | ||
| 157 | """The UA when connecting directly from repo.""" | ||
| 158 | if self._repo_ua is None: | ||
| 159 | py_version = sys.version_info | ||
| 160 | self._repo_ua = 'git-repo/%s (%s) git/%s Python/%d.%d.%d' % ( | ||
| 161 | RepoSourceVersion(), | ||
| 162 | self.os, | ||
| 163 | git.version_tuple().full, | ||
| 164 | py_version.major, py_version.minor, py_version.micro) | ||
| 165 | |||
| 166 | return self._repo_ua | ||
| 167 | |||
| 168 | user_agent = UserAgent() | ||
| 146 | 169 | ||
| 147 | def git_require(min_version, fail=False, msg=''): | 170 | def git_require(min_version, fail=False, msg=''): |
| 148 | git_version = git.version_tuple() | 171 | git_version = git.version_tuple() |
| @@ -171,17 +194,7 @@ class GitCommand(object): | |||
| 171 | ssh_proxy = False, | 194 | ssh_proxy = False, |
| 172 | cwd = None, | 195 | cwd = None, |
| 173 | gitdir = None): | 196 | gitdir = None): |
| 174 | env = os.environ.copy() | 197 | env = self._GetBasicEnv() |
| 175 | |||
| 176 | for key in [REPO_TRACE, | ||
| 177 | GIT_DIR, | ||
| 178 | 'GIT_ALTERNATE_OBJECT_DIRECTORIES', | ||
| 179 | 'GIT_OBJECT_DIRECTORY', | ||
| 180 | 'GIT_WORK_TREE', | ||
| 181 | 'GIT_GRAFT_FILE', | ||
| 182 | 'GIT_INDEX_FILE']: | ||
| 183 | if key in env: | ||
| 184 | del env[key] | ||
| 185 | 198 | ||
| 186 | # If we are not capturing std* then need to print it. | 199 | # If we are not capturing std* then need to print it. |
| 187 | self.tee = {'stdout': not capture_stdout, 'stderr': not capture_stderr} | 200 | self.tee = {'stdout': not capture_stdout, 'stderr': not capture_stderr} |
| @@ -273,6 +286,23 @@ class GitCommand(object): | |||
| 273 | self.process = p | 286 | self.process = p |
| 274 | self.stdin = p.stdin | 287 | self.stdin = p.stdin |
| 275 | 288 | ||
| 289 | @staticmethod | ||
| 290 | def _GetBasicEnv(): | ||
| 291 | """Return a basic env for running git under. | ||
| 292 | |||
| 293 | This is guaranteed to be side-effect free. | ||
| 294 | """ | ||
| 295 | env = os.environ.copy() | ||
| 296 | for key in (REPO_TRACE, | ||
| 297 | GIT_DIR, | ||
| 298 | 'GIT_ALTERNATE_OBJECT_DIRECTORIES', | ||
| 299 | 'GIT_OBJECT_DIRECTORY', | ||
| 300 | 'GIT_WORK_TREE', | ||
| 301 | 'GIT_GRAFT_FILE', | ||
| 302 | 'GIT_INDEX_FILE'): | ||
| 303 | env.pop(key, None) | ||
| 304 | return env | ||
| 305 | |||
| 276 | def Wait(self): | 306 | def Wait(self): |
| 277 | try: | 307 | try: |
| 278 | p = self.process | 308 | p = self.process |
| @@ -46,7 +46,7 @@ except ImportError: | |||
| 46 | from color import SetDefaultColoring | 46 | from color import SetDefaultColoring |
| 47 | import event_log | 47 | import event_log |
| 48 | from repo_trace import SetTrace | 48 | from repo_trace import SetTrace |
| 49 | from git_command import git, GitCommand, RepoUserAgent | 49 | from git_command import git, GitCommand, user_agent |
| 50 | from git_config import init_ssh, close_ssh | 50 | from git_config import init_ssh, close_ssh |
| 51 | from command import InteractiveCommand | 51 | from command import InteractiveCommand |
| 52 | from command import MirrorSafeCommand | 52 | from command import MirrorSafeCommand |
| @@ -297,11 +297,11 @@ def _PruneOptions(argv, opt): | |||
| 297 | 297 | ||
| 298 | class _UserAgentHandler(urllib.request.BaseHandler): | 298 | class _UserAgentHandler(urllib.request.BaseHandler): |
| 299 | def http_request(self, req): | 299 | def http_request(self, req): |
| 300 | req.add_header('User-Agent', RepoUserAgent()) | 300 | req.add_header('User-Agent', user_agent.repo) |
| 301 | return req | 301 | return req |
| 302 | 302 | ||
| 303 | def https_request(self, req): | 303 | def https_request(self, req): |
| 304 | req.add_header('User-Agent', RepoUserAgent()) | 304 | req.add_header('User-Agent', user_agent.repo) |
| 305 | return req | 305 | return req |
| 306 | 306 | ||
| 307 | def _AddPasswordFromUserInput(handler, msg, req): | 307 | def _AddPasswordFromUserInput(handler, msg, req): |
diff --git a/tests/test_git_command.py b/tests/test_git_command.py index 4d65d3ce..5ceb0b33 100644 --- a/tests/test_git_command.py +++ b/tests/test_git_command.py | |||
| @@ -50,12 +50,20 @@ class GitCallUnitTest(unittest.TestCase): | |||
| 50 | self.assertNotEqual('', ver.full) | 50 | self.assertNotEqual('', ver.full) |
| 51 | 51 | ||
| 52 | 52 | ||
| 53 | class RepoUserAgentUnitTest(unittest.TestCase): | 53 | class UserAgentUnitTest(unittest.TestCase): |
| 54 | """Tests the RepoUserAgent function.""" | 54 | """Tests the UserAgent function.""" |
| 55 | 55 | ||
| 56 | def test_smoke(self): | 56 | def test_smoke_os(self): |
| 57 | """Make sure it returns something useful.""" | 57 | """Make sure UA OS setting returns something useful.""" |
| 58 | ua = git_command.RepoUserAgent() | 58 | os_name = git_command.user_agent.os |
| 59 | # We can't dive too deep because of OS/tool differences, but we can check | ||
| 60 | # the general form. | ||
| 61 | m = re.match(r'^[^ ]+$', os_name) | ||
| 62 | self.assertIsNotNone(m) | ||
| 63 | |||
| 64 | def test_smoke_repo(self): | ||
| 65 | """Make sure repo UA returns something useful.""" | ||
| 66 | ua = git_command.user_agent.repo | ||
| 59 | # We can't dive too deep because of OS/tool differences, but we can check | 67 | # We can't dive too deep because of OS/tool differences, but we can check |
| 60 | # the general form. | 68 | # the general form. |
| 61 | m = re.match(r'^git-repo/[^ ]+ ([^ ]+) git/[^ ]+ Python/[0-9.]+', ua) | 69 | m = re.match(r'^git-repo/[^ ]+ ([^ ]+) git/[^ ]+ Python/[0-9.]+', ua) |
