Skip to content

Commit

Permalink
Add patches direct in yaml
Browse files Browse the repository at this point in the history
  • Loading branch information
mt-software-de committed Dec 11, 2023
1 parent 4a706d7 commit 5c9b381
Show file tree
Hide file tree
Showing 5 changed files with 135 additions and 18 deletions.
2 changes: 2 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -343,11 +343,13 @@ Contributors
* Simone Orsi (camptocamp_)
* Artem Kostyuk
* Jan Verbeek
* Michael Tietz (MT_Software_)

.. _ACSONE: https://www.acsone.eu
.. _Tecnativa: https://www.tecnativa.com
.. _camptocamp: https://www.camptocamp.com
.. _LasLabs: https://laslabs.com
.. _MT_Software: https://github.com/mt-software-de

Maintainer
----------
Expand Down
30 changes: 30 additions & 0 deletions git_aggregator/command.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# -*- coding: utf-8 -*-
# © 2015 ACSONE SA/NV
# Copyright 2023 Michael Tietz (MT Software) <[email protected]>
# License AGPLv3 (http://www.gnu.org/licenses/agpl-3.0-standalone.html)
# Parts of the code comes from ANYBOX
# https://github.com/anybox/anybox.recipe.odoo
import subprocess
import logging
from ._compat import console_to_str
logger = logging.getLogger(__name__)


class CommandExecutor(object):
def __init__(self, cwd):
self.cwd = cwd

def log_call(self, cmd, callwith=subprocess.check_call,
log_level=logging.DEBUG, **kw):
"""Wrap a subprocess call with logging
:param meth: the calling method to use.
"""
logger.log(log_level, "%s> call %r", self.cwd, cmd)
try:
ret = callwith(cmd, **kw)
except Exception:
logger.error("%s> error calling %r", self.cwd, cmd)
raise
if callwith == subprocess.check_output:
ret = console_to_str(ret)
return ret
12 changes: 12 additions & 0 deletions git_aggregator/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,22 @@

from .exception import ConfigException
from ._compat import string_types
from .patch import Patches


log = logging.getLogger(__name__)


def update_patches(repo_dict, repo_data):
"""Check and update repo_dict with patch files"""
patches_data = repo_data.get("patches")
patches = repo_dict.setdefault("patches", Patches())
if not patches_data:
return
for patch in patches_data:
patches += Patches.prepare_patches(patch, repo_dict.get("cwd"))


def get_repos(config, force=False):
"""Return a :py:obj:`list` list of repos from config file.
:param config: the repos config in :py:class:`dict` format.
Expand Down Expand Up @@ -128,6 +139,7 @@ def get_repos(config, force=False):
cmds = [cmds]
commands = cmds
repo_dict['shell_command_after'] = commands
update_patches(repo_dict, repo_data)
repo_list.append(repo_dict)
return repo_list

Expand Down
67 changes: 67 additions & 0 deletions git_aggregator/patch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# -*- coding: utf-8 -*-
# Copyright 2023 Michael Tietz (MT Software) <[email protected]>
# License AGPLv3 (http://www.gnu.org/licenses/agpl-3.0-standalone.html)
import logging
from pathlib import Path
import subprocess

from .command import CommandExecutor

logger = logging.getLogger(__name__)


class Patch(CommandExecutor):
is_local = False

def __init__(self, path, cwd):
super().__init__(cwd)
self.path = path
path = Path(path)
if path.exists():
self.is_local = True

def retrive_data(self):
path = self.path
if self.is_local:
patch_path = Path(path).absolute()
path = "FILE:{}".format(str(patch_path))
cmd = [
"curl",
path,
]
if logger.getEffectiveLevel() != logging.DEBUG:
cmd.append('-s')
return self.log_call(
cmd,
callwith=subprocess.Popen,
stdout=subprocess.PIPE
)

def apply(self):
res = self.retrive_data()
cmd = [
"git",
"am",
]
if logger.getEffectiveLevel() != logging.DEBUG:
cmd.append('--quiet')
self.log_call(cmd, cwd=self.cwd, stdin=res.stdout)


class Patches(list):
"""List of patches"""
@staticmethod
def prepare_patches(path, cwd):
_path = Path(path)
patches = Patches()
if not _path.exists() or _path.is_file():
patches.append(Patch(path, cwd))
elif _path.is_dir():
for fpath in _path.iterdir():
if fpath.is_file():
patches.append(Patch(str(fpath), cwd))
return patches

def apply(self):
for patch in self:
patch.apply()
42 changes: 24 additions & 18 deletions git_aggregator/repo.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

from .exception import DirtyException, GitAggregatorException
from ._compat import console_to_str
from .command import CommandExecutor

FETCH_DEFAULTS = ("depth", "shallow-since", "shallow-exclude")
logger = logging.getLogger(__name__)
Expand All @@ -32,13 +33,13 @@ def ishex(s):
return True


class Repo(object):
class Repo(CommandExecutor):

_git_version = None

def __init__(self, cwd, remotes, merges, target,
shell_command_after=None, fetch_all=False, defaults=None,
force=False):
force=False, patches=None):
"""Initialize a git repository aggregator
:param cwd: path to the directory where to initialize the repository
Expand All @@ -58,7 +59,7 @@ def __init__(self, cwd, remotes, merges, target,
:param bool force:
When ``False``, it will stop if repo is dirty.
"""
self.cwd = cwd
super().__init__(cwd)
self.remotes = remotes
if fetch_all is True:
self.fetch_all = frozenset(r["name"] for r in remotes)
Expand All @@ -69,6 +70,7 @@ def __init__(self, cwd, remotes, merges, target,
self.shell_command_after = shell_command_after or []
self.defaults = defaults or dict()
self.force = force
self.patches = patches

@property
def git_version(self):
Expand Down Expand Up @@ -150,21 +152,6 @@ def query_remote_ref(self, remote, ref):
return 'HEAD', sha
return None, ref

def log_call(self, cmd, callwith=subprocess.check_call,
log_level=logging.DEBUG, **kw):
"""Wrap a subprocess call with logging
:param meth: the calling method to use.
"""
logger.log(log_level, "%s> call %r", self.cwd, cmd)
try:
ret = callwith(cmd, **kw)
except Exception:
logger.error("%s> error calling %r", self.cwd, cmd)
raise
if callwith == subprocess.check_output:
ret = console_to_str(ret)
return ret

def aggregate(self):
""" Aggregate all merges into the target branch
If the target_dir doesn't exist, create an empty git repo otherwise
Expand All @@ -189,6 +176,7 @@ def aggregate(self):
self._reset_to(origin["remote"], origin["ref"])
for merge in merges:
self._merge(merge)
self.patches.apply()
self._execute_shell_command_after()
logger.info('End aggregation of %s', self.cwd)

Expand Down Expand Up @@ -315,6 +303,24 @@ def _merge(self, merge):
cmd += self._fetch_options(merge) + (merge["remote"], merge["ref"])
self.log_call(cmd, cwd=self.cwd)

def _patch(self, patch_path):
cmd = (
"patch",
"-p1",
"--no-backup-if-mismatch",
"-t",
"-i",
str(patch_path.resolve()),
)
if logger.getEffectiveLevel() != logging.DEBUG:
cmd += ('--quiet',)
self.log_call(cmd, cwd=self.cwd)
self.log_call(("git", "add", "."), cwd=self.cwd)
self.log_call(
("git", "commit", "-am", "Applied patch %s" % str(patch_path)),
cwd=self.cwd,
)

def _get_remotes(self):
lines = self.log_call(
['git', 'remote', '-v'],
Expand Down

0 comments on commit 5c9b381

Please sign in to comment.