Compare commits

..

1 Commits

Author SHA1 Message Date
Sylvain LE GAL
c1b1827a7c [TEST] switch to postgresql 14 2024-03-04 09:49:08 +01:00
75 changed files with 1783 additions and 3455 deletions

View File

@ -6,7 +6,7 @@ stages:
- release - release
pre-commit: pre-commit:
image: python:alpine image: python
stage: lint stage: lint
rules: rules:
# Run only if merge request # Run only if merge request
@ -14,14 +14,13 @@ pre-commit:
# Run if commit on default branch # Run if commit on default branch
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
before_script: before_script:
- apk add git
- pip install pre-commit - pip install pre-commit
script: script:
- pre-commit run --all --show-diff-on-failure --verbose --color always - pre-commit run --all --show-diff-on-failure --verbose --color always
check_version: check_version:
stage: lint stage: lint
image: python:alpine image: python:latest
rules: rules:
# Run if commit that start with a version number is pushed # Run if commit that start with a version number is pushed
- if: $CI_COMMIT_TAG =~ /^[0-9]+\.[0-9]+\.[0-9]+.*/ - if: $CI_COMMIT_TAG =~ /^[0-9]+\.[0-9]+\.[0-9]+.*/
@ -34,7 +33,7 @@ check_version:
check_changelog: check_changelog:
stage: lint stage: lint
image: python:alpine image: python:latest
rules: rules:
# Run if commit that start with a version number is pushed # Run if commit that start with a version number is pushed
- if: $CI_COMMIT_TAG =~ /^[0-9]+\.[0-9]+\.[0-9]+$/ - if: $CI_COMMIT_TAG =~ /^[0-9]+\.[0-9]+\.[0-9]+$/
@ -47,7 +46,7 @@ check_changelog:
pytest: pytest:
image: image:
name: python:$PYTHON_VERSION-alpine name: python:$PYTHON_VERSION
stage: test stage: test
tags: tags:
- cie-oow-dind-runner - cie-oow-dind-runner
@ -66,9 +65,7 @@ pytest:
DOCKER_TLS_CERTDIR: "" DOCKER_TLS_CERTDIR: ""
coverage: '/TOTAL\s+\d+\s+\d+\s+(\d+)\%/' coverage: '/TOTAL\s+\d+\s+\d+\s+(\d+)\%/'
before_script: before_script:
- apk add git
- pip install poetry - pip install poetry
- poetry --version
- export PATH="$HOME/.local/bin:$PATH" - export PATH="$HOME/.local/bin:$PATH"
- poetry install --all-extras - poetry install --all-extras
script: script:
@ -76,12 +73,13 @@ pytest:
parallel: parallel:
matrix: matrix:
- PYTHON_VERSION: - PYTHON_VERSION:
- "3.7"
- "3.8"
- "3.9" - "3.9"
- "3.13"
build: build:
stage: build stage: build
image: python:alpine image: python:latest
rules: rules:
# Run if merge request # Run if merge request
- if: $CI_PIPELINE_SOURCE == "merge_request_event" - if: $CI_PIPELINE_SOURCE == "merge_request_event"
@ -101,7 +99,7 @@ build:
publish: publish:
stage: publish stage: publish
image: python:alpine image: python:latest
rules: rules:
# Run if commit that start with a version number is pushed # Run if commit that start with a version number is pushed
- if: $CI_COMMIT_TAG =~ /^[0-9]+\.[0-9]+\.[0-9]+.*/ - if: $CI_COMMIT_TAG =~ /^[0-9]+\.[0-9]+\.[0-9]+.*/

23
.vscode/launch.json vendored
View File

@ -1,23 +0,0 @@
{
// Verwendet IntelliSense zum Ermitteln möglicher Attribute.
// Zeigen Sie auf vorhandene Attribute, um die zugehörigen Beschreibungen anzuzeigen.
// Weitere Informationen finden Sie unter https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Python-Debugger: Remoteanfügung",
"type": "debugpy",
"request": "attach",
"connect": {
"host": "localhost",
"port": 5678
},
"pathMappings": [
{
"localRoot": "${workspaceFolder}",
"remoteRoot": "/home/lotzm/odoo-openupgrade-wizard"
}
]
}
]
}

View File

@ -1,3 +0,0 @@
{
"python.defaultInterpreterPath": "/home/lotzm/.local/share/pipx/venvs/odoo-openupgrade-wizard/bin/python"
}

View File

@ -1,285 +0,0 @@
odoo-openupgrade-wizard changes
*******************************
This file compiles releases and changes made in
``odoo-openupgrade-wizard``.
.. towncrier release notes start
odoo-openupgrade-wizard 1.3.1 (2025-09-18)
==========================================
Bugfixes
--------
- Fix BSD mkdir incompatibility on macOS by using single-character
flags for 'mode' and 'parents' for cross-platform folder creation (#39)
- Fix debian buster Dockerfile. It uses the old https://deb.debian.org
instead of https://archive.debian.org.
- Improve error handling for missing config file outside initialized
environment.
- Prevent creating a log file outside of an initialized directory.
- Set 'rm=True' when building Docker images, ensuring intermediate (i.e.
orphaned) containers are automatically removed after builds and keeping
local Docker environments clean.
By default, the docker build CLI now sets --rm=true, but the SDK has
kept the old default of False to preserve backward compatibility.
Documentation
-------------
- Many improvements/additions to README.md and help string, including missing
Command help, additional examples, a Quick Start Guide, standard technical
writing changes, etc.
- Remove obsolete ROADMAP file. Team now uses issues on gitlab.
Misc
----
- Do not set False to an argument that is hint typed as string. Using None
instead.
odoo-openupgrade-wizard 1.3.0 (2025-05-04)
==========================================
Features
--------
- Disable default Odoo memory limit (of 2560 MiB) by default.
This can be adjusted in the `odoo.conf` file for each version.
Documentation
-------------
- Improve release documentation for developpers.
Misc
----
- set a default 'postgres' database in ``oow psql`` command, to avoid
force user to set a database, in case of dbless command.
odoo-openupgrade-wizard 1.2.0 (2025-04-12)
==========================================
Features
--------
- Add an option ``update`` in the configuration file at migration_step level.
If disabled, the update=all step will be skipped during this step.
That can be interesting to save time, during the first and the last steps of the migration
process (when ``execution_context='regular'``).
- Improve workload analysis file with button to hide done module.
Bugfixes
--------
- Handles the case where the filestore data provided in the source is in a "filestore" subfolder.
Misc
----
- restoredb: Use builtins click feature to check if provided paths exist
- Refactoring of the function tools.tools_odoo.generate_odoo_command.
odoo-openupgrade-wizard 1.1.0 (2024-11-07)
==========================================
Features
--------
- Support 17.0 serie.
- Support 18.0 serie.
- add a new command ``odoo-openupgrade-wizard guess-requirement`` that
initialize the python and bin requirements files, based on the odoo
modules present in the modules.csv file, and analyzing code.
(``__manifest__.py`` and ``setup.py`` files)
Misc
----
- Add support of python 3.13.
- Test only on the first and the last supported python version. (python3.9 and
python3.13).
This change allow to save time and resources on CI execution.
odoo-openupgrade-wizard 1.0.3 (2024-10-09)
==========================================
Bugfixes
--------
- Fix crash when building container fails.
- Make odoo openupgrade wizard working in the following combination:
Odoo version <= 12 + Postgresql version >= 14
- New fix for error that append randomly when removing a container.
Documentation
-------------
- Add towncrier and newsfragments info in dev documentation.
- Improve the README.md file, hightlighting code section.
- Update contributors list until October 2024
Misc
----
- Update of python libraries version, using ``poetry update``.
* Removing incremental (22.10.0)
* Updating attrs (23.2.0 -> 24.2.0)
* Updating certifi (2024.2.2 -> 2024.8.30)
* Updating filelock (3.13.2 -> 3.16.1)
* Updating idna (3.6 -> 3.10)
* Updating packaging (24.0 -> 24.1)
* Updating platformdirs (4.2.0 -> 4.3.6)
* Updating pygments (2.17.2 -> 2.18.0)
* Updating pyyaml (6.0.1 -> 6.0.2)
* Updating tomli (2.0.1 -> 2.0.2)
* Updating typing-extensions (4.10.0 -> 4.12.2)
* Updating urllib3 (2.2.1 -> 2.2.3)
* Updating zipp (3.18.1 -> 3.20.2)
* Updating argcomplete (3.2.3 -> 3.5.1)
* Updating astroid (3.1.0 -> 3.3.5)
* Updating coverage (7.4.4 -> 7.6.1)
* Updating dill (0.3.8 -> 0.3.9)
* Updating gitpython (3.1.42 -> 3.1.43)
* Updating jinja2 (3.1.3 -> 3.1.4)
* Updating requests (2.31.0 -> 2.32.3)
* Updating rich (13.7.1 -> 13.9.2)
* Updating setuptools (69.2.0 -> 75.1.0)
* Updating tomlkit (0.12.4 -> 0.13.2)
* Updating virtualenv (20.25.1 -> 20.26.6)
* Updating docker (7.0.0 -> 7.1.0)
* Updating plumbum (1.8.2 -> 1.9.0)
* Updating pylint (3.1.0 -> 3.3.1)
* Updating towncrier (23.11.0 -> 24.8.0)
odoo-openupgrade-wizard 1.0.2 (2024-10-06)
==========================================
Bugfixes
--------
- Added a check to ensure the source exists before database operations,
preventing the destination from being dropped if the source is missing.
(check-db-exist-before-operations)
- Fix error that append randomly when removing a container. (container-removal)
- Require to specify the --database arg for every command that needs it
(install_from_csv, psql, run, generate_module_analysis)
(require-database-arg)
odoo-openupgrade-wizard 1.0.1 (2024-10-01)
==========================================
Features
--------
- Avoid to crash if postgresql-version is not set, adding prompt option
and add extra text to mention postgresql version constraints.
(postgresql-version-prompt)
Misc
----
- Refactor to simplify configuration_version_dependant.py file.
(version-simplification)
odoo-openupgrade-wizard 1.0.0 (2024-09-30)
==========================================
Features
--------
- Add option ``p`` (SQL format) allowing use from ``--database-format`` CLI.
This allows you to restore database in SQL format (used by odoo full backup)
(add-sql-option-for-database-format-cli)
Bugfixes
--------
- Allow to run multiple `post-*.py` script for each steps.
(allow-run-multiple-post-scripts)
- Fix metadata of the python package on PyPI. (fix-package-metadata)
odoo-openupgrade-wizard 0.7.0 (2024-05-02)
==========================================
Features
--------
- Add ``--config-file`` and ``--modules-file`` CLI options. This allows to use
different files than the default ones. This is useful when using the same
environment for different databases. (add-config-file-cli-option)
- Add database name to container name and publish Docker ports only when needed
to allow to upgrade multiple databases in parallel.
(allow-to-upgrade-multiple-databases-in-parallel)
- Drop support for python version < 3.9. Update dependencies and fix some
issue liked to that. (drop-old-python-support)
- Add a new option ``--postgresql-version`` in ``oow init`` command to
define the version of the postgresql image to be used for the project.
(option-postgresql-version)
- Factorize code. Allways set --log-level=DEBUG in tests.
(set-log-level-debug-default-in-cli_runner_invoke)
Bugfixes
--------
- Allow hyphen-minus character in database names.
(allow-minus-in-database-names)
odoo-openupgrade-wizard 0.6.0 (2024-03-20)
==========================================
Features
--------
- Add ``dropdb`` command to easily delete database and filestore of existing
Odoo databases in PostgreSQL container. (add-dropdb)
- With ``install-from-csv`` and the ``--with-demo / --without-demo``, you
can control if the created database will be populated with demo data or
not. (add-install-from-csv-demo)
- Add ``restoredb`` command to restore database and filestore in the
PostgreSQL container. (add-restoredb)
- Add ``--update-modules`` option to ``run`` command. (imp-run-update-modules)
- ``run`` and ``upgrade`` command are now harmonized with the option
``--with-demo / --without-demo``. By default demo data is always false.
(imp-run-upgrade)
Bugfixes
--------
- ``copydb`` now copy also the filestore of the database. (copydb-filestore)
- Fix warning message for ``estimate-workload`` command.
(fix-estimate-workload-warning-message)
- Fix getting url on apps.odoo.com that prevent from running
``estimate-workload`` command. (fix-getting-url)
- Fix crash when a addons-path directory does not contain modules.
Directory that does not contains odoo modules are now removed from
addons-path option of odoo. (fix-repo)
Misc
----
- ci-improvement

View File

@ -1,18 +1,9 @@
# Developers # Developers
* Sylvain LE GAL, from [GRAP](http://www.grap.coop), since March 2022 * Sylvain LE GAL, from GRAP (http://www.grap.coop)
* Rémy TAYMANS, from [Coop It Easy](https://coopiteasy.be/), since June 2022 * Rémy TAYMANS, from Coop It Easy (https://coopiteasy.be/)
* Cyril JEANNERET, from Camptocamp (https://www.camptocamp.com)
* Cyril JEANNERET, from [Camptocamp](https://www.camptocamp.com), since April 2023 * Simon MAILLARD
* Simon MAILLARD, form [Ogesta](https://ogesta.fr/), since July 2023
* Hugues DE KEYSER, from [Coop It Easy](https://coopiteasy.be/), since April 2024
* Gabriel PICKENHAYN, since May 2024
* Boris GALLET, since September 2024
* Ahmet YIĞIT BUDAK, from [Altinkaya](https://www.altinkaya.com/fr), since October 2024
* Alexandre AUBIN, from [Coopaname](https://www.coopaname.coop/), since October 2024
* Sergio Zanchetta, from [PNLug](https://www.pnlug.it/), since January 2025
* Ken WOYCHESKO, since May 2025
# Reviewers # Reviewers

View File

@ -88,37 +88,6 @@ docker exec -it POSTGRES_CONTAINER_NAME /bin/bash
# Contribute # Contribute
## Provide newsfragments in your merge requests
If you propose a merge request, please add a newsfragments with it.
A newsfragment is a small file describing what is done in the merge
request. The file has a extension (e.g. `.feature`, `.bugfix`, etc) that
describe witch type of modification you are doing. This newsfragment
file will populate the CHANGES.rst file for the next release.
Documentation and the full list of default available extension can be
found [here](https://towncrier.readthedocs.io/en/stable/tutorial.html#creating-news-fragments).
Newsfragments must be put in the `newsfragments` directory at the root
of the project. You can install `towncrier` via `pipx install
towncrier`.
The newsfragments file will be processed with `towncrier` by the
maintainers during the release process.
Use `towncrier create --help` to see the available extension for the
newsfragement files. The name of the file can be a number referring an
issue of the project or a
[slug](https://en.wikipedia.org/wiki/Clean_URL#Slug) that start with
a `+` symbol.
This is a example of newsfragments.
`newsfragments/+sub-command-cowsay.feature`:
```
Adds a new subcommand `cowsay` to allow poeple to speak like a cow.
```
## Add python dependencies ## Add python dependencies
If you add new dependencies, you have to: If you add new dependencies, you have to:
@ -152,14 +121,6 @@ release, if it is a major, minor or patch release. For alpha, beta, etc
release information will be published with the next major, minor or release information will be published with the next major, minor or
patch release. patch release.
To generate the `CHANGES.rst`, run the towncrier tool:
```sh
towncrier build
```
Use `--draft` option to preview the result.
Then push a commit with the version and the changlog updated on the main Then push a commit with the version and the changlog updated on the main
branch. branch.

View File

@ -1,19 +0,0 @@
# Loakale Entwicklung an oow
Das geht am einfachsten mit **pipx** im "editable mode" (`--editable`), sodass Änderungen im Arbeitsverzeichnis sofort wirksam sind.
Angenommen, dein Arbeitsverzeichnis ist odoo-openupgrade-wizard, dann führe im Terminal aus:
```bash
pipx install --editable /home/lotzm/gitea.hobbyhimmel/odoo-openupgrade-wizard
```
Dadurch wird ein Symlink auf dein Arbeitsverzeichnis erstellt und du kannst direkt am Code arbeiten.
Das Kommando `oow` ist dann wie gewohnt verfügbar, aber nutzt immer den aktuellen Stand deines Codes.
**Hinweis:**
Falls du vorher schon eine pipx-Installation hattest, führe vorher aus:
```bash
pipx uninstall odoo-openupgrade-wizard
```
Danach kannst du Änderungen im Arbeitsverzeichnis machen und direkt testen!

1005
README.md

File diff suppressed because it is too large Load Diff

View File

@ -1 +0,0 @@
!.gitignore

View File

@ -1,10 +1,3 @@
#import debugpy
#debugpy.listen(("0.0.0.0", 5678))
#print("⏳ Warten auf Debugger-Anbindung (VS Code: Python: Remote Attach)...")
#debugpy.wait_for_client()
#print("✅ Debugger verbunden!")
import datetime import datetime
import logging import logging
import sys import sys
@ -31,7 +24,6 @@ from odoo_openupgrade_wizard.cli.cli_generate_module_analysis import (
generate_module_analysis, generate_module_analysis,
) )
from odoo_openupgrade_wizard.cli.cli_get_code import get_code from odoo_openupgrade_wizard.cli.cli_get_code import get_code
from odoo_openupgrade_wizard.cli.cli_guess_requirement import guess_requirement
from odoo_openupgrade_wizard.cli.cli_init import init from odoo_openupgrade_wizard.cli.cli_init import init
from odoo_openupgrade_wizard.cli.cli_install_from_csv import install_from_csv from odoo_openupgrade_wizard.cli.cli_install_from_csv import install_from_csv
from odoo_openupgrade_wizard.cli.cli_psql import psql from odoo_openupgrade_wizard.cli.cli_psql import psql
@ -41,9 +33,6 @@ from odoo_openupgrade_wizard.cli.cli_run import run
from odoo_openupgrade_wizard.cli.cli_upgrade import upgrade from odoo_openupgrade_wizard.cli.cli_upgrade import upgrade
from odoo_openupgrade_wizard.tools.tools_system import ensure_folder_exists from odoo_openupgrade_wizard.tools.tools_system import ensure_folder_exists
DEFAULT_CONFIG_FILE = "config.yml"
DEFAULT_MODULES_FILE = "modules.csv"
@click.group() @click.group()
@click.version_option(version=odoo_openupgrade_wizard.__version__) @click.version_option(version=odoo_openupgrade_wizard.__version__)
@ -56,60 +45,29 @@ DEFAULT_MODULES_FILE = "modules.csv"
writable=True, writable=True,
resolve_path=True, resolve_path=True,
), ),
help="Directory that will contain all the configuration of the wizard " help="Folder that will contains all the configuration of the wizard"
"and all the Odoo code required to perform the migrations. Leave " " and all the Odoo code required to make the migrations. Let empty to"
"empty to use current directory (./).", " use current folder (./).",
)
@click.option(
"-c",
"--config-file",
type=click.Path(
exists=True,
file_okay=True,
),
help=(
f"Configuration file to use. By default, a file named "
f'"{DEFAULT_CONFIG_FILE}" in the environment directory will be used.'
),
)
@click.option(
"--modules-file",
type=click.Path(
exists=True,
file_okay=True,
),
help=(
f"Modules file to use. By default, a file named "
f'"{DEFAULT_MODULES_FILE}" in the environment directory will be used.'
),
) )
@click.option( @click.option(
"--filestore-folder", "--filestore-folder",
type=click.Path( type=click.Path(
exists=True, file_okay=False, writable=True, resolve_path=True exists=True, file_okay=False, writable=True, resolve_path=True
), ),
help="Directory that contains the Odoo filestore of the database(s) to " help="Folder that contains the Odoo filestore of the database(s)"
"migrate. Leave empty to use the subdirectory 'filestore' of the " " to migrate. Let empty to use the subfolder 'filestore' of the"
"environment directory.", " environment folder.",
) )
@click.option("-l", "--log-level", type=LogLevel(), default=logging.INFO) @click.option("-l", "--log-level", type=LogLevel(), default=logging.INFO)
@click.pass_context @click.pass_context
def main( def main(ctx, env_folder, filestore_folder, log_level):
ctx, env_folder, config_file, modules_file, filestore_folder, log_level """
): Provides a command set to perform odoo Community Edition migrations.
"""Provides a command set to perform Odoo Community Edition migrations.
Instructions are provided in the README.md file, or see the
help provided for each 'Command' listed below. For
example, to learn more about the `init` command,
run:
`oow init --help`
""" """
date_begin = datetime.datetime.now() date_begin = datetime.datetime.now()
logger.remove() logger.remove()
logger.add(sys.stderr, level=log_level) logger.add(sys.stderr, level=log_level)
logger.debug(f"Beginning script '{ctx.invoked_subcommand}'...") logger.debug("Beginning script '%s' ..." % (ctx.invoked_subcommand))
if not isinstance(ctx.obj, dict): if not isinstance(ctx.obj, dict):
ctx.obj = {} ctx.obj = {}
@ -124,22 +82,6 @@ def main(
script_folder_path = env_folder_path / Path("./scripts/") script_folder_path = env_folder_path / Path("./scripts/")
log_folder_path = env_folder_path / Path("./log/") log_folder_path = env_folder_path / Path("./log/")
if config_file:
config_file_path = Path(config_file)
else:
config_file_path = env_folder_path / Path(DEFAULT_CONFIG_FILE)
if modules_file:
module_file_path = Path(modules_file)
else:
module_file_path = env_folder_path / Path(DEFAULT_MODULES_FILE)
if config_file_path.exists():
# Load the main configuration file
with open(config_file_path) as file:
config = yaml.safe_load(file)
ctx.obj["config"] = config
# ensure log folder exists # ensure log folder exists
ensure_folder_exists(log_folder_path, git_ignore_content=True) ensure_folder_exists(log_folder_path, git_ignore_content=True)
@ -150,12 +92,8 @@ def main(
log_file_path = log_folder_path / Path(log_prefix + ".log") log_file_path = log_folder_path / Path(log_prefix + ".log")
logger.add(log_file_path) logger.add(log_file_path)
ctx.obj["log_prefix"] = log_prefix config_file_path = env_folder_path / Path("config.yml")
elif ctx.invoked_subcommand not in ("init", None): module_file_path = env_folder_path / Path("modules.csv")
ctx.fail(
f"Environment not initialized. "
f"Expected config file at: {config_file_path}",
)
# Add all global values in the context # Add all global values in the context
ctx.obj["env_folder_path"] = env_folder_path ctx.obj["env_folder_path"] = env_folder_path
@ -163,9 +101,18 @@ def main(
ctx.obj["filestore_folder_path"] = filestore_folder_path ctx.obj["filestore_folder_path"] = filestore_folder_path
ctx.obj["script_folder_path"] = script_folder_path ctx.obj["script_folder_path"] = script_folder_path
ctx.obj["log_folder_path"] = log_folder_path ctx.obj["log_folder_path"] = log_folder_path
ctx.obj["log_prefix"] = log_prefix
ctx.obj["config_file_path"] = config_file_path ctx.obj["config_file_path"] = config_file_path
ctx.obj["module_file_path"] = module_file_path ctx.obj["module_file_path"] = module_file_path
# Load the main configuration file
if config_file_path.exists():
with open(config_file_path) as file:
config = yaml.safe_load(file)
ctx.obj["config"] = config
elif ctx.invoked_subcommand != "init":
raise
main.add_command(copydb) main.add_command(copydb)
main.add_command(restoredb) main.add_command(restoredb)
@ -176,7 +123,6 @@ main.add_command(estimate_workload)
main.add_command(execute_script_python) main.add_command(execute_script_python)
main.add_command(execute_script_sql) main.add_command(execute_script_sql)
main.add_command(generate_module_analysis) main.add_command(generate_module_analysis)
main.add_command(guess_requirement)
main.add_command(get_code) main.add_command(get_code)
main.add_command(init) main.add_command(init)
main.add_command(install_from_csv) main.add_command(install_from_csv)

View File

@ -10,20 +10,17 @@ from odoo_openupgrade_wizard.tools import (
"-s", "-s",
"--source", "--source",
type=str, type=str,
help="Name of the source database to copy.", help="Name of the database to copy",
) )
@click.option( @click.option(
"-d", "-d",
"--dest", "--dest",
type=str, type=str,
help="Name of the destination database to create.", help="Name of the new database",
) )
@click.pass_context @click.pass_context
def copydb(ctx, source, dest): def copydb(ctx, source, dest):
"""Create a new Odoo database by copying another. """Create an Odoo database by copying an existing one.
it will copy the postgres database and the filestore.
This command duplicates both the PostgreSQL database and its associated
filestore.
""" """
click_odoo_contrib.copydb(ctx, source, dest) click_odoo_contrib.copydb(ctx, source, dest)

View File

@ -17,16 +17,11 @@ from odoo_openupgrade_wizard.tools.tools_system import get_local_user_id
@versions_options @versions_options
@click.pass_context @click.pass_context
def docker_build(ctx, versions): def docker_build(ctx, versions):
"""Build Docker images and pull PostgreSQL image. """Build Odoo Docker Images and pull Postgres image"""
This command prepares the Docker environment needed for running migration
steps. Make sure to run `oow get-code` first to ensure all required code
and requirements files are available for each version.
"""
# Pull DB image # Pull DB image
logger.info( logger.info(
"Pulling the PostgreSQL Docker image. This can take a while..." "Pulling the postgresql docker image. This can take a while..."
) )
pull_image(ctx.obj["config"]["postgres_image_name"]) pull_image(ctx.obj["config"]["postgres_image_name"])
@ -37,21 +32,21 @@ def docker_build(ctx, versions):
) )
if not odoo_requirement_file_path.exists(): if not odoo_requirement_file_path.exists():
logger.error( logger.error(
"Cannot build Odoo Docker image for version {odoo_version}, " "Building Odoo docker image for version {odoo_version}, "
"because file {odoo_requirement_file_path} cannot be found. " "because file {odoo_requirement_file_path} cannot be found. "
"Have you run the get-code command?", "Have your run the get-code command ?",
odoo_version=odoo_version, odoo_version=odoo_version,
odoo_requirement_file_path=odoo_requirement_file_path, odoo_requirement_file_path=odoo_requirement_file_path,
) )
continue continue
logger.info( logger.info(
f"Building Odoo Docker image for version '{odoo_version}'." "Building Odoo docker image for version '%s'. "
" This can take a while..." "This can take a while..." % (odoo_version)
) )
image = build_image( image = build_image(
get_odoo_env_path(ctx, odoo_version), get_odoo_env_path(ctx, odoo_version),
get_docker_image_tag(ctx, odoo_version), get_docker_image_tag(ctx, odoo_version),
{"LOCAL_USER_ID": str(get_local_user_id())}, {"LOCAL_USER_ID": str(get_local_user_id())},
) )
logger.info(f"Docker Image build. '{image[0].tags[0]}'") logger.info("Docker Image build. '%s'" % image[0].tags[0])

View File

@ -4,10 +4,7 @@ import shutil
import click import click
from odoo_openupgrade_wizard.cli.cli_options import database_option_required from odoo_openupgrade_wizard.cli.cli_options import database_option_required
from odoo_openupgrade_wizard.tools.tools_postgres import ( from odoo_openupgrade_wizard.tools.tools_postgres import execute_pg_dump
check_db_exist,
execute_pg_dump,
)
from odoo_openupgrade_wizard.tools.tools_system import dump_filestore from odoo_openupgrade_wizard.tools.tools_system import dump_filestore
@ -17,34 +14,33 @@ from odoo_openupgrade_wizard.tools.tools_system import dump_filestore
"--database-path", "--database-path",
type=click.Path(writable=True, resolve_path=True), type=click.Path(writable=True, resolve_path=True),
required=True, required=True,
help="Destination path for the database dump relative to the project " help="Path to the database dump relative project folder.",
"directory.",
) )
@click.option( @click.option(
"--database-format", "--database-format",
type=click.Choice(("p", "c", "d", "t")), type=click.Choice(("p", "c", "d", "t")),
default="c", default="c",
help="Database format (see pg_dump options): plain SQL (p), " help="Database format (see pg_dump options): plain sql text (p), "
"custom format compressed (c), directory (d), or tar file (t).", "custom format compressed (c), directory (d), tar file (t).",
) )
@click.option( @click.option(
"--filestore-path", "--filestore-path",
type=click.Path(writable=True, resolve_path=True), type=click.Path(writable=True, resolve_path=True),
required=True, required=True,
help="Destination path for the filestore backup.", help="Path to the filestore backup.",
) )
@click.option( @click.option(
"--filestore-format", "--filestore-format",
type=click.Choice(("d", "t", "tgz")), type=click.Choice(("d", "t", "tgz")),
default="tgz", default="tgz",
help="Filestore format: directory (d), tar file (t), " help="Filestore format: directory (d), tar file (t), "
"or tar file compressed with gzip (tgz)", "tar file compressed with gzip (tgz)",
) )
@click.option( @click.option(
"--force", "--force",
is_flag=True, is_flag=True,
default=False, default=False,
help="Overwrite files if they already exist.", help="Overwrite file if they already exists.",
) )
@click.pass_context @click.pass_context
def dumpdb( def dumpdb(
@ -56,14 +52,7 @@ def dumpdb(
filestore_format, filestore_format,
force, force,
): ):
"""Create a dump of an Odoo database and filestore. """Create an dump of an Odoo database and its filestore."""
Creates a backup of the selected Odoo database and
its associated filestore. Both output locations must
reside inside the project directory. Existing files
will be overwritten if --force is specified.
"""
database_path = pathlib.Path(database_path) database_path = pathlib.Path(database_path)
filestore_path = pathlib.Path(filestore_path) filestore_path = pathlib.Path(filestore_path)
@ -75,7 +64,7 @@ def dumpdb(
): ):
ctx.fail( ctx.fail(
"database-path should be inside the project path to allow " "database-path should be inside the project path to allow "
"PostgreSQL to write to it." "postgresql to write to it."
) )
# Fail if dumps already exists and force argument not given # Fail if dumps already exists and force argument not given
@ -101,8 +90,6 @@ def dumpdb(
absolute_env_folder_path absolute_env_folder_path
) )
check_db_exist(ctx, database, raise_exception=True)
# dump the database # dump the database
output = execute_pg_dump( output = execute_pg_dump(
ctx, ctx,

View File

@ -17,43 +17,35 @@ from odoo_openupgrade_wizard.tools.tools_system import (
dir_okay=False, dir_okay=False,
), ),
default="./analysis.html", default="./analysis.html",
help="Path where the HTML analysis report will be saved. "
"Default is './analysis.html'",
) )
@click.option( @click.option(
"--extra-modules", "--extra-modules",
"extra_modules_list", "extra_modules_list",
# TODO, add a callback to check the quality of the argument # TODO, add a callback to check the quality of the argument
help="Comma-separated list of modules to analyze. If not set, " help="Coma separated modules to analyse. If not set, the modules.csv"
"modules.csv will be used. Example: 'account,product,base'.", " file will be used to define the list of module to analyse."
"Ex: 'account,product,base'",
) )
@click.option( @click.option(
"--time-unit", "--time-unit",
type=click.Choice(["hour", "minute", "separator"]), type=click.Choice(["hour", "minute", "separator"]),
default="separator", default="separator",
show_default=True, show_default=True,
help="Format to use for displaying time in the report. " help="Select unit to display time in the report. "
"*separator* display time as `HHH<sep>MM`, " "*separator* display time as `HHH<sep>MM`, "
"*hour* display time as decimal hour, " "*hour* display time as decimal hour, "
"*min* display time as minutes (rounded).", "*min* display time as minute (rounded).",
) )
@click.option( @click.option(
"--time-separator", "--time-separator",
default=":", default=":",
help="Character to use as a separator in time output. " help="Specify time separator, if time-unit is separator. "
"Used only if --time-unit=separator. Default is ':' (e.g. HHH:MM).", "Default to `:` (it will produce time like this HHH:MM).",
) )
@click.pass_context @click.pass_context
def estimate_workload( def estimate_workload(
ctx, analysis_file_path, extra_modules_list, time_unit, time_separator ctx, analysis_file_path, extra_modules_list, time_unit, time_separator
): ):
"""Estimate workload and create an analysis report.
This command estimates the workload required for an Odoo
migration based on the module set provided. The output is
an HTML report showing time estimations for each module.
"""
# Analyse # Analyse
analysis = Analysis(ctx) analysis = Analysis(ctx)

View File

@ -22,29 +22,11 @@ from odoo_openupgrade_wizard.tools.tools_odoo import (
exists=True, exists=True,
dir_okay=False, dir_okay=False,
), ),
help="""List of Python files to execute, with either an absolute path help="List of python files that will be executed, replacing the default"
or path relative to the project directory. With either method, the " scripts placed in the migration step folder.",
path must be located inside the project directory so that the
Docker container can access it.
Files will be executed in
the order listed. If no files are specified, all Python (.py) files
in the `step` directory will be sorted alphabetically and then
executed in order.
See README.md for more information and examples.""",
) )
@click.pass_context @click.pass_context
def execute_script_python(ctx, step, database, script_file_path): def execute_script_python(ctx, step, database, script_file_path):
"""Execute Python scripts for a migration step.
Executes one or more custom Python scripts in the context of a specific
migration step, using the Odoo shell (with full ORM access). This command
allows you to manually run Python logic outside of the default
post-migration.py file for a given step. It allows fine-tuning
of migration behavior by manually specifying logic.
"""
migration_step = get_migration_step_from_options(ctx, step) migration_step = get_migration_step_from_options(ctx, step)
execute_click_odoo_python_files( execute_click_odoo_python_files(

View File

@ -22,21 +22,11 @@ from odoo_openupgrade_wizard.tools.tools_postgres import (
exists=True, exists=True,
dir_okay=False, dir_okay=False,
), ),
help="List of SQL files to execute. Files will be executed in the order " help="List of SQL files that will be executed, replacing the default"
"listed. If no files are specified, all SQL files (.sql) in the " " scripts placed in the migration step folder.",
"step's directory will be sorted alphabetically and then executed "
"in order.",
) )
@click.pass_context @click.pass_context
def execute_script_sql(ctx, step, database, script_file_path): def execute_script_sql(ctx, step, database, script_file_path):
"""Execute SQL scripts for a migration step.
Executes one or more custom SQL scripts against the specified database,
using the PostgreSQL Docker container. This command allows you to manually
run SQL logic, allowing you to test or apply SQL changes in the context
of a specific migration step outside the automatic oow upgrade process.
"""
migration_step = get_migration_step_from_options(ctx, step) migration_step = get_migration_step_from_options(ctx, step)
execute_sql_files_pre_migration( execute_sql_files_pre_migration(

View File

@ -2,7 +2,7 @@ import click
from loguru import logger from loguru import logger
from odoo_openupgrade_wizard.cli.cli_options import ( from odoo_openupgrade_wizard.cli.cli_options import (
database_option_required, database_option,
get_migration_steps_from_options, get_migration_steps_from_options,
step_option, step_option,
) )
@ -23,29 +23,16 @@ from odoo_openupgrade_wizard.tools.tools_system import ensure_folder_writable
@click.command() @click.command()
@step_option @step_option
@database_option_required @database_option
@click.option( @click.option(
"-m", "-m",
"--modules", "--modules",
type=str, type=str,
help="Comma-separated list of modules to analyse." help="Coma-separated list of modules to analysis."
" Leave empty to analyse all the Odoo modules.", " Let empty to analyse all the Odoo modules.",
) )
@click.pass_context @click.pass_context
def generate_module_analysis(ctx, step, database, modules): def generate_module_analysis(ctx, step, database, modules):
"""Analyzes changes in data model & module data.
Performs an analysis between the target version (represented by the step
parameter) and the previous version to indicate how the data model and
module data have changed between the two versions (uses the
OCA/server-tools `upgrade_analysis` tool internally).
You can also use this function to analyze differences for custom & OCA
modules between several versions (e.g. in case of refactoring).
See the README.md for more information, including the location of the
resultant analysis files.
"""
migration_steps = get_migration_steps_from_options(ctx, step - 1, step) migration_steps = get_migration_steps_from_options(ctx, step - 1, step)
initial_step = migration_steps[0].copy() initial_step = migration_steps[0].copy()
@ -54,16 +41,17 @@ def generate_module_analysis(ctx, step, database, modules):
alternative_xml_rpc_port = ctx.obj["config"]["odoo_host_xmlrpc_port"] + 10 alternative_xml_rpc_port = ctx.obj["config"]["odoo_host_xmlrpc_port"] + 10
if not database: if not database:
database = ( database = "%s__analysis__" % (
f"{ctx.obj['config']['project_name'].replace('-', '_')}" ctx.obj["config"]["project_name"].replace("-", "_"),
"__analysis__"
) )
initial_database = ( initial_database = "%s_%s" % (
f"{database}_{str(initial_step['version']).replace('.', '')}" database,
str(initial_step["version"]).replace(".", ""),
) )
final_database = ( final_database = "%s_%s" % (
f"{database}_{str(final_step['version']).replace('.', '')}" database,
str(final_step["version"]).replace(".", ""),
) )
modules = modules and modules.split(",") or [] modules = modules and modules.split(",") or []
@ -87,7 +75,6 @@ def generate_module_analysis(ctx, step, database, modules):
database=initial_database, database=initial_database,
execution_context="openupgrade", execution_context="openupgrade",
detached_container=True, detached_container=True,
publish_ports=True,
) )
# INITIAL : install modules to analyse and generate records # INITIAL : install modules to analyse and generate records
initial_instance = OdooInstance(ctx, initial_database) initial_instance = OdooInstance(ctx, initial_database)
@ -108,6 +95,7 @@ def generate_module_analysis(ctx, step, database, modules):
stop_after_init=True, stop_after_init=True,
init=get_upgrade_analysis_module(final_step), init=get_upgrade_analysis_module(final_step),
execution_context="openupgrade", execution_context="openupgrade",
alternative_xml_rpc_port=alternative_xml_rpc_port,
) )
# name of the first odoo instance inside the second odoo instance # name of the first odoo instance inside the second odoo instance
@ -122,7 +110,6 @@ def generate_module_analysis(ctx, step, database, modules):
alternative_xml_rpc_port=alternative_xml_rpc_port, alternative_xml_rpc_port=alternative_xml_rpc_port,
execution_context="openupgrade", execution_context="openupgrade",
links={initial_container.name: odoo_initial_host_name}, links={initial_container.name: odoo_initial_host_name},
publish_ports=True,
) )
# FINAL : install modules to analyse and generate records # FINAL : install modules to analyse and generate records
@ -156,5 +143,5 @@ def generate_module_analysis(ctx, step, database, modules):
except (KeyboardInterrupt, SystemExit): except (KeyboardInterrupt, SystemExit):
logger.info("Received Keyboard Interrupt or System Exiting...") logger.info("Received Keyboard Interrupt or System Exiting...")
finally: finally:
kill_odoo(ctx, initial_database, initial_step) kill_odoo(ctx, initial_step)
kill_odoo(ctx, final_database, final_step) kill_odoo(ctx, final_step)

View File

@ -19,15 +19,11 @@ from odoo_openupgrade_wizard.tools.tools_system import git_aggregate
type=int, type=int,
default=10, default=10,
help="Jobs used to call the git-aggregate command." help="Jobs used to call the git-aggregate command."
" Reasonably set to 10 by default.", " reasonably set to 10 by default.",
) )
@click.pass_context @click.pass_context
def get_code(ctx, versions, jobs): def get_code(ctx, versions, jobs):
"""Get all required source code for each version. """Get code by running gitaggregate command for each version"""
Downloads all required Odoo source code and dependencies for each version
defined in your migration (uses the `gitaggregate` tools internally).
"""
for odoo_version in get_odoo_versions_from_options(ctx, versions): for odoo_version in get_odoo_versions_from_options(ctx, versions):
folder_path = get_odoo_env_path(ctx, odoo_version) folder_path = get_odoo_env_path(ctx, odoo_version)

View File

@ -1,61 +0,0 @@
from pathlib import Path
import click
from odoo_openupgrade_wizard.tools.tools_odoo import (
get_odoo_env_path,
get_odoo_modules_from_csv,
)
from odoo_openupgrade_wizard.tools.tools_odoo_module import Analysis
from odoo_openupgrade_wizard.tools.tools_system import (
ensure_file_exists_from_template,
)
@click.command()
@click.option(
"--extra-modules",
"extra_modules_list",
# TODO, add a callback to check the quality of the argument
help="Comma-separated list of modules to analyze. If not set, "
"modules.csv will be used. Example: 'account,product,base'.",
)
@click.pass_context
def guess_requirement(ctx, extra_modules_list):
"""Guess system & Python requirements for modules.
Analyzes the list of modules defined in your modules.csv file
to generate the required Python and Debian package dependencies per
environment.
For each module and each version, this command tries to parse the
corresponding __manifest__.py file (and, if present, the setup.py
file). It then appends any discovered requirements to the appropriate
addons_debian_requirements.txt and addons_python_requirements.txt files
present in each env directory.
"""
# Analyse
analysis = Analysis(ctx)
if extra_modules_list:
module_list = extra_modules_list.split(",")
else:
module_list = get_odoo_modules_from_csv(ctx.obj["module_file_path"])
analysis.analyse_module_version(ctx, module_list)
analysis.analyse_missing_module()
result = analysis.get_requirements(ctx)
for odoo_version in [x for x in ctx.obj["config"]["odoo_versions"]]:
path_version = get_odoo_env_path(ctx, odoo_version)
ensure_file_exists_from_template(
path_version / Path("addons_python_requirements.txt"),
"odoo/addons_python_requirements.txt.j2",
dependencies=result[odoo_version]["python"],
)
ensure_file_exists_from_template(
path_version / Path("addons_debian_requirements.txt"),
"odoo/addons_debian_requirements.txt.j2",
dependencies=result[odoo_version]["bin"],
)

View File

@ -19,58 +19,36 @@ from odoo_openupgrade_wizard.tools.tools_system import (
required=True, required=True,
prompt=True, prompt=True,
type=str, type=str,
help="Name of your project without spaces, special" help="Name of your project without spaces neither special"
" characters, or uppercases. Example: 'my-customer-9-12'." " chars or uppercases. exemple 'my-customer-9-12'."
" This will be used to tag the Odoo Docker images " " This will be used to tag with a friendly"
" with a friendly name.", " name the odoo docker images.",
) )
@click.option( @click.option(
"--initial-version", "--initial-version",
required=True, required=True,
prompt=True, prompt=True,
type=click.Choice(get_version_options("initial")), type=click.Choice(get_version_options("initial")),
help="Initial Odoo version to migrate from.",
) )
@click.option( @click.option(
"--final-version", "--final-version",
required=True, required=True,
prompt=True, prompt=True,
type=click.Choice(get_version_options("final")), type=click.Choice(get_version_options("final")),
help="Target Odoo version to migrate to.",
)
@click.option(
"--postgresql-version",
required=True,
prompt=True,
help="""The version of PostgreSQL that will be used
to create the PostgreSQL container. Example: '9.1', '16', etc.
The version should be available in Docker hub.
(https://hub.docker.com/_/postgres)
Avoid the 'latest' version if you want a deterministic installation.
Key Point: If your current production server uses PostgreSQL version A
and your future production server will use PostgreSQL version B,
you should select here a version X, with A <= X <= B.""",
) )
@click.option( @click.option(
"--extra-repository", "--extra-repository",
"extra_repository_list", "extra_repository_list",
# TODO, add a callback to check the quality of the argument # TODO, add a callback to check the quality of the argument
help="Comma-separated extra repositories to use in the Odoo environment." help="Coma separated extra repositories to use in the odoo environment."
"Example: 'OCA/web,OCA/server-tools,GRAP/grap-odoo-incubator'", "Ex: 'OCA/web,OCA/server-tools,GRAP/grap-odoo-incubator'",
) )
@click.pass_context @click.pass_context
def init( def init(
ctx, ctx, project_name, initial_version, final_version, extra_repository_list
project_name,
initial_version,
final_version,
postgresql_version,
extra_repository_list,
): ):
"""Initialize the OOW project environment. """Initialize OOW Environment based on the initial and
the final version of Odoo you want to migrate.
This command sets up the project folder structure, configuration
files, and default templates needed to begin an Odoo migration.
""" """
# Handle arguments # Handle arguments
@ -97,7 +75,7 @@ def init(
"name": 1, "name": 1,
"execution_context": "regular", "execution_context": "regular",
"version": odoo_versions[0], "version": odoo_versions[0],
"complete_name": f"step_01__regular__{odoo_versions[0]}", "complete_name": "step_01__regular__%s" % (odoo_versions[0]),
} }
] ]
# Add all Openupgrade steps # Add all Openupgrade steps
@ -108,9 +86,8 @@ def init(
"name": step_nbr, "name": step_nbr,
"execution_context": "openupgrade", "execution_context": "openupgrade",
"version": odoo_version, "version": odoo_version,
"complete_name": ( "complete_name": "step_%s__openupgrade__%s"
f"step_{step_nbr:>02}__openupgrade__{odoo_version}" % (str(step_nbr).rjust(2, "0"), odoo_version),
),
} }
) )
step_nbr += 1 step_nbr += 1
@ -122,9 +99,8 @@ def init(
"name": step_nbr, "name": step_nbr,
"execution_context": "regular", "execution_context": "regular",
"version": odoo_versions[-1], "version": odoo_versions[-1],
"complete_name": ( "complete_name": "step_%s__regular__%s"
f"step_{step_nbr:>02}__regular__{odoo_versions[-1]}" % (str(step_nbr).rjust(2, "0"), odoo_versions[-1]),
),
} }
) )
@ -136,7 +112,6 @@ def init(
ctx.obj["config_file_path"], ctx.obj["config_file_path"],
"config.yml.j2", "config.yml.j2",
project_name=project_name, project_name=project_name,
postgresql_version=postgresql_version,
steps=steps, steps=steps,
odoo_versions=odoo_versions, odoo_versions=odoo_versions,
) )
@ -156,30 +131,18 @@ def init(
path_version = get_odoo_env_path(ctx, odoo_version) path_version = get_odoo_env_path(ctx, odoo_version)
ensure_folder_exists(path_version) ensure_folder_exists(path_version)
# Create python requirements files # Create python requirements file
ensure_file_exists_from_template( ensure_file_exists_from_template(
path_version / Path("extra_python_requirements.txt"), path_version / Path("extra_python_requirements.txt"),
"odoo/extra_python_requirements.txt.j2", "odoo/extra_python_requirements.txt.j2",
) )
ensure_file_exists_from_template( # Create debian requirements file
path_version / Path("addons_python_requirements.txt"),
"odoo/addons_python_requirements.txt.j2",
dependencies={},
)
# Create debian requirements files
ensure_file_exists_from_template( ensure_file_exists_from_template(
path_version / Path("extra_debian_requirements.txt"), path_version / Path("extra_debian_requirements.txt"),
"odoo/extra_debian_requirements.txt.j2", "odoo/extra_debian_requirements.txt.j2",
) )
ensure_file_exists_from_template(
path_version / Path("addons_debian_requirements.txt"),
"odoo/addons_debian_requirements.txt.j2",
dependencies={},
)
# Create odoo config file # Create odoo config file
ensure_file_exists_from_template( ensure_file_exists_from_template(
path_version / Path("odoo.conf"), path_version / Path("odoo.conf"),

View File

@ -2,7 +2,7 @@ import click
from loguru import logger from loguru import logger
from odoo_openupgrade_wizard.cli.cli_options import ( from odoo_openupgrade_wizard.cli.cli_options import (
database_option_required, database_option,
demo_option, demo_option,
get_migration_step_from_options, get_migration_step_from_options,
) )
@ -16,27 +16,21 @@ from odoo_openupgrade_wizard.tools.tools_postgres import ensure_database
@click.command() @click.command()
@database_option_required @database_option
@demo_option @demo_option
@click.pass_context @click.pass_context
def install_from_csv(ctx, database, with_demo): def install_from_csv(ctx, database, with_demo):
"""Install modules from a CSV file.
This command reads the modules.csv file and installs the
modules listed for a specific Odoo version. The database will be
created if it doesn't exist.
"""
migration_step = get_migration_step_from_options(ctx, 1) migration_step = get_migration_step_from_options(ctx, 1)
ensure_database(ctx, database, state="present") ensure_database(ctx, database, state="present")
# Get modules list from the CSV file # Get modules list from the CSV file
module_names = get_odoo_modules_from_csv(ctx.obj["module_file_path"]) module_names = get_odoo_modules_from_csv(ctx.obj["module_file_path"])
module_names.sort() module_names.sort()
logger.info(f"Found {len(module_names)} modules.") logger.info("Found %d modules." % (len(module_names)))
logger.debug(module_names) logger.debug(module_names)
try: try:
logger.info(f"Install 'base' module on {database} database...") logger.info("Install 'base' module on %s database ..." % (database))
run_odoo( run_odoo(
ctx, ctx,
migration_step, migration_step,
@ -44,7 +38,6 @@ def install_from_csv(ctx, database, with_demo):
detached_container=True, detached_container=True,
init="base", init="base",
demo=with_demo, demo=with_demo,
publish_ports=True,
) )
odoo_instance = OdooInstance(ctx, database) odoo_instance = OdooInstance(ctx, database)
odoo_default_company = ctx.obj["config"].get( odoo_default_company = ctx.obj["config"].get(
@ -62,10 +55,12 @@ def install_from_csv(ctx, database, with_demo):
) )
if len(countries) != 1: if len(countries) != 1:
raise Exception( raise Exception(
f"Unable to find a country, based on the" "Unable to find a country, based on the code %s."
f" code {odoo_default_company['country_code']}." " countries found : %s "
f" Countries found:" % (
f" {', '.join([x.name for x in countries])}" odoo_default_company["country_code"],
", ".join([x.name for x in countries]),
)
) )
vals = { vals = {
"country_id": countries[0].id, "country_id": countries[0].id,
@ -86,4 +81,4 @@ def install_from_csv(ctx, database, with_demo):
except (KeyboardInterrupt, SystemExit): except (KeyboardInterrupt, SystemExit):
logger.info("Received Keyboard Interrupt or System Exiting...") logger.info("Received Keyboard Interrupt or System Exiting...")
finally: finally:
kill_odoo(ctx, database, migration_step) kill_odoo(ctx, migration_step)

View File

@ -6,8 +6,10 @@ def versions_options(function):
"-v", "-v",
"--versions", "--versions",
type=str, type=str,
help="Comma-separated Odoo versions to target. Leave empty to " help="Coma-separated values of odoo versions for which"
"perform the operation on all versions in the project", " you want to perform the operation."
" Let empty to perform the operation on all the versions"
" of the project",
)(function) )(function)
return function return function
@ -28,7 +30,7 @@ def first_step_option(function):
function = click.option( function = click.option(
"--first-step", "--first-step",
type=int, type=int,
help="First step to include in the operation.", help="First step for which to perform the operation",
)(function) )(function)
return function return function
@ -37,7 +39,7 @@ def last_step_option(function):
function = click.option( function = click.option(
"--last-step", "--last-step",
type=int, type=int,
help="Last step to include in the operation.", help="Last step for which to perform the operation",
)(function) )(function)
return function return function
@ -46,7 +48,17 @@ def demo_option(function):
function = click.option( function = click.option(
"--with-demo/--without-demo", "--with-demo/--without-demo",
default=False, default=False,
help="Whether to include demo data when creating the database.", help="Create database with or without demo data.",
)(function)
return function
def database_option(function):
function = click.option(
"-d",
"--database",
type=str,
help="Odoo Database for which you want to perform the operation.",
)(function) )(function)
return function return function
@ -57,9 +69,8 @@ def database_option_required(function):
"--database", "--database",
required=True, required=True,
prompt=True, prompt=True,
default="postgres",
type=str, type=str,
help="Name of the Odoo database to operate on. Default is 'postgres'.", help="Odoo Database for which you want to perform the operation.",
)(function) )(function)
return function return function
@ -82,7 +93,7 @@ def get_migration_step_from_options(ctx, step_arg):
if migration_step["name"] == step: if migration_step["name"] == step:
return migration_step return migration_step
raise ValueError( raise ValueError(
f"No migration step found in configuration for step {step_arg}" "No migration step found in configuration for step %s" % step_arg
) )
@ -103,6 +114,7 @@ def get_migration_steps_from_options(ctx, first_step_arg, last_step_arg):
return result return result
raise ValueError( raise ValueError(
"Unable to define steps in configuration from options." "Unable to define steps in configuration"
f" (first step {first_step_arg} ; last step {last_step_arg})" " from options. (first step %s ; last step %s)"
% (first_step_arg, last_step_arg)
) )

View File

@ -1,34 +1,19 @@
import click import click
from odoo_openupgrade_wizard.cli.cli_options import database_option_required from odoo_openupgrade_wizard.cli.cli_options import database_option
from odoo_openupgrade_wizard.tools.tools_postgres import execute_psql_command from odoo_openupgrade_wizard.tools.tools_postgres import execute_psql_command
@click.command(context_settings={"ignore_unknown_options": True}) @click.command(context_settings={"ignore_unknown_options": True})
@database_option_required @database_option
@click.option( @click.option("-c", "--command", "request")
"-c", @click.option("--pager/--no-pager", default=True)
"--command",
"request",
help="SQL command to execute inside the container.",
)
@click.option(
"--pager/--no-pager",
default=True,
help="Enable or disable pager when displaying output.",
)
@click.argument("psql_args", nargs=-1, type=click.UNPROCESSED) @click.argument("psql_args", nargs=-1, type=click.UNPROCESSED)
@click.pass_context @click.pass_context
def psql(ctx, request, database, pager, psql_args): def psql(ctx, request, database, pager, psql_args):
"""Run a SQL command in the PostgreSQL container. """Run psql in the postgres container. Fill any parameters of psql
as PSQLARGS.
This command executes the provided SQL command using `psql`
within the database container. Use --command for inline SQL
or pass additional arguments directly to psql via PSQLARGS.
See the README.md for more information.
""" """
result = execute_psql_command(ctx, request, database, psql_args) result = execute_psql_command(ctx, request, database, psql_args)
if pager: if pager:
click.echo_via_pager(result) click.echo_via_pager(result)

View File

@ -13,12 +13,8 @@ from odoo_openupgrade_wizard.tools.tools_system import execute_check_output
@versions_options @versions_options
@click.pass_context @click.pass_context
def pull_submodule(ctx, versions): def pull_submodule(ctx, versions):
"""Pull the repos.yml file from a Git repository. """Pull submodule that contains repos.yml file, if define in config.yml"""
This command runs `git submodule update --init --recursive`
for the submodule that contains your `repos.yml` file.
This ensures all nested submodules are also fetched.
"""
for odoo_version in get_odoo_versions_from_options(ctx, versions): for odoo_version in get_odoo_versions_from_options(ctx, versions):
version_cfg = ( version_cfg = (
ctx.obj["config"]["odoo_version_settings"][odoo_version] or {} ctx.obj["config"]["odoo_version_settings"][odoo_version] or {}
@ -26,7 +22,7 @@ def pull_submodule(ctx, versions):
if version_cfg.get("repo_url"): if version_cfg.get("repo_url"):
logger.info( logger.info(
f"Pull repos.yml from git repository" f"Pull repos.yml from git repository"
f" for version {odoo_version}..." f" for version {odoo_version} ..."
) )
submodule_path = ( submodule_path = (
get_odoo_env_path(ctx, odoo_version) / "repo_submodule" get_odoo_env_path(ctx, odoo_version) / "repo_submodule"
@ -57,5 +53,5 @@ def pull_submodule(ctx, versions):
else: else:
logger.warning( logger.warning(
f"No submodule configuration found" f"No submodule configuration found"
f" for version {odoo_version}..." f" for version {odoo_version} ..."
) )

View File

@ -12,30 +12,31 @@ from odoo_openupgrade_wizard.tools.tools_system import restore_filestore
@click.option( @click.option(
"--database-path", "--database-path",
required=True, required=True,
type=click.Path(readable=True, resolve_path=True, exists=True), type=click.Path(readable=True, resolve_path=True),
help="Path to the database dump (inside the environment folder).", help="Path to the database dump relative project folder.",
) )
@click.option( @click.option(
"--database-format", "--database-format",
required=True, required=True,
type=click.Choice(("c", "d", "t", "p")), type=click.Choice(("c", "d", "t")),
default="c", default="c",
help="Format of the database dump: custom (c), directory (d), tar (t), " help="Database format (see pg_dump options): "
"or plain SQL (p).", "custom format compressed (c), directory (d), tar file (t)."
"plain sql text (p) is not implemented",
) )
@click.option( @click.option(
"--filestore-path", "--filestore-path",
required=True, required=True,
type=click.Path(readable=True, resolve_path=True, exists=True), type=click.Path(readable=True, resolve_path=True),
help="Path to the filestore backup (inside the environment folder).", help="Path to the filestore backup.",
) )
@click.option( @click.option(
"--filestore-format", "--filestore-format",
required=True, required=True,
type=click.Choice(("d", "t", "tgz")), type=click.Choice(("d", "t", "tgz")),
default="tgz", default="tgz",
help="Format of the filestore: directory (d), tar (t), or gzip-compressed " help="Filestore format: directory (d), tar file (t), "
"tar (tgz).", "tar file compressed with gzip (tgz)",
) )
@click.pass_context @click.pass_context
def restoredb( def restoredb(
@ -46,13 +47,7 @@ def restoredb(
filestore_path, filestore_path,
filestore_format, filestore_format,
): ):
"""Restore a database and its associated filestore. """Restore an Odoo database and associated filestore."""
This command restores a PostgreSQL database and its matching Odoo
filestore into the current OOW environment. The filestore and
database dump must be accessible from within the environment directory
so that the Docker container can read them during restore.
"""
database_path = Path(database_path) database_path = Path(database_path)
filestore_path = Path(filestore_path) filestore_path = Path(filestore_path)
@ -62,9 +57,8 @@ def restoredb(
if not str(database_path).startswith(str(absolute_env_folder_path)): if not str(database_path).startswith(str(absolute_env_folder_path)):
ctx.fail( ctx.fail(
"database-path should be inside the project path to allow " "database-path should be inside the project path to allow "
"PostgreSQL to read it." "postgresql to read to it."
) )
# Restore the database # Restore the database
output = execute_pg_restore( output = execute_pg_restore(
ctx, ctx,

View File

@ -2,7 +2,7 @@ import click
from loguru import logger from loguru import logger
from odoo_openupgrade_wizard.cli.cli_options import ( from odoo_openupgrade_wizard.cli.cli_options import (
database_option_required, database_option,
demo_option, demo_option,
get_migration_step_from_options, get_migration_step_from_options,
step_option, step_option,
@ -13,7 +13,7 @@ from odoo_openupgrade_wizard.tools.tools_postgres import ensure_database
@click.command() @click.command()
@step_option @step_option
@database_option_required @database_option
@demo_option @demo_option
@click.option( @click.option(
"--stop-after-init", "--stop-after-init",
@ -27,13 +27,13 @@ from odoo_openupgrade_wizard.tools.tools_postgres import ensure_database
"-i", "-i",
"--init-modules", "--init-modules",
type=str, type=str,
help="List of modules to install. Equivalent to -i Odoo options.", help="List of modules to install. Equivalent to -i odoo options.",
) )
@click.option( @click.option(
"-u", "-u",
"--update-modules", "--update-modules",
type=str, type=str,
help="List of modules to update. Equivalent to -u Odoo options.", help="List of modules to update. Equivalent to -u odoo options.",
) )
@click.option( @click.option(
"-e", "-e",
@ -41,7 +41,7 @@ from odoo_openupgrade_wizard.tools.tools_postgres import ensure_database
type=click.Choice(["regular", "openupgrade"]), type=click.Choice(["regular", "openupgrade"]),
help="Force to use an openupgrade (OCA/openupgrade)" help="Force to use an openupgrade (OCA/openupgrade)"
" or a regular (odoo/odoo or OCA/OCB) base code when running odoo." " or a regular (odoo/odoo or OCA/OCB) base code when running odoo."
" Leave empty to use the default execution of the migration step.", " Let empty to use the defaut execution of the migration step.",
) )
@click.pass_context @click.pass_context
def run( def run(
@ -54,14 +54,6 @@ def run(
update_modules, update_modules,
execution_context, execution_context,
): ):
"""Run Odoo for a specific migration step.
The database will be created if it doesn't already exist. Unless the
`stop-after-init` flag is used, the Odoo instance will be available
on your host at the following URL: http://localhost:9069
(port depends on the `host_odoo_xmlrpc_port` setting in your
`config.yml` file).
"""
migration_step = get_migration_step_from_options(ctx, step) migration_step = get_migration_step_from_options(ctx, step)
ensure_database(ctx, database, state="present") ensure_database(ctx, database, state="present")
try: try:
@ -75,15 +67,15 @@ def run(
stop_after_init=stop_after_init, stop_after_init=stop_after_init,
demo=with_demo, demo=with_demo,
execution_context=execution_context, execution_context=execution_context,
publish_ports=True,
) )
if not stop_after_init: if not stop_after_init:
logger.info( logger.info(
"Odoo is available on your host at http://localhost:" "Odoo is available on your host at"
f"{ctx.obj['config']['odoo_host_xmlrpc_port']}" " http://localhost:%s"
% ctx.obj["config"]["odoo_host_xmlrpc_port"]
) )
input("Press 'Enter' to kill the odoo container and exit...") input("Press 'Enter' to kill the odoo container and exit ...")
except (KeyboardInterrupt, SystemExit): except (KeyboardInterrupt, SystemExit):
logger.info("Received Keyboard Interrupt or System Exiting...") logger.info("Received Keyboard Interrupt or System Exiting...")
finally: finally:
kill_odoo(ctx, database, migration_step) kill_odoo(ctx, migration_step)

View File

@ -25,22 +25,11 @@ from odoo_openupgrade_wizard.tools.tools_postgres import (
@demo_option @demo_option
@click.pass_context @click.pass_context
def upgrade(ctx, first_step, last_step, database, with_demo): def upgrade(ctx, first_step, last_step, database, with_demo):
"""Performs the full db migration across all steps.
For each step, this will:
1. Run `pre-migration.sql` scripts.
2. Apply "update all" (in an upgrade or update context).
3. Run `post-migration.py` scripts via XML-RPC/Odoo shell (via `odoorpc`).
"""
migration_steps = get_migration_steps_from_options( migration_steps = get_migration_steps_from_options(
ctx, first_step, last_step ctx, first_step, last_step
) )
for migration_step in migration_steps: for migration_step in migration_steps:
execute_sql_files_pre_migration(ctx, database, migration_step) execute_sql_files_pre_migration(ctx, database, migration_step)
if migration_step.get("update", True):
try: try:
run_odoo( run_odoo(
ctx, ctx,
@ -54,16 +43,5 @@ def upgrade(ctx, first_step, last_step, database, with_demo):
except (KeyboardInterrupt, SystemExit): except (KeyboardInterrupt, SystemExit):
logger.info("Received Keyboard Interrupt or System Exiting...") logger.info("Received Keyboard Interrupt or System Exiting...")
finally: finally:
kill_odoo(ctx, database, migration_step) kill_odoo(ctx, migration_step)
elif migration_step.get("execution_context") == "openupgrade":
raise ValueError(
"Incorrect setting 'update: True'"
" and 'execution_context: openupgrade'"
)
else:
logger.info(
"Skip update=all for"
f" step {migration_step.get('complete_name')}"
)
execute_click_odoo_python_files(ctx, database, migration_step) execute_click_odoo_python_files(ctx, database, migration_step)

View File

@ -2,14 +2,34 @@ from pathlib import Path
from loguru import logger from loguru import logger
FIRST_ODOO_VERSION_SUPPORTED = 8 _ODOO_VERSION_TEMPLATES = [
LAST_ODOO_VERSION_SUPPORTED = 18 {
"version": 8.0,
_ALL_ODOO_VERSIONS = [ },
float(x) {
for x in range( "version": 9.0,
FIRST_ODOO_VERSION_SUPPORTED, LAST_ODOO_VERSION_SUPPORTED + 1 },
) {
"version": 10.0,
},
{
"version": 11.0,
},
{
"version": 12.0,
},
{
"version": 13.0,
},
{
"version": 14.0,
},
{
"version": 15.0,
},
{
"version": 16.0,
},
] ]
@ -22,7 +42,7 @@ def get_version_options(mode: str) -> list:
Exemple: Exemple:
['9.0', '10.0', '11.0'] ['9.0', '10.0', '11.0']
""" """
version_options = [str(version) for version in _ALL_ODOO_VERSIONS] version_options = [str(x["version"]) for x in _ODOO_VERSION_TEMPLATES]
if mode == "initial": if mode == "initial":
version_options = version_options[:-1] version_options = version_options[:-1]
if mode == "final": if mode == "final":
@ -35,9 +55,12 @@ def get_odoo_versions(initial_version: float, final_version: float) -> list:
version version
""" """
result = [] result = []
for version in _ALL_ODOO_VERSIONS: for version_template in _ODOO_VERSION_TEMPLATES:
if version >= initial_version and version <= final_version: if (
result.append(version) version_template["version"] >= initial_version
and version_template["version"] <= final_version
):
result.append(version_template["version"])
return result return result
@ -51,7 +74,7 @@ def get_odoo_run_command(migration_step: dict) -> str:
def get_odoo_folder( def get_odoo_folder(
migration_step: dict, execution_context: str = None migration_step: dict, execution_context: str = False
) -> str: ) -> str:
"""return the main odoo folder, depending on the migration step. """return the main odoo folder, depending on the migration step.
(./src/odoo, ./src/openupgrade, ...)""" (./src/odoo, ./src/openupgrade, ...)"""
@ -100,7 +123,7 @@ def skip_addon_path(migration_step: dict, path: Path) -> bool:
def get_server_wide_modules_upgrade( def get_server_wide_modules_upgrade(
migration_step: dict, execution_context: str = None migration_step: dict, execution_context: str = False
) -> list: ) -> list:
"""return a list of modules to load, depending on the migration step.""" """return a list of modules to load, depending on the migration step."""
if ( if (
@ -125,8 +148,8 @@ def get_upgrade_analysis_module(migration_step: dict) -> str:
def generate_records(odoo_instance, migration_step: dict): def generate_records(odoo_instance, migration_step: dict):
logger.info( logger.info(
"Generate Records in version %s..." "Generate Records in version %s ..."
" (This may take a while)" % (migration_step["version"]) " (It can take a while)" % (migration_step["version"])
) )
if migration_step["version"] < 14.0: if migration_step["version"] < 14.0:
wizard = odoo_instance.browse_by_create( wizard = odoo_instance.browse_by_create(
@ -165,7 +188,7 @@ def generate_analysis_files(
): ):
logger.info( logger.info(
"Generate analysis files for" "Generate analysis files for"
" the modules installed on %s..." % (initial_database) " the modules installed on %s ..." % (initial_database)
) )
proxy_vals = { proxy_vals = {
"name": "Proxy to Previous version", "name": "Proxy to Previous version",
@ -176,12 +199,12 @@ def generate_analysis_files(
"password": "admin", "password": "admin",
} }
if final_step["version"] < 14.0: if final_step["version"] < 14.0:
logger.info("> Create proxy...") logger.info("> Create proxy ...")
proxy = final_odoo_instance.browse_by_create( proxy = final_odoo_instance.browse_by_create(
"openupgrade.comparison.config", proxy_vals "openupgrade.comparison.config", proxy_vals
) )
logger.info("> Create wizard...") logger.info("> Create wizard ...")
wizard = final_odoo_instance.browse_by_create( wizard = final_odoo_instance.browse_by_create(
"openupgrade.analysis.wizard", "openupgrade.analysis.wizard",
{ {
@ -189,16 +212,16 @@ def generate_analysis_files(
"write_files": True, "write_files": True,
}, },
) )
logger.info("> Launch analysis. This can take a while...") logger.info("> Launch analysis. This can take a while ...")
wizard.get_communication() wizard.get_communication()
else: else:
logger.info("> Create proxy...") logger.info("> Create proxy ...")
proxy = final_odoo_instance.browse_by_create( proxy = final_odoo_instance.browse_by_create(
"upgrade.comparison.config", proxy_vals "upgrade.comparison.config", proxy_vals
) )
logger.info("> Create wizard...") logger.info("> Create wizard ...")
analysis = final_odoo_instance.browse_by_create( analysis = final_odoo_instance.browse_by_create(
"upgrade.analysis", "upgrade.analysis",
{ {
@ -206,7 +229,7 @@ def generate_analysis_files(
}, },
) )
logger.info("> Launch analysis. This can take a while...") logger.info("> Launch analysis. This can take a while ...")
analysis.analyze() analysis.analyze()
@ -267,6 +290,6 @@ def get_openupgrade_analysis_files(
module_name = file.parent.parent.name module_name = file.parent.parent.name
result[module_name] = file result[module_name] = file
logger.debug( logger.debug(
"Version %s: %d analysis files found." % (version, len(result)) "Version %s : %d analysis files found." % (version, len(result))
) )
return result return result

View File

@ -1,23 +1,4 @@
<html> <html>
<head>
<script>
function ShowAll(){
var elements = document.getElementsByClassName('work_done');
for (var i = 0; i < elements.length; i ++) {
elements[i].style.display = 'table-row';
}
}
function HideWorkDone(){
var elements = document.getElementsByClassName('work_done');
for (var i = 0; i < elements.length; i ++) {
elements[i].style.display = 'none';
}
}
</script>
</head>
<body> <body>
<h1>Migration Analysis</h1> <h1>Migration Analysis</h1>
<table border="1" width="100%"> <table border="1" width="100%">
@ -37,14 +18,6 @@
<td>{{ current_date }}</td> <td>{{ current_date }}</td>
</tr> </tr>
</tbody> </tbody>
<tfoot>
<tr>
<td colspan="4" style="text-align: center;">
<a href="#" onclick="ShowAll()">Show All Modules</a>
<a href="#" onclick="HideWorkDone()">Hide Done Modules</a>
</td>
</tr>
</tfoot>
</table> </table>
<h2>Summary</h2> <h2>Summary</h2>
@ -60,31 +33,29 @@
<tr> <tr>
<td>Odoo</td> <td>Odoo</td>
<td>{{ analysis.get_module_qty('odoo') }}</td> <td>{{ analysis.get_module_qty('odoo') }}</td>
<td>{{ time_to_text(analysis.workload('odoo', False)) }}</td> <td>{{ analysis.workload_hour_text('odoo') }}</td>
</tr> </tr>
<tr> <tr>
<td>OCA</td> <td>OCA</td>
<td>{{ analysis.get_module_qty('oca') }}</td> <td>{{ analysis.get_module_qty('oca') }}</td>
<td>{{ time_to_text(analysis.workload('oca', False)) }}</td> <td>{{ analysis.workload_hour_text('oca') }}</td>
</tr> </tr>
<tr> <tr>
<td>Custom</td> <td>Custom</td>
<td>{{ analysis.get_module_qty('custom') }}</td> <td>{{ analysis.get_module_qty('custom') }}</td>
<td>{{ time_to_text(analysis.workload('custom', False)) }}</td> <td>{{ analysis.workload_hour_text('custom') }}</td>
</tr> </tr>
{%- if analysis.get_module_qty('not_found') -%}
<tr> <tr>
<td>Not Found</td> <td>Not Found</td>
<td>{{ analysis.get_module_qty('not_found') }}</td> <td>{{ analysis.get_module_qty('not_found') }}</td>
<td>&nbsp;</td> <td>&nbsp;</td>
</tr> </tr>
{%- endif -%}
</tbody> </tbody>
<tfood> <tfood>
<tr> <tr>
<th>Total</th> <th>Total</th>
<td>{{ analysis.get_module_qty() }}</td> <td>{{ analysis.get_module_qty() }}</td>
<td>{{ time_to_text(analysis.workload(False, False)) }}</td> <td>{{ analysis.workload_hour_text() }}</td>
</tr> </tr>
</tfood> </tfood>
</table> </table>
@ -95,100 +66,116 @@
<tr> <tr>
<th>&nbsp;</th> <th>&nbsp;</th>
<th>Total</th> <th>Total</th>
{%- for odoo_version in ctx.obj['config']['odoo_versions'] %} {%- for odoo_version in ctx.obj['config']['odoo_versions'] -%}
<th>{{ odoo_version }}</th> <th>{{ odoo_version }}</th>
{%- endfor %} {% endfor %}
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{%- set ns = namespace(current_repository='', current_module_type='') %} {% set ns = namespace(
current_repository='',
current_module_type='',
) %}
{% for odoo_module in analysis.modules %}
{%- for odoo_module in analysis.modules %} <!-- ---------------------- -->
{%- if (ns.current_module_type != odoo_module.module_type) %} <!-- Handle New Module Type -->
{%- set ns.current_module_type = odoo_module.module_type %} <!-- ---------------------- -->
<!-- ------------------------------------------------ --> {% if (
<!-- Handle New Module Type {{ns.current_module_type}}--> ns.current_module_type != odoo_module.module_type
<!-- ------------------------------------------------ --> and odoo_module.module_type != 'odoo') %}
{% set ns.current_module_type = odoo_module.module_type %}
<tr> <tr>
<th colspan="{{2 + ctx.obj['config']['odoo_versions']|length}}"> <th colspan="{{2 + ctx.obj['config']['odoo_versions']|length}}">
<h3>{{ odoo_module.module_type}}</h3> {{ ns.current_module_type}}
</th> </th>
<tr> <tr>
{%- endif %}
{%- if ns.current_repository != odoo_module.repository %}
{%- set ns.current_repository = odoo_module.repository %}
{%- set repository_workload = analysis.workload(False, odoo_module.repository) %}
<!-- ---------------------------------------------- -->
<!-- Handle New Repository {{ns.current_repository}}-->
<!-- ---------------------------------------------- -->
{%- if ns.current_repository %}
<tr class="{{repository_workload == 0 and 'work_done' or ''}}">
<th colspan="{{2 + ctx.obj['config']['odoo_versions']|length}}">
<h4>{{ odoo_module.repository}}
{% if repository_workload %}
({{ time_to_text(repository_workload) }})
{% endif %} {% endif %}
</h4>
<!-- -------------------- -->
<!-- Handle New Repository-->
<!-- -------------------- -->
{% if ns.current_repository != odoo_module.repository %}
{% set ns.current_repository = odoo_module.repository %}
{% if ns.current_repository %}
<tr>
<th colspan="{{2 + ctx.obj['config']['odoo_versions']|length}}">
{{ ns.current_repository}}
</th> </th>
<tr> <tr>
{%- endif %} {% endif %}
{%- endif %} {% endif %}
<tr class="{{odoo_module.workload == 0 and 'work_done' or ''}}"> <!-- -------------------- -->
<!-- Display Module Line -->
<!-- -------------------- -->
<tr>
<td>{{odoo_module.name}} <td>{{odoo_module.name}}
{%- if odoo_module.module_type == 'not_found' -%} {% if odoo_module.module_type == 'not_found' %}
{%- set odoo_apps_url = odoo_module.get_odoo_apps_url() -%} {% set odoo_apps_url = odoo_module.get_odoo_apps_url() %}
{%- if odoo_apps_url -%} {% if odoo_apps_url %}
<a href="{{odoo_apps_url}}" target="_blank">AppsStore</a> <a href="{{odoo_apps_url}}" target="_blank">AppsStore</a>
{%- else -%} {% else %}
{%- set odoo_code_search_url = odoo_module.get_odoo_code_search_url() -%} {% set odoo_code_search_url = odoo_module.get_odoo_code_search_url() %}
{%- if odoo_code_search_url -%} {% if odoo_code_search_url %}
<a href="{{odoo_code_search_url}}" target="_blank">OdooCodeSearch</a> <a href="{{odoo_code_search_url}}" target="_blank">
{%- endif -%} OdooCodeSearch
{%- endif -%} </a>
{%- endif -%} {% endif %}
</td> {% endif %}
<td> {% endif %}
{%- if odoo_module.workload -%}
{{time_to_text(odoo_module.workload)}}
{%- endif -%}
</td> </td>
{%- for version in odoo_module.analyse.all_versions %} <td>
{%- set module_version = odoo_module.get_module_version(version) %} {% if odoo_module.workload %}
{%- if module_version %} {{time_to_text(odoo_module.workload)}}
{%- set size_text = module_version.get_size_text() %} {% endif %}
{%- set analysis_text = module_version.get_analysis_text() %} </td>
{%- set workload = module_version.workload %}
{% for version in odoo_module.analyse.all_versions %}
{% set module_version = odoo_module.get_module_version(version) %}
{% if module_version %}
{% set size_text = module_version.get_size_text() %}
{% set analysis_text = module_version.get_analysis_text() %}
{% set workload = module_version.workload %}
<td style="background-color:{{module_version.get_bg_color()}};"> <td style="background-color:{{module_version.get_bg_color()}};">
{{module_version.get_text()}} {{module_version.get_text()}}
{%- if workload -%} {% if workload %}
<span style="background-color:lightblue;">({{time_to_text(workload)}})</span> <span style="background-color:lightblue;">
{%- endif -%} ({{time_to_text(workload)}})
{%- if size_text -%} </span>
<br/> {% endif %}
<span style="color:gray;font-size:11px;font-family:monospace;">({{ size_text}})</span> {% if size_text %}
{%- endif -%}
{%- if analysis_text -%}
<br/> <br/>
<span style="color:gray;font-size:11px;font-family:monospace;"> <span style="color:gray;font-size:11px;font-family:monospace;">
<a href="{{module_version.analysis_url()}}" target="_blank">({{ analysis_text}})</a> ({{ size_text}})
</span> </span>
{%- endif %} {% endif %}
{% if analysis_text %}
<br/>
<span style="color:gray;font-size:11px;font-family:monospace;">
<a href="{{module_version.analysis_url()}}" target="_blank">
({{ analysis_text}})
</a>
</span>
{% endif %}
</td> </td>
{%- else %} {% else %}
<td style="background-color:gray;">&nbsp;</td> <td style="background-color:gray;">&nbsp;</td>
{%- endif %} {% endif %}
{%- endfor %} {% endfor %}
</tr> </tr>
{%- endfor %}
{% endfor %}
</tbody> </tbody>
</table> </table>

View File

@ -1,9 +1,9 @@
project_name: {{ project_name }} project_name: {{ project_name }}
postgres_image_name: postgres:{{postgresql_version}} postgres_image_name: postgres:14
postgres_container_name: {{project_name}}-container-postgres-{{postgresql_version}} postgres_container_name: {{project_name}}-container-postgres
postgres_volume_name: {{project_name}}-volume-postgres-{{postgresql_version}} postgres_volume_name: {{project_name}}-volume-postgres
postgres_extra_settings: postgres_extra_settings:
@ -31,9 +31,6 @@ migration_steps:
version: {{ step['version'] }} version: {{ step['version'] }}
execution_context: {{ step['execution_context'] }} execution_context: {{ step['execution_context'] }}
complete_name: {{ step['complete_name'] }} complete_name: {{ step['complete_name'] }}
{%- if step['execution_context'] == 'regular'%}
update: True
{%- endif %}
{% endfor %} {% endfor %}
workload_settings: workload_settings:

View File

@ -13,7 +13,7 @@ RUN apt-get update && \
RUN dpkg-reconfigure locales && \ RUN dpkg-reconfigure locales && \
locale-gen C.UTF-8 && \ locale-gen C.UTF-8 && \
/usr/sbin/update-locale LANG=C.UTF-8 /usr/sbin/update-locale LANG=C.UTF-8
ENV LC_ALL=C.UTF-8 ENV LC_ALL C.UTF-8
RUN apt-get update -qq && \ RUN apt-get update -qq && \
apt-get upgrade -qq -y && \ apt-get upgrade -qq -y && \
@ -39,22 +39,18 @@ RUN apt-get update -qq && \
# <OOW> Install Debian packages # <OOW> Install Debian packages
COPY extra_debian_requirements.txt /extra_debian_requirements.txt COPY extra_debian_requirements.txt /extra_debian_requirements.txt
COPY addons_debian_requirements.txt /addons_debian_requirements.txt
RUN apt-get update -qq \ RUN apt-get update -qq \
&& apt-get install -y git \ && apt-get install -y git \
&& grep -vE '^\s*#|^\s*$' extra_debian_requirements.txt | xargs apt-get install -y --no-install-recommends \ && xargs apt-get install -y --no-install-recommends <extra_debian_requirements.txt \
&& grep -vE '^\s*#|^\s*$' addons_debian_requirements.txt | xargs apt-get install -y --no-install-recommends \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
# <OOW> Install Python librairies # <OOW> Install Python librairies
COPY ./src/odoo/requirements.txt /odoo_python_requirements.txt COPY ./src/odoo/requirements.txt /odoo_python_requirements.txt
COPY addons_python_requirements.txt /addons_python_requirements.txt
COPY extra_python_requirements.txt /extra_python_requirements.txt COPY extra_python_requirements.txt /extra_python_requirements.txt
RUN pip3 install --upgrade pip \ RUN pip3 install --upgrade pip \
&& python3 -m pip install --no-cache-dir setuptools-scm \ && python3 -m pip install --no-cache-dir setuptools-scm \
&& python3 -m pip install --no-cache-dir -r /odoo_python_requirements.txt \ && python3 -m pip install --no-cache-dir -r /odoo_python_requirements.txt \
&& python3 -m pip install --no-cache-dir -r /extra_python_requirements.txt \ && python3 -m pip install --no-cache-dir -r /extra_python_requirements.txt
&& python3 -m pip install --no-cache-dir -r /addons_python_requirements.txt
# <OOW> Get local user id and set it to the odoo user # <OOW> Get local user id and set it to the odoo user
ARG LOCAL_USER_ID ARG LOCAL_USER_ID

View File

@ -13,7 +13,7 @@ RUN apt-get update && \
RUN dpkg-reconfigure locales && \ RUN dpkg-reconfigure locales && \
locale-gen C.UTF-8 && \ locale-gen C.UTF-8 && \
/usr/sbin/update-locale LANG=C.UTF-8 /usr/sbin/update-locale LANG=C.UTF-8
ENV LC_ALL=C.UTF-8 ENV LC_ALL C.UTF-8
RUN apt-get update -qq && \ RUN apt-get update -qq && \
apt-get upgrade -qq -y && \ apt-get upgrade -qq -y && \
@ -39,22 +39,18 @@ RUN apt-get update -qq && \
# <OOW> Install Debian packages # <OOW> Install Debian packages
COPY extra_debian_requirements.txt /extra_debian_requirements.txt COPY extra_debian_requirements.txt /extra_debian_requirements.txt
COPY addons_debian_requirements.txt /addons_debian_requirements.txt
RUN apt-get update -qq \ RUN apt-get update -qq \
&& apt-get install -y git \ && apt-get install -y git \
&& grep -vE '^\s*#|^\s*$' extra_debian_requirements.txt | xargs apt-get install -y --no-install-recommends \ && xargs apt-get install -y --no-install-recommends <extra_debian_requirements.txt \
&& grep -vE '^\s*#|^\s*$' addons_debian_requirements.txt | xargs apt-get install -y --no-install-recommends \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
# <OOW> Install Python librairies # <OOW> Install Python librairies
COPY ./src/odoo/requirements.txt /odoo_python_requirements.txt COPY ./src/odoo/requirements.txt /odoo_python_requirements.txt
COPY addons_python_requirements.txt /addons_python_requirements.txt
COPY extra_python_requirements.txt /extra_python_requirements.txt COPY extra_python_requirements.txt /extra_python_requirements.txt
RUN pip3 install --upgrade pip \ RUN pip3 install --upgrade pip \
&& python3 -m pip install --no-cache-dir setuptools-scm \ && python3 -m pip install --no-cache-dir setuptools-scm \
&& python3 -m pip install --no-cache-dir -r /odoo_python_requirements.txt \ && python3 -m pip install --no-cache-dir -r /odoo_python_requirements.txt \
&& python3 -m pip install --no-cache-dir -r /extra_python_requirements.txt \ && python3 -m pip install --no-cache-dir -r /extra_python_requirements.txt
&& python3 -m pip install --no-cache-dir -r /addons_python_requirements.txt
# <OOW> Get local user id and set it to the odoo user # <OOW> Get local user id and set it to the odoo user
ARG LOCAL_USER_ID ARG LOCAL_USER_ID

View File

@ -1,9 +1,6 @@
# <OOW> : Copy of https://github.com/odoo/odoo/blob/13.0/setup/package.dfsrc # <OOW> : Copy of https://github.com/odoo/odoo/blob/13.0/setup/package.dfsrc
FROM debian:buster FROM debian:buster
RUN sed -i -- 's/security.debian.org/archive.debian.org/g' /etc/apt/**.list
RUN sed -i -- 's/deb.debian.org/archive.debian.org/g' /etc/apt/**.list
RUN apt-get update && \ RUN apt-get update && \
apt-get install -y locales && \ apt-get install -y locales && \
rm -rf /var/lib/apt/lists/* rm -rf /var/lib/apt/lists/*
@ -12,7 +9,7 @@ RUN apt-get update && \
RUN dpkg-reconfigure locales && \ RUN dpkg-reconfigure locales && \
locale-gen C.UTF-8 && \ locale-gen C.UTF-8 && \
/usr/sbin/update-locale LANG=C.UTF-8 /usr/sbin/update-locale LANG=C.UTF-8
ENV LC_ALL=C.UTF-8 ENV LC_ALL C.UTF-8
RUN apt-get update -qq && \ RUN apt-get update -qq && \
apt-get upgrade -qq -y && \ apt-get upgrade -qq -y && \
@ -38,22 +35,18 @@ RUN apt-get update -qq && \
# <OOW> Install Debian packages # <OOW> Install Debian packages
COPY extra_debian_requirements.txt /extra_debian_requirements.txt COPY extra_debian_requirements.txt /extra_debian_requirements.txt
COPY addons_debian_requirements.txt /addons_debian_requirements.txt
RUN apt-get update -qq \ RUN apt-get update -qq \
&& apt-get install -y git \ && apt-get install -y git \
&& grep -vE '^\s*#|^\s*$' extra_debian_requirements.txt | xargs apt-get install -y --no-install-recommends \ && xargs apt-get install -y --no-install-recommends <extra_debian_requirements.txt \
&& grep -vE '^\s*#|^\s*$' addons_debian_requirements.txt | xargs apt-get install -y --no-install-recommends \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
# <OOW> Install Python librairies # <OOW> Install Python librairies
COPY ./src/odoo/requirements.txt /odoo_python_requirements.txt COPY ./src/odoo/requirements.txt /odoo_python_requirements.txt
COPY addons_python_requirements.txt /addons_python_requirements.txt
COPY extra_python_requirements.txt /extra_python_requirements.txt COPY extra_python_requirements.txt /extra_python_requirements.txt
RUN pip3 install --upgrade pip \ RUN pip3 install --upgrade pip \
&& python3 -m pip install --no-cache-dir setuptools-scm \ && python3 -m pip install --no-cache-dir setuptools-scm \
&& python3 -m pip install --no-cache-dir -r /odoo_python_requirements.txt \ && python3 -m pip install --no-cache-dir -r /odoo_python_requirements.txt \
&& python3 -m pip install --no-cache-dir -r /extra_python_requirements.txt \ && python3 -m pip install --no-cache-dir -r /extra_python_requirements.txt
&& python3 -m pip install --no-cache-dir -r /addons_python_requirements.txt
# <OOW> Get local user id and set it to the odoo user # <OOW> Get local user id and set it to the odoo user
ARG LOCAL_USER_ID ARG LOCAL_USER_ID

View File

@ -1,9 +1,6 @@
# <OOW> : Copy of https://github.com/odoo/odoo/blob/14.0/setup/package.dfsrc # <OOW> : Copy of https://github.com/odoo/odoo/blob/14.0/setup/package.dfsrc
FROM debian:buster FROM debian:buster
RUN sed -i -- 's/security.debian.org/archive.debian.org/g' /etc/apt/**.list
RUN sed -i -- 's/deb.debian.org/archive.debian.org/g' /etc/apt/**.list
RUN apt-get update && \ RUN apt-get update && \
apt-get install -y locales && \ apt-get install -y locales && \
rm -rf /var/lib/apt/lists/* rm -rf /var/lib/apt/lists/*
@ -12,7 +9,7 @@ RUN apt-get update && \
RUN dpkg-reconfigure locales && \ RUN dpkg-reconfigure locales && \
locale-gen C.UTF-8 && \ locale-gen C.UTF-8 && \
/usr/sbin/update-locale LANG=C.UTF-8 /usr/sbin/update-locale LANG=C.UTF-8
ENV LC_ALL=C.UTF-8 ENV LC_ALL C.UTF-8
RUN apt-get update -qq && \ RUN apt-get update -qq && \
apt-get upgrade -qq -y && \ apt-get upgrade -qq -y && \
@ -31,22 +28,18 @@ RUN apt-get update -qq && \
# <OOW> Install Debian packages # <OOW> Install Debian packages
COPY extra_debian_requirements.txt /extra_debian_requirements.txt COPY extra_debian_requirements.txt /extra_debian_requirements.txt
COPY addons_debian_requirements.txt /addons_debian_requirements.txt
RUN apt-get update -qq \ RUN apt-get update -qq \
&& apt-get install -y git \ && apt-get install -y git \
&& grep -vE '^\s*#|^\s*$' extra_debian_requirements.txt | xargs apt-get install -y --no-install-recommends \ && xargs apt-get install -y --no-install-recommends <extra_debian_requirements.txt \
&& grep -vE '^\s*#|^\s*$' addons_debian_requirements.txt | xargs apt-get install -y --no-install-recommends \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
# <OOW> Install Python librairies # <OOW> Install Python librairies
COPY ./src/odoo/requirements.txt /odoo_python_requirements.txt COPY ./src/odoo/requirements.txt /odoo_python_requirements.txt
COPY addons_python_requirements.txt /addons_python_requirements.txt
COPY extra_python_requirements.txt /extra_python_requirements.txt COPY extra_python_requirements.txt /extra_python_requirements.txt
RUN pip3 install --upgrade pip \ RUN pip3 install --upgrade pip \
&& python3 -m pip install --no-cache-dir setuptools-scm \ && python3 -m pip install --no-cache-dir setuptools-scm \
&& python3 -m pip install --no-cache-dir -r /odoo_python_requirements.txt \ && python3 -m pip install --no-cache-dir -r /odoo_python_requirements.txt \
&& python3 -m pip install --no-cache-dir -r /extra_python_requirements.txt \ && python3 -m pip install --no-cache-dir -r /extra_python_requirements.txt
&& python3 -m pip install --no-cache-dir -r /addons_python_requirements.txt
# <OOW> Get local user id and set it to the odoo user # <OOW> Get local user id and set it to the odoo user
ARG LOCAL_USER_ID ARG LOCAL_USER_ID

View File

@ -1,9 +1,6 @@
# <OOW> : Copy of https://github.com/odoo/odoo/blob/15.0/setup/package.dfsrc # <OOW> : Copy of https://github.com/odoo/odoo/blob/15.0/setup/package.dfsrc
FROM debian:buster FROM debian:buster
RUN sed -i -- 's/security.debian.org/archive.debian.org/g' /etc/apt/**.list
RUN sed -i -- 's/deb.debian.org/archive.debian.org/g' /etc/apt/**.list
RUN apt-get update && \ RUN apt-get update && \
apt-get install -y locales && \ apt-get install -y locales && \
rm -rf /var/lib/apt/lists/* rm -rf /var/lib/apt/lists/*
@ -12,7 +9,7 @@ RUN apt-get update && \
RUN dpkg-reconfigure locales && \ RUN dpkg-reconfigure locales && \
locale-gen C.UTF-8 && \ locale-gen C.UTF-8 && \
/usr/sbin/update-locale LANG=C.UTF-8 /usr/sbin/update-locale LANG=C.UTF-8
ENV LC_ALL=C.UTF-8 ENV LC_ALL C.UTF-8
RUN apt-get update -qq && \ RUN apt-get update -qq && \
apt-get upgrade -qq -y && \ apt-get upgrade -qq -y && \
@ -31,22 +28,18 @@ RUN apt-get update -qq && \
# <OOW> Install Debian packages # <OOW> Install Debian packages
COPY extra_debian_requirements.txt /extra_debian_requirements.txt COPY extra_debian_requirements.txt /extra_debian_requirements.txt
COPY addons_debian_requirements.txt /addons_debian_requirements.txt
RUN apt-get update -qq \ RUN apt-get update -qq \
&& apt-get install -y git \ && apt-get install -y git \
&& grep -vE '^\s*#|^\s*$' extra_debian_requirements.txt | xargs apt-get install -y --no-install-recommends \ && xargs apt-get install -y --no-install-recommends <extra_debian_requirements.txt \
&& grep -vE '^\s*#|^\s*$' addons_debian_requirements.txt | xargs apt-get install -y --no-install-recommends \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
# <OOW> Install Python librairies # <OOW> Install Python librairies
COPY ./src/odoo/requirements.txt /odoo_python_requirements.txt COPY ./src/odoo/requirements.txt /odoo_python_requirements.txt
COPY addons_python_requirements.txt /addons_python_requirements.txt
COPY extra_python_requirements.txt /extra_python_requirements.txt COPY extra_python_requirements.txt /extra_python_requirements.txt
RUN pip3 install --upgrade pip \ RUN pip3 install --upgrade pip \
&& python3 -m pip install --no-cache-dir setuptools-scm \ && python3 -m pip install --no-cache-dir setuptools-scm \
&& python3 -m pip install --no-cache-dir -r /odoo_python_requirements.txt \ && python3 -m pip install --no-cache-dir -r /odoo_python_requirements.txt \
&& python3 -m pip install --no-cache-dir -r /extra_python_requirements.txt \ && python3 -m pip install --no-cache-dir -r /extra_python_requirements.txt
&& python3 -m pip install --no-cache-dir -r /addons_python_requirements.txt
# <OOW> Get local user id and set it to the odoo user # <OOW> Get local user id and set it to the odoo user
ARG LOCAL_USER_ID ARG LOCAL_USER_ID

View File

@ -1,9 +1,6 @@
# <OOW> : Copy of https://github.com/odoo/odoo/blob/16.0/setup/package.dfsrc # <OOW> : Copy of https://github.com/odoo/odoo/blob/16.0/setup/package.dfsrc
FROM debian:buster FROM debian:buster
RUN sed -i -- 's/security.debian.org/archive.debian.org/g' /etc/apt/**.list
RUN sed -i -- 's/deb.debian.org/archive.debian.org/g' /etc/apt/**.list
RUN apt-get update && \ RUN apt-get update && \
apt-get install -y locales && \ apt-get install -y locales && \
rm -rf /var/lib/apt/lists/* rm -rf /var/lib/apt/lists/*
@ -12,7 +9,7 @@ RUN apt-get update && \
RUN dpkg-reconfigure locales && \ RUN dpkg-reconfigure locales && \
locale-gen C.UTF-8 && \ locale-gen C.UTF-8 && \
/usr/sbin/update-locale LANG=C.UTF-8 /usr/sbin/update-locale LANG=C.UTF-8
ENV LC_ALL=C.UTF-8 ENV LC_ALL C.UTF-8
RUN apt-get update -qq && \ RUN apt-get update -qq && \
apt-get upgrade -qq -y && \ apt-get upgrade -qq -y && \
@ -31,22 +28,18 @@ RUN apt-get update -qq && \
# <OOW> Install Debian packages # <OOW> Install Debian packages
COPY extra_debian_requirements.txt /extra_debian_requirements.txt COPY extra_debian_requirements.txt /extra_debian_requirements.txt
COPY addons_debian_requirements.txt /addons_debian_requirements.txt
RUN apt-get update -qq \ RUN apt-get update -qq \
&& apt-get install -y git \ && apt-get install -y git \
&& grep -vE '^\s*#|^\s*$' extra_debian_requirements.txt | xargs apt-get install -y --no-install-recommends \ && xargs apt-get install -y --no-install-recommends <extra_debian_requirements.txt \
&& grep -vE '^\s*#|^\s*$' addons_debian_requirements.txt | xargs apt-get install -y --no-install-recommends \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
# <OOW> Install Python librairies # <OOW> Install Python librairies
COPY ./src/odoo/requirements.txt /odoo_python_requirements.txt COPY ./src/odoo/requirements.txt /odoo_python_requirements.txt
COPY addons_python_requirements.txt /addons_python_requirements.txt
COPY extra_python_requirements.txt /extra_python_requirements.txt COPY extra_python_requirements.txt /extra_python_requirements.txt
RUN pip3 install --upgrade pip \ RUN pip3 install --upgrade pip \
&& python3 -m pip install --no-cache-dir setuptools-scm \ && python3 -m pip install --no-cache-dir setuptools-scm \
&& python3 -m pip install --no-cache-dir -r /odoo_python_requirements.txt \ && python3 -m pip install --no-cache-dir -r /odoo_python_requirements.txt \
&& python3 -m pip install --no-cache-dir -r /extra_python_requirements.txt \ && python3 -m pip install --no-cache-dir -r /extra_python_requirements.txt
&& python3 -m pip install --no-cache-dir -r /addons_python_requirements.txt
# <OOW> Get local user id and set it to the odoo user # <OOW> Get local user id and set it to the odoo user
ARG LOCAL_USER_ID ARG LOCAL_USER_ID

View File

@ -1,54 +0,0 @@
# <OOW> : Copy of https://github.com/odoo/odoo/blob/17.0/setup/package.dfsrc
FROM debian:bookworm
RUN apt-get update && \
apt-get install -y locales && \
rm -rf /var/lib/apt/lists/*
# Reconfigure locales such that postgresql uses UTF-8 encoding
RUN dpkg-reconfigure locales && \
locale-gen C.UTF-8 && \
/usr/sbin/update-locale LANG=C.UTF-8
ENV LC_ALL=C.UTF-8
RUN apt-get update -qq && \
apt-get upgrade -qq -y && \
apt-get install \
postgresql \
postgresql-server-dev-all \
postgresql-client \
adduser \
libldap2-dev \
libsasl2-dev \
python3-pip \
python3-venv \
python3-wheel \
build-essential \
python3 -y && \
rm -rf /var/lib/apt/lists/*
# <OOW> Install Debian packages
COPY extra_debian_requirements.txt /extra_debian_requirements.txt
COPY addons_debian_requirements.txt /addons_debian_requirements.txt
RUN apt-get update -qq \
&& apt-get install -y git \
&& grep -vE '^\s*#|^\s*$' extra_debian_requirements.txt | xargs apt-get install -y --no-install-recommends \
&& grep -vE '^\s*#|^\s*$' addons_debian_requirements.txt | xargs apt-get install -y --no-install-recommends \
&& rm -rf /var/lib/apt/lists/*
# <OOW> Install Python librairies
COPY ./src/odoo/requirements.txt /odoo_python_requirements.txt
COPY addons_python_requirements.txt /addons_python_requirements.txt
COPY extra_python_requirements.txt /extra_python_requirements.txt
RUN pip3 install --upgrade pip --break-system-packages \
&& python3 -m pip install --no-cache-dir setuptools-scm --break-system-packages \
&& python3 -m pip install --no-cache-dir -r /odoo_python_requirements.txt --break-system-packages \
&& python3 -m pip install --no-cache-dir -r /extra_python_requirements.txt --break-system-packages \
&& python3 -m pip install --no-cache-dir -r /addons_python_requirements.txt --break-system-packages
# <OOW> Get local user id and set it to the odoo user
ARG LOCAL_USER_ID
RUN useradd --uid $LOCAL_USER_ID --non-unique odoo
USER odoo

View File

@ -1,54 +0,0 @@
# <OOW> : Copy of https://github.com/odoo/odoo/blob/18.0/setup/package.dfsrc
FROM debian:bookworm
RUN apt-get update && \
apt-get install -y locales && \
rm -rf /var/lib/apt/lists/*
# Reconfigure locales such that postgresql uses UTF-8 encoding
RUN dpkg-reconfigure locales && \
locale-gen C.UTF-8 && \
/usr/sbin/update-locale LANG=C.UTF-8
ENV LC_ALL=C.UTF-8
RUN apt-get update -qq && \
apt-get upgrade -qq -y && \
apt-get install \
postgresql \
postgresql-server-dev-all \
postgresql-client \
adduser \
libldap2-dev \
libsasl2-dev \
python3-pip \
python3-venv \
python3-wheel \
build-essential \
python3 -y && \
rm -rf /var/lib/apt/lists/*
# <OOW> Install Debian packages
COPY extra_debian_requirements.txt /extra_debian_requirements.txt
COPY addons_debian_requirements.txt /addons_debian_requirements.txt
RUN apt-get update -qq \
&& apt-get install -y git \
&& grep -vE '^\s*#|^\s*$' extra_debian_requirements.txt | xargs apt-get install -y --no-install-recommends \
&& grep -vE '^\s*#|^\s*$' addons_debian_requirements.txt | xargs apt-get install -y --no-install-recommends \
&& rm -rf /var/lib/apt/lists/*
# <OOW> Install Python librairies
COPY ./src/odoo/requirements.txt /odoo_python_requirements.txt
COPY addons_python_requirements.txt /addons_python_requirements.txt
COPY extra_python_requirements.txt /extra_python_requirements.txt
RUN pip3 install --upgrade pip --break-system-packages \
&& python3 -m pip install --no-cache-dir setuptools-scm --break-system-packages \
&& python3 -m pip install --no-cache-dir -r /odoo_python_requirements.txt --break-system-packages \
&& python3 -m pip install --no-cache-dir -r /extra_python_requirements.txt --break-system-packages \
&& python3 -m pip install --no-cache-dir -r /addons_python_requirements.txt --break-system-packages
# <OOW> Get local user id and set it to the odoo user
ARG LOCAL_USER_ID
RUN useradd --uid $LOCAL_USER_ID --non-unique odoo
USER odoo

View File

@ -1,8 +0,0 @@
# Do NOT edit manually. Changes here will be overwritten by the command 'guess-requirement'
{%- for bin_lib, module_list in dependencies.items() %}
# Required by the module(s): {{ ','.join(module_list) }}
{{ bin_lib }}
{%- endfor %}

View File

@ -1,8 +0,0 @@
# Do NOT edit manually. Changes here will be overwritten by the command 'guess-requirement'
{%- for python_lib, module_list in dependencies.items() %}
# Required by the module(s): {{ ','.join(module_list) }}
{{ python_lib }}
{%- endfor %}

View File

@ -7,7 +7,3 @@
# - http or xml port # - http or xml port
# - etc. # - etc.
# Only non standard or special options should be specified here. # Only non standard or special options should be specified here.
[options]
# Disable default memory limit of 2560 MiB
limit_memory_hard = 0

View File

@ -1,7 +1,7 @@
import logging import logging
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
_logger.info("Executing post-migration.py script...") _logger.info("Executing post-migration.py script ...")
env = env # noqa: F821 env = env # noqa: F821

View File

@ -3,21 +3,19 @@ import shutil
from loguru import logger from loguru import logger
from odoo_openupgrade_wizard.tools.tools_postgres import ( from odoo_openupgrade_wizard.tools.tools_postgres import (
check_db_exist,
ensure_database, ensure_database,
execute_sql_request,
) )
def copydb(ctx, source, dest): def copydb(ctx, source, dest):
# check if source exists
logger.info(f"Check if source database '{source}' exists...")
check_db_exist(ctx, source, raise_exception=True)
# drop database if exist # drop database if exist
ensure_database(ctx, dest, state="absent") ensure_database(ctx, dest, state="absent")
# Copy database # Copy database
ensure_database(ctx, dest, state="present", template=source) logger.info(f"Copy database {source} into {dest} ...")
request = f"CREATE DATABASE {dest} WITH TEMPLATE {source};"
execute_sql_request(ctx, request)
main_path = ctx.obj["filestore_folder_path"] / "filestore" main_path = ctx.obj["filestore_folder_path"] / "filestore"
source_path = main_path / source source_path = main_path / source
@ -27,17 +25,14 @@ def copydb(ctx, source, dest):
shutil.rmtree(dest_path, ignore_errors=True) shutil.rmtree(dest_path, ignore_errors=True)
# Copy Filestore # Copy Filestore
logger.info(f"Copy filestore of '{source}' into '{dest}' directory...") logger.info(f"Copy filestore of '{source}' into '{dest}' folder ...")
shutil.copytree(source_path, dest_path) shutil.copytree(source_path, dest_path)
def dropdb(ctx, database): def dropdb(ctx, database):
"""Drop a database and its filestore""" """Drop a database and its filestore"""
# Check if database exists
logger.info(f"Check if database '{database}' exists...")
check_db_exist(ctx, database, raise_exception=True)
# Drop database # Drop database
logger.info(f"Drop database '{database}'...") logger.info(f"Drop database '{database}' if it exists...")
ensure_database(ctx, database, state="absent") ensure_database(ctx, database, state="absent")
# Drop filestore # Drop filestore
root_filestore_path = ctx.obj["filestore_folder_path"] / "filestore" root_filestore_path = ctx.obj["filestore_folder_path"] / "filestore"

View File

@ -1,5 +1,3 @@
import time
import docker import docker
from loguru import logger from loguru import logger
@ -15,29 +13,21 @@ def pull_image(image_name):
def build_image(path, tag, buildargs={}): def build_image(path, tag, buildargs={}):
logger.debug( logger.debug(
f"Building image named based on {path}/Dockerfile." "Building image named based on %s/Dockerfile."
" This can take a long time..." " This can take a big while ..." % (path)
) )
debug_docker_command = f"docker build {path} --tag {tag}" debug_docker_command = "docker build %s --tag %s" % (path, tag)
for arg_name, arg_value in buildargs.items(): for arg_name, arg_value in buildargs.items():
debug_docker_command += f"\\\n --build-arg {arg_name}={arg_value}" debug_docker_command += f"\\\n --build-arg {arg_name}={arg_value}"
logger.debug(f"DOCKER COMMAND:\n\n{debug_docker_command}\n") logger.debug("DOCKER COMMAND:\n\n%s\n" % debug_docker_command)
docker_client = get_docker_client() docker_client = get_docker_client()
try:
image = docker_client.images.build( image = docker_client.images.build(
path=str(path), path=str(path),
tag=tag, tag=tag,
buildargs=buildargs, buildargs=buildargs,
rm=True,
) )
logger.debug("Image build done.") logger.debug("Image build.")
except docker.errors.BuildError as buildError:
logger.error("\n".join([str(log) for log in buildError.build_log]))
logger.error("Image build failed.")
raise buildError
return image return image
@ -55,29 +45,32 @@ def run_container(
client = get_docker_client() client = get_docker_client()
if not client.images.list(filters={"reference": image_name}): if not client.images.list(filters={"reference": image_name}):
raise Exception( raise Exception(
f"The image {image_name} is not available on your system." "The image %s is not available on your system."
" Did you run 'odoo-openupgrade-wizard docker-build' ?" " Did you run 'odoo-openupgrade-wizard docker-build' ?"
% image_name
) )
logger.debug(f"Launching Docker container named {image_name}...") logger.debug("Launching Docker container named %s ..." % (image_name))
debug_docker_command = f"docker run --name {container_name}\\\n" debug_docker_command = "docker run --name %s\\\n" % (container_name)
for k, v in ports.items(): for k, v in ports.items():
debug_docker_command += f" --publish {k}:{v}\\\n" debug_docker_command += " --publish {k}:{v}\\\n".format(k=k, v=v)
for k, v in volumes.items(): for k, v in volumes.items():
debug_docker_command += f" --volume {k}:{v}\\\n" debug_docker_command += " --volume {k}:{v}\\\n".format(
k=str(k), v=str(v)
)
for k, v in environments.items(): for k, v in environments.items():
debug_docker_command += f" --env {k}={v}\\\n" debug_docker_command += " --env {k}={v}\\\n".format(k=k, v=v)
for k, v in links.items(): for k, v in links.items():
debug_docker_command += f" --link {k}:{v}\\\n" debug_docker_command += " --link {k}:{v}\\\n".format(k=k, v=v)
if auto_remove: if auto_remove:
debug_docker_command += " --rm" debug_docker_command += " --rm"
if detach: if detach:
debug_docker_command += " --detach" debug_docker_command += " --detach"
debug_docker_command += f" {image_name}" debug_docker_command += " %s" % (image_name)
if command: if command:
debug_docker_command += f" \\\n{command}" debug_docker_command += " \\\n%s" % (command)
logger.debug(f"DOCKER COMMAND:\n{debug_docker_command}") logger.debug("DOCKER COMMAND:\n%s" % debug_docker_command)
container = client.containers.run( container = client.containers.run(
image_name, image_name,
@ -91,7 +84,7 @@ def run_container(
auto_remove=auto_remove, auto_remove=auto_remove,
) )
if detach: if detach:
logger.debug(f"Container {image_name} launched.") logger.debug("Container %s launched." % image_name)
elif auto_remove: elif auto_remove:
logger.debug("Container closed.") logger.debug("Container closed.")
@ -99,43 +92,27 @@ def run_container(
def exec_container(container, command): def exec_container(container, command):
debug_docker_command = f"docker exec {container.name}" debug_docker_command = "docker exec %s" % (container.name)
debug_docker_command += f" \\\n{command}" debug_docker_command += " \\\n%s" % (command)
logger.debug(f"DOCKER COMMAND:\n{debug_docker_command}") logger.debug("DOCKER COMMAND:\n%s" % debug_docker_command)
docker_result = container.exec_run(command) docker_result = container.exec_run(command)
if docker_result.exit_code != 0: if docker_result.exit_code != 0:
raise Exception( raise Exception(
f"The command failed in the container {container.name}.\n" "The command failed in the container %s.\n"
f"- Command: {command}\n" "- Command : %s\n"
f"- Exit Code: {docker_result.exit_code}\n" "- Exit Code : %d\n"
f"- Output: {docker_result.output}" "- Output: %s"
% (
container.name,
command,
docker_result.exit_code,
docker_result.output,
)
) )
return docker_result return docker_result
def kill_container(container_name): def kill_container(container_name):
# In some situation, containers.list return
# containers with removal already in progress
# when we call container.remove(), it is raising an
# docker.errors.APIError
# "removal of container xx is already in progress".
# so, we retry a few seconds after
# and raise an exception after five failures.
for i in [1, 5, 10, 60, False]:
try:
_kill_container(container_name)
return
except docker.errors.APIError as e:
if not i:
logger.error(f"Fail to kill {container_name} after 5 retries")
raise e
logger.warning(
f"Fail to kill {container_name}. Retrying in {i} seconds"
)
time.sleep(i)
def _kill_container(container_name):
client = get_docker_client() client = get_docker_client()
try: try:
@ -145,7 +122,7 @@ def _kill_container(container_name):
ignore_removed=True, ignore_removed=True,
) )
except docker.errors.NotFound as err: except docker.errors.NotFound as err:
logger.debug(f"Cannot kill container {container_name}: {err}") logger.debug(f"Cannot kill container {container_name}: " + str(err))
containers = [] containers = []
for container in containers: for container in containers:
@ -156,14 +133,12 @@ def _kill_container(container_name):
) )
try: try:
container.stop() container.stop()
container.wait()
logger.debug(
"Container %s status is now '%s'."
% (container.name, container.status)
)
if container.status != "removed":
container.remove()
container.wait(condition="removed")
except docker.errors.NotFound as err: except docker.errors.NotFound as err:
logger.debug(f"Cannot kill container {container.name}: {err}") logger.debug(
f"Cannot kill container {container.name}: " + str(err)
)
# TODO, we should here filter by name
# but filters={"name": container_name}
# doesn't work...
client.containers.prune()

View File

@ -5,6 +5,7 @@ import sys
import traceback import traceback
from pathlib import Path from pathlib import Path
# import docker
import yaml import yaml
from loguru import logger from loguru import logger
@ -23,8 +24,6 @@ from odoo_openupgrade_wizard.tools.tools_docker import (
from odoo_openupgrade_wizard.tools.tools_postgres import get_postgres_container from odoo_openupgrade_wizard.tools.tools_postgres import get_postgres_container
from odoo_openupgrade_wizard.tools.tools_system import get_script_folder from odoo_openupgrade_wizard.tools.tools_system import get_script_folder
DEFAULT_ODOO_HTTP_PORT = 8069
def get_repo_file_path(ctx, odoo_version: float) -> Path: def get_repo_file_path(ctx, odoo_version: float) -> Path:
"""return the relative path of the repos.yml file """return the relative path of the repos.yml file
@ -52,7 +51,7 @@ def get_odoo_addons_path(
ctx, ctx,
odoo_env_path: Path, odoo_env_path: Path,
migration_step: dict, migration_step: dict,
execution_context: str = None, execution_context: str = False,
) -> str: ) -> str:
"""Return """Return
- addons_path: a list of Path of that contains odoo module - addons_path: a list of Path of that contains odoo module
@ -88,33 +87,14 @@ def get_odoo_addons_path(
def is_addons_path(ctx, path: Path, migration_step: dict): def is_addons_path(ctx, path: Path, migration_step: dict):
logger.debug(f"Untersuche Pfad: {path}")
for folder in [x for x in path.iterdir() if x.is_dir()]: for folder in [x for x in path.iterdir() if x.is_dir()]:
logger.debug(f" Untersuche Pfad: {folder}")
if (folder / "__init__.py").exists() and ( if (folder / "__init__.py").exists() and (
folder / get_manifest_name(migration_step) folder / get_manifest_name(migration_step)
).exists(): ).exists():
logger.info(f" ✔️ Odoo-Modul gefunden in: {folder}")
return True return True
return False return False
#def is_addons_path(ctx, path: Path, migration_step: dict):
# """Prüft, ob im Verzeichnis mindestens ein Odoo-Modul liegt."""
# logger.debug(f"Untersuche Pfad: {path}")
# for folder in path.iterdir():
# if folder.is_dir():
# init_exists = (folder / "__init__.py").exists()
# manifest_path = folder / get_manifest_name(migration_step)
# manifest_exists = manifest_path.exists()
# logger.debug(
# f" Untersuche Unterordner: {folder} | __init__.py: {init_exists}, Manifest: {manifest_path.name}: {manifest_exists}"
# )
# if init_exists and manifest_exists:
# logger.info(f" ✔️ Odoo-Modul gefunden in: {folder}")
# return True
# return False
def get_odoo_env_path(ctx, odoo_version: float) -> Path: def get_odoo_env_path(ctx, odoo_version: float) -> Path:
folder_name = "env_%s" % str(odoo_version).rjust(4, "0") folder_name = "env_%s" % str(odoo_version).rjust(4, "0")
return ctx.obj["src_folder_path"] / folder_name return ctx.obj["src_folder_path"] / folder_name
@ -128,34 +108,32 @@ def get_docker_image_tag(ctx, odoo_version: float) -> str:
) )
def get_docker_container_name(ctx, database: str, migration_step: dict) -> str: def get_docker_container_name(ctx, migration_step: dict) -> str:
"""Return a docker container name, based on project name, database name, """Return a docker container name, based on project name,
odoo version and migration step""" odoo version and migration step"""
return "oow-{project}-{database}-{version}-step-{step}".format( return "odoo-openupgrade-wizard-container__%s__%s__step-%s" % (
project=ctx.obj["config"]["project_name"], ctx.obj["config"]["project_name"],
database=database, str(migration_step["version"]).rjust(4, "0"),
# FIXME: version should be a string, but it is a float str(migration_step["name"]).rjust(2, "0"),
version=str(migration_step["version"]).rjust(4, "0"),
step=str(migration_step["name"]).rjust(2, "0"),
) )
def generate_odoo_command_options( def generate_odoo_command(
ctx, ctx,
migration_step: dict, migration_step: dict,
execution_context: str, execution_context: str,
database: str, database: str,
demo: bool = False, demo: bool = False,
update: str = None, update: str = False,
init: str = None, init: str = False,
stop_after_init: bool = False, stop_after_init: bool = False,
) -> list: shell: bool = False,
""" ) -> str:
Generate Odoo command options as a list of string to append to any command.
"""
odoo_env_path = get_odoo_env_path(ctx, migration_step["version"]) odoo_env_path = get_odoo_env_path(ctx, migration_step["version"])
# Compute 'server_wide_modules' # Compute 'server_wide_modules'
# For that purpose, read the custom odoo.conf file
# to know if server_wide_modules is defined
custom_odoo_config_file = odoo_env_path / "odoo.conf" custom_odoo_config_file = odoo_env_path / "odoo.conf"
parser = configparser.RawConfigParser() parser = configparser.RawConfigParser()
parser.read(custom_odoo_config_file) parser.read(custom_odoo_config_file)
@ -166,22 +144,10 @@ def generate_odoo_command_options(
migration_step, execution_context migration_step, execution_context
) )
# Compute 'addons_path' # compute 'addons_path'
addons_path_list, empty_addons_path_list = get_odoo_addons_path( addons_path_list, empty_addons_path_list = get_odoo_addons_path(
ctx, odoo_env_path, migration_step, execution_context ctx, odoo_env_path, migration_step, execution_context
) )
# custom_addons from odoo_version_settings[version]
version = migration_step["version"]
#TODO: check if custom_addons is in ctx.obj['config']
custom_addons = ctx.obj.get("config", {}).get("odoo_version_settings", {}).get(version, {}).get("custom_addons", [])
for path in custom_addons:
logger.info(f"📁 custom_addons: adding path '{path}'")
addons_path_list.append(path)
# Normalize addons path (relative to /odoo_env in container)
addons_path = ",".join( addons_path = ",".join(
[str(Path("/odoo_env") / x) for x in addons_path_list] [str(Path("/odoo_env") / x) for x in addons_path_list]
) )
@ -192,91 +158,64 @@ def generate_odoo_command_options(
" because it doesn't contain any odoo module." " because it doesn't contain any odoo module."
) )
# Compute 'log_file' # compute 'log_file'
log_file_name = ( log_file_name = "{}____{}.log".format(
f"{ctx.obj['log_prefix']}____{migration_step['complete_name']}.log" ctx.obj["log_prefix"], migration_step["complete_name"]
) )
log_file_docker_path = f"/env/log/{log_file_name}" log_file_docker_path = "/env/log/%s" % log_file_name
# Build options string database_cmd = database and "--database %s" % database or ""
options = [ load_cmd = (
"--config=/odoo_env/odoo.conf", server_wide_modules
"--data-dir=/env/filestore/", and "--load %s" % ",".join(server_wide_modules)
f"--addons-path={addons_path}", or ""
f"--logfile={log_file_docker_path}",
"--db_host=db",
"--db_port=5432",
"--db_user=odoo",
"--db_password=odoo",
"--workers=0",
f"{'--without-demo=all' if not demo else ''}",
f"{'--load ' + ','.join(server_wide_modules) if server_wide_modules else ''}", # noqa
f"{'--database=' + database if database else ''}",
f"{'--update ' + update if update else ''}",
f"{'--init ' + init if init else ''}",
f"{'--stop-after-init' if stop_after_init else ''}",
]
# Log resolved addons_path
logger.info(f"📦 Final --addons-path: {addons_path}")
# Remove empty strings
return [x for x in options if x]
def generate_odoo_command(
ctx,
migration_step: dict,
execution_context: str,
database: str,
demo: bool = False,
update: str = None,
init: str = None,
stop_after_init: bool = False,
shell: bool = False,
) -> str:
"""
Generate the full Odoo command using options from
generate_odoo_command_options.
"""
options = generate_odoo_command_options(
ctx,
migration_step,
execution_context,
database,
demo,
update,
init,
stop_after_init,
) )
update_cmd = update and "--update %s" % update or ""
base_command = ( init_cmd = init and "--init %s" % init or ""
stop_after_init_cmd = stop_after_init and "--stop-after-init" or ""
shell_cmd = shell and "shell" or ""
demo_cmd = not demo and "--without-demo all" or ""
command = (
Path("/odoo_env") Path("/odoo_env")
/ Path(get_odoo_folder(migration_step, execution_context)) / Path(get_odoo_folder(migration_step, execution_context))
/ Path(get_odoo_run_command(migration_step)) / Path(get_odoo_run_command(migration_step))
) )
options_as_string = " ".join(options) result = (
if shell: f" {command}"
return f"{base_command} shell {options_as_string}" f" {shell_cmd}"
else: f" --config=/odoo_env/odoo.conf"
return f"{base_command} {options_as_string}" f" --data-dir=/env/filestore/"
f" --addons-path={addons_path}"
f" --logfile={log_file_docker_path}"
f" --db_host=db"
f" --db_port=5432"
f" --db_user=odoo"
f" --db_password=odoo"
f" --workers=0"
f" {demo_cmd}"
f" {load_cmd}"
f" {database_cmd}"
f" {update_cmd}"
f" {init_cmd}"
f" {stop_after_init_cmd}"
)
return result
def run_odoo( def run_odoo(
ctx, ctx,
migration_step: dict, migration_step: dict,
detached_container: bool = False, detached_container: bool = False,
database: str = None, database: str = False,
update: str = None, update: str = False,
init: str = None, init: str = False,
stop_after_init: bool = False, stop_after_init: bool = False,
shell: bool = False, shell: bool = False,
demo: bool = False, demo: bool = False,
execution_context: str = None, execution_context: str = False,
alternative_xml_rpc_port: int = False, alternative_xml_rpc_port: int = False,
links: dict = {}, links: dict = {},
publish_ports: bool = False,
): ):
# Ensure that Postgres container exist # Ensure that Postgres container exist
get_postgres_container(ctx) get_postgres_container(ctx)
@ -290,8 +229,8 @@ def run_odoo(
or migration_step["execution_context"], or migration_step["execution_context"],
demo_text=demo and "enabled" or "disabled", demo_text=demo and "enabled" or "disabled",
stop_text=stop_after_init and " (stop-after-init)" or "", stop_text=stop_after_init and " (stop-after-init)" or "",
init_text=init and " (Init: %s)" % init or "", init_text=init and " (Init : %s)" % init or "",
update_text=update and " (Update: %s)" % update or "", update_text=update and " (Update : %s)" % update or "",
) )
) )
@ -316,7 +255,6 @@ def run_odoo(
execution_context=execution_context, execution_context=execution_context,
alternative_xml_rpc_port=alternative_xml_rpc_port, alternative_xml_rpc_port=alternative_xml_rpc_port,
links=links, links=links,
publish_ports=publish_ports,
) )
@ -325,11 +263,10 @@ def run_container_odoo(
migration_step: dict, migration_step: dict,
command: str, command: str,
detached_container: bool = False, detached_container: bool = False,
database: str = None, database: str = False,
alternative_xml_rpc_port: int = False, alternative_xml_rpc_port: int = False,
execution_context: str = None, execution_context: str = False,
links: dict = {}, links: dict = {},
publish_ports: bool = False,
): ):
env_path = ctx.obj["env_folder_path"] env_path = ctx.obj["env_folder_path"]
odoo_env_path = get_odoo_env_path(ctx, migration_step["version"]) odoo_env_path = get_odoo_env_path(ctx, migration_step["version"])
@ -342,16 +279,14 @@ def run_container_odoo(
links.update({ctx.obj["config"]["postgres_container_name"]: "db"}) links.update({ctx.obj["config"]["postgres_container_name"]: "db"})
if publish_ports: # try:
ports = {host_xmlrpc_port: DEFAULT_ODOO_HTTP_PORT}
else:
ports = {}
return run_container( return run_container(
get_docker_image_tag(ctx, migration_step["version"]), get_docker_image_tag(ctx, migration_step["version"]),
get_docker_container_name(ctx, database, migration_step), get_docker_container_name(ctx, migration_step),
command=command, command=command,
ports=ports, ports={
host_xmlrpc_port: 8069,
},
volumes={ volumes={
env_path: "/env/", env_path: "/env/",
odoo_env_path: "/odoo_env/", odoo_env_path: "/odoo_env/",
@ -360,10 +295,22 @@ def run_container_odoo(
detach=detached_container, detach=detached_container,
auto_remove=True, auto_remove=True,
) )
# except docker.errors.ContainerError as exception:
# host_log_file_path = ctx.obj["log_folder_path"] / log_file_name
# if host_log_file_path.exists():
# with open(host_log_file_path) as _log_file:
# logger.debug("*" * 50)
# logger.debug("*" * 50)
# logger.debug("*" * 50)
# logger.debug(_log_file.read())
# logger.debug("*" * 50)
# logger.debug("*" * 50)
# logger.debug("*" * 50)
# raise exception
def kill_odoo(ctx, database, migration_step: dict): def kill_odoo(ctx, migration_step: dict):
kill_container(get_docker_container_name(ctx, database, migration_step)) kill_container(get_docker_container_name(ctx, migration_step))
def execute_click_odoo_python_files( def execute_click_odoo_python_files(
@ -371,7 +318,7 @@ def execute_click_odoo_python_files(
database: str, database: str,
migration_step: dict, migration_step: dict,
python_files: list = [], python_files: list = [],
execution_context: str = None, execution_context: str = False,
): ):
if not python_files: if not python_files:
# Get post-migration python scripts to execute # Get post-migration python scripts to execute
@ -384,7 +331,7 @@ def execute_click_odoo_python_files(
] ]
python_files = sorted(python_files) python_files = sorted(python_files)
base_command = generate_odoo_command( command = generate_odoo_command(
ctx, ctx,
migration_step, migration_step,
execution_context, execution_context,
@ -393,13 +340,16 @@ def execute_click_odoo_python_files(
) )
for python_file in python_files: for python_file in python_files:
command = f"/bin/bash -c 'cat /env/{python_file} | {base_command}'" command = ("/bin/bash -c 'cat /env/{python_file} | {command}'").format(
command=command,
python_file=str(python_file),
)
try: try:
logger.info( logger.info(
f"Step {migration_step['complete_name']}." "Executing script %s / %s"
f" Executing script {python_file}..." % (migration_step["complete_name"], python_file)
) )
run_container_odoo( return run_container_odoo(
ctx, ctx,
migration_step, migration_step,
command, command,
@ -415,11 +365,11 @@ def execute_click_odoo_python_files(
) )
raise e raise e
finally: finally:
kill_odoo(ctx, database, migration_step) kill_odoo(ctx, migration_step)
def get_odoo_modules_from_csv(module_file_path: Path) -> list: def get_odoo_modules_from_csv(module_file_path: Path) -> list:
logger.debug(f"Reading '{module_file_path}' file...") logger.debug("Reading '%s' file ..." % module_file_path)
module_names = [] module_names = []
csvfile = open(module_file_path, "r") csvfile = open(module_file_path, "r")
spamreader = csv.reader(csvfile, delimiter=",", quotechar='"') spamreader = csv.reader(csvfile, delimiter=",", quotechar='"')

View File

@ -20,7 +20,8 @@ class OdooInstance:
or ctx.obj["config"]["odoo_host_xmlrpc_port"] or ctx.obj["config"]["odoo_host_xmlrpc_port"]
) )
logger.info( logger.info(
f"Connect to database {database} via odoorpc (Port {port})..." "Connect to Odoo database %s via odoorpc (Port %s)... "
% (database, port)
) )
for x in range(1, _ODOO_RPC_MAX_TRY + 1): for x in range(1, _ODOO_RPC_MAX_TRY + 1):
@ -37,24 +38,33 @@ class OdooInstance:
except (socket.gaierror, socket.error) as e: except (socket.gaierror, socket.error) as e:
if x < _ODOO_RPC_MAX_TRY: if x < _ODOO_RPC_MAX_TRY:
logger.debug( logger.debug(
f"{x}/{_ODOO_RPC_MAX_TRY}" "%d/%d Unable to connect to the server."
" Unable to connect to the server." " Retrying in 1 second ..." % (x, _ODOO_RPC_MAX_TRY)
" Retrying in 1 second..."
) )
time.sleep(1) time.sleep(1)
else: else:
logger.critical( logger.critical(
f"{x}/{_ODOO_RPC_MAX_TRY}" "%d/%d Unable to connect to the server."
" Unable to connect to the server." % (x, _ODOO_RPC_MAX_TRY)
) )
raise e raise e
# Login # Login
try: try:
rpc_connexion.login(database, "admin", "admin") rpc_connexion.login(
database,
"admin",
"admin",
)
except Exception as e: except Exception as e:
logger.error( logger.error(
f"Unable to connect to http://localhost:{port}" "Unable to connect to http://localhost:%s"
" with login 'admin' and password 'admin." " with login %s and password %s"
% (
port,
"admin",
"admin",
)
) )
raise e raise e
@ -79,24 +89,35 @@ class OdooInstance:
i = 0 i = 0
for module_name in module_names: for module_name in module_names:
i += 1 i += 1
log_prefix = f"{i}/{len(module_names)} - Module '{module_name}': " prefix = str(i) + "/" + str(len(module_names))
modules = self.browse_by_search( modules = self.browse_by_search(
"ir.module.module", [("name", "=", module_name)] "ir.module.module", [("name", "=", module_name)]
) )
if not len(modules): if not len(modules):
logger.error(f"{log_prefix}': Not found.") logger.error(
"%s - Module '%s': Not found." % (prefix, module_name)
)
continue continue
module = modules[0] module = modules[0]
if module.state == "installed": if module.state == "installed":
logger.info(f"{log_prefix}': still installed. Skipped.") logger.info(
"%s - Module %s still installed."
" skipped." % (prefix, module_name)
)
elif module.state == "uninstalled": elif module.state == "uninstalled":
try_qty = 0 try_qty = 0
installed = False installed = False
while installed is False: while installed is False:
try_qty += 1 try_qty += 1
try_qty_text = f" (try #{try_qty})" if try_qty != 1 else "" logger.info(
logger.info(f"{log_prefix}': Installing... {try_qty_text}") "%s - Module '%s': Installing ... %s"
% (
prefix,
module_name,
"(try #%d)" % try_qty if try_qty != 1 else "",
)
)
try: try:
module.button_immediate_install() module.button_immediate_install()
installed = True installed = True
@ -106,18 +127,20 @@ class OdooInstance:
if try_qty <= 5: if try_qty <= 5:
sleeping_time = 2 * try_qty * 60 sleeping_time = 2 * try_qty * 60
logger.warning( logger.warning(
f"Error. Retrying in {sleeping_time} seconds." "Error. Retrying in %d seconds.\n %s"
f"\n{e}" % (sleeping_time, e)
) )
time.sleep(sleeping_time) time.sleep(sleeping_time)
else: else:
logger.critical( logger.critical(
f"Error after {try_qty} try. Exiting." f"\n{e}" "Error after %d try. Exiting.\n %s"
% (try_qty, e)
) )
raise e raise e
else: else:
logger.error( logger.error(
f"{log_prefix}': In the {module.state} state." "%s - Module '%s': In the %s state."
" (Unable to install)" " (Unable to install)"
% (prefix, module_name, module.state)
) )
return installed_modules return installed_modules

View File

@ -1,4 +1,3 @@
import ast
import importlib import importlib
import os import os
from functools import total_ordering from functools import total_ordering
@ -65,8 +64,8 @@ class Analysis(object):
).strip() ).strip()
elif len(splited_line) > 3: elif len(splited_line) > 3:
raise ValueError( raise ValueError(
"Incorrect value in openupgrade analysis" "Incorrect value in openupgrade analysis file %s"
f" file {coverage_path} for line {line}" " for line %s" % (coverage_path, line)
) )
for odoo_module in filter( for odoo_module in filter(
@ -106,29 +105,15 @@ class Analysis(object):
last_module_version.analyse_missing_module() last_module_version.analyse_missing_module()
def estimate_workload(self, ctx): def estimate_workload(self, ctx):
logger.info("Estimate workload...") logger.info("Estimate workload ...")
for odoo_module in self.modules: for odoo_module in self.modules:
for module_version in odoo_module.module_versions.values(): for module_version in odoo_module.module_versions.values():
module_version.estimate_workload(ctx) module_version.estimate_workload(ctx)
def get_requirements(self, ctx):
logger.info("Get requirements...")
result = {x: {"python": {}, "bin": {}} for x in self.all_versions}
for odoo_module in self.modules:
for module_version in odoo_module.module_versions.values():
module_result = module_version.get_requirements(ctx)
python_result = result[module_result["version"]]["python"]
module_name = module_result["module_name"]
for python_lib in module_result["python"]:
if python_lib not in python_result:
python_result[python_lib] = [module_name]
else:
python_result[python_lib].append(module_name)
return result
def _generate_module_version_first_version(self, ctx, module_list): def _generate_module_version_first_version(self, ctx, module_list):
logger.info(f"Analyse version {self.initial_version}. (First version)") logger.info(
"Analyse version %s. (First version)" % self.initial_version
)
# Instanciate a new odoo_module # Instanciate a new odoo_module
for module_name in module_list: for module_name in module_list:
@ -137,17 +122,19 @@ class Analysis(object):
) )
if addon_path: if addon_path:
repository_name = OdooModule.get_repository_name(addon_path) repository_name = OdooModule.get_repository_name(addon_path)
if f"{repository_name}.{module_name}" not in self.modules: if (
"%s.%s" % (repository_name, module_name)
not in self.modules
):
logger.debug( logger.debug(
f"Discovering module '{module_name}'" "Discovering module '%s' in %s for version %s"
f" in {repository_name}" % (module_name, repository_name, self.initial_version)
f" for version {self.initial_version}"
) )
else: else:
repository_name = False repository_name = False
logger.error( logger.error(
f"Module {module_name} not found" "Module %s not found for version %s."
f" for version {self.initial_version}." % (module_name, self.initial_version)
) )
new_odoo_module = OdooModule( new_odoo_module = OdooModule(
ctx, self, module_name, repository_name ctx, self, module_name, repository_name
@ -164,7 +151,8 @@ class Analysis(object):
self, ctx, previous_version, current_version self, ctx, previous_version, current_version
): ):
logger.info( logger.info(
f"Analyse change between {previous_version} and {current_version}" "Analyse change between %s and %s"
% (previous_version, current_version)
) )
# Get changes between the two versions # Get changes between the two versions
( (
@ -176,8 +164,8 @@ class Analysis(object):
) )
if not apriori_module_path: if not apriori_module_path:
raise ValueError( raise ValueError(
f"Unable to find the path of the module {apriori_module_name}" "Unable to find the path of the module %s for the version %s"
f" for the version {current_version}." % (apriori_module_name, current_version)
) )
apriori_absolute_path = ( apriori_absolute_path = (
apriori_module_path apriori_module_path
@ -201,15 +189,25 @@ class Analysis(object):
state = "renamed" state = "renamed"
new_module_name = renamed_modules[odoo_module.name] new_module_name = renamed_modules[odoo_module.name]
logger.debug( logger.debug(
f"{previous_version} -> {current_version}:" "%s -> %s : %s renamed into %s"
f" {odoo_module.name} renamed into {new_module_name}" % (
previous_version,
current_version,
odoo_module.name,
new_module_name,
)
) )
elif odoo_module.name in merged_modules: elif odoo_module.name in merged_modules:
state = "merged" state = "merged"
new_module_name = merged_modules[odoo_module.name] new_module_name = merged_modules[odoo_module.name]
logger.debug( logger.debug(
f"{previous_version} -> {current_version}:" "%s -> %s : %s merged into %s"
f" {odoo_module.name} merged into {new_module_name}" % (
previous_version,
current_version,
odoo_module.name,
new_module_name,
)
) )
# Handle new module # Handle new module
@ -220,9 +218,9 @@ class Analysis(object):
) )
if not new_addon_path: if not new_addon_path:
raise ValueError( raise ValueError(
f"The module {new_module_name} has not been found" "The module %s has not been found in the version %s."
f" in the version {current_version}."
" Analyse can not be done." " Analyse can not be done."
% (new_module_name, current_version)
) )
else: else:
new_repository_name = OdooModule.get_repository_name( new_repository_name = OdooModule.get_repository_name(
@ -233,9 +231,12 @@ class Analysis(object):
not in self.modules not in self.modules
): ):
logger.debug( logger.debug(
f"Discovering module '{new_module_name}'" "Discovering module '%s' in %s for version %s"
f" in {new_repository_name}" % (
f" for version {current_version}" new_module_name,
new_repository_name,
current_version,
)
) )
new_odoo_module = OdooModule( new_odoo_module = OdooModule(
ctx, self, new_module_name, new_repository_name ctx, self, new_module_name, new_repository_name
@ -291,27 +292,22 @@ class Analysis(object):
odoo_modules = self.modules odoo_modules = self.modules
return len(odoo_modules) return len(odoo_modules)
def workload(self, module_type, repository): def workload_hour_text(self, module_type=False):
odoo_modules = self.modules
if module_type: if module_type:
odoo_modules = [ odoo_modules = [
x x
for x in filter( for x in filter(
lambda x: x.module_type == module_type, odoo_modules lambda x: x.module_type == module_type, self.modules
)
]
if repository:
odoo_modules = [
x
for x in filter(
lambda x: x.repository == repository, odoo_modules
) )
] ]
else:
odoo_modules = self.modules
total = 0 total = 0
for odoo_module in odoo_modules: for odoo_module in odoo_modules:
for module_version in list(odoo_module.module_versions.values()): for module_version in list(odoo_module.module_versions.values()):
total += module_version.workload total += module_version.workload
return total return "%d h" % (int(round(total / 60)))
@total_ordering @total_ordering
@ -320,7 +316,7 @@ class OdooModule(object):
self.analyse = analyse self.analyse = analyse
self.name = module_name self.name = module_name
self.repository = repository_name self.repository = repository_name
self.unique_name = f"{repository_name}.{module_name}" self.unique_name = "%s.%s" % (repository_name, module_name)
self.ignored = self.is_ignored(ctx, module_name) self.ignored = self.is_ignored(ctx, module_name)
self.module_versions = {} self.module_versions = {}
if not repository_name: if not repository_name:
@ -349,7 +345,7 @@ class OdooModule(object):
return res return res
def get_odoo_apps_url(self): def get_odoo_apps_url(self):
logger.info(f"Searching {self.name} in the Odoo appstore...") logger.info("Searching %s in the Odoo appstore ..." % self.name)
url = ( url = (
f"https://apps.odoo.com/apps/modules/" f"https://apps.odoo.com/apps/modules/"
f"{self.analyse.initial_version}/{self.name}/" f"{self.analyse.initial_version}/{self.name}/"
@ -364,7 +360,7 @@ class OdooModule(object):
return False return False
def get_odoo_code_search_url(self): def get_odoo_code_search_url(self):
logger.info(f"Searching {self.name} in Odoo-Code-Search...") logger.info("Searching %s in Odoo-Code-Search ..." % self.name)
url = ( url = (
f"https://odoo-code-search.com/ocs/search?" f"https://odoo-code-search.com/ocs/search?"
f"q=name%3A%3D{self.name}+version%3A{self.analyse.initial_version}" f"q=name%3A%3D{self.name}+version%3A{self.analyse.initial_version}"
@ -495,7 +491,7 @@ class OdooModuleVersion(object):
"doc", "doc",
"description", "description",
] ]
_manifest_files = ["__openerp__.py", "__manifest__.py"] _exclude_files = ["__openerp__.py", "__manifest__.py"]
_file_extensions = [".py", ".xml", ".js"] _file_extensions = [".py", ".xml", ".js"]
@ -537,63 +533,6 @@ class OdooModuleVersion(object):
else: else:
return False return False
def get_requirements(self, ctx):
result = {
"python": [],
"bin": [],
"module_name": self.odoo_module.name,
"version": self.version,
}
manifest_path = False
for manifest_name in self._manifest_files:
if not self.odoo_module.name or not self.addon_path:
continue
manifest_path = (
self.addon_path / self.odoo_module.name / manifest_name
)
if manifest_path.exists():
break
if not manifest_path or not manifest_path.exists():
return result
with manifest_path.open(mode="r", encoding="utf-8") as f_manifest:
manifest = ast.literal_eval(f_manifest.read())
python_libs = manifest.get("external_dependencies", {}).get(
"python", []
)
bin_libs = manifest.get("external_dependencies", {}).get("bin", [])
result["bin"] = bin_libs
# Handle specific replacement in the setup folder
setup_path = (
self.addon_path / "setup" / self.odoo_module.name / "setup.py"
)
if setup_path.exists():
with setup_path.open(mode="r", encoding="utf-8") as f_setup:
tree = ast.parse(source=f_setup.read())
for node in ast.walk(tree):
if (
node.__class__.__name__ == "Dict"
and "external_dependencies_override"
in [k.value for k in node.keys]
):
python_replacements = ast.literal_eval(
ast.unparse(node)
)["external_dependencies_override"]
break
else:
python_replacements = {}
for k, v in python_replacements.items():
if k in python_libs:
python_libs.remove(k)
result["python"].append(v)
result["python"] += python_libs
return result
def estimate_workload(self, ctx): def estimate_workload(self, ctx):
settings = ctx.obj["config"]["workload_settings"] settings = ctx.obj["config"]["workload_settings"]
port_minimal_time = settings["port_minimal_time"] port_minimal_time = settings["port_minimal_time"]
@ -684,7 +623,7 @@ class OdooModuleVersion(object):
if set(Path(relative_path).parts) & set(self._exclude_directories): if set(Path(relative_path).parts) & set(self._exclude_directories):
continue continue
for name in files: for name in files:
if name in self._manifest_files: if name in self._exclude_files:
continue continue
filename, file_extension = os.path.splitext(name) filename, file_extension = os.path.splitext(name)
if file_extension in self._file_extensions: if file_extension in self._file_extensions:
@ -735,7 +674,7 @@ class OdooModuleVersion(object):
elif line.startswith("---nothing has changed in this module"): elif line.startswith("---nothing has changed in this module"):
continue continue
elif line.startswith("---"): elif line.startswith("---"):
raise Exception(f"comment {line} not understood") raise Exception("comment %s not undestood" % line)
if line_type == "model": if line_type == "model":
self.openupgrade_model_lines += 1 self.openupgrade_model_lines += 1
@ -835,9 +774,9 @@ class OdooModuleVersion(object):
return "To analyse" return "To analyse"
else: else:
if self.state == "merged": if self.state == "merged":
return f"Merged into {self.target_module}" return "Merged into %s" % self.target_module
elif self.state == "renamed": elif self.state == "renamed":
return f"Renamed into {self.target_module}" return "Renamed into %s" % self.target_module
elif self.state == "ignored": elif self.state == "ignored":
return "Ignored" return "Ignored"
elif self.state == "normal_loss": elif self.state == "normal_loss":
@ -854,4 +793,4 @@ class OdooModuleVersion(object):
elif self.version != self.odoo_module.analyse.final_version: elif self.version != self.odoo_module.analyse.final_version:
return "Unported" return "Unported"
else: else:
return f"To port from {last_existing_version.version}" return "To port from %s" % last_existing_version.version

View File

@ -1,5 +1,4 @@
import os import os
import shlex
import time import time
from pathlib import Path from pathlib import Path
@ -30,71 +29,39 @@ def get_postgres_container(ctx):
container = containers[0] container = containers[0]
if container.status == "exited": if container.status == "exited":
logger.warning( logger.warning(
f"Found container {container_name} in a exited status." "Found container %s in a exited status. Removing it..."
" Removing it..." % container_name
) )
if container.status != "removed":
container.remove() container.remove()
container.wait(condition="removed")
else: else:
return container return container
# Check if volume exists # Check if volume exists
try: try:
client.volumes.get(volume_name) client.volumes.get(volume_name)
logger.debug(f"Recovering existing PostgreSQL volume: {volume_name}") logger.debug("Recovering existing postgres volume: %s" % volume_name)
except docker.errors.NotFound: except docker.errors.NotFound:
logger.info(f"Creating Postgres volume: {volume_name}") logger.info("Creating Postgres volume: %s" % volume_name)
client.volumes.create(volume_name) client.volumes.create(volume_name)
command = "postgres" command = None
postgres_extra_settings = ctx.obj["config"].get("postgres_extra_settings") postgres_extra_settings = ctx.obj["config"].get("postgres_extra_settings")
if postgres_extra_settings: if postgres_extra_settings:
command = "postgres"
for key, value in postgres_extra_settings.items(): for key, value in postgres_extra_settings.items():
command += f" -c {key}={value}" command += f" -c {key}={value}"
logger.info(f"Launching PostgreSQL Container. (Image {image_name})") logger.info("Launching Postgres Container. (Image %s)" % image_name)
# base environement variables
environments = {
"POSTGRES_USER": "odoo",
"POSTGRES_PASSWORD": "odoo",
"POSTGRES_DB": "postgres",
"PGDATA": "/var/lib/postgresql/data/pgdata",
}
# if postgresql version is >= 14 and odoo <= 12 we need to use md5 for auth
# method and password encryption
try:
postgres_version = float(image_name.split(":")[1])
except ValueError:
raise Exception(
"Unable to extract PostgreSQL version "
f"from image name {image_name}. "
"Define version in the image name is mandatory."
)
try:
odoo_start_version = float(ctx.obj["config"]["odoo_versions"][0])
except ValueError:
raise Exception(
"Unable to extract start odoo version from odoo_versions "
f"{ctx.obj['config']['odoo_versions']}"
)
if odoo_start_version <= 12 and postgres_version >= 14:
environments |= {
"POSTGRES_HOST_AUTH_METHOD": "md5",
"POSTGRES_INITDB_ARGS": "--auth-host=md5",
}
command += " -c password_encryption=md5"
container = run_container( container = run_container(
image_name, image_name,
container_name, container_name,
command=command, command=command,
environments=environments, environments={
"POSTGRES_USER": "odoo",
"POSTGRES_PASSWORD": "odoo",
"POSTGRES_DB": "postgres",
"PGDATA": "/var/lib/postgresql/data/pgdata",
},
volumes={ volumes={
# Data volume # Data volume
volume_name: "/var/lib/postgresql/data/pgdata/", volume_name: "/var/lib/postgresql/data/pgdata/",
@ -102,7 +69,6 @@ def get_postgres_container(ctx):
ctx.obj["env_folder_path"].absolute(): "/env/", ctx.obj["env_folder_path"].absolute(): "/env/",
}, },
detach=True, detach=True,
# ports={"5432/tcp": 5432}, # <--- Port-Mapping hinzugefügt
) )
# TODO, improve me. # TODO, improve me.
# Postgres container doesn't seems available immediately. # Postgres container doesn't seems available immediately.
@ -119,9 +85,10 @@ def execute_sql_file(ctx, database, sql_file):
# call psql in the container # call psql in the container
if str(ctx.obj["env_folder_path"]) not in str(sql_file): if str(ctx.obj["env_folder_path"]) not in str(sql_file):
raise Exception( raise Exception(
f"The SQL file {sql_file} is not in the" "The SQL file %s is not in the"
f" main directory {ctx.obj['env_folder_path']} available" " main folder %s available"
" in the PostgreSQL container." " in the postgres container."
% (sql_file, ctx.obj["env_folder_path"])
) )
relative_path = Path( relative_path = Path(
str(sql_file).replace(str(ctx.obj["env_folder_path"]), ".") str(sql_file).replace(str(ctx.obj["env_folder_path"]), ".")
@ -132,8 +99,8 @@ def execute_sql_file(ctx, database, sql_file):
"psql --username=odoo --dbname={database} --file {file_path}" "psql --username=odoo --dbname={database} --file {file_path}"
).format(database=database, file_path=container_path) ).format(database=database, file_path=container_path)
logger.info( logger.info(
f"Executing the script '{relative_path}' in PostgreSQL container" "Executing the script '%s' in postgres container"
f" on database {database}" " on database %s" % (relative_path, database)
) )
exec_container(container, command) exec_container(container, command)
@ -159,60 +126,44 @@ def execute_psql_command(
"psql" "psql"
" --username=odoo" " --username=odoo"
f" --dbname={database or 'postgres'}" f" --dbname={database or 'postgres'}"
f" --command {shlex.quote(request)}" f' --command "{request}"'
f" {' '.join(psql_args)}" f" {' '.join(psql_args)}"
) )
logger.debug( logger.debug(
f"Executing the following command in PostgreSQL container\n{command}" "Executing the following command in postgres container\n"
"%s" % (command)
) )
docker_result = exec_container(container, command) docker_result = exec_container(container, command)
return docker_result.output.decode("utf-8") return docker_result.output.decode("utf-8")
def check_db_exist(ctx, database: str, raise_exception=False): def ensure_database(ctx, database: str, state="present"):
"""
- Connect to postgres container.
- Check if the database exist.
- Return True if exists, False otherwise.
- raise_exception paramater used for source database checking
"""
request = "select datname FROM pg_database WHERE datistemplate = false;"
result = execute_sql_request(ctx, request)
if [database] in result:
return True
if raise_exception:
raise Exception(f"Database '{database}' not found.")
return False
def ensure_database(ctx, database: str, state="present", template: str = ""):
""" """
- Connect to postgres container. - Connect to postgres container.
- Check if the database exist. - Check if the database exist.
- if doesn't exists and state == 'present', create it. - if doesn't exists and state == 'present', create it.
- if exists and state == 'absent', drop it. - if exists and state == 'absent', drop it.
""" """
request = "select datname FROM pg_database WHERE datistemplate = false;"
result = execute_sql_request(ctx, request)
if state == "present": if state == "present":
if check_db_exist(ctx, database): if [database] in result:
return return
if template: logger.info("Create database '%s' ..." % database)
logger.info(f'Copy database "{template}" into "{database}"...') request = "CREATE DATABASE {database} owner odoo;".format(
request = ( database=database
f'CREATE DATABASE "{database}" WITH TEMPLATE "{template}";'
) )
execute_sql_request(ctx, request)
else: else:
logger.info(f"Create database '{database}'...") if [database] not in result:
request = f'CREATE DATABASE "{database}" OWNER odoo;'
execute_psql_command(ctx, request)
else:
if not check_db_exist(ctx, database):
return return
logger.info(f'Drop database "{database}"...') logger.info("Drop database '%s' ..." % database)
request = f'DROP DATABASE "{database}";' request = "DROP DATABASE {database};".format(database=database)
execute_psql_command(ctx, request) execute_sql_request(ctx, request)
def execute_sql_files_pre_migration( def execute_sql_files_pre_migration(
@ -241,7 +192,8 @@ def chown_to_local_user(ctx, filepath: os.PathLike):
uid=user_uid, filepath=filepath uid=user_uid, filepath=filepath
) )
logger.debug( logger.debug(
f"Executing the following command in PostgreSQL container:\n{command}" "Executing the following command in postgres container: %s"
% (command,)
) )
chown_result = exec_container(container, command) chown_result = exec_container(container, command)
return chown_result.output.decode("utf8") return chown_result.output.decode("utf8")
@ -277,7 +229,8 @@ def execute_pg_dump(
pg_dump_args=pg_dump_args, pg_dump_args=pg_dump_args,
) )
logger.debug( logger.debug(
f"Executing the following command in PostgreSQL container:\n{command}" "Executing the following command in postgres container: %s"
% (command,)
) )
pg_dump_result = exec_container(container, command) pg_dump_result = exec_container(container, command)
@ -295,14 +248,6 @@ def execute_pg_restore(
container = get_postgres_container(ctx) container = get_postgres_container(ctx)
ensure_database(ctx, database, "absent") ensure_database(ctx, database, "absent")
ensure_database(ctx, database, "present") ensure_database(ctx, database, "present")
if database_format == "p":
command = (
"psql"
f" --file='{Path('/env') / filepath}'"
" --username odoo"
f" --dbname={database}"
)
else:
command = ( command = (
"pg_restore" "pg_restore"
f" {Path('/env') / filepath}" f" {Path('/env') / filepath}"

View File

@ -19,7 +19,7 @@ def get_script_folder(ctx, migration_step: dict) -> Path:
def ensure_folder_writable(folder_path: Path): def ensure_folder_writable(folder_path: Path):
logger.info(f"Make writable the directory '{folder_path}'") logger.info("Make writable the folder '%s'" % folder_path)
try: try:
chmod(["--silent", "--recursive", "o+w", str(folder_path)]) chmod(["--silent", "--recursive", "o+w", str(folder_path)])
except ProcessExecutionError: except ProcessExecutionError:
@ -35,9 +35,9 @@ def ensure_folder_exists(
- a log is done at INFO level. - a log is done at INFO level.
""" """
if not folder_path.exists(): if not folder_path.exists():
cmd = ["-p", folder_path] cmd = ["--parents", folder_path]
cmd = ["-m", mode] + cmd cmd = ["--mode", mode] + cmd
logger.info(f"Creating directory '{folder_path}'...") logger.info("Creating folder '%s' ..." % (folder_path))
mkdir(cmd) mkdir(cmd)
if git_ignore_content: if git_ignore_content:
@ -74,35 +74,31 @@ def ensure_file_exists_from_template(
if data == output: if data == output:
return return
log_text = f"Updating file '{file_path}' from template..." log_text = "Updating file '%s' from template ..." % (file_path)
else: else:
log_text = f"Creating file '{file_path}' from template..." log_text = "Creating file '%s' from template ..." % (file_path)
with open(file_path, "w") as f: with open(file_path, "w") as f:
logger.info(log_text) logger.info(log_text)
print(output, file=f) print(output, file=f)
def git_aggregate(folder_path: Path, config_path: Path, jobs: int, env_file: str = ".env"): def git_aggregate(folder_path: Path, config_path: Path, jobs: int):
# Convert relative env_file path to absolute path before changing directory
if not os.path.isabs(env_file):
env_file = os.path.abspath(env_file)
args = argparse.Namespace( args = argparse.Namespace(
command="aggregate", command="aggregate",
config=str(config_path), config=str(config_path),
jobs=jobs, jobs=jobs,
dirmatch=None, dirmatch=None,
do_push=False, do_push=False,
expand_env=True, expand_env=False,
env_file=env_file, env_file=None,
force=True, force=True,
) )
with working_directory_keeper: with working_directory_keeper:
os.chdir(folder_path) os.chdir(folder_path)
logger.info( logger.info(
f"Gitaggregate source code for {config_path}." "Gitaggregate source code for %s. This can take a while ..."
" This can take a while..." % config_path
) )
gitaggregate_cmd.run(args) gitaggregate_cmd.run(args)
@ -111,8 +107,8 @@ def get_local_user_id():
return os.getuid() return os.getuid()
def execute_check_output(args_list, working_directory=None): def execute_check_output(args_list, working_directory=False):
logger.debug(f"Execute {' '.join(args_list)}") logger.debug("Execute %s" % " ".join(args_list))
subprocess.check_output(args_list, cwd=working_directory) subprocess.check_output(args_list, cwd=working_directory)
@ -177,12 +173,3 @@ def restore_filestore(
else: # works for "t" and "tgz" else: # works for "t" and "tgz"
tar = tarfile.open(src_path) tar = tarfile.open(src_path)
tar.extractall(path=filestore_path) tar.extractall(path=filestore_path)
# If a filestore/filestore/database/filestore directory exists,
# it means that the filestore was in a "filestore" subdirectory
# and we need to move this content to the parent directory.
filestore_subfolder = filestore_path / "filestore"
if filestore_subfolder.exists():
for file in filestore_subfolder.iterdir():
shutil.move(file, filestore_path)
shutil.rmtree(filestore_subfolder)

1577
poetry.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,56 +1,47 @@
[project] [tool.poetry]
name = "odoo-openupgrade-wizard" name = "odoo-openupgrade-wizard"
version = "1.3.1.1" version = "0.5.0"
description = "CLI tool to manage Odoo Major Upgrades" description = "CLI tool to manage Odoo Major Upgrades"
authors = [ authors = [
{name = "Sylvain LE GAL", email = "sylvain.legal@grap.coop"}, "GRAP, Groupement Régional Alimentaire de Proximité",
{name = "Rémy TAYMANS", email = "remy@coopiteasy.be"},
{name = "Simon MAILLARD", email = "simon@ogesta.fr"},
] ]
maintainers = [ maintainers = [
{name = "Sylvain LE GAL", email = "sylvain.legal@grap.coop"}, "Sylvain LE GAL",
{name = "Rémy TAYMANS", email = "remy@coopiteasy.be"},
] ]
license = "AGPLv3+"
readme = "README.md" readme = "README.md"
repository = "https://gitlab.com/odoo-openupgrade-wizard/odoo-openupgrade-wizard" repository = "https://gitlab.com/odoo-openupgrade-wizard/odoo-openupgrade-wizard"
keywords = ["cli", "odoo", "openupgrade"] keywords = ["cli", "odoo", "openupgrade"]
classifiers = [ classifiers = [
"License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)",
"Intended Audience :: Developers", "Intended Audience :: Developers",
"Development Status :: 4 - Beta", "Development Status :: 2 - Pre-Alpha",
"Operating System :: Unix", "Operating System :: Unix",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
# Currently poetry 2.0.0 failed to find the following classifier
#"Programming Language :: Python :: 3.13",
"Framework :: Odoo", "Framework :: Odoo",
] ]
requires-python = ">=3.9,<4.0"
dependencies = [
"click (>=7.0,<8.0)",
"click-loglevel (>=0.4)",
"docker (>=7.0,<8.0)",
"importlib-resources (>=5.4,<6.0)",
"git-aggregator (>=2.1,<3.0)",
"GitPython (>=3.1,<4.0)",
"jinja2 (>=3.0,<4.0)",
"loguru (>=0.6)",
"odoorpc (>=0.10.1)",
"plumbum (>=1.7,<2.0)",
"pygount (>=1.4,<2.0)",
"pyyaml (>=6.0.0,<7.0.0)",
"single-source (>=0.3)",
]
[project.urls] [tool.poetry.scripts]
Repository = "https://gitlab.com/odoo-openupgrade-wizard/odoo-openupgrade-wizard"
[project.scripts]
oow = "odoo_openupgrade_wizard.cli.cli:main" oow = "odoo_openupgrade_wizard.cli.cli:main"
odoo-openupgrade-wizard = "odoo_openupgrade_wizard.cli.cli:main" odoo-openupgrade-wizard = "odoo_openupgrade_wizard.cli.cli:main"
[tool.poetry.dependencies]
python = ">=3.6.3,<4.0.0"
click = "^7.0"
click-loglevel = "^0.4"
docker = "^5.0"
importlib-resources = "^5.4"
git-aggregator = "^2.1"
GitPython = "^3.1"
jinja2 = "^3.0"
loguru = "^0.6"
odoorpc = "^0.8"
plumbum = "^1.7"
pygount = "^1.4"
pyyaml = "5.4.1"
single-source = "^0.3"
[tool.poetry.group.dev.dependencies] [tool.poetry.group.dev.dependencies]
pytest = [ pytest = [
@ -74,36 +65,3 @@ line-length = 79
[tool.isort] [tool.isort]
profile = "black" profile = "black"
line_length = 79 line_length = 79
[tool.towncrier]
name = "odoo-openupgrade-wizard"
package = "odoo_openupgrade_wizard"
package_dir = "."
filename = "CHANGES.rst"
directory = "newsfragments"
wrap = true
[[tool.towncrier.type]]
directory = "feature"
name = "Features"
showcontent = true
[[tool.towncrier.type]]
directory = "bugfix"
name = "Bugfixes"
showcontent = true
[[tool.towncrier.type]]
directory = "removal"
name = "Removals"
showcontent = true
[[tool.towncrier.type]]
directory = "doc"
name = "Documentation"
showcontent = true
[[tool.towncrier.type]]
directory = "misc"
name = "Misc"
showcontent = true

View File

@ -25,18 +25,15 @@ def move_to_test_folder():
if os.getcwd().endswith("tests/data/output"): if os.getcwd().endswith("tests/data/output"):
return return
test_folder_path = Path("tests/data/output") test_folder_path = Path("tests/data/output")
mkdir(["-p", test_folder_path]) mkdir([test_folder_path, "--parents"])
os.chdir(test_folder_path) os.chdir(test_folder_path)
def cli_runner_invoke(cmd, expect_success=True): def cli_runner_invoke(cmd, expect_success=True):
if not cmd:
cmd = []
cmd = ["--log-level=DEBUG"] + cmd
try: try:
result = CliRunner().invoke( result = CliRunner().invoke(
main, main,
args=cmd, cmd,
catch_exceptions=False, catch_exceptions=False,
) )
if expect_success: if expect_success:

View File

@ -11,11 +11,11 @@ def test_cli_init():
cli_runner_invoke( cli_runner_invoke(
[ [
"--log-level=DEBUG",
"init", "init",
"--project-name=test-cli", "--project-name=test-cli",
"--initial-version=14.0", "--initial-version=14.0",
"--final-version=15.0", "--final-version=15.0",
"--postgresql-version=13",
"--extra-repository=" "--extra-repository="
"OCA/web,OCA/server-tools,OCA/bank-statement-import", "OCA/web,OCA/server-tools,OCA/bank-statement-import",
], ],

View File

@ -6,7 +6,12 @@ from . import cli_runner_invoke, move_to_test_folder
def test_cli_get_code(): def test_cli_get_code():
move_to_test_folder() move_to_test_folder()
cli_runner_invoke(["get-code"]) cli_runner_invoke(
[
"--log-level=DEBUG",
"get-code",
],
)
# Check V14 # Check V14
web_path = Path("./src/env_14.0/src/OCA/web") web_path = Path("./src/env_14.0/src/OCA/web")

View File

@ -19,7 +19,13 @@ def test_cli_docker_build():
# be sure that the call (environment, etc...) is correct now.) # be sure that the call (environment, etc...) is correct now.)
kill_container(ctx.obj["config"]["postgres_container_name"]) kill_container(ctx.obj["config"]["postgres_container_name"])
cli_runner_invoke(["docker-build", "--versions=14.0,15.0"]) cli_runner_invoke(
[
"--log-level=DEBUG",
"docker-build",
"--versions=14.0,15.0",
],
)
docker_client = get_docker_client() docker_client = get_docker_client()

View File

@ -22,16 +22,17 @@ def test_cli_run():
cli_runner_invoke( cli_runner_invoke(
[ [
"--log-level=DEBUG",
"run", "run",
"--step=1", "--step=1",
f"--database={db_name}", "--database=%s" % db_name,
"--init-modules=base", "--init-modules=base",
"--stop-after-init", "--stop-after-init",
], ],
) )
# Ensure that a subfolder filestore/DB_NAME has been created # Ensure that a subfolder filestore/DB_NAME has been created
db_filestore_path = Path("./filestore/filestore") / db_name db_filestore_path = Path("./filestore/filestore/%s" % db_name)
assert db_filestore_path.exists() assert db_filestore_path.exists()
# Ensure that 'base' module is installed # Ensure that 'base' module is installed

View File

@ -19,11 +19,11 @@ def test_cli_execute_script_python():
ctx = build_ctx_from_config_file() ctx = build_ctx_from_config_file()
extra_script_path = Path( extra_script_path = Path(
"../extra_script/01-post-migration-custom_test.py" "../extra_script/post-migration-custom_test.py"
).absolute() ).absolute()
cp( cp(
extra_script_path, extra_script_path,
Path("01-post-migration-custom_test.py"), Path("post-migration-custom_test.py"),
) )
db_name = "database_test_cli___execute_script_python" db_name = "database_test_cli___execute_script_python"
@ -32,9 +32,10 @@ def test_cli_execute_script_python():
# Install Odoo on V14 with base installed # Install Odoo on V14 with base installed
cli_runner_invoke( cli_runner_invoke(
[ [
"--log-level=DEBUG",
"run", "run",
"--step=1", "--step=1",
f"--database={db_name}", "--database=%s" % db_name,
"--init-modules=base", "--init-modules=base",
"--stop-after-init", "--stop-after-init",
], ],
@ -49,10 +50,11 @@ def test_cli_execute_script_python():
# Execute Custom Python Script # Execute Custom Python Script
cli_runner_invoke( cli_runner_invoke(
[ [
"--log-level=DEBUG",
"execute-script-python", "execute-script-python",
"--step=1", "--step=1",
f"--database={db_name}", "--database=%s" % db_name,
"--script-file-path=01-post-migration-custom_test.py", "--script-file-path=post-migration-custom_test.py",
], ],
) )
partner_quantity_after = int( partner_quantity_after = int(

View File

@ -34,7 +34,12 @@ def test_cli_execute_script_sql():
# TODO call with script-file-path # TODO call with script-file-path
# to avoid to copy file in scripts/step_xxx folder # to avoid to copy file in scripts/step_xxx folder
cli_runner_invoke( cli_runner_invoke(
["execute-script-sql", "--step=1", f"--database={db_name}"] [
"--log-level=DEBUG",
"execute-script-sql",
"--step=1",
"--database=%s" % db_name,
],
) )
# Ensure that the request has been done correctly # Ensure that the request has been done correctly

View File

@ -1,6 +1,3 @@
from pathlib import Path
from shutil import copy
from odoo_openupgrade_wizard.tools.tools_postgres import ( from odoo_openupgrade_wizard.tools.tools_postgres import (
ensure_database, ensure_database,
execute_sql_request, execute_sql_request,
@ -17,26 +14,16 @@ def test_cli_upgrade():
move_to_test_folder() move_to_test_folder()
ctx = build_ctx_from_config_file() ctx = build_ctx_from_config_file()
for n in ["01", "02"]:
copy(
Path(
f"../extra_script/{n}-post-migration-custom_test.py"
).absolute(),
Path(
"scripts/step_01__regular__14.0/"
f"{n}-post-migration-custom_test.py"
),
)
# Initialize database # Initialize database
db_name = "database_test_cli___upgrade" db_name = "database_test_cli___upgrade"
ensure_database(ctx, db_name, state="absent") ensure_database(ctx, db_name, state="absent")
cli_runner_invoke( cli_runner_invoke(
[ [
"--log-level=DEBUG",
"run", "run",
"--step=1", "--step=1",
f"--database={db_name}", "--database=%s" % db_name,
"--init-modules=base", "--init-modules=base",
"--stop-after-init", "--stop-after-init",
], ],
@ -55,8 +42,9 @@ def test_cli_upgrade():
cli_runner_invoke( cli_runner_invoke(
[ [
"--log-level=DEBUG",
"upgrade", "upgrade",
f"--database={db_name}", "--database=%s" % db_name,
"--first-step=1", "--first-step=1",
"--last-step=3", "--last-step=3",
], ],
@ -72,23 +60,3 @@ def test_cli_upgrade():
latest_version = execute_sql_request(ctx, request, database=db_name) latest_version = execute_sql_request(ctx, request, database=db_name)
assert latest_version[0][0].startswith("15.") assert latest_version[0][0].startswith("15.")
# ensure the first post-migration-custom scripts have been executed
request = (
"SELECT name"
" FROM res_partner"
" WHERE name like 'Post Script 1 - Partner #%';"
)
result = execute_sql_request(ctx, request, database=db_name)
assert len(result) == 10
# ensure the second post-migration-custom scripts have been executed
request = (
"SELECT name"
" FROM res_partner"
" WHERE name = 'Post Script 2 - Partner #1';"
)
result = execute_sql_request(ctx, request, database=db_name)
assert len(result) == 1

View File

@ -10,6 +10,7 @@ class TestCliEstimateWorkload(unittest.TestCase):
cli_runner_invoke( cli_runner_invoke(
[ [
"--log-level=DEBUG",
"estimate-workload", "estimate-workload",
"--extra-modules=" "--extra-modules="
# Done Module # Done Module

View File

@ -1,23 +0,0 @@
import filecmp
import unittest
from pathlib import Path
from . import cli_runner_invoke, move_to_test_folder
class TestCliGuessRequirement(unittest.TestCase):
def test_cli_guess_requirement(self):
move_to_test_folder()
expected_folder_path = Path("../output_expected").absolute()
cli_runner_invoke(
["guess-requirement", "--extra-modules=sentry"],
)
relative_path = Path("src/env_14.0/addons_python_requirements.txt")
assert filecmp.cmp(
relative_path,
expected_folder_path / relative_path,
)

View File

@ -21,7 +21,13 @@ def test_cli_install_from_csv(mocker):
ensure_database(ctx, db_name, state="absent") ensure_database(ctx, db_name, state="absent")
cli_runner_invoke(["install-from-csv", f"--database={db_name}"]) cli_runner_invoke(
[
"--log-level=DEBUG",
"install-from-csv",
"--database=%s" % db_name,
],
)
# Ensure that 'base' is installed # Ensure that 'base' is installed
request = ( request = (

View File

@ -32,9 +32,10 @@ def test_cli_generate_module_analysis(mocker):
analysis_file_path analysis_file_path
cli_runner_invoke( cli_runner_invoke(
[ [
"--log-level=DEBUG",
"generate-module-analysis", "generate-module-analysis",
"--step=2", "--step=2",
f"--database={db_name}", "--database=%s" % db_name,
"--modules=base", "--modules=base",
], ],
) )

View File

@ -20,7 +20,13 @@ def test_cli_dumpdb(mocker):
ctx = build_ctx_from_config_file() ctx = build_ctx_from_config_file()
ensure_database(ctx, db_name, state="absent") ensure_database(ctx, db_name, state="absent")
cli_runner_invoke(["install-from-csv", f"--database={db_name}"]) cli_runner_invoke(
[
"--log-level=DEBUG",
"install-from-csv",
f"--database={db_name}",
],
)
# Dump database and filestore # Dump database and filestore
formatlist = [("p", "d"), ("c", "tgz"), ("t", "t"), ("d", "d")] formatlist = [("p", "d"), ("c", "tgz"), ("t", "t"), ("d", "d")]
@ -140,20 +146,6 @@ def test_cli_dumpdb_failure(mocker):
expect_success=False, expect_success=False,
) )
# With a non-existing database
cli_runner_invoke(
[
"--log-level=DEBUG",
"dumpdb",
"--database=database_test_cli___dumpdb_non_existing",
f"--database-path={database_path}",
f"--database-format={formats[0]}",
f"--filestore-path={filestore_path}",
f"--filestore-format={formats[1]}",
],
expect_success=False,
)
# Cleanup files # Cleanup files
if database_path.is_dir(): if database_path.is_dir():
shutil.rmtree(database_path) shutil.rmtree(database_path)

View File

@ -33,6 +33,7 @@ def test_cli_restoredb(mocker):
cli_runner_invoke( cli_runner_invoke(
[ [
"--log-level=DEBUG",
"restoredb", "restoredb",
f"--database={db_name}", f"--database={db_name}",
f"--database-path={database_path}", f"--database-path={database_path}",
@ -45,38 +46,6 @@ def test_cli_restoredb(mocker):
# check filestore exists # check filestore exists
assert dest_filestore_path.exists() assert dest_filestore_path.exists()
# check the filestore content is at the right place
assert (dest_filestore_path / "01").exists()
# Check database exists
assert_database(ctx, db_name, "present")
# Delete filestore and database
shutil.rmtree(dest_filestore_path)
ensure_database(ctx, db_name, state="absent")
shutil.copyfile(
pathlib.Path("../restoredb/test_with_nested_filestore_dir.tar.gz"),
filestore_path,
)
cli_runner_invoke(
[
"restoredb",
f"--database={db_name}",
f"--database-path={database_path}",
"--database-format=c",
f"--filestore-path={filestore_path}",
"--filestore-format=tgz",
],
)
# check filestore exists
assert dest_filestore_path.exists()
# check the filestore content is at the right place
assert (dest_filestore_path / "01").exists()
# Check database exists # Check database exists
assert_database(ctx, db_name, "present") assert_database(ctx, db_name, "present")

View File

@ -22,9 +22,10 @@ def test_cli_psql():
# initialize database # initialize database
cli_runner_invoke( cli_runner_invoke(
[ [
"--log-level=DEBUG",
"run", "run",
"--step=1", "--step=1",
f"--database={db_name}", "--database=%s" % db_name,
"--init-modules=base", "--init-modules=base",
"--stop-after-init", "--stop-after-init",
], ],
@ -48,9 +49,10 @@ def test_cli_psql():
# test via cli ok # test via cli ok
cli_runner_invoke( cli_runner_invoke(
[ [
"--log-level=DEBUG",
"psql", "psql",
f"--database={db_name}", "--database=%s" % db_name,
f'--command "{request}"', '--command "%s"' % request,
"--no-pager", "--no-pager",
"--tuples-only", "--tuples-only",
], ],
@ -60,9 +62,10 @@ def test_cli_psql():
with raises(Exception): with raises(Exception):
cli_runner_invoke( cli_runner_invoke(
[ [
"--log-level=DEBUG",
"psql", "psql",
f"--database={db_name}", "--database=%s" % db_name,
f'--command "{request}"', '--command "%s"' % request,
"--no-pager", "--no-pager",
"--tuples-only", "--tuples-only",
"---unkwon-argument", "---unkwon-argument",

View File

@ -26,11 +26,18 @@ def test_cli_copydb(mocker):
shutil.rmtree(dest_filestore_path, ignore_errors=True) shutil.rmtree(dest_filestore_path, ignore_errors=True)
# Initialize database # Initialize database
cli_runner_invoke(["install-from-csv", f"--database={db_name}"]) cli_runner_invoke(
[
"--log-level=DEBUG",
"install-from-csv",
f"--database={db_name}",
],
)
# Copy database # Copy database
cli_runner_invoke( cli_runner_invoke(
[ [
"--log-level=DEBUG",
"copydb", "copydb",
f"--source={db_name}", f"--source={db_name}",
f"--dest={db_dest_name}", f"--dest={db_dest_name}",

View File

@ -25,10 +25,22 @@ def test_cli_dropdb(mocker):
shutil.rmtree(filestore_path, ignore_errors=True) shutil.rmtree(filestore_path, ignore_errors=True)
# Initialize database # Initialize database
cli_runner_invoke(["install-from-csv", f"--database={db_name}"]) cli_runner_invoke(
[
"--log-level=DEBUG",
"install-from-csv",
f"--database={db_name}",
],
)
# Drop database # Drop database
cli_runner_invoke(["dropdb", f"--database={db_name}"]) cli_runner_invoke(
[
"--log-level=DEBUG",
"dropdb",
f"--database={db_name}",
],
)
# Check database does not exists # Check database does not exists
assert_database(ctx, db_name, "absent") assert_database(ctx, db_name, "absent")

View File

@ -1,30 +0,0 @@
from . import cli_runner_invoke, move_to_test_folder
def test_cli_downgrade_pg_auth_method_for_old_versions():
move_to_test_folder()
cli_runner_invoke(
[
"init",
"--project-name=test-cli-downgrade-auth-method",
"--initial-version=12.0",
"--final-version=13.0",
"--postgresql-version=14",
],
)
move_to_test_folder()
cli_runner_invoke(["get-code"])
cli_runner_invoke(["docker-build", "--versions=12.0"])
db_name = "database_test_cli-downgrade-auth-method__run"
cli_runner_invoke(
[
"run",
"--step=1",
f"--database={db_name}",
"--init-modules=base",
"--stop-after-init",
],
)

View File

@ -1,12 +0,0 @@
import logging
_logger = logging.getLogger(__name__)
_logger.info("02-post-migration-custom_test.py : Begin of script ...")
env = env # noqa: F821
env["res.partner"].create({"name": "Post Script 2 - Partner #1"})
_logger.info("02-post-migration-custom_test.py : End of script.")
env.cr.commit()

View File

@ -1,15 +1,15 @@
import logging import logging
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
_logger.info("01-post-migration-custom_test.py : Begin of script ...") _logger.info("post-migration-custom_test.py : Begin of script ...")
env = env # noqa: F821 env = env # noqa: F821
for i in range(0, 10): for i in range(0, 10):
partner_name = "Post Script 1 - Partner #%d" % (i) partner_name = "Partner #%d" % (i)
_logger.info("Create Partner %s" % partner_name) _logger.info("Create Partner %s" % partner_name)
env["res.partner"].create({"name": partner_name}) env["res.partner"].create({"name": partner_name})
_logger.info("01-post-migration-custom_test.py : End of script.") _logger.info("post-migration-custom_test.py : End of script.")
env.cr.commit() env.cr.commit()

View File

@ -1,9 +1,9 @@
project_name: test-cli project_name: test-cli
postgres_image_name: postgres:13 postgres_image_name: postgres:14
postgres_container_name: test-cli-container-postgres-13 postgres_container_name: test-cli-container-postgres
postgres_volume_name: test-cli-volume-postgres-13 postgres_volume_name: test-cli-volume-postgres
postgres_extra_settings: postgres_extra_settings:
@ -28,7 +28,6 @@ migration_steps:
version: 14.0 version: 14.0
execution_context: regular execution_context: regular
complete_name: step_01__regular__14.0 complete_name: step_01__regular__14.0
update: True
- name: 2 - name: 2
version: 15.0 version: 15.0
@ -39,7 +38,6 @@ migration_steps:
version: 15.0 version: 15.0
execution_context: regular execution_context: regular
complete_name: step_03__regular__15.0 complete_name: step_03__regular__15.0
update: True
workload_settings: workload_settings:

View File

@ -1,4 +0,0 @@
# Do NOT edit manually. Changes here will be overwritten by the command 'guess-requirement'
# Required by the module(s): sentry
sentry_sdk<=1.9.0

Binary file not shown.