sync/repos.py (124 lines of code) (raw):
import abc
import git
import json
import os
import pygit2
import subprocess
import shutil
from . import log
from typing import Any, Dict, Optional, Union
from git.repo.base import Repo
from git.objects.commit import Commit
from pygit2.repository import Repository
logger = log.get_logger(__name__)
wrapper_map = {}
pygit2_map = {}
cinnabar_map = {}
class GitSettings(metaclass=abc.ABCMeta):
name: str = ""
cinnabar = False
def __init__(self, config: Dict[str, Any]) -> None:
self.config = config
@property
def root(self) -> str:
return os.path.join(self.config["repo_root"],
self.config["paths"]["repos"],
self.name)
@property
def remotes(self):
return self.config[self.name]["repo"]["remote"].items()
def repo(self) -> Repo:
repo = git.Repo(self.root)
wrapper_map[repo] = self
pygit2_map[repo] = pygit2.Repository(repo.git_dir)
logger.debug("Existing repo found at " + self.root)
if self.cinnabar:
cinnabar_map[repo] = Cinnabar(repo)
self.setup(repo)
return repo
def setup(self, repo: Repo) -> None:
pass
def configure(self, file):
if not os.path.exists(self.root):
os.makedirs(self.root)
git.Repo.init(self.root, bare=True)
r = self.repo()
shutil.copyfile(file, os.path.normpath(os.path.join(r.git_dir, "config")))
logger.debug("Config from {} copied to {}".format(file, os.path.join(r.git_dir, "config")))
def after_worktree_create(self, path: str) -> None:
pass
def after_worktree_delete(self, path: str) -> None:
pass
class Gecko(GitSettings):
name = "gecko"
cinnabar = True
fetch_args = ["mozilla"]
def setup(self, repo):
data_ref = git.Reference(repo, self.config["sync"]["ref"])
if not data_ref.is_valid():
from . import base
with base.CommitBuilder(repo, "Create initial sync metadata",
ref=data_ref.path) as commit:
path = "_metadata"
data = json.dumps({"name": "wptsync"})
commit.add_tree({path: data})
from . import index
for idx in index.indicies:
idx.get_or_create(repo)
@staticmethod
def get_state_path(config: Dict[str, Any], path: str) -> str:
return os.path.join(config["root"],
config["paths"]["state"],
os.path.relpath(path, config["root"]))
def after_worktree_create(self, path: str) -> None:
from sync.projectutil import Mach
state_path = self.get_state_path(self.config, path)
if not os.path.exists(state_path):
os.makedirs(state_path)
mach = Mach(path)
# create-mach-environment no longer exists, but keep trying
# to run it in case we have an old tree
try:
mach.create_mach_environment()
except subprocess.CalledProcessError:
pass
def after_worktree_delete(self, path: str) -> None:
state_path = self.get_state_path(self.config, path)
if os.path.exists(state_path):
shutil.rmtree(state_path)
class WebPlatformTests(GitSettings):
name = "web-platform-tests"
fetch_args = ["origin", "master", "--no-tags"]
class WptMetadata(GitSettings):
name = "wpt-metadata"
fetch_args = ["origin", "master", "--no-tags"]
class Cinnabar:
hg2git_cache: Dict[str, str] = {}
git2hg_cache: Dict[str, str] = {}
def __init__(self, repo):
self.git = repo.git
def hg2git(self, rev: str) -> str:
if rev not in self.hg2git_cache:
value = self.git.cinnabar("hg2git", rev)
if all(c == "0" for c in value):
raise ValueError("No git rev corresponding to hg rev %s" % rev)
self.hg2git_cache[rev] = value
return self.hg2git_cache[rev]
def git2hg(self, rev: Union[str, Commit]) -> str:
if rev not in self.git2hg_cache:
value = self.git.cinnabar("git2hg", rev)
if all(c == "0" for c in value):
raise ValueError("No hg rev corresponding to git rev %s" % rev)
self.git2hg_cache[rev] = value
return self.git2hg_cache[rev]
wrappers = {
"gecko": Gecko,
"web-platform-tests": WebPlatformTests,
"wpt-metadata": WptMetadata,
}
def pygit2_get(repo: Repo) -> Repository:
if repo not in pygit2_map:
pygit2_map[repo] = pygit2.Repository(repo.git_dir)
return pygit2_map[repo]
def wrapper_get(repo: Repo) -> Optional[GitSettings]:
return wrapper_map.get(repo)
def cinnabar(repo: Repo) -> Cinnabar:
return cinnabar_map[repo]