mirror of
https://github.com/bringout/oca-ocb-core.git
synced 2026-04-20 10:52:02 +02:00
18.0 vanilla
This commit is contained in:
parent
d72e748793
commit
0a7ae8db93
337 changed files with 399651 additions and 232598 deletions
231
odoo-bringout-oca-ocb-base/odoo/cli/upgrade_code.py
Executable file
231
odoo-bringout-oca-ocb-base/odoo/cli/upgrade_code.py
Executable file
|
|
@ -0,0 +1,231 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Rewrite the entire source code using the scripts found at
|
||||
/odoo/upgrade_code
|
||||
|
||||
Each script is named {version}-{name}.py and exposes an upgrade function
|
||||
that takes a single argument, the file_manager, and returns nothing.
|
||||
|
||||
The file_manager acts as a list of files, files have 3 attributes:
|
||||
* path: the pathlib.Path where the file is on the file system;
|
||||
* addon: the odoo addon in which the file is;
|
||||
* content: the re-writtable content of the file (lazy).
|
||||
|
||||
There are additional utilities on the file_manager, such as:
|
||||
* print_progress(current, total)
|
||||
|
||||
Example:
|
||||
|
||||
def upgrade(file_manager):
|
||||
files = [f for f in file_manager if f.path.suffix == '.py']
|
||||
for fileno, file in enumerate(files, start=1):
|
||||
file.content = file.content.replace(..., ...)
|
||||
file_manager.print_progress(fileno, len(files))
|
||||
|
||||
The command line offers a way to select and run those scripts.
|
||||
|
||||
Please note that all the scripts are doing a best-effort a migrating the
|
||||
source code, they only help do the heavy-lifting, they are not silver
|
||||
bullets.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
from importlib.machinery import SourceFileLoader
|
||||
from pathlib import Path
|
||||
from types import ModuleType
|
||||
from typing import Iterator
|
||||
|
||||
ROOT = Path(__file__).parent.parent
|
||||
|
||||
try:
|
||||
import odoo.addons
|
||||
from . import Command
|
||||
from odoo import release
|
||||
from odoo.modules import initialize_sys_path
|
||||
from odoo.tools import config, parse_version
|
||||
except ImportError:
|
||||
# Assume the script is directy executed (by opposition to be
|
||||
# executed via odoo-bin), happily release/parse_version are
|
||||
# standalone so we can hack our way there without importing odoo
|
||||
sys.path.insert(0, str(ROOT))
|
||||
sys.path.insert(0, str(ROOT / 'tools'))
|
||||
import release
|
||||
from parse_version import parse_version
|
||||
class Command:
|
||||
pass
|
||||
config = {'addons_path': ''}
|
||||
initialize_sys_path = None
|
||||
|
||||
|
||||
UPGRADE = ROOT / 'upgrade_code'
|
||||
AVAILABLE_EXT = ('.py', '.js', '.css', '.scss', '.xml', '.csv')
|
||||
|
||||
|
||||
class FileAccessor:
|
||||
addon: Path
|
||||
path: Path
|
||||
content: str
|
||||
|
||||
def __init__(self, path: Path, addon_path: Path) -> None:
|
||||
self.path = path
|
||||
self.addon = addon_path / path.relative_to(addon_path).parts[0]
|
||||
self._content = None
|
||||
self.dirty = False
|
||||
|
||||
@property
|
||||
def content(self):
|
||||
if self._content is None:
|
||||
self._content = self.path.read_text()
|
||||
return self._content
|
||||
|
||||
@content.setter
|
||||
def content(self, value):
|
||||
if self._content != value:
|
||||
self._content = value
|
||||
self.dirty = True
|
||||
|
||||
|
||||
class FileManager:
|
||||
addons_path: list[str]
|
||||
glob: str
|
||||
|
||||
def __init__(self, addons_path: list[str], glob: str = '**/*') -> None:
|
||||
self.addons_path = addons_path
|
||||
self.glob = glob
|
||||
self._files = {
|
||||
str(path): FileAccessor(path, Path(addon_path))
|
||||
for addon_path in addons_path
|
||||
for path in Path(addon_path).glob(glob)
|
||||
if '__pycache__' not in path.parts
|
||||
if path.suffix in AVAILABLE_EXT
|
||||
if path.is_file()
|
||||
}
|
||||
|
||||
def __iter__(self) -> Iterator[FileAccessor]:
|
||||
return iter(self._files.values())
|
||||
|
||||
def __len__(self):
|
||||
return len(self._files)
|
||||
|
||||
def get_file(self, path):
|
||||
return self._files.get(str(path))
|
||||
|
||||
if sys.stdout.isatty():
|
||||
def print_progress(self, current, total=None):
|
||||
total = total or len(self) or 1
|
||||
print(f'{current / total:>4.0%}', end='\r', file=sys.stderr) # noqa: T201
|
||||
else:
|
||||
def print_progress(self, current, total=None):
|
||||
pass
|
||||
|
||||
|
||||
def get_upgrade_code_scripts(from_version: tuple[int, ...], to_version: tuple[int, ...]) -> list[tuple[str, ModuleType]]:
|
||||
modules: list[tuple[str, ModuleType]] = []
|
||||
for script_path in sorted(UPGRADE.glob('*.py')):
|
||||
version = parse_version(script_path.name.partition('-')[0])
|
||||
if from_version <= version <= to_version:
|
||||
module = SourceFileLoader(script_path.name, str(script_path)).load_module()
|
||||
modules.append((script_path.name, module))
|
||||
return modules
|
||||
|
||||
|
||||
def migrate(
|
||||
addons_path: list[str],
|
||||
glob: str,
|
||||
from_version: tuple[int, ...] | None = None,
|
||||
to_version: tuple[int, ...] | None = None,
|
||||
script: str | None = None,
|
||||
dry_run: bool = False,
|
||||
):
|
||||
if script:
|
||||
script_path = next(UPGRADE.glob(f'*{script.removesuffix(".py")}*.py'), None)
|
||||
if not script_path:
|
||||
raise FileNotFoundError(script)
|
||||
script_path.relative_to(UPGRADE) # safeguard, prevent going up
|
||||
module = SourceFileLoader(script_path.name, str(script_path)).load_module()
|
||||
modules = [(script_path.name, module)]
|
||||
else:
|
||||
modules = get_upgrade_code_scripts(from_version, to_version)
|
||||
|
||||
file_manager = FileManager(addons_path, glob)
|
||||
for (name, module) in modules:
|
||||
file_manager.print_progress(0) # 0%
|
||||
module.upgrade(file_manager)
|
||||
file_manager.print_progress(len(file_manager)) # 100%
|
||||
|
||||
for file in file_manager:
|
||||
if file.dirty:
|
||||
print(file.path) # noqa: T201
|
||||
if not dry_run:
|
||||
with file.path.open("w") as f:
|
||||
f.write(file.content)
|
||||
|
||||
return any(file.dirty for file in file_manager)
|
||||
|
||||
|
||||
class UpgradeCode(Command):
|
||||
""" Rewrite the entire source code using the scripts found at /odoo/upgrade_code """
|
||||
name = 'upgrade_code'
|
||||
prog_name = Path(sys.argv[0]).name
|
||||
|
||||
def __init__(self):
|
||||
self.parser = argparse.ArgumentParser(
|
||||
prog=(
|
||||
f"{self.prog_name} [--addons-path=PATH,...] {self.name}"
|
||||
if initialize_sys_path else
|
||||
self.prog_name
|
||||
),
|
||||
description=__doc__.replace('/odoo/upgrade_code', str(UPGRADE)),
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
)
|
||||
group = self.parser.add_mutually_exclusive_group(required=True)
|
||||
group.add_argument(
|
||||
'--script',
|
||||
metavar='NAME',
|
||||
help="run this single script")
|
||||
group.add_argument(
|
||||
'--from',
|
||||
dest='from_version',
|
||||
type=parse_version,
|
||||
metavar='VERSION',
|
||||
help="run all scripts starting from this version, inclusive")
|
||||
self.parser.add_argument(
|
||||
'--to',
|
||||
dest='to_version',
|
||||
type=parse_version,
|
||||
default=parse_version(release.version),
|
||||
metavar='VERSION',
|
||||
help=f"run all scripts until this version, inclusive (default: {release.version})")
|
||||
self.parser.add_argument(
|
||||
'--glob',
|
||||
default='**/*',
|
||||
help="select the files to rewrite (default: %(default)s)")
|
||||
self.parser.add_argument(
|
||||
'--dry-run',
|
||||
action='store_true',
|
||||
help="list the files that would be re-written, but rewrite none")
|
||||
self.parser.add_argument(
|
||||
'--addons-path',
|
||||
default=config['addons_path'],
|
||||
metavar='PATH,...',
|
||||
help="specify additional addons paths (separated by commas)",
|
||||
)
|
||||
|
||||
def run(self, cmdargs):
|
||||
options = self.parser.parse_args(cmdargs)
|
||||
if initialize_sys_path:
|
||||
config['addons_path'] = options.addons_path
|
||||
initialize_sys_path()
|
||||
options.addons_path = odoo.addons.__path__
|
||||
else:
|
||||
options.addons_path = [p for p in options.addons_path.split(',') if p]
|
||||
if not options.addons_path:
|
||||
self.parser.error("--addons-path is required when used standalone")
|
||||
is_dirty = migrate(**vars(options))
|
||||
sys.exit(int(is_dirty))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
UpgradeCode().run(sys.argv[1:])
|
||||
Loading…
Add table
Add a link
Reference in a new issue