1
0
mirror of https://github.com/openSUSE/osc.git synced 2025-02-21 01:32:10 +01:00

207 lines
7.0 KiB
Python

import json
import os
import subprocess
import urllib.parse
from pathlib import Path
from .. import conf as osc_conf
from .. import oscerr
class GitStore:
@classmethod
def is_project_dir(cls, path):
try:
store = cls(path)
except oscerr.NoWorkingCopy:
return False
return store.is_project
@classmethod
def is_package_dir(cls, path):
try:
store = cls(path)
except oscerr.NoWorkingCopy:
return False
return store.is_package
def __init__(self, path, check=True):
self.path = path
self.abspath = os.path.abspath(self.path)
try:
self.toplevel = self._run_git(["rev-parse", "--show-toplevel"])
self.toplevel = os.path.abspath(self.toplevel)
except subprocess.CalledProcessError:
self.toplevel = None
# TODO: how to determine if the current git repo contains a project or a package?
self.is_project = False
self.is_package = False
if self.toplevel:
# NOTE: we have only one store in project-git for all packages
config_path = os.path.join(self.toplevel, "_config")
pbuild_path = os.path.join(self.toplevel, "_pbuild")
if self.toplevel == self.abspath and (os.path.isfile(config_path) or os.path.isfile(pbuild_path)):
self.is_project = True
self.is_package = False
else:
self.is_project = False
self.is_package = True
self._package = None
self._project = None
if check and not any([self.is_project, self.is_package]):
msg = f"Directory '{self.path}' is not a Git SCM working copy"
raise oscerr.NoWorkingCopy(msg)
if check and not self.scmurl:
msg = f"Directory '{self.path}' is a Git SCM repo that lacks the 'origin' remote"
raise oscerr.NoWorkingCopy(msg)
# TODO: decide if we need explicit 'git lfs pull' or not
# self._run_git(["lfs", "pull"])
def assert_is_project(self):
if not self.is_project:
msg = f"Directory '{self.path}' is not a Git SCM working copy of a project"
raise oscerr.NoWorkingCopy(msg)
def assert_is_package(self):
if not self.is_package:
msg = f"Directory '{self.path}' is not a Git SCM working copy of a package"
raise oscerr.NoWorkingCopy(msg)
def _run_git(self, args):
return subprocess.check_output(["git"] + args, encoding="utf-8", cwd=self.abspath).strip()
@property
def apiurl(self):
from ..obs_scm.store import Store
# read apiurl from the current directory with .osc metadata
# NOTE: this never triggers if a store is retrieved from osc.store.get_store(),
# because obs_scm store takes precedence as .osc is present
store = Store(self.toplevel, check=False)
if store.apiurl:
return store.apiurl
# read project from parent directory that contains a project with .osc metadata
store = Store(os.path.join(self.toplevel, ".."), check=False)
if store.is_project and store.apiurl:
return store.apiurl
# HACK: we're using the currently configured apiurl
return osc_conf.config["apiurl"]
@property
def project(self):
from ..obs_scm.store import Store
# read project from the current directory with .osc metadata
# NOTE: this never triggers if a store is retrieved from osc.store.get_store(),
# because obs_scm store takes precedence as .osc is present
if self._project is None:
store = Store(self.toplevel, check=False)
if store.project:
self._project = store.project
# read project from parent directory that contains a project with .osc metadata
if self._project is None:
store = Store(os.path.join(self.toplevel, ".."), check=False)
if store.is_project and store.project:
self._project = store.project
# guess project from git branch
if self._project is None:
# get project from the branch name
branch = self._run_git(["branch", "--show-current"])
# HACK: replace hard-coded mapping with metadata from git or the build service
# NOTE: you never know which git repo is supposed to be used in which project
if branch == "factory":
self._project = "openSUSE:Factory"
else:
raise oscerr.NoWorkingCopy(f"Couldn't map git branch '{branch}' to a project")
return self._project
@project.setter
def project(self, value):
self._project = value
@property
def package(self):
if self._package is None:
origin = self._run_git(["remote", "get-url", "origin"])
self._package = Path(urllib.parse.urlsplit(origin).path).stem
return self._package
@package.setter
def package(self, value):
self._package = value
def _get_option(self, name):
try:
result = self._run_git(["config", "--local", "--get", f"osc.{name}"])
except subprocess.CalledProcessError:
result = None
return result
def _check_type(self, name, value, expected_type):
if not isinstance(value, expected_type):
raise TypeError(f"The option '{name}' should be {expected_type.__name__}, not {type(value).__name__}")
def _set_option(self, name, value):
self._run_git(["config", "--local", f"osc.{name}", value])
def _unset_option(self, name):
try:
self._run_git(["config", "--local", "--unset", f"osc.{name}"])
except subprocess.CalledProcessError:
pass
def _get_dict_option(self, name):
result = self._get_option(name)
if result is None:
return None
result = json.loads(result)
self._check_type(name, result, dict)
return result
def _set_dict_option(self, name, value):
if value is None:
self._unset_option(name)
return
self._check_type(name, value, dict)
value = json.dumps(value)
self._set_option(name, value)
@property
def last_buildroot(self):
self.assert_is_package()
result = self._get_dict_option("last-buildroot")
if result is not None:
result = (result["repo"], result["arch"], result["vm_type"])
return result
@last_buildroot.setter
def last_buildroot(self, value):
self.assert_is_package()
if len(value) != 3:
raise ValueError("A tuple with exactly 3 items is expected: (repo, arch, vm_type)")
value = {
"repo": value[0],
"arch": value[1],
"vm_type": value[2],
}
self._set_dict_option("last-buildroot", value)
@property
def scmurl(self):
try:
return self._run_git(["remote", "get-url", "origin"])
except subprocess.CalledProcessError:
return None