1 # Copyright 2020 The Pigweed Authors
3 # Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 # use this file except in compliance with the License. You may obtain a copy of
7 # https://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 # License for the specific language governing permissions and limitations under
14 """Install and check status of Git repository-based packages."""
20 from typing import Union
23 import pw_package.package_manager
25 PathOrStr = Union[pathlib.Path, str]
28 def git_stdout(*args: PathOrStr,
30 repo: PathOrStr = '.') -> str:
31 return subprocess.run(['git', '-C', repo, *args],
32 stdout=subprocess.PIPE,
33 stderr=None if show_stderr else subprocess.DEVNULL,
34 check=True).stdout.decode().strip()
37 def git(*args: PathOrStr,
38 repo: PathOrStr = '.') -> subprocess.CompletedProcess:
39 return subprocess.run(['git', '-C', repo, *args], check=True)
42 class GitRepo(pw_package.package_manager.Package):
43 """Install and check status of Git repository-based packages."""
44 def __init__(self, url, commit, *args, **kwargs):
45 super().__init__(*args, **kwargs)
49 def status(self, path: pathlib.Path) -> bool:
50 if not os.path.isdir(path / '.git'):
53 remote = git_stdout('remote', 'get-url', 'origin', repo=path)
54 url = urllib.parse.urlparse(remote)
55 if url.scheme == 'sso' or '.git.corp.google.com' in url.netloc:
56 host = url.netloc.replace(
57 '.git.corp.google.com',
60 if not host.endswith('.googlesource.com'):
61 host += '.googlesource.com'
62 remote = 'https://{}{}'.format(host, url.path)
64 commit = git_stdout('rev-parse', 'HEAD', repo=path)
65 status = git_stdout('status', '--porcelain=v1', repo=path)
66 return remote == self._url and commit == self._commit and not status
68 def install(self, path: pathlib.Path) -> None:
69 # If already installed and at correct version exit now.
73 # Otherwise delete current version and clone again.
74 if os.path.isdir(path):
77 # --filter=blob:none means we don't get history, just the current
78 # revision. If we later run commands that need history it will be
79 # retrieved on-demand. For small repositories the effect is negligible
80 # but for large repositories this should be a significant improvement.
81 git('clone', '--filter=blob:none', self._url, path)
82 git('reset', '--hard', self._commit, repo=path)