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>
diff --git a/git_command.py b/git_command.py
index 32dcde0..a4081f4 100644
--- a/git_command.py
+++ b/git_command.py
@@ -22,6 +22,7 @@
from signal import SIGTERM
from error import GitError
+from git_refs import HEAD
import platform_utils
from repo_trace import REPO_TRACE, IsTrace, Trace
from wrapper import Wrapper
@@ -99,50 +100,72 @@
git = _GitCall()
-_user_agent = None
+def RepoSourceVersion():
+ """Return the version of the repo.git tree."""
+ ver = getattr(RepoSourceVersion, 'version', None)
-def RepoUserAgent():
- """Return a User-Agent string suitable for HTTP-like services.
+ # We avoid GitCommand so we don't run into circular deps -- GitCommand needs
+ # to initialize version info we provide.
+ if ver is None:
+ env = GitCommand._GetBasicEnv()
+
+ proj = os.path.dirname(os.path.abspath(__file__))
+ env[GIT_DIR] = os.path.join(proj, '.git')
+
+ p = subprocess.Popen([GIT, 'describe', HEAD], stdout=subprocess.PIPE,
+ env=env)
+ if p.wait() == 0:
+ ver = p.stdout.read().strip().decode('utf-8')
+ if ver.startswith('v'):
+ ver = ver[1:]
+ else:
+ ver = 'unknown'
+ setattr(RepoSourceVersion, 'version', ver)
+
+ return ver
+
+
+class UserAgent(object):
+ """Mange User-Agent settings when talking to external services
We follow the style as documented here:
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent
"""
- global _user_agent
- if _user_agent is None:
- py_version = sys.version_info
+ _os = None
+ _repo_ua = None
- os_name = sys.platform
- if os_name == 'linux2':
- os_name = 'Linux'
- elif os_name == 'win32':
- os_name = 'Win32'
- elif os_name == 'cygwin':
- os_name = 'Cygwin'
- elif os_name == 'darwin':
- os_name = 'Darwin'
+ @property
+ def os(self):
+ """The operating system name."""
+ if self._os is None:
+ os_name = sys.platform
+ if os_name.lower().startswith('linux'):
+ os_name = 'Linux'
+ elif os_name == 'win32':
+ os_name = 'Win32'
+ elif os_name == 'cygwin':
+ os_name = 'Cygwin'
+ elif os_name == 'darwin':
+ os_name = 'Darwin'
+ self._os = os_name
- p = GitCommand(
- None, ['describe', 'HEAD'],
- cwd=os.path.dirname(__file__),
- capture_stdout=True)
- if p.Wait() == 0:
- repo_version = p.stdout
- if repo_version and repo_version[-1] == '\n':
- repo_version = repo_version[0:-1]
- if repo_version and repo_version[0] == 'v':
- repo_version = repo_version[1:]
- else:
- repo_version = 'unknown'
+ return self._os
- _user_agent = 'git-repo/%s (%s) git/%s Python/%d.%d.%d' % (
- repo_version,
- os_name,
- git.version_tuple().full,
- py_version.major, py_version.minor, py_version.micro)
+ @property
+ def repo(self):
+ """The UA when connecting directly from repo."""
+ if self._repo_ua is None:
+ py_version = sys.version_info
+ self._repo_ua = 'git-repo/%s (%s) git/%s Python/%d.%d.%d' % (
+ RepoSourceVersion(),
+ self.os,
+ git.version_tuple().full,
+ py_version.major, py_version.minor, py_version.micro)
- return _user_agent
+ return self._repo_ua
+user_agent = UserAgent()
def git_require(min_version, fail=False, msg=''):
git_version = git.version_tuple()
@@ -171,17 +194,7 @@
ssh_proxy = False,
cwd = None,
gitdir = None):
- env = os.environ.copy()
-
- for key in [REPO_TRACE,
- GIT_DIR,
- 'GIT_ALTERNATE_OBJECT_DIRECTORIES',
- 'GIT_OBJECT_DIRECTORY',
- 'GIT_WORK_TREE',
- 'GIT_GRAFT_FILE',
- 'GIT_INDEX_FILE']:
- if key in env:
- del env[key]
+ env = self._GetBasicEnv()
# If we are not capturing std* then need to print it.
self.tee = {'stdout': not capture_stdout, 'stderr': not capture_stderr}
@@ -273,6 +286,23 @@
self.process = p
self.stdin = p.stdin
+ @staticmethod
+ def _GetBasicEnv():
+ """Return a basic env for running git under.
+
+ This is guaranteed to be side-effect free.
+ """
+ env = os.environ.copy()
+ for key in (REPO_TRACE,
+ GIT_DIR,
+ 'GIT_ALTERNATE_OBJECT_DIRECTORIES',
+ 'GIT_OBJECT_DIRECTORY',
+ 'GIT_WORK_TREE',
+ 'GIT_GRAFT_FILE',
+ 'GIT_INDEX_FILE'):
+ env.pop(key, None)
+ return env
+
def Wait(self):
try:
p = self.process