Work in Progress
This commit is contained in:
parent
d64140487f
commit
e81e22e91b
5
.gitignore
vendored
5
.gitignore
vendored
|
|
@ -1,2 +1,7 @@
|
||||||
env
|
env
|
||||||
__pycache__
|
__pycache__
|
||||||
|
.tox
|
||||||
|
.coverage
|
||||||
|
.pytest_cache
|
||||||
|
/tests/output/*
|
||||||
|
log/
|
||||||
|
|
|
||||||
61
.gitlab-ci.yml
Normal file
61
.gitlab-ci.yml
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
---
|
||||||
|
image: python
|
||||||
|
|
||||||
|
cache:
|
||||||
|
key: one-key-to-rule-them-all
|
||||||
|
paths:
|
||||||
|
- .venv
|
||||||
|
|
||||||
|
stages:
|
||||||
|
- prepare
|
||||||
|
- linting
|
||||||
|
- tests
|
||||||
|
|
||||||
|
|
||||||
|
install_tools:
|
||||||
|
stage: prepare
|
||||||
|
script:
|
||||||
|
- python -m venv .venv
|
||||||
|
- source .venv/bin/activate
|
||||||
|
- pip install poetry
|
||||||
|
- poetry --version
|
||||||
|
- poetry install -v
|
||||||
|
- echo $PATH
|
||||||
|
- echo $PYTHONPATH
|
||||||
|
|
||||||
|
black:
|
||||||
|
stage: linting
|
||||||
|
script:
|
||||||
|
- source .venv/bin/activate
|
||||||
|
- pip install black
|
||||||
|
- black --version
|
||||||
|
- black --check .
|
||||||
|
|
||||||
|
pylint:
|
||||||
|
stage: linting
|
||||||
|
script:
|
||||||
|
- source .venv/bin/activate
|
||||||
|
- pylint --version
|
||||||
|
# - pylint --disable fixme ociedoo
|
||||||
|
# - pylint --disable fixme tests
|
||||||
|
|
||||||
|
pytest:
|
||||||
|
stage: tests
|
||||||
|
image: python
|
||||||
|
cache: {}
|
||||||
|
script:
|
||||||
|
- pip install poetry
|
||||||
|
- poetry --version
|
||||||
|
- poetry install -v
|
||||||
|
- poetry run pytest --version
|
||||||
|
- poetry run pytest --cov odoo_openupgrade_wizard -v
|
||||||
|
|
||||||
|
tox:
|
||||||
|
stage: tests
|
||||||
|
image: themattrix/tox
|
||||||
|
cache: {}
|
||||||
|
script:
|
||||||
|
- pip install poetry tox
|
||||||
|
- tox --version
|
||||||
|
- poetry --version
|
||||||
|
- tox
|
||||||
36
.pre-commit-config.yaml
Normal file
36
.pre-commit-config.yaml
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
---
|
||||||
|
# See https://pre-commit.com for more information
|
||||||
|
# See https://pre-commit.com/hooks.html for more hooks
|
||||||
|
repos:
|
||||||
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
|
rev: v3.4.0
|
||||||
|
hooks:
|
||||||
|
- id: trailing-whitespace
|
||||||
|
- id: end-of-file-fixer
|
||||||
|
- id: debug-statements
|
||||||
|
- id: mixed-line-ending
|
||||||
|
- id: name-tests-test
|
||||||
|
- id: check-yaml
|
||||||
|
- id: check-json
|
||||||
|
- id: check-toml
|
||||||
|
- id: check-added-large-files
|
||||||
|
args: ['--maxkb=2048']
|
||||||
|
- id: check-docstring-first
|
||||||
|
- id: check-merge-conflict
|
||||||
|
- id: check-symlinks
|
||||||
|
- repo: https://github.com/pre-commit/mirrors-isort
|
||||||
|
rev: v5.7.0
|
||||||
|
hooks:
|
||||||
|
- id: isort
|
||||||
|
- repo: https://github.com/psf/black
|
||||||
|
rev: 20.8b1
|
||||||
|
hooks:
|
||||||
|
- id: black
|
||||||
|
- repo: https://github.com/PyCQA/flake8
|
||||||
|
rev: "3.9.2"
|
||||||
|
hooks:
|
||||||
|
- id: flake8
|
||||||
|
# - repo: https://gitlab.com/smop/pre-commit-hooks
|
||||||
|
# rev: v1.0.0
|
||||||
|
# hooks:
|
||||||
|
# - id: check-gitlab-ci
|
||||||
81
README.md
81
README.md
|
|
@ -1,9 +1,82 @@
|
||||||
# odoo-openupgrade-wizard
|
# odoo-openupgrade-wizard
|
||||||
|
|
||||||
|
Odoo Openupgrade Wizard is a tool that helps developpers to make major
|
||||||
|
upgrade of Odoo Community Edition. (formely OpenERP).
|
||||||
|
It works with Openupgrade OCA tools. (https://github.com/oca/openupgrade)
|
||||||
|
|
||||||
|
this tool is useful for complex migrations:
|
||||||
|
- skip several versions
|
||||||
|
- complex custom code
|
||||||
|
|
||||||
|
It will create a migration environment (with all the code available)
|
||||||
|
and provides helpers to run (and replay) migrations until it works.
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
### ``odoo-openupgrade-wizard init``
|
||||||
|
|
||||||
|
```
|
||||||
|
odoo-openupgrade-wizard init\
|
||||||
|
--initial-version=10.0\
|
||||||
|
--final-version=12.0\
|
||||||
|
--extra-repository=OCA/web,OCA/server-tools
|
||||||
|
```
|
||||||
|
|
||||||
|
Initialize a folder to make a migration from a 10.0 and a 12.0 database.
|
||||||
|
This will generate the following structure :
|
||||||
|
|
||||||
|
```
|
||||||
|
config.yml
|
||||||
|
log/
|
||||||
|
2022_03_25__23_12_41__init.log
|
||||||
|
...
|
||||||
|
repos/
|
||||||
|
10.0.yml
|
||||||
|
11.0.yml
|
||||||
|
12.0.yml
|
||||||
|
requirements/
|
||||||
|
10.0_requirements.txt
|
||||||
|
11.0_requirements.txt
|
||||||
|
12.0_requirements.txt
|
||||||
|
scripts/
|
||||||
|
step_1__update__10.0/
|
||||||
|
pre-migration.sql
|
||||||
|
post-migration.py
|
||||||
|
step_2__upgrade__11.0/
|
||||||
|
pre-migration.sql
|
||||||
|
post-migration.py
|
||||||
|
step_2__upgrade__12.0/
|
||||||
|
pre-migration.sql
|
||||||
|
post-migration.py
|
||||||
|
step_4__update__12.0/
|
||||||
|
pre-migration.sql
|
||||||
|
post-migration.py
|
||||||
|
src/
|
||||||
|
```
|
||||||
|
|
||||||
|
* ``log/`` will contains all the log of the ``odoo-openupgrade-wizard``
|
||||||
|
and the logs of the odoo instance that will be executed.
|
||||||
|
|
||||||
|
|
||||||
|
* ``repos/`` contains a file per version of odoo, that enumerates the
|
||||||
|
list of the repositories to use to run each odoo instance.
|
||||||
|
The syntax should respect the ``gitaggregate`` command.
|
||||||
|
(See : https://pypi.org/project/git-aggregator/)
|
||||||
|
Repo files are pre-generated. You can update them with your custom settings.
|
||||||
|
(custom branches, extra PRs, git shallow options, etc...)
|
||||||
|
|
||||||
|
* ``requirements/`` contains a file per version of odoo, that enumerates the
|
||||||
|
list of extra python librairies required to run each odoo instance.
|
||||||
|
The syntax should respect the ``pip install -r`` command.
|
||||||
|
(See : https://pip.pypa.io/en/stable/reference/requirements-file-format/)
|
||||||
|
|
||||||
|
* ``scripts`` contains a folder per migration step. In each step folder:
|
||||||
|
- ``pre-migration.sql`` can contains extra SQL queries you want to execute
|
||||||
|
before beginning the step.
|
||||||
|
- ``post-migration.py`` can contains extra python command to execute
|
||||||
|
after the execution of the step. (the orm will be available)
|
||||||
|
|
||||||
# TODO
|
# TODO
|
||||||
|
|
||||||
* restore ``# @click.version_option(version=ociedoo.__version__)`` when we know how
|
* with coop it easy :
|
||||||
to avoid to duplicate information in ``pyproject.toml`` file and ``__init__.py`` file.
|
- short_help of group decorator ? seems useless...
|
||||||
|
|
||||||
* move jinja2 templates files into dedicated files.
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from single_source import get_version
|
||||||
|
|
||||||
|
__version__ = get_version(__name__, Path(__file__).parent.parent)
|
||||||
|
|
@ -1,47 +1,45 @@
|
||||||
import datetime
|
import datetime
|
||||||
# import os
|
|
||||||
# import signal
|
|
||||||
# import subprocess
|
|
||||||
# import time
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from plumbum.cmd import mkdir
|
|
||||||
from jinja2 import Template
|
|
||||||
|
|
||||||
import time
|
|
||||||
import click
|
import click
|
||||||
|
import yaml
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
from plumbum.cmd import mkdir
|
||||||
|
|
||||||
import odoo_openupgrade_wizard
|
import odoo_openupgrade_wizard
|
||||||
from odoo_openupgrade_wizard.configuration_version_dependant import (
|
from odoo_openupgrade_wizard.cli_build import build
|
||||||
_get_odoo_version_str_list,
|
from odoo_openupgrade_wizard.cli_init import init
|
||||||
_get_odoo_versions,
|
|
||||||
)
|
|
||||||
from odoo_openupgrade_wizard.templates import (
|
|
||||||
_CONFIG_YML_TEMPLATE,
|
|
||||||
_REPO_YML_TEMPLATE,
|
|
||||||
_REQUIREMENTS_TXT_TEMPLATE,
|
|
||||||
_PRE_MIGRATION_SQL_TEMPLATE,
|
|
||||||
_POST_MIGRATION_PY_TEMPLATE,
|
|
||||||
)
|
|
||||||
|
|
||||||
# ############################################################################
|
|
||||||
# main()
|
@click.group()
|
||||||
# ############################################################################
|
@click.version_option(version=odoo_openupgrade_wizard.__version__)
|
||||||
@click.group(
|
@click.option(
|
||||||
short_help="Provide a wizard to simplify the use of OpenUpgrade.",
|
"-ef",
|
||||||
|
"--env-folder",
|
||||||
|
default="./",
|
||||||
|
type=click.Path(
|
||||||
|
exists=True,
|
||||||
|
dir_okay=True,
|
||||||
|
file_okay=False,
|
||||||
|
writable=True,
|
||||||
|
resolve_path=True,
|
||||||
|
),
|
||||||
|
help="Folder that will contains all the configuration of the wizard"
|
||||||
|
" and all the Odoo code required to make the migrations. Let empty to"
|
||||||
|
" use current folder (./).",
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
"-fs",
|
||||||
|
"--filestore-folder",
|
||||||
|
type=click.Path(dir_okay=True, file_okay=False, resolve_path=True),
|
||||||
|
help="Folder that contains the Odoo filestore of the database(s)"
|
||||||
|
" to migrate. Let empty to use the subfolder 'filestore' of the"
|
||||||
|
" environment folder.",
|
||||||
)
|
)
|
||||||
|
|
||||||
@click.option("-ef", "--env-folder", required=True,
|
|
||||||
type=click.Path(exists=True, dir_okay=True, file_okay=False, writable=True,
|
|
||||||
resolve_path=True))
|
|
||||||
@click.option("-fs", "--filestore-folder",
|
|
||||||
type=click.Path(dir_okay=True, file_okay=False,
|
|
||||||
resolve_path=True))
|
|
||||||
|
|
||||||
@click.pass_context
|
@click.pass_context
|
||||||
def main(ctx, env_folder, filestore_folder):
|
def main(ctx, env_folder, filestore_folder):
|
||||||
"""
|
"""
|
||||||
TODO
|
Provides a command set to perform odoo Community Edition migrations.
|
||||||
"""
|
"""
|
||||||
date_begin = datetime.datetime.now()
|
date_begin = datetime.datetime.now()
|
||||||
logger.debug("Beginning script '%s' ..." % (ctx.invoked_subcommand))
|
logger.debug("Beginning script '%s' ..." % (ctx.invoked_subcommand))
|
||||||
|
|
@ -72,8 +70,11 @@ def main(ctx, env_folder, filestore_folder):
|
||||||
mkdir(["--mode", "777", log_folder_path])
|
mkdir(["--mode", "777", log_folder_path])
|
||||||
|
|
||||||
# Create log file
|
# Create log file
|
||||||
log_file_path = log_folder_path / Path("{}__{}.log".format(
|
log_file_path = log_folder_path / Path(
|
||||||
date_begin.strftime("%Y_%m_%d__%H_%M_%S"), ctx.invoked_subcommand))
|
"{}__{}.log".format(
|
||||||
|
date_begin.strftime("%Y_%m_%d__%H_%M_%S"), ctx.invoked_subcommand
|
||||||
|
)
|
||||||
|
)
|
||||||
logger.add(log_file_path)
|
logger.add(log_file_path)
|
||||||
ctx.obj["env_folder_path"] = env_folder_path
|
ctx.obj["env_folder_path"] = env_folder_path
|
||||||
ctx.obj["src_folder_path"] = src_folder_path
|
ctx.obj["src_folder_path"] = src_folder_path
|
||||||
|
|
@ -82,133 +83,18 @@ def main(ctx, env_folder, filestore_folder):
|
||||||
ctx.obj["filestore_folder_path"] = filestore_folder_path
|
ctx.obj["filestore_folder_path"] = filestore_folder_path
|
||||||
ctx.obj["config_file_path"] = config_file_path
|
ctx.obj["config_file_path"] = config_file_path
|
||||||
ctx.obj["requirement_folder_path"] = requirement_folder_path
|
ctx.obj["requirement_folder_path"] = requirement_folder_path
|
||||||
# logger.info(ctx.obj)
|
|
||||||
|
if config_file_path.exists():
|
||||||
|
with open(config_file_path) as file:
|
||||||
|
config = yaml.safe_load(file)
|
||||||
|
for step in config["migration_steps"]:
|
||||||
|
step["local_path"] = src_folder_path / Path(
|
||||||
|
"env_%s" % step["version"]
|
||||||
|
)
|
||||||
|
ctx.obj["config"] = config
|
||||||
|
elif ctx.invoked_subcommand != "init":
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
# ############################################################################
|
main.add_command(init)
|
||||||
# init()
|
main.add_command(build)
|
||||||
# ############################################################################
|
|
||||||
@main.command(
|
|
||||||
short_help="Initialize OpenUpgrade Wizard Environment.",
|
|
||||||
)
|
|
||||||
@click.option("-iv", "--initial-version", required=True, prompt=True,
|
|
||||||
type=click.Choice(_get_odoo_version_str_list("initial")))
|
|
||||||
@click.option("-fv", "--final-version", required=True, prompt=True,
|
|
||||||
type=click.Choice(_get_odoo_version_str_list("final")))
|
|
||||||
@click.option("-er", "--extra-repository", "extra_repository_list",
|
|
||||||
# TODO, add a callback to check the quality of the argument
|
|
||||||
help="Coma separated extra repositories to use in the odoo environment."
|
|
||||||
"Ex: 'OCA/web,OCA/server-tools,GRAP/grap-odoo-incubator'")
|
|
||||||
@click.pass_context
|
|
||||||
def init(ctx, initial_version, final_version, extra_repository_list):
|
|
||||||
"""
|
|
||||||
TODO
|
|
||||||
"""
|
|
||||||
# 1. create src directory if not exists
|
|
||||||
if not ctx.obj["src_folder_path"].exists():
|
|
||||||
logger.info("Creating folder '%s' ..." % (ctx.obj["src_folder_path"]))
|
|
||||||
mkdir(["--mode", "777", ctx.obj["src_folder_path"]])
|
|
||||||
|
|
||||||
# 2. create filestore directory if not exists
|
|
||||||
if not ctx.obj["filestore_folder_path"].exists():
|
|
||||||
logger.info("Creating folder '%s' ..." % (ctx.obj["filestore_folder_path"]))
|
|
||||||
mkdir(["--mode", "777", ctx.obj["filestore_folder_path"]])
|
|
||||||
|
|
||||||
# 3. Create main config file
|
|
||||||
series = _get_odoo_versions(float(initial_version), float(final_version))
|
|
||||||
|
|
||||||
# Create initial first step
|
|
||||||
steps = [series[0].copy()]
|
|
||||||
steps[0].update({
|
|
||||||
"name": "step_1",
|
|
||||||
"action": "update",
|
|
||||||
"complete_name": "step_1__update__%s" % (steps[0]["version"]),
|
|
||||||
})
|
|
||||||
|
|
||||||
# Add all upgrade steps
|
|
||||||
count = 1
|
|
||||||
for serie in series[1:]:
|
|
||||||
steps.append(serie.copy())
|
|
||||||
steps[count].update({
|
|
||||||
"name": "step_%d" % (count+1),
|
|
||||||
"action": "upgrade",
|
|
||||||
"complete_name": "step_%d__upgrade__%s" % (count+1, serie["version"]),
|
|
||||||
})
|
|
||||||
count += 1
|
|
||||||
|
|
||||||
# add final update step
|
|
||||||
steps.append(series[-1].copy())
|
|
||||||
steps[-1].update({
|
|
||||||
"name": "step_%d" % (count + 1),
|
|
||||||
"action": "update",
|
|
||||||
"complete_name": "step_%d__update__%s" % (count+1, steps[-1]["version"]),
|
|
||||||
})
|
|
||||||
|
|
||||||
template = Template(_CONFIG_YML_TEMPLATE)
|
|
||||||
output = template.render(steps=steps)
|
|
||||||
with open(ctx.obj["config_file_path"], "w") as f:
|
|
||||||
logger.info("Creating configuration file '%s' ..." % (ctx.obj["config_file_path"]))
|
|
||||||
f.write(output)
|
|
||||||
f.close()
|
|
||||||
|
|
||||||
distinct_versions = list(set(x["version"] for x in series))
|
|
||||||
|
|
||||||
# 4. Create Repo folder and files
|
|
||||||
if not ctx.obj["repo_folder_path"].exists():
|
|
||||||
logger.info("Creating folder '%s' ..." % (ctx.obj["repo_folder_path"]))
|
|
||||||
mkdir([ctx.obj["repo_folder_path"]])
|
|
||||||
|
|
||||||
extra_repositories = extra_repository_list.split(",")
|
|
||||||
|
|
||||||
orgs = {x: [] for x in set([x.split("/")[0] for x in extra_repositories])}
|
|
||||||
for extra_repository in extra_repositories:
|
|
||||||
org, repo = extra_repository.split("/")
|
|
||||||
orgs[org].append(repo)
|
|
||||||
|
|
||||||
for version in distinct_versions:
|
|
||||||
template = Template(_REPO_YML_TEMPLATE)
|
|
||||||
output = template.render(version=version, orgs=orgs)
|
|
||||||
file_name =ctx.obj["repo_folder_path"] / Path("%s.yml" % (version))
|
|
||||||
with open(file_name, "w") as f:
|
|
||||||
logger.info("Creating Repo file '%s' ..." % (file_name))
|
|
||||||
f.write(output)
|
|
||||||
f.close()
|
|
||||||
|
|
||||||
# 5. Create Requirements folder and files
|
|
||||||
if not ctx.obj["requirement_folder_path"].exists():
|
|
||||||
logger.info("Creating folder '%s' ..." % (ctx.obj["requirement_folder_path"]))
|
|
||||||
mkdir([ctx.obj["requirement_folder_path"]])
|
|
||||||
|
|
||||||
for serie in series:
|
|
||||||
template = Template(_REQUIREMENTS_TXT_TEMPLATE)
|
|
||||||
output = template.render(python_libraries=serie["python_libraries"])
|
|
||||||
file_name =ctx.obj["requirement_folder_path"] / Path("%s_requirements.txt" % (serie["version"]))
|
|
||||||
with open(file_name, "w") as f:
|
|
||||||
logger.info("Creating Requirements file '%s' ..." % (file_name))
|
|
||||||
f.write(output)
|
|
||||||
f.close()
|
|
||||||
|
|
||||||
# 6. Create Scripts folder and files
|
|
||||||
if not ctx.obj["script_folder_path"].exists():
|
|
||||||
logger.info("Creating folder '%s' ..." % (ctx.obj["script_folder_path"]))
|
|
||||||
mkdir([ctx.obj["script_folder_path"]])
|
|
||||||
|
|
||||||
for step in steps:
|
|
||||||
step_path = ctx.obj["script_folder_path"] / step["complete_name"]
|
|
||||||
if not step_path.exists():
|
|
||||||
logger.info("Creating folder '%s' ..." % (step_path))
|
|
||||||
mkdir([step_path])
|
|
||||||
template = Template(_PRE_MIGRATION_SQL_TEMPLATE)
|
|
||||||
output = template.render()
|
|
||||||
file_name =step_path / Path("pre-migration.sql")
|
|
||||||
with open(file_name, "w") as f:
|
|
||||||
logger.info("Creating pre-migration.sql file '%s' ..." % (file_name))
|
|
||||||
f.write(output)
|
|
||||||
f.close()
|
|
||||||
template = Template(_POST_MIGRATION_PY_TEMPLATE)
|
|
||||||
output = template.render()
|
|
||||||
file_name =step_path / Path("post-migration.py")
|
|
||||||
with open(file_name, "w") as f:
|
|
||||||
logger.info("Creating post-migration.py file '%s' ..." % (file_name))
|
|
||||||
f.write(output)
|
|
||||||
f.close()
|
|
||||||
|
|
|
||||||
28
odoo_openupgrade_wizard/cli_build.py
Normal file
28
odoo_openupgrade_wizard/cli_build.py
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
# from pathlib import Path
|
||||||
|
|
||||||
|
import click
|
||||||
|
from loguru import logger
|
||||||
|
from plumbum.cmd import mkdir
|
||||||
|
|
||||||
|
|
||||||
|
@click.command()
|
||||||
|
@click.pass_context
|
||||||
|
def build(ctx):
|
||||||
|
"""
|
||||||
|
Build OpenUpgrade Wizard Environment:
|
||||||
|
- gitaggregate all the repositories
|
||||||
|
- build virtualenv (TODO)
|
||||||
|
"""
|
||||||
|
|
||||||
|
# distinct_versions = list(set(x["version"] for x in series))
|
||||||
|
|
||||||
|
for step in ctx.obj["config"]["migration_steps"]:
|
||||||
|
# 1. Create main folder for the odoo version
|
||||||
|
if not step["local_path"].exists():
|
||||||
|
logger.info("Creating folder '%s' ..." % (step["local_path"]))
|
||||||
|
mkdir(["--mode", "777", step["local_path"]])
|
||||||
|
|
||||||
|
# # 2. gitaggregate source code
|
||||||
|
# repo_file = ctx.obj["repo_folder_path"] / Path(
|
||||||
|
# "%s.yml" % (step["version"])
|
||||||
|
# )
|
||||||
180
odoo_openupgrade_wizard/cli_init.py
Normal file
180
odoo_openupgrade_wizard/cli_init.py
Normal file
|
|
@ -0,0 +1,180 @@
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import click
|
||||||
|
from jinja2 import Template
|
||||||
|
from loguru import logger
|
||||||
|
from plumbum.cmd import mkdir
|
||||||
|
|
||||||
|
from odoo_openupgrade_wizard.configuration_version_dependant import (
|
||||||
|
_get_odoo_version_str_list,
|
||||||
|
_get_odoo_versions,
|
||||||
|
)
|
||||||
|
from odoo_openupgrade_wizard.templates import (
|
||||||
|
_CONFIG_YML_TEMPLATE,
|
||||||
|
_POST_MIGRATION_PY_TEMPLATE,
|
||||||
|
_PRE_MIGRATION_SQL_TEMPLATE,
|
||||||
|
_REPO_YML_TEMPLATE,
|
||||||
|
_REQUIREMENTS_TXT_TEMPLATE,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@click.command()
|
||||||
|
@click.option(
|
||||||
|
"-iv",
|
||||||
|
"--initial-version",
|
||||||
|
required=True,
|
||||||
|
prompt=True,
|
||||||
|
type=click.Choice(_get_odoo_version_str_list("initial")),
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
"-fv",
|
||||||
|
"--final-version",
|
||||||
|
required=True,
|
||||||
|
prompt=True,
|
||||||
|
type=click.Choice(_get_odoo_version_str_list("final")),
|
||||||
|
)
|
||||||
|
@click.option(
|
||||||
|
"-er",
|
||||||
|
"--extra-repository",
|
||||||
|
"extra_repository_list",
|
||||||
|
# TODO, add a callback to check the quality of the argument
|
||||||
|
help="Coma separated extra repositories to use in the odoo environment."
|
||||||
|
"Ex: 'OCA/web,OCA/server-tools,GRAP/grap-odoo-incubator'",
|
||||||
|
)
|
||||||
|
@click.pass_context
|
||||||
|
def init(ctx, initial_version, final_version, extra_repository_list):
|
||||||
|
"""
|
||||||
|
Initialize OpenUpgrade Wizard Environment based on the initial and
|
||||||
|
the final version of Odoo you want to migrate.
|
||||||
|
"""
|
||||||
|
# 1. create src directory if not exists
|
||||||
|
if not ctx.obj["src_folder_path"].exists():
|
||||||
|
logger.info("Creating folder '%s' ..." % (ctx.obj["src_folder_path"]))
|
||||||
|
mkdir(["--mode", "777", ctx.obj["src_folder_path"]])
|
||||||
|
|
||||||
|
# 2. create filestore directory if not exists
|
||||||
|
if not ctx.obj["filestore_folder_path"].exists():
|
||||||
|
logger.info(
|
||||||
|
"Creating folder '%s' ..." % (ctx.obj["filestore_folder_path"])
|
||||||
|
)
|
||||||
|
mkdir(["--mode", "777", ctx.obj["filestore_folder_path"]])
|
||||||
|
|
||||||
|
# 3. Create main config file
|
||||||
|
series = _get_odoo_versions(float(initial_version), float(final_version))
|
||||||
|
|
||||||
|
# Create initial first step
|
||||||
|
steps = [series[0].copy()]
|
||||||
|
steps[0].update(
|
||||||
|
{
|
||||||
|
"name": "step_1",
|
||||||
|
"action": "update",
|
||||||
|
"complete_name": "step_1__update__%s" % (steps[0]["version"]),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Add all upgrade steps
|
||||||
|
count = 1
|
||||||
|
for serie in series[1:]:
|
||||||
|
steps.append(serie.copy())
|
||||||
|
steps[count].update(
|
||||||
|
{
|
||||||
|
"name": "step_%d" % (count + 1),
|
||||||
|
"action": "upgrade",
|
||||||
|
"complete_name": "step_%d__upgrade__%s"
|
||||||
|
% (count + 1, serie["version"]),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
count += 1
|
||||||
|
|
||||||
|
# add final update step
|
||||||
|
steps.append(series[-1].copy())
|
||||||
|
steps[-1].update(
|
||||||
|
{
|
||||||
|
"name": "step_%d" % (count + 1),
|
||||||
|
"action": "update",
|
||||||
|
"complete_name": "step_%d__update__%s"
|
||||||
|
% (count + 1, steps[-1]["version"]),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
template = Template(_CONFIG_YML_TEMPLATE)
|
||||||
|
output = template.render(steps=steps)
|
||||||
|
with open(ctx.obj["config_file_path"], "w") as f:
|
||||||
|
logger.info(
|
||||||
|
"Creating configuration file '%s' ..."
|
||||||
|
% (ctx.obj["config_file_path"])
|
||||||
|
)
|
||||||
|
f.write(output)
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
distinct_versions = list(set(x["version"] for x in series))
|
||||||
|
|
||||||
|
# 4. Create Repo folder and files
|
||||||
|
if not ctx.obj["repo_folder_path"].exists():
|
||||||
|
logger.info("Creating folder '%s' ..." % (ctx.obj["repo_folder_path"]))
|
||||||
|
mkdir([ctx.obj["repo_folder_path"]])
|
||||||
|
|
||||||
|
extra_repositories = extra_repository_list.split(",")
|
||||||
|
|
||||||
|
orgs = {x: [] for x in set([x.split("/")[0] for x in extra_repositories])}
|
||||||
|
for extra_repository in extra_repositories:
|
||||||
|
org, repo = extra_repository.split("/")
|
||||||
|
orgs[org].append(repo)
|
||||||
|
|
||||||
|
for version in distinct_versions:
|
||||||
|
template = Template(_REPO_YML_TEMPLATE)
|
||||||
|
output = template.render(version=version, orgs=orgs)
|
||||||
|
file_name = ctx.obj["repo_folder_path"] / Path("%s.yml" % (version))
|
||||||
|
with open(file_name, "w") as f:
|
||||||
|
logger.info("Creating Repo file '%s' ..." % (file_name))
|
||||||
|
f.write(output)
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
# 5. Create Requirements folder and files
|
||||||
|
if not ctx.obj["requirement_folder_path"].exists():
|
||||||
|
logger.info(
|
||||||
|
"Creating folder '%s' ..." % (ctx.obj["requirement_folder_path"])
|
||||||
|
)
|
||||||
|
mkdir([ctx.obj["requirement_folder_path"]])
|
||||||
|
|
||||||
|
for serie in series:
|
||||||
|
template = Template(_REQUIREMENTS_TXT_TEMPLATE)
|
||||||
|
output = template.render(python_libraries=serie["python_libraries"])
|
||||||
|
file_name = ctx.obj["requirement_folder_path"] / Path(
|
||||||
|
"%s_requirements.txt" % (serie["version"])
|
||||||
|
)
|
||||||
|
with open(file_name, "w") as f:
|
||||||
|
logger.info("Creating Requirements file '%s' ..." % (file_name))
|
||||||
|
f.write(output)
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
# 6. Create Scripts folder and files
|
||||||
|
if not ctx.obj["script_folder_path"].exists():
|
||||||
|
logger.info(
|
||||||
|
"Creating folder '%s' ..." % (ctx.obj["script_folder_path"])
|
||||||
|
)
|
||||||
|
mkdir([ctx.obj["script_folder_path"]])
|
||||||
|
|
||||||
|
for step in steps:
|
||||||
|
step_path = ctx.obj["script_folder_path"] / step["complete_name"]
|
||||||
|
if not step_path.exists():
|
||||||
|
logger.info("Creating folder '%s' ..." % (step_path))
|
||||||
|
mkdir([step_path])
|
||||||
|
template = Template(_PRE_MIGRATION_SQL_TEMPLATE)
|
||||||
|
output = template.render()
|
||||||
|
file_name = step_path / Path("pre-migration.sql")
|
||||||
|
with open(file_name, "w") as f:
|
||||||
|
logger.info(
|
||||||
|
"Creating pre-migration.sql file '%s' ..." % (file_name)
|
||||||
|
)
|
||||||
|
f.write(output)
|
||||||
|
f.close()
|
||||||
|
template = Template(_POST_MIGRATION_PY_TEMPLATE)
|
||||||
|
output = template.render()
|
||||||
|
file_name = step_path / Path("post-migration.py")
|
||||||
|
with open(file_name, "w") as f:
|
||||||
|
logger.info(
|
||||||
|
"Creating post-migration.py file '%s' ..." % (file_name)
|
||||||
|
)
|
||||||
|
f.write(output)
|
||||||
|
f.close()
|
||||||
|
|
@ -2,19 +2,64 @@
|
||||||
# python version is defined, based on the OCA CI.
|
# python version is defined, based on the OCA CI.
|
||||||
# https://github.com/OCA/oca-addons-repo-template/blob/master/src/.github/workflows/%7B%25%20if%20ci%20%3D%3D%20'GitHub'%20%25%7Dtest.yml%7B%25%20endif%20%25%7D.jinja
|
# https://github.com/OCA/oca-addons-repo-template/blob/master/src/.github/workflows/%7B%25%20if%20ci%20%3D%3D%20'GitHub'%20%25%7Dtest.yml%7B%25%20endif%20%25%7D.jinja
|
||||||
_ODOO_SERIES = [
|
_ODOO_SERIES = [
|
||||||
{"version": 6.0, "python": "python2.7", "python_libraries": ["openupgradelib"]},
|
{
|
||||||
{"version": 6.1, "python": "python2.7", "python_libraries": ["openupgradelib"]},
|
"version": 6.0,
|
||||||
{"version": 7.0, "python": "python2.7", "python_libraries": ["openupgradelib"]},
|
"python": "python2.7",
|
||||||
{"version": 8.0, "python": "python2.7", "python_libraries": ["openupgradelib"]},
|
"python_libraries": ["openupgradelib"],
|
||||||
{"version": 9.0, "python": "python2.7", "python_libraries": ["openupgradelib"]},
|
},
|
||||||
{"version": 10.0, "python": "python2.7", "python_libraries": ["openupgradelib"]},
|
{
|
||||||
{"version": 11.0, "python": "python3.5", "python_libraries": ["openupgradelib"]},
|
"version": 6.1,
|
||||||
{"version": 12.0, "python": "python3.6", "python_libraries": ["openupgradelib"]},
|
"python": "python2.7",
|
||||||
{"version": 13.0, "python": "python3.6", "python_libraries": ["openupgradelib"]},
|
"python_libraries": ["openupgradelib"],
|
||||||
{"version": 14.0, "python": "python3.6", "python_libraries": ["openupgradelib"]},
|
},
|
||||||
{"version": 15.0, "python": "python3.8", "python_libraries": ["openupgradelib"]},
|
{
|
||||||
|
"version": 7.0,
|
||||||
|
"python": "python2.7",
|
||||||
|
"python_libraries": ["openupgradelib"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": 8.0,
|
||||||
|
"python": "python2.7",
|
||||||
|
"python_libraries": ["openupgradelib"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": 9.0,
|
||||||
|
"python": "python2.7",
|
||||||
|
"python_libraries": ["openupgradelib"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": 10.0,
|
||||||
|
"python": "python2.7",
|
||||||
|
"python_libraries": ["openupgradelib"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": 11.0,
|
||||||
|
"python": "python3.5",
|
||||||
|
"python_libraries": ["openupgradelib"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": 12.0,
|
||||||
|
"python": "python3.6",
|
||||||
|
"python_libraries": ["openupgradelib"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": 13.0,
|
||||||
|
"python": "python3.6",
|
||||||
|
"python_libraries": ["openupgradelib"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": 14.0,
|
||||||
|
"python": "python3.6",
|
||||||
|
"python_libraries": ["openupgradelib"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": 15.0,
|
||||||
|
"python": "python3.8",
|
||||||
|
"python_libraries": ["openupgradelib"],
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def _get_odoo_version_str_list(mode):
|
def _get_odoo_version_str_list(mode):
|
||||||
serie_list = [x["version"] for x in _ODOO_SERIES]
|
serie_list = [x["version"] for x in _ODOO_SERIES]
|
||||||
if mode == "initial":
|
if mode == "initial":
|
||||||
|
|
@ -23,6 +68,7 @@ def _get_odoo_version_str_list(mode):
|
||||||
serie_list = serie_list[1:]
|
serie_list = serie_list[1:]
|
||||||
return [str(x) for x in serie_list]
|
return [str(x) for x in serie_list]
|
||||||
|
|
||||||
|
|
||||||
def _get_odoo_versions(initial, final):
|
def _get_odoo_versions(initial, final):
|
||||||
result = []
|
result = []
|
||||||
for serie in _ODOO_SERIES:
|
for serie in _ODOO_SERIES:
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
_CONFIG_YML_TEMPLATE = """
|
_CONFIG_YML_TEMPLATE = """migration_steps:
|
||||||
migration_steps:
|
|
||||||
{% for step in steps %}
|
{% for step in steps %}
|
||||||
- name: {{ step['name'] }}
|
- name: {{ step['name'] }}
|
||||||
- complete_name: {{ step['complete_name'] }}
|
complete_name: {{ step['complete_name'] }}
|
||||||
version: {{ step['version'] }}
|
version: {{ step['version'] }}
|
||||||
action: {{ step['action'] }}
|
action: {{ step['action'] }}
|
||||||
python: {{ step['python'] }}
|
python: {{ step['python'] }}
|
||||||
|
|
|
||||||
877
poetry.lock
generated
877
poetry.lock
generated
File diff suppressed because it is too large
Load Diff
|
|
@ -25,10 +25,11 @@ classifiers = [
|
||||||
odoo-openupgrade-wizard = "odoo_openupgrade_wizard.cli:main"
|
odoo-openupgrade-wizard = "odoo_openupgrade_wizard.cli:main"
|
||||||
|
|
||||||
[tool.poetry.dependencies]
|
[tool.poetry.dependencies]
|
||||||
python = "^3.5"
|
python = "^3.6"
|
||||||
click = "^7.0"
|
click = "^7.0"
|
||||||
loguru = "^0.6"
|
loguru = "^0.6"
|
||||||
plumbum = "^1.7"
|
plumbum = "^1.7"
|
||||||
|
single-source = "^0.3"
|
||||||
|
|
||||||
[tool.poetry.dev-dependencies]
|
[tool.poetry.dev-dependencies]
|
||||||
pytest = [
|
pytest = [
|
||||||
|
|
|
||||||
30
tests/cli_init_test.py
Normal file
30
tests/cli_init_test.py
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
import filecmp
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from click.testing import CliRunner
|
||||||
|
from plumbum.cmd import mkdir
|
||||||
|
|
||||||
|
from odoo_openupgrade_wizard.cli import main
|
||||||
|
|
||||||
|
|
||||||
|
def test_cli_init():
|
||||||
|
output_folder_path = Path("./tests/output")
|
||||||
|
expected_folder_path = Path("./tests/output_expected")
|
||||||
|
mkdir([output_folder_path, "--parents"])
|
||||||
|
result = CliRunner().invoke(
|
||||||
|
main,
|
||||||
|
[
|
||||||
|
"--env-folder=%s" % output_folder_path,
|
||||||
|
"init",
|
||||||
|
"--initial-version=9.0",
|
||||||
|
"--final-version=12.0",
|
||||||
|
"--extra-repository="
|
||||||
|
"OCA/web,OCA/server-tools,GRAP/grap-odoo-incubator",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
assert result.exit_code == 0
|
||||||
|
|
||||||
|
assert filecmp.cmp(
|
||||||
|
output_folder_path / Path("config.yml"),
|
||||||
|
expected_folder_path / Path("config.yml"),
|
||||||
|
)
|
||||||
31
tests/output_expected/config.yml
Normal file
31
tests/output_expected/config.yml
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
migration_steps:
|
||||||
|
|
||||||
|
- name: step_1
|
||||||
|
complete_name: step_1__update__9.0
|
||||||
|
version: 9.0
|
||||||
|
action: update
|
||||||
|
python: python2.7
|
||||||
|
|
||||||
|
- name: step_2
|
||||||
|
complete_name: step_2__upgrade__10.0
|
||||||
|
version: 10.0
|
||||||
|
action: upgrade
|
||||||
|
python: python2.7
|
||||||
|
|
||||||
|
- name: step_3
|
||||||
|
complete_name: step_3__upgrade__11.0
|
||||||
|
version: 11.0
|
||||||
|
action: upgrade
|
||||||
|
python: python3.5
|
||||||
|
|
||||||
|
- name: step_4
|
||||||
|
complete_name: step_4__upgrade__12.0
|
||||||
|
version: 12.0
|
||||||
|
action: upgrade
|
||||||
|
python: python3.6
|
||||||
|
|
||||||
|
- name: step_5
|
||||||
|
complete_name: step_5__update__12.0
|
||||||
|
version: 12.0
|
||||||
|
action: update
|
||||||
|
python: python3.6
|
||||||
Loading…
Reference in New Issue
Block a user