Compare commits

..

319 Commits

Author SHA1 Message Date
94a718457b feat: add configurable env_file parameter to git_aggregate function
- Add optional env_file parameter with default value ".env"
- Convert relative env_file paths to absolute paths before directory change
- Maintain backward compatibility with existing calls
- Improve flexibility for different environment file locations
2025-10-04 13:24:49 +02:00
a5f7f2e88c merge 1.3.1 2025-10-03 20:52:24 +02:00
bf07bc1865 [FIX] cli.py: comment import debugypy 2025-06-02 07:40:08 +02:00
e41abfc928 revert pyproject.toml 2025-06-01 15:28:30 +02:00
a34d880721 added option custom_addon in config.yaml 2025-05-27 07:52:50 +02:00
a362783e6d odoo_openupgrade_wizard/tools/tools_system.py aktualisiert 2025-05-19 20:57:08 +02:00
Rémy Taymans
6bb379d92b [VER] Bump to version 1.3.0 2025-05-04 16:16:24 +02:00
Rémy Taymans
e8da97fca7 [IMP] release documentation 2025-05-04 16:05:56 +02:00
Rémy Taymans
7b8f9f828a Merge branch '16.0-add-postgres-default-database' into 'main'
[IMP] cli tools: default 'postgres' database.

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!112
2025-05-04 13:16:51 +00:00
Rémy Taymans
3c5a589cd8 Merge branch 'disable_odoo_memory_limit' into 'main'
disable odoo memory limit

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!114
2025-05-04 13:14:22 +00:00
hugues de keyzer
32cde3f0e7 disable odoo memory limit by default
by default, odoo limits the available memory to 2560 MiB, which can
cause MemoryError exceptions, especially when dealing with high amounts
of records. disable this limit by default.
2025-05-02 16:37:29 +02:00
LE GAL SYLVAIN
2e2799c612 Merge branch 'bump-version-1.2.0' into 'main'
[VER] Bump to version 1.2.0

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!113
2025-04-12 15:09:23 +00:00
Sylvain LE GAL
43370fae23 [VER] Bump to version 1.2.0 2025-04-12 15:39:22 +02:00
Sylvain LE GAL
8bf6dd385a [IMP] cli tools: default 'postgres' database.
Rational: when calling oow psql, sometimes, we don't want to put a database. Example, if we call "oow -d postgres -c 'psql'".
In that case, this improvment makes that default postgres database will be proposed in the 'prompt', if no database is provided
2025-02-27 12:45:16 +01:00
Rémy Taymans
42bf4f4379 Merge branch 'add-option-skip-update-all' into 'main'
[IMP] oow upgrade. Add skip_update as an option in config.yml.

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!111
2025-02-11 13:41:47 +00:00
Sylvain LE GAL
bfde1ddc1f fixup! [IMP] oow upgrade. Add skip_update as an option in config.yml. (migration_step option). If enabled, in this step, the update all will be skipped, and only pre-migration (SQL) and post-migration (python script) will be executed. This is interesting: - for the first step, if your production is up-to-date, to save time. - for the last step, in recent version (since V14), where openupgrade doesn't contain odoo code. In that case, you should be sure that all your migration is OK, because in openupgrade context, some errors are just ignored. Use this option with caution. 2025-02-11 10:45:25 +01:00
Rémy Taymans
9687e8656a Merge branch 'imp-restoredb' into 'main'
[IMP] cli restoredb: manage filstore in subfolder

Closes #63

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!109
2025-02-03 15:18:04 +00:00
Rémy Taymans
65e7e2c57e [FIX] newsfragments 2025-02-03 15:56:00 +01:00
Sylvain LE GAL
4fc7056932 [IMP] oow upgrade. Add skip_update as an option in config.yml. (migration_step option). If enabled, in this step, the update all will be skipped, and only pre-migration (SQL) and post-migration (python script) will be executed.
This is interesting:
- for the first step, if your production is up-to-date, to save time.
- for the last step, in recent version (since V14), where openupgrade doesn't contain odoo code. In that case, you should be sure that all your migration is OK, because in openupgrade context, some errors are just ignored.
Use this option with caution.
2025-02-03 11:26:37 +01:00
Simon Maillard
ca53f46248 [IMP] Use builtins "exists" click feature to check if provided files
exists
2025-02-01 13:59:11 +00:00
Simon Maillard
7d1183b140 [IMP] cli restoredb: manage filestore in subfolder 2025-01-31 19:28:01 +00:00
Rémy Taymans
a00ee58b62 Merge branch 'IMP-estimate-workload-hide-done-work' into 'main'
[IMP] estimate-workload html file

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!110
2025-01-31 15:28:30 +00:00
Rémy Taymans
2abf4ee896 [ADD] Newsfragments 2025-01-31 16:06:00 +01:00
Sylvain LE GAL
c66789dc71 [FIX] template: correct indentation 2025-01-31 15:24:40 +01:00
Rémy Taymans
026cae6f5e Merge branch 'split_generate_odoo_command' into 'main'
[IMP] split generate_odoo_command

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!104
2025-01-31 12:45:14 +00:00
Rémy Taymans
a960ddc83c [ADD] newsfragments 2025-01-31 13:23:38 +01:00
Rémy Taymans
95f58f3a9f [FIX] generate_odoo_command_options docstring 2025-01-31 13:23:38 +01:00
Simon Maillard
d595c6584c [IMP] split generate_odoo_command
In 2 functions:

-  generate_odoo_command_options
-  generate_odoo_command

To be able to use generate_odoo_command_options from other places
2025-01-31 13:23:38 +01:00
Rémy Taymans
eec7068628 Merge branch 'update-poetry' into 'main'
[UPD] update to poetry 2.0.0

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!108
2025-01-09 11:16:20 +00:00
Rémy Taymans
a3015e579a [UPD] update to poetry 2.0.0 2025-01-09 10:45:32 +01:00
LE GAL SYLVAIN
8003a86bc1 Merge branch 'fix-readme' into 'main'
[FIX] missing shell command and typos in README.md

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!106
2025-01-07 21:00:06 +00:00
Sergio Zanchetta
c4f0214cc7 [FIX] missing shell command and typos in README.md 2025-01-07 21:32:45 +01:00
Rémy Taymans
f965f867ec Merge branch 'bump-version-1.1.0' into 'main'
Bump to version 1.1.0

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!103
2024-11-07 10:12:32 +00:00
Rémy Taymans
6c069dbffd [VER] Bump to version 1.1.0 2024-11-07 10:48:43 +01:00
Rémy Taymans
188744be2e [FIX] newsfragments 2024-11-07 10:47:18 +01:00
LE GAL SYLVAIN
88b2789f9b Merge branch 'ADD-guess-dependencies' into 'main'
[ADD] guess requirements feature

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!92
2024-11-06 13:29:17 +00:00
Rémy Taymans
86b59fe127 Merge branch 'save-planet-python3-12--3-13' into 'main'
[REM] remove compatibility with python3.9 / python3.10 / python3.11. Keep only 3.12 and 3.13

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!101
2024-11-06 13:08:02 +00:00
Sylvain LE GAL
429a1da9b1 [REM] 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.
2024-11-05 17:30:21 +01:00
LE GAL SYLVAIN
4a44369d84 Merge branch 'FIX-MR-98' into 'main'
[FIX] move fragment in the correct place

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!102
2024-11-05 16:07:31 +00:00
Sylvain LE GAL
09347959fe [FIX] move fragment in the correct place 2024-11-05 15:15:04 +01:00
Sylvain LE GAL
4b4ec890b2 fixup! fixup! fixup! [ADD] guess requirements feature 2024-11-05 15:03:13 +01:00
Sylvain LE GAL
5178bbc8ff fixup! fixup! [ADD] guess requirements feature 2024-11-05 13:44:09 +01:00
Sylvain LE GAL
0bfacbd133 fixup! [ADD] guess requirements feature 2024-11-05 01:00:52 +01:00
Sylvain LE GAL
68bfe19acd [REF] simplify code 2024-11-04 20:42:07 +01:00
Sylvain LE GAL
3b3d652756 [ADD] guess requirements feature 2024-11-04 20:42:07 +01:00
LE GAL SYLVAIN
8791a883f9 Merge branch 'ADD-17.0-branch' into 'main'
[ADD] Support 17.0

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!100
2024-11-04 13:06:16 +00:00
Sylvain LE GAL
2d4a7d6917 [FIX] remove docker build warning, regarding ENV values.
WARN: LegacyKeyValueFormat: 'ENV key=value' should be used instead of legacy 'ENV key value' format
2024-11-04 11:32:53 +01:00
Sylvain LE GAL
c0b8be6b20 [ADD] support 18.0 2024-11-04 11:26:23 +01:00
Sylvain LE GAL
1f079e088e [ADD] support 17.0 2024-11-04 11:22:37 +01:00
Sylvain LE GAL
0785c31825 [REF] Improve settings. Introduce _FIRST_ODOO_VERSION_SUPPORTED and _LAST_ODOO_VERSION_SUPPORTED 2024-11-04 10:55:46 +01:00
LE GAL SYLVAIN
684b1b1d3e Merge branch 'FIX-raise-error' into 'main'
[FIX] raise error when building a docker image

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!99
2024-11-03 23:12:40 +00:00
Sylvain LE GAL
1b201a52d4 [FIX] raise error. 2024-11-02 14:44:23 +01:00
LE GAL SYLVAIN
02cf5f2d51 Merge branch 'python3.13' into 'main'
[ADD] compatibility with python3.13

Closes #62

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!98
2024-10-29 14:40:17 +00:00
Sylvain LE GAL
4bf0a1c055 [ADD] fragment 2024-10-29 13:55:06 +01:00
Sylvain LE GAL
f500cbc6aa [REF] poetry update 2024-10-29 13:53:32 +01:00
Sylvain LE GAL
ab513187f5 [ADD] compatibility with python3.13 2024-10-29 13:38:08 +01:00
Rémy Taymans
ea925e2a17 Merge branch 'bump-version-1.0.3' into 'main'
[VER] Bump to version 1.0.3

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!97
2024-10-10 08:12:52 +00:00
Rémy Taymans
f8dea5022a [VER] Bump to version 1.0.3 2024-10-09 21:34:32 +02:00
Rémy Taymans
02a22a80f7 [FIX] newsfragments 2024-10-09 15:45:30 +02:00
Rémy Taymans
3159d61b8c Merge branch 'fix_psycopg2_and_pg_auth_method_conflict' into 'main'
[FIX] psycopg2 / pg versions auth conflict

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!83
2024-10-09 13:40:18 +00:00
Sylvain LE GAL
e77b99ea82 [ADD] fragment to describe odoo 12 / Postgreqsl 14 Bug Fix 2024-10-09 14:50:59 +02:00
Simon Maillard
f0bc155c3b [FIX] psycopg2 / pg versions auth conflict
Downgrade auth method/encryption to md5 if initial odoo version is <=12
and postgres version >=14
2024-10-09 14:50:59 +02:00
Rémy Taymans
ce28b99600 [ADD] build-error bugfix newsfragments 2024-10-09 14:48:00 +02:00
Rémy Taymans
5ddbbc39e2 Merge branch 'fix-docker-build-handling' into 'main'
[FIX] Docker build failure doesn't display logs because build_log is now a list of dicts?

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!89
2024-10-09 12:46:36 +00:00
Rémy Taymans
477164e68a [ADD] newsfragments 2024-10-08 16:55:14 +02:00
Rémy Taymans
fbb4732fa2 [FIX] newsfragments 2024-10-08 16:14:11 +02:00
Rémy Taymans
6b7f1d008f Merge branch 'fix-60-second-try-container-already-in-progress' into 'main'
[Fix]  container already in progress (second try)

Closes #60

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!94
2024-10-08 14:00:17 +00:00
Sylvain LE GAL
1d15b1f8d3 [FIX] Try to #60. introduce retry mechanism when container removal failed 2024-10-08 15:16:06 +02:00
LE GAL SYLVAIN
5b49b2028f Merge branch 'imp-dev-doc' into 'main'
[DOC] explain newsfragments usage in dev doc

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!96
2024-10-08 13:11:12 +00:00
Rémy Taymans
91c4e0804d [FIX] newsfragments extension 2024-10-08 14:44:30 +02:00
Rémy Taymans
1417df2315 [DOC] explain newsfragments usage in dev doc 2024-10-08 14:44:04 +02:00
Rémy Taymans
c5c7c24ee6 Merge branch 'log_container_status_for_debug' into 'main'
[DBG] temporary add debug statement

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!95
2024-10-08 12:32:51 +00:00
Simon Maillard
eb22c93e15 [DBG] temporary add debug statement 2024-10-08 11:40:02 +02:00
Rémy Taymans
d5c9919139 Merge branch 'readme-highlight' into 'main'
[DOC] hightlight code

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!93
2024-10-08 09:24:12 +00:00
Sylvain LE GAL
2aefc0da6f [DOC] hightlight code 2024-10-08 01:28:30 +02:00
Rémy Taymans
4c244fa445 Merge branch 'poetry-update-2024-10-07' into 'main'
[REF] Update python libraries, using poetry update. Details:

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!91
2024-10-07 13:01:14 +00:00
Sylvain LE GAL
f5ba815607 [ADD] fragment 2024-10-07 14:07:33 +02:00
Sylvain LE GAL
d9e8824b04 [REF] Update python libraries, using poetry update. Details:
Package operations: 0 installs, 27 updates, 1 removal

  • 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)
2024-10-07 11:00:17 +02:00
Rémy Taymans
6d8de8765b Merge branch 'readme-submodule' into 'main'
[DOC] specify that pull-submodule only work in a git context.

Closes #59

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!88
2024-10-06 16:34:45 +00:00
Rémy Taymans
f38b4ac5c2 Merge branch 'bump-version-1.0.2' into 'main'
Bump version 1.0.2

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!90
2024-10-06 16:31:42 +00:00
Rémy Taymans
996940cc91 [VER] Bump to version 1.0.2 2024-10-06 16:26:31 +02:00
Rémy Taymans
4c105146ba Add newsfragments 2024-10-06 16:24:52 +02:00
LE GAL SYLVAIN
11d425e73f Merge branch 'contributors-2024-10-03' into 'main'
[DOC] Update contributors list until October 2024

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!86
2024-10-04 13:25:42 +00:00
Rémy Taymans
20a53ab0b6 Merge branch 'alexAubin1-main-patch-05082' into 'main'
[FIX] cli: Require --database argument for commands that need it (run, psql, generate_module_analysis, install_from_csv)

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!87
2024-10-03 18:48:12 +00:00
Alexandre Aubin
99b710a703 [FIX] Docker build failure doesn't display logs because build_log is now a list of dicts? 2024-10-03 20:04:13 +02:00
Sylvain LE GAL
1ff3c20c76 [DOC] specify that pull-submodule only work in a git context. 2024-10-03 19:16:12 +02:00
Alexandre Aubin
d59c487d1b cli: always require --database argument for commands that needs it
(run, psql, generate_module_analysis, install_from_csv), prevent it from
being None resulting in funky stuff
2024-10-03 19:11:16 +02:00
Sylvain LE GAL
39b7e0932a [DOC] Update contributors list until October 2024 2024-10-03 19:01:42 +02:00
Rémy Taymans
393411f937 Merge branch 'fix_docker_container_removal_already_in_progress' into 'main'
[FIX] Container removal already in progress

Closes #60

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!84
2024-10-03 16:16:12 +00:00
Rémy Taymans
79b499d6e0 Merge branch 'copydb_check_sourcedb' into 'main'
[FIX] Make sure that source exist before droping dest

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!85
2024-10-03 12:41:57 +00:00
Simon Maillard
f649627d63 [FIX] Container removal already in progress
fixes #60
2024-10-03 14:17:20 +02:00
Ahmet Yiğit Budak
4a8b7f467f [IMP] towncrier description for fixing issue/57 2024-10-03 11:57:03 +00:00
Ahmet Yiğit Budak
c08accffac [IMP] Improve test checking database exist 2024-10-03 11:55:52 +00:00
Ahmet Yiğit Budak
ff56b47a94 [FIX] Make sure that source exist before droping dest 2024-10-03 10:45:58 +00:00
Rémy Taymans
5a511fa43d Merge branch 'bump-version-1.0.1' into 'main'
Bump version 1.0.1

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!82
2024-10-01 10:43:32 +00:00
Rémy Taymans
0253a0b110 [VER] Bump to version 1.0.1 2024-10-01 11:53:16 +02:00
Rémy Taymans
04636852ea [IMP] towncrier configuration 2024-10-01 11:51:41 +02:00
LE GAL SYLVAIN
1f95377efa Merge branch 'main' into 'main'
[FIX] pull-submodule work with python3.10

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!80
2024-10-01 09:27:18 +00:00
Rémy Taymans
29674afe84 Merge branch 'ref-simplify-odoo-versions-list' into 'main'
[REF] simplify odoo version list configuration

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!75
2024-10-01 09:11:22 +00:00
Rémy Taymans
166156b531 Merge branch 'postgresql-prompt' into 'main'
[IMP] postgtresql-version option

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!81
2024-10-01 08:15:03 +00:00
Sylvain LE GAL
15b111eaa7 [REF] simplify odoo version list configuration 2024-10-01 09:55:32 +02:00
Rémy Taymans
457117f265 Merge branch 'imp-pyproject-file' into 'main'
[REF] Update pyproject.toml file + remove "Beta" in README file

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!78
2024-09-30 16:51:05 +00:00
Boris Gallet
415fe743f6 [FIX] pull-submodule work with python3.10 2024-09-30 17:22:33 +02:00
Rémy Taymans
6c0ff13399 [CHG] changes for 1.0.0 2024-09-30 17:16:18 +02:00
Rémy Taymans
00f31a2d22 [ADD] newsfragments for previous change 2024-09-30 17:06:41 +02:00
Sylvain LE GAL
846d2f631b [IMP] postgtresql-version option 2024-09-30 17:05:56 +02:00
Rémy Taymans
0e42c27b8b [ADD] newsfragments for previous change 2024-09-30 17:03:14 +02:00
LE GAL SYLVAIN
4536c52558 Merge branch 'display_docker_error' into 'main'
[IMP] Display docker error

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!79
2024-09-30 14:39:55 +00:00
Rémy Taymans
6e5e7206e5 [ADD] poetry info in CI pytest 2024-09-30 16:11:48 +02:00
Rémy Taymans
0cd4b2d67e [FIX] authors and maintainers compatibility with poetry 2024-09-30 16:07:34 +02:00
Sylvain LE GAL
efb31546d0 [REF] add extra project.urls element in pyproject.toml file 2024-09-30 15:06:52 +02:00
Sylvain LE GAL
520e24658f [REF] Update authors and maintainers list 2024-09-30 15:06:52 +02:00
Sylvain LE GAL
7812e99cba [REF] remove 'Beta' label on all commands that are working quite well since a while 2024-09-30 15:06:52 +02:00
Sylvain LE GAL
84a7ab1ca8 [REF] set correct classifiers regarding python version. (same versions as in gitlab-ci.yml file) 2024-09-30 15:06:52 +02:00
Sylvain LE GAL
c6b3fe24cb [REF] Bump version. release of the version 1.0.0 ! 2024-09-30 15:06:52 +02:00
Sylvain LE GAL
24a3c0fdb5 [REF] set project as Beta Project 2024-09-30 15:06:52 +02:00
Rémy Taymans
103a0e7dbb Merge branch 'fix-bad-licence' into 'main'
[FIX] licence is bad displayed on pypi. (currently marked as 'Licence :...

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!77
2024-09-30 09:33:13 +00:00
Rémy Taymans
44e1009fa5 [FIX] keep AGPLv3+ license 2024-09-30 09:16:13 +00:00
Simon Maillard
f941240fea Display docker error
Display error if any docker container creation fail
2024-09-30 10:14:31 +02:00
Sylvain LE GAL
6cd4a0a431 [FIX] licence is bad displayed on pypi. (currently marked as 'Licence : Other/Proprietary License (AGPLv3+) ' 2024-08-29 01:17:06 +02:00
LE GAL SYLVAIN
45df9053ff Merge branch 'fix_run_multi_post_py_files_by_step' into 'main'
[FIX] allow to run multiple post-*py by step

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!76
2024-08-22 09:19:40 +00:00
Simon Maillard
437cf41d4a [IMP] Litte code improvment, Removes useless transtyping 2024-08-22 09:06:08 +02:00
Simon Maillard
23cc76f7f0 [IMP] Improve tests around custom migration scripts 2024-08-21 16:02:00 +02:00
Simon Maillard
39f70d1012 [FIX] allow to run multiple post-*py by step
Like:
scripts/step_01__regular__12.0/01-post-migration-uninstall-modules.py
scripts/step_01__regular__12.0/02-post-migration-fix-project-issues.py
scripts/step_01__regular__12.0/03-post-migration-fix-mail-aliases.py
scripts/step_01__regular__12.0/04-fix-misc-inconsistant-records.py
2024-08-21 10:12:14 +02:00
Rémy Taymans
5eed63ca56 Merge branch 'restore-sql' into 'main'
[ADD] restore-sql

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!74
2024-05-07 14:48:45 +00:00
Gabriel Pickenhayn
7dcf456525 [ADD] restore-sql 2024-05-07 14:48:44 +00:00
Rémy Taymans
1498148d65 Merge branch 'bump-version-0.7.0' into 'main'
[VER] Bump to version 0.7.0

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!73
2024-05-02 20:44:14 +00:00
Rémy Taymans
cd80fe1a72 [VER] Bump to version 0.7.0 2024-05-02 22:17:21 +02:00
Rémy Taymans
62f77499fb Merge branch 'allow-to-upgrade-multiple-databases-in-parallel' into 'main'
allow to upgrade multiple databases in parallel

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!69
2024-05-02 20:14:48 +00:00
hugues de keyzer
af1b6efce6 remove specific container instead of pruning 2024-04-30 15:11:20 +02:00
hugues de keyzer
f6f9c0ad80 add news fragments 2024-04-30 15:10:43 +02:00
hugues de keyzer
560f4f5485 publish docker ports only when needed 2024-04-30 15:10:43 +02:00
LE GAL SYLVAIN
4facd60e30 Merge branch 'lighter-ci-images' into 'main'
Change for lighter ci images

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!72
2024-04-12 12:10:16 +00:00
Rémy Taymans
d286f868ef Change for lighter ci images
Default python images are based on debian and are heavy (~1Go).

Using alpine version for smaller images (~100Mo).

This will save space on the runner and allow more container to be run at
same time.
2024-04-12 12:50:09 +02:00
Rémy Taymans
0a0c763a49 Merge branch 'allow-minus-in-database-names' into 'main'
allow hyphen-minus character in database names

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!70
2024-04-12 10:32:13 +00:00
LE GAL SYLVAIN
4804926c1e Merge branch 'add-config-file-cli-option-newsfragment' into 'main'
add news fragment for new cli options

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!71
2024-04-12 10:09:26 +00:00
hugues de keyzer
58efc7db37 add news fragment for new cli options 2024-04-12 11:48:53 +02:00
hugues de keyzer
bb2b3be3b4 allow hyphen-minus character in database names 2024-04-12 11:35:22 +02:00
LE GAL SYLVAIN
de0ea69f59 Merge branch 'add-config-file-cli-option' into 'main'
add config file and modules file cli options

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!68
2024-04-12 08:30:45 +00:00
hugues de keyzer
58bc87380d add database name to container name 2024-04-10 17:15:21 +02:00
hugues de keyzer
148aa5f89f add config file and modules file cli options
allow to provide a config file and a modules file option to use
different files than the default ones. this is useful when using the
same environment for different databases.
2024-04-10 15:32:29 +02:00
LE GAL SYLVAIN
93dc52ee5f Merge branch 'fix-dependencies' into 'main'
[FIX] dependencies between docker and requests

Closes #35

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!67
2024-03-25 17:55:22 +00:00
Rémy Taymans
2ae157988b [FIX] dependencies between docker and requests 2024-03-25 16:23:38 +01:00
Rémy Taymans
bd1190ab06 Merge branch 'cosmetic-changes' into 'main'
[REF] Cosmetic changes (remove useless comment + f-string everywhere)

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!61
2024-03-23 22:04:48 +00:00
Rémy Taymans
f1cf0dac23 Apply all suggestion made by Rémy Taymans. 2024-03-23 21:59:12 +01:00
Sylvain LE GAL
a73de5f240 [REF] apply f-string to improve readability 2024-03-23 20:53:57 +01:00
Sylvain LE GAL
7b5f922c61 [REF] remove useless commented code 2024-03-23 20:51:18 +01:00
Rémy Taymans
f04da743bb Merge branch 'ref-set-log-level-debug-default-in-cli_runner_invoke' into 'main'
[REF] factorize code : allways set --log-level=DEBUG in test framework

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!63
2024-03-23 16:09:32 +00:00
Rémy Taymans
6a2acce0ab Merge branch 'psql-refactor-code' into 'main'
[REF] Psql refactor code

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!65
2024-03-23 16:00:40 +00:00
Sylvain LE GAL
853f37889c [FIX] 'CREATE DATABASE' is a command, not a request that requires result 2024-03-23 10:23:54 +01:00
Sylvain LE GAL
4a874957ed [REF] move postgres code into dedicated tools file 2024-03-23 10:16:32 +01:00
Sylvain LE GAL
e124f3d27f [REF] factorize code : allways set --log-level=DEBUG in test framework 2024-03-22 23:49:52 +01:00
Rémy Taymans
e5b0ebe6e8 Merge branch 'main-add-postgresql-version-in-container-name' into 'main'
[IMP] oow init : new option --postgresql-version

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!62
2024-03-22 21:49:56 +00:00
Sylvain LE GAL
a11d137eed [IMP] oow init : new option --postgresql-version 2024-03-22 20:30:43 +01:00
LE GAL SYLVAIN
908a38ab81 Merge branch 'fix-36' into 'main'
[UPD] drop python < 3.9 support and update dependencies

Closes #35 et #36

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!60
2024-03-22 08:59:06 +00:00
Rémy Taymans
2fa52d4602 [UPD] drop python < 3.9 support
This also fixes dependencies issues.
2024-03-22 00:51:40 +01:00
Rémy Taymans
b54c865256 Merge branch 'add-towncrier' into 'main'
Add Towncrier and bump to version 0.6.0

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!59
2024-03-20 16:27:17 +00:00
Rémy Taymans
c960cf4f70 Bump to version 0.6.0 2024-03-20 15:14:04 +01:00
Rémy Taymans
4796e769f7 [ADD] news fragments for previous changes 2024-03-20 12:33:36 +01:00
Rémy Taymans
abefad1607 [ADD] towncrier support for building changlog 2024-03-20 12:33:36 +01:00
Sylvain LE GAL
c345d831bc [IMP] add new assert_database test function and harmonize tests for restoredb, copydb, dropdb 2024-03-20 12:15:59 +01:00
Sylvain LE GAL
8016b35624 [ADD] restoredb function 2024-03-20 12:15:59 +01:00
Rémy Taymans
567ffd4428 Merge branch 'ADD-restoredb' into 'main'
[ADD] restoredb function

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!40
2024-03-03 08:25:39 +00:00
Sylvain LE GAL
b7e3587767 [IMP] add new assert_database test function and harmonize tests for restoredb, copydb, dropdb 2024-03-02 23:59:36 +01:00
Sylvain LE GAL
748d5a54f6 [ADD] restoredb function 2024-03-02 23:59:36 +01:00
Rémy Taymans
e5a3861cde Merge branch 'ADD-option-execution-demo-etc' into 'main'
[IMP] New options in various cli commands. (with-demo, etc...)

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!57
2024-03-01 19:03:15 +00:00
Sylvain LE GAL
dbdf592990 [IMP] 'run' cli command. Add '--update-modules' option 2024-03-01 15:51:09 +01:00
Sylvain LE GAL
d890a460ea [IMP] Harmonize run and upgrade cli command. Allow to call command with argument --with-demo / --without-demo like in install-from-csv.
For that purpose, create a new decorator.
[REF] Change the default of install-from-csv to harmonize. (allways False)
2024-03-01 15:51:09 +01:00
Rémy Taymans
02c6fa48cf Merge branch 'FIX-prevent-error-empty-addon-path' into 'main'
[FIX] Do not crash silently, when a repos.yml file reference an empty addons folder.

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!56
2024-03-01 14:12:35 +00:00
Sylvain LE GAL
f75821c2bc [FIX] Do not crash silently, when a repos.yml file reference an empty addons folder.
Step to reproduce :
- run a V15 instance
- add a reference to 'OCA/geospatial' repo that doesn't contain any odoo module.
As a result, Odoo will exit with the following error:
    odoo-bin: error: option --addons-path: the path '/odoo_env/src/OCA/geospatial' is not a valid addons directory

To avoid such problem, and avoid to have to remove empty repository
(that could become non empty in the future, and contains some migrations scripts),
- we reimplement a version of the odoo function _is_addons_path (odoo/odoo/tools/config.py)
- we add an info log :
    Skipping addons path '.../src/env_15.0/src/OCA/geospatial'  because it doesn't contain any odoo module.
2024-03-01 13:47:26 +01:00
Rémy Taymans
52568cedc5 Merge branch '42-add-dropdb' into 'main'
[ADD] dropdb

Closes #42

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!55
2024-02-22 08:09:48 +00:00
Rémy Taymans
30198392c5 [ADD] dropdb 2024-02-22 08:53:45 +01:00
Rémy Taymans
e5921fe4c7 Merge branch 'update_ci' into 'main'
New CI to auto build and publish

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!51
2024-02-21 18:49:41 +00:00
Rémy Taymans
72a5dcf434 New CI to auto build and publish
Using a commit message to trigger the build does not work when merging a
PR because last commit is the merge commit and not the commit edited
with the right name.

Given that, the jobs that will run, are defined at the creation of the
pipeline, publishing and creating a release cannot be done based on the
sate of the code.

A way to trigger publication and release is the git tags.

So with theses changes:

- linting is done only on a merge request
- testing and building are performed on a merge request and on the main
  branch

When a tag is pushed:

- check are done to ensure that the tag is the same as the version of
  the program, in order to not publish and release someting that is not
  coherent.
- the program is published on pypi.
- a release is created, but only if the tag is for a major, minor or
  patch version. No release created for an alpha, beta or pre-release
  version.

So all versions of the program are published on PyPI, but only the
important ones are published via the release mechanism. Because the
release mechanism will warn user for a new version. Version that are not
major, minor or patch are not intended to be used by end users.

The idea of auto publishing and releasing every time a commit is pushed
on the main branch does not work with semantic versioning. For doing
that maybe a calversioning will be better.

The idea of using the CI to push a tag for a new release lead to
security risk. Because the CI will contains credential for writing to
the repository, any contributor can read this token by editing the
gitlab-ci file and use token for bad purposes. Gitlab does not provide
token for writing to a repository owned by the project.

So for now, we control the publication and release of a new version with
two actions:

- updating the version on the pyproject.toml file.
- creating a tag with the same version as in the pyproject.toml file.
2024-02-21 11:50:27 +01:00
LE GAL SYLVAIN
dea0bbd70c Merge branch 'fix-estimate-workload' into 'main'
[FIX] estimate-workload: wrong warning message

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!53
2024-02-08 09:51:34 +00:00
LE GAL SYLVAIN
717f396dc6 Merge branch 'fix-install-from-csv-demo' into 'main'
[FIX] install-from-csv: add with-demo option

Closes #41

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!54
2024-02-08 09:45:39 +00:00
Rémy Taymans
25655d4b7a [FIX] install-from-csv: add with-demo option
Add a flag to specify if the database created by install-from-csv should
install demo data or not.

Fix #41
2024-02-07 17:25:56 +01:00
Rémy Taymans
2b0e359d4e [FIX] estimate-workload: wrong warning message
Previous syntax does not replace correctly the markers in the string.

Using an f-string fix this.

Before:
2024-02-06 10:47:02.303 | WARNING  | odoo_openupgrade_wizard.tools.tools_odoo_module:get_odoo_apps_url:359 - Error when trying to get %s: %s

After:
2024-02-07 10:51:11.964 | WARNING  | odoo_openupgrade_wizard.tools.tools_odoo_module:get_odoo_apps_url:356 - Error when trying to get https://apps.odoo.com/apps/modules/12.0/partner_contact_address/: Exceeded 30 redirects.
2024-02-07 11:15:23 +01:00
LE GAL SYLVAIN
a878df2915 Merge branch 'fix-estimate-workload' into 'main'
[FIX] getting url on apps.odoo.com

Closes #40

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!52
2024-02-06 00:42:09 +00:00
Rémy Taymans
38ac07c141 [FIX] getting url on apps.odoo.com
Fix #40
2024-02-05 16:14:06 +01:00
Rémy Taymans
372d797430 Merge branch 'main-add-C2C-author' into 'main'
[DOC] Update contributors list

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!46
2023-11-16 10:59:55 +00:00
Sylvain LE GAL
51f90a5338 [DOC] Update contributors list 2023-11-16 11:40:28 +01:00
Rémy Taymans
1f720c667e Merge branch 'update_ci' into 'main'
New CI with docker-in-docker

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!48
2023-11-16 10:36:01 +00:00
Rémy Taymans
d781709cb5 Fix 404 not found errors when using docker 2023-11-09 20:50:46 +01:00
Rémy Taymans
0dc011684d Use docker-in-docker to run tests
Cortesy @maisim.

This commit allow to use docker-in-docker to run tests.

Also it will run the tests on python version defined in the
PYTHON_VERSION array. The tox.ini file is not needed anymore.
Test locally can be run on the user python version, and check against
several python version will be done by the CI.

Notice the ODOO_RPC_URL environment variable which allow to mock the RPC
request to the right url.

This tests needs to be run on custom privileged runner.
! Using gitlab runner does not work !
2023-11-09 20:50:46 +01:00
Rémy Taymans
6a36c97ee4 Use last python image for linting
The linting check can be done on any docker image with the latest python
version.

This updates de pre-commit checks and fix file linting to conform to new
standards.
2023-11-09 17:02:24 +01:00
Rémy Taymans
690ab60699 Fix Dockerfile when using root on host
root user should not be used to create container and oow should not be
run as root.

But when running oow in a docker, then the default user of this docker
can be root.

This fix allow to link the odoo user in the odoo environment docker to
match a root user of the host docker.
2023-11-09 16:36:53 +01:00
Rémy Taymans
a3a8af9a70 Fix listing containers during removal of containers
Removing a container can take some time to be performed. Listing
containers when a container is currently being removed may cause errors.
Because listing container and filtering them by name requires to fetch
data from the container that is currently being removed.
2023-11-09 16:27:29 +01:00
Rémy Taymans
a0307847b9 Allow mocking the ODOO_RPC_URL for testing
Depending of the test environment, the url of the odoo container may
change.

Using pytest in a local shell, Odoo will be available at 0.0.0.0
address.

Using pytest in a docker-in-docker environment, the url for the Odoo
container will be exposed on a different address depending on the
configuration of the docker-in-docker environment.

The mock check whether the user has configured a différent URL for the
Odoo RPC calls, and set it accordingly using mock feature.

In the gitlab-ci, the ODOO_RPC_URL should be used to set the correct
address for the RPC calls.
2023-11-09 16:21:20 +01:00
Rémy Taymans
8ea0195aaf Add pytest-mock dev dependencies
The lock file is regenerated and some dependencies are updated.

Poetry now uses a system of group for extra dependencies. Dependencies
for testing purposes has been put in the "dev" group in the
pyproject.toml file.
2023-11-09 16:17:04 +01:00
LE GAL SYLVAIN
71ae846462 Merge branch 'test-disable-copy-db' into 'main'
[FIX] fix copydb tests. do no use case sensitive database name

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!50
2023-11-07 09:57:23 +00:00
Sylvain LE GAL
8235481032 [FIX] fix copydb tests. do no use case sensitive database name 2023-11-07 10:25:22 +01:00
LE GAL SYLVAIN
0c6110373a Merge branch 'FIX-copydb-add-copyfilestore' into 'main'
Finish copydb. (add copy of the filestore)

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!39
2023-07-18 08:55:41 +00:00
Sylvain LE GAL
7a31cc666d [FIX] copydb : add the copy of the filestore
[DOC] Add documentation section for the copydb function
2023-07-17 14:12:14 +02:00
Rémy Taymans
114f7bfe32 Bump to version 0.5.0
Main new features:
- oow psql: run any query on a database
- oow dumpdb: dump database and its filestore
- Documentation improvement.

Other small fixes.
2023-07-13 17:57:15 +02:00
Rémy Taymans
f143e218a0 (Also improve CI) 2023-07-13 14:24:20 +00:00
Simon
070143f3fe Wait for the container name to be truly freed before returning 2023-07-13 14:24:20 +00:00
Rémy Taymans
60f7b78413 Merge branch '26-dump-db-command' into 'main'
[ADD] dumpdb command

Closes #26

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!32
2023-07-12 06:52:11 +00:00
Rémy Taymans
757cc19576 [FIX] dumpdb: error when using force 2023-07-12 08:35:20 +02:00
Rémy Taymans
f130ecf078 [ADD] dumpdb: tests 2023-07-12 08:35:20 +02:00
Rémy Taymans
e9e02d450d [ADD] dumpdb: doc 2023-07-12 08:32:57 +02:00
Rémy Taymans
debf57bac3 [ADD] dumpdb command 2023-07-12 08:32:57 +02:00
LE GAL SYLVAIN
0764b811d5 Merge branch 'ci-build-and-release' into 'main'
[ADD] build and release in CI

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!41
2023-07-11 21:11:18 +00:00
Rémy Taymans
59025a14e0 [ADD] build and release in CI
This will produce the following behaviour:

When a commit with a title like "Bump to version x.y.z" is committed to
main branch (either by merging a merge request or by pushing it directly
to the main branch), a build of the project is triggered and the result
is pushed on PyPI.

Also a release on the github project is created with the version of the
program as title and the content of the commit message as description.

This will help deploying release of oow. It will also warn followers
that a new version is released by a notification if they choose to.

PYPI_USER and PYPI_TOKEN are value set on the gitlab settings of the
project and can only be viewed by maintainers.
2023-07-11 17:03:00 +02:00
LE GAL SYLVAIN
badba4bd31 Merge branch '21-psql-command' into 'main'
[ADD] psql command

Closes #21

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!31
2023-07-06 15:23:02 +00:00
Rémy Taymans
a436490a68 Merge branch '21-psql-command-improvement-sylvain' into '21-psql-command'
Improve #31

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!38
2023-07-06 15:14:39 +00:00
Sylvain LE GAL
c813f3ddd4 -[DOC] psl command : add description 2023-07-06 16:37:52 +02:00
Sylvain LE GAL
8a79974232 [REF] Use f-string to make the code lighter
[FIX] execute_psql_command: make optional database argument working correctly
[FIX] execute_psql_command: avoid to have an error
2023-07-06 16:37:52 +02:00
Rémy Taymans
284417bb00 [ADD] psql command 2023-05-09 21:34:17 +02:00
LE GAL SYLVAIN
3b34f52ddd Merge branch 'DOC-legalsylvain-add-extra-badge' into 'main'
[DOC] Add extra badges

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!35
2023-04-26 19:08:56 +00:00
Sylvain LE GAL
9afa13054e [DOC] Add extra badges 2023-04-26 21:04:18 +02:00
LE GAL SYLVAIN
ec537d1abf Merge branch 'legalsylvain-table-of-content' into 'main'
[DOC] Add Table of content in Readme.md file

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!34
2023-04-26 18:13:07 +00:00
Sylvain LE GAL
17ea6aa9c2 [DOC] Add badges 2023-04-26 19:45:58 +02:00
Sylvain LE GAL
ad0f51267c [DOC] Add Table of content in Readme.md file 2023-04-26 14:49:41 +02:00
LE GAL SYLVAIN
3aab973729 Merge branch 'fix-debian-image-archive' into 'main'
[FIX] Image debian changed repo to archive for V11 and V12 dockerfile

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!33
2023-04-26 10:22:34 +00:00
Cyril Jeanneret
c5c4276a82 Fix image debian changed repo to archive 2023-04-26 06:45:19 +00:00
Rémy Taymans
a161e4d02f Bump to version 0.4.0 2023-03-17 12:33:42 +01:00
LE GAL SYLVAIN
6a7ad7ec37 Merge branch '32-ignore-module-list' into 'main'
Resolve "[oow estimate-workload] Add ignore modules list"

Closes #32

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!30
2023-03-17 10:13:58 +00:00
Rémy Taymans
40c54d2260 [FIX] missing newline in generated files
In a proper file each line end with a newline character, even the last
one.

Wrong file:

Using `write()` function does not add a newline character at the end of
the write. So writing a string in a file with `write()` leads to a file
like this:

```
line1\n
line2\n
lastline
```

Good file:

Using the `print()` command automatically ends the string with a newline
character. So that we ends with a proper file:
```
line1\n
line2\n
lastline\n
```

Also the with statement automatically closes the file at the end of the
block.
2023-03-17 08:54:44 +01:00
Rémy Taymans
15088c9d0f [FIX] config: indentation 2023-03-17 00:16:00 +01:00
Rémy Taymans
a04353b683 [ADD] estimate-workload: ignored module list 2023-03-16 23:26:45 +01:00
Rémy Taymans
acf70013cb Bump to version 0.3.0 2023-03-16 21:09:19 +01:00
LE GAL SYLVAIN
a55b921457 Merge branch 'fix-docker_kill_error_not_found' into 'main'
Fix docker kill error not found and pre-migration files (fix #15 #16)

Closes #16 and #15

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!28
2023-02-28 10:54:42 +00:00
Rémy Taymans
9e0489f1f1 [FIX] upgrade: run pre_migration files 2023-02-28 09:43:54 +01:00
Rémy Taymans
7a2b19664d [FIX] error when killing container 2023-02-28 09:21:53 +01:00
LE GAL SYLVAIN
9a212eeeb5 Merge branch 'imp-estimate_workload-report' into 'main'
[IMP] estimate-workload: add total column in report

Closes #29

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!27
2023-02-22 10:47:23 +00:00
Rémy Taymans
312e654e88 [IMP] estimate_workload: add time configuration 2023-01-27 17:30:50 +01:00
Rémy Taymans
a2423ec4f8 [IMP] estimate-workload: add total column in report 2023-01-18 19:50:29 +01:00
Rémy Taymans
e35aa8d804 Merge branch 'fix-repository_name-case-estimate-workload' into 'main'
[FIX] repository_name

Closes #30 and #24

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!26
2023-01-17 09:48:39 +00:00
Rémy Taymans
0b5ff9ede1 Merge branch 'fix-typo-config' into 'main'
[FIX] Typo in config file

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!25
2023-01-11 11:41:34 +00:00
Rémy Taymans
cfb0c8b65e [FIX] Typo in config file 2023-01-11 12:32:40 +01:00
Rémy Taymans
a5dcd8fdcc [IMP] module repository_name detection
Some repository may have multiples remotes. This tries to find the main
remote when possible.
2023-01-10 19:52:51 +01:00
Rémy Taymans
8c55892d53 [FIX] estimate-workload: not_found module not categorized 2023-01-10 19:51:20 +01:00
Rémy Taymans
9281d6b572 [FIX] repository_name case insensitive 2023-01-10 19:50:20 +01:00
LE GAL SYLVAIN
4bdfa89758 Merge branch 'imp-errors-docker-build' into 'main'
[IMP] docker-build: errors if no odoo_requirements file

Closes #11

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!21
2023-01-03 14:31:03 +00:00
Rémy Taymans
5777d9004f [IMP] docker-build: errors if no odoo_requirements file 2023-01-03 14:16:29 +01:00
LE GAL SYLVAIN
30c0f1e61e Merge branch 'FIX-use-correct-dockerfiles' into 'main'
[ADD] 11.0 docker file : Add Dockerfile, based on...

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!23
2023-01-03 12:01:38 +00:00
Sylvain LE GAL
f7a198823a [REF] move lines in dockerfile to optimize image builds if python / debian requirements files changed 2023-01-03 10:41:42 +01:00
Rémy Taymans
ada619279d fix typo. 2023-01-03 10:41:42 +01:00
Sylvain LE GAL
eeb8c1dab2 [DOC] upgrade documentation, to explain Dockerfile creation (and current issues)
[ADD] 11.0 docker file : Add Dockerfile, based on https://raw.githubusercontent.com/odoo/odoo/11.0/setup/package.dfsrc
[FIX] 12.0 docker file : use correct Dockerfile, based on https://raw.githubusercontent.com/odoo/odoo/12.0/setup/package.dfsrc
[FIX] 13.0 docker file : use correct Dockerfile, based on https://raw.githubusercontent.com/odoo/odoo/13.0/setup/package.dfsrc
[FIX] 14.0 docker file : use correct Dockerfile, based on https://raw.githubusercontent.com/odoo/odoo/14.0/setup/package.dfsrc
[FIX] 15.0 docker file : use correct Dockerfile, based on https://raw.githubusercontent.com/odoo/odoo/15.0/setup/package.dfsrc
[FIX] 16.0 docker file : use correct Dockerfile, based on https://raw.githubusercontent.com/odoo/odoo/16.0/setup/package.dfsrc
2023-01-03 10:41:42 +01:00
Rémy Taymans
c37f25e723 Merge branch 'fix-18-estimate-workload' into 'main'
Fix #18

Closes #18

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!24
2023-01-02 18:42:50 +00:00
Rémy Taymans
82da4bb3ba [FIX] estimate-workload: InvalidGitRepositoryError
For older Odoo version directories are named openerp.
2023-01-02 18:52:00 +01:00
Rémy Taymans
8838438492 Merge branch 'ADD-16.0-branch' into 'main'
various minor changes

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!22
2022-12-14 10:17:06 +00:00
Sylvain LE GAL
a56d9cdf0c [REF] trivial refacgtoring avoid to compute odoo_folder for each key in data.keys() 2022-11-10 20:52:40 +01:00
Sylvain LE GAL
b9240b9dc4 [DOC] mention current limitation, regarding the fact that odoo and openupgrade path are forced in the repos.yml project 2022-11-10 20:52:00 +01:00
Sylvain LE GAL
1ddc86385b [DOC] update documentation, regarding 16.0 branch 2022-11-10 20:26:11 +01:00
Sylvain LE GAL
ee8fb9bd1a [FIX] pull submodule : use str instead of Posix path 2022-11-10 20:23:58 +01:00
Rémy Taymans
2f5f1edaf0 Merge branch 'add-docker-odoo-16' into 'main'
[ADD] odoo 16.0 template

Closes #10

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!20
2022-10-13 20:02:05 +00:00
Rémy Taymans
ec89747ee5 Merge branch 'rename-odoo-conf' into 'main'
[FIX] odoo.cfg to odoo.conf

Closes #9 and #8

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!19
2022-10-13 20:01:24 +00:00
Rémy Taymans
95a6643d88 [ADD] odoo 16.0 template 2022-10-12 10:38:56 +02:00
LE GAL SYLVAIN
dea87753d6 [DOC] improve badges in README.md file, courety @remytms 2022-10-11 20:54:10 +00:00
Rémy Taymans
6d0d6f355e [IMP] documentation about odoo.conf
Close #9
2022-10-11 16:01:08 +02:00
Rémy Taymans
f3c9d3b533 [FIX] odoo.cfg to odoo.conf
Close issue #8
2022-10-11 14:39:38 +02:00
LE GAL SYLVAIN
ed086f7df7 Merge branch 'add-postgresql-conf' into 'main'
[IMP] Add postgresql custom configuration ; [ADD] extra setting into...

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!18
2022-10-11 10:32:25 +00:00
Sylvain LE GAL
4634def83f [IMP] use always the master branch of openupgradelib 2022-10-11 12:26:41 +02:00
Sylvain LE GAL
e63851cf9e [ADD][WIP] copydb (pseudo click-odoo function 2022-10-11 12:26:41 +02:00
Sylvain LE GAL
bdc9b606ec [IMP] Add postgresql custom configuration ; [ADD] extra setting into odoo_default_company key. (in config.yml file) ; [ADD] odoo_rpc_timeout extra settings ; [FIX] restore call to custom odoo.cfg file 2022-10-11 12:26:41 +02:00
Rémy Taymans
66fa0b45b8 Fix coverage information for badges 2022-10-10 12:06:26 +02:00
Rémy Taymans
946f051404 Fix test pipeline on main 2022-10-10 08:33:22 +00:00
LE GAL SYLVAIN
0e44cad60d Merge branch 'add-repo-url' into 'main'
[ADD] repos url git log!

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!17
2022-07-12 15:25:12 +00:00
Sylvain LE GAL
531978eb00 [ADD] repos url git log! 2022-07-12 00:55:36 +02:00
LE GAL SYLVAIN
ae9294f8b4 Merge branch 'dockerfile-pocalypse-2-use-custom-docker-file' into 'main'
[IMP] use LOCAL_USER_ID calling odoo-bedrock, so no need to create filestore...

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!16
2022-07-11 15:50:57 +00:00
Sylvain LE GAL
d7a576890a [IMP] prune postgres container 2022-07-11 16:46:08 +02:00
Sylvain LE GAL
72cbc90b61 [FIX] CI 2022-07-11 16:08:52 +02:00
Sylvain LE GAL
e6e05c6ab7 [FIX] CI 2022-07-11 15:50:41 +02:00
Sylvain LE GAL
c924103fa0 [IMP] add local_user_id in build call. 2022-07-11 15:15:22 +02:00
Sylvain LE GAL
11c43cf8b2 [IMP] description in config.yml file 2022-07-11 13:57:13 +02:00
Sylvain LE GAL
be74ce5bab [REF] Set correct dockerfile, based on odoo/debian ones. (for 12 to 15) 2022-07-11 13:43:55 +02:00
Sylvain LE GAL
590f92a1c9 [REM] fucking inadapted docker images 2022-07-11 00:15:56 +02:00
Sylvain LE GAL
acb02fd9cc wip de chez wip 2022-07-01 13:38:59 +02:00
Sylvain LE GAL
a964efd029 wip 2022-06-29 22:12:29 +02:00
Sylvain LE GAL
4500d9fe43 [REF] introduce exec_container command 2022-06-29 21:53:26 +02:00
Sylvain LE GAL
afcdeaa81d ADD a lot of logger to try to understand why it works locally and not on the CI 2022-06-29 21:16:27 +02:00
Sylvain LE GAL
a9f7fa3568 improve log 2022-06-29 21:03:06 +02:00
Sylvain LE GAL
798e27ab16 [IMP] add logger in execute_sql_file 2022-06-29 20:13:08 +02:00
Sylvain LE GAL
f44ac7f7cd [FIX] par exemple, introduire des bugs, c'est pas gentils (2) 2022-06-29 18:15:14 +02:00
Sylvain LE GAL
819ef6a417 [FIX] par exemple, introduire des bugs, c'est pas gentils 2022-06-29 18:02:21 +02:00
Sylvain LE GAL
80bd469095 [FIX] replace hardcoded 1000 value by os.getuid() 2022-06-29 17:52:58 +02:00
Sylvain LE GAL
4427470a41 various 2022-06-29 17:46:58 +02:00
Sylvain LE GAL
842e2ca389 various 2022-06-29 17:45:21 +02:00
Sylvain LE GAL
9f5dea8a00 [REF] test add absolute file path 2022-06-29 17:40:38 +02:00
Sylvain LE GAL
1261dfaa2f [REF] Add logger in test 2022-06-29 17:37:08 +02:00
Sylvain LE GAL
36db85e4b9 [ADD] Log if container fails during odoo execution 2022-06-29 17:17:17 +02:00
Sylvain LE GAL
63c547f61a [DOC] update doc, removing click-odoo 2022-06-29 16:53:10 +02:00
Sylvain LE GAL
c34715a393 [FIX] add importlib-resources as a dependency 2022-06-29 16:47:08 +02:00
Sylvain LE GAL
424131e9b2 [REF] Remove bind fucking mounts (postgres) replaced by nice volumes 2022-06-29 16:28:48 +02:00
Sylvain LE GAL
7fca87bae7 [FIX] GREENIFY ALL THE TESTgit status! 2022-06-29 15:50:17 +02:00
Sylvain LE GAL
ae3619e766 [REF] replace click-odoo by calling shell command directly 2022-06-29 15:13:13 +02:00
Sylvain LE GAL
1ead68bef0 [REF] remove click-odoo that requires to import odoo that is a mess 2022-06-29 14:12:12 +02:00
Sylvain LE GAL
8035ba2871 [FIX] various test. Readd odoorpc in the requilrements.txt file to generate_module_analysis and add comments in the file 2022-06-29 11:25:29 +02:00
Sylvain LE GAL
b22d9b5ad4 [FIX] cli_06_execute_script_sql_test.py 2022-06-29 11:08:38 +02:00
Sylvain LE GAL
01b2a94cb4 [FIX] do not crash if postgres container exists in a exited status 2022-06-29 10:58:27 +02:00
Sylvain LE GAL
db3d16e37c [IMP] make cli_05_execute_script_python_test working 2022-06-29 10:34:58 +02:00
Sylvain LE GAL
b5419e5d19 [IMP] use LOCAL_USER_ID calling odoo-bedrock, so no need to create filestore and log folder with 777 2022-06-28 16:55:08 +02:00
Sylvain LE GAL
4d3bb66e4e [FIX] test from 01 to 04 2022-06-28 16:45:44 +02:00
Sylvain LE GAL
451be8c389 FIX pass execution_context 2022-06-28 01:22:24 +02:00
Sylvain LE GAL
b64c445cb4 [WIP] 2022-06-28 01:06:23 +02:00
Sylvain LE GAL
429b8013b9 le run, ça marche ! (bon bah pour toute les versions...) 2022-06-27 23:53:51 +02:00
Sylvain LE GAL
bac5d0d22e wip 2022-06-27 21:59:57 +02:00
Sylvain LE GAL
bacca4f7c6 wip 2022-06-27 17:21:15 +02:00
Sylvain LE GAL
80e5e3ad87 [REF] Dockerfiles : switch from odoo/odoo to acsone/odoo-bedrock 2022-06-27 15:56:28 +02:00
Sylvain LE GAL
b3638126e0 [DOC] Update Readme file 2022-06-27 12:25:46 +02:00
LE GAL SYLVAIN
ec73058d5d Merge branch 'publish-0.2.0' into 'main'
[REF] remove coop it easy as author in the pyproject.toml. The mention is done...

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!14
2022-06-27 10:15:03 +00:00
Sylvain LE GAL
01d889cfc9 [REF] remove coop it easy as author in the pyproject.toml. The mention is done as reviewer in the CONTRIBUTORS.rst file 2022-06-27 12:14:21 +02:00
Sylvain LE GAL
92e67dc3a0 [REM] obsolete gitlab-ci file. (used for gitlab-ci). Now akretion CI is used 2022-06-27 12:13:16 +02:00
LE GAL SYLVAIN
9ba5d5cffe Merge branch 'dev-12-add-alternative-name' into 'main'
[ADD] alternative short name 'oow' + [FIX] bad path for cli:main function

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!13
2022-06-27 00:23:36 +00:00
Sylvain LE GAL
92c09d5109 [ADD] alternative short name 'oow' + [FIX] bad path for cli:main function 2022-06-27 02:23:00 +02:00
LE GAL SYLVAIN
b4131c9600 Merge branch 'dev-11-REF-create-cli-tools-folders' into 'main'
Dev 11 ref create cli tools folders

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!12
2022-06-27 00:04:44 +00:00
Sylvain LE GAL
0977781a3c [FIX] set jinja2 as main library to install 2022-06-27 02:01:23 +02:00
Sylvain LE GAL
b37bfa8e31 [REF] create tools & cli folders 2022-06-27 01:57:31 +02:00
LE GAL SYLVAIN
59370207b6 Merge branch 'dev-10-refactor-templates-folder' into 'main'
[REF] move all templates present in python file into a 'templates' subfolder

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!11
2022-06-26 22:35:15 +00:00
Sylvain LE GAL
f895b7d05a [REF] move all templates present in python file into a 'templates' subfolder 2022-06-27 00:34:39 +02:00
LE GAL SYLVAIN
945916ecc5 Merge branch 'dev-9-estimate-workload-handle-unknown-modules' into 'main'
[IMP] make resilient the call estimate-workload if some modules are unknown

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!10
2022-06-26 19:29:50 +00:00
Sylvain LE GAL
8836c2c656 [IMP] make resilient the call estimate-workload if some modules are unknown 2022-06-26 21:28:12 +02:00
LE GAL SYLVAIN
5e3b7d51bf Merge branch 'publish-0.1.0' into 'main'
[META] publish version 0.1.0

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!9
2022-06-24 22:15:39 +00:00
Sylvain LE GAL
b5230fe947 [META] publish version 0.1.0 2022-06-25 00:15:05 +02:00
LE GAL SYLVAIN
81fa7b909c Merge branch 'dev-7-harmonization-revision-release-3' into 'main'
[FIX] various fixes

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!8
2022-06-23 18:53:56 +00:00
Sylvain LE GAL
599ba9599c [FIX] various fixes 2022-06-23 20:42:34 +02:00
LE GAL SYLVAIN
e27e49e209 Merge branch 'dev-7-harmonization-revision-release-2' into 'main'
[REF] merge version and release concept and simplify version settings

See merge request odoo-openupgrade-wizard/odoo-openupgrade-wizard!7
2022-06-21 22:32:34 +00:00
105 changed files with 6909 additions and 3363 deletions

1
.gitignore vendored
View File

@ -2,6 +2,7 @@ env
.gitlab-ci-venv
__pycache__
.tox
dist
.coverage
.pytest_cache
/tests/data/output/*

View File

@ -1,63 +0,0 @@
image: docker:19.03
services:
- docker:dind
stages:
- linting
- tests
# TODO, fix me : call all the pre-commit stuff instead.
black:
stage: linting
image: python
script:
# Install pipx
- pip install --user pipx
- python -m pipx ensurepath
- source ~/.profile
# Install black
- pipx install black
- black --version
# Log
# Call black Check
- black --check .
pytest:
stage: tests
script:
# Install the the version 3.8. (the version 3.9 is the latest available)
# however, docker 19.03 comes with python3.8 and docker 20.10 comes with python3.10
- apk add python3==~3.8 python3-dev==~3.8
- apk add gcc g++ libffi-dev
- apk add git
- python3 -m venv /.gitlab-ci-venv
- source /.gitlab-ci-venv/bin/activate
- pip install --upgrade pip
- pip install poetry
- poetry --version
- poetry install -v
- echo $PATH
- echo $PYTHONPATH
- poetry run pytest --version
- poetry run pytest --verbosity=2 --exitfirst --cov odoo_openupgrade_wizard
tests/cli_01_init_test.py
tests/cli_02_get_code_test.py
tests/cli_03_docker_build_test.py
tests/cli_04_run_test.py
tests/cli_05_execute_script_python_test.py
tests/cli_06_execute_script_sql_test.py
tests/cli_07_upgrade_test.py
tests/cli_08_estimate_workload_test.py
# Disabled test on gitlab-ci :
# The following tests should work locally but doesn't on gitlab-ci
# because calling OdooRPC on mounted container
# in a container (gitlab) doesn't work.
# tests/cli_20_install_from_csv_test.py
# tests/cli_21_generate_module_analysis_test.py

View File

@ -1,41 +1,131 @@
stages:
- lint
- test
- build
- publish
- release
pre-commit:
image: python:alpine
stage: lint
tags:
- shell
only:
- merge_requests
rules:
# Run only if merge request
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
# Run if commit on default branch
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
before_script:
- apk add git
- pip install pre-commit
script:
- pre-commit run --all --show-diff-on-failure --verbose --color always
pytest:
stage: test
tags:
- shell
only:
- merge_requests
script:
- python3 -m venv ./.gitlab-ci-venv
- source ./.gitlab-ci-venv/bin/activate
# - pip install --upgrade pip
check_version:
stage: lint
image: python:alpine
rules:
# Run if commit that start with a version number is pushed
- if: $CI_COMMIT_TAG =~ /^[0-9]+\.[0-9]+\.[0-9]+.*/
before_script:
- pip install poetry
- poetry --version
- poetry install -v
- echo $PATH
- echo $PYTHONPATH
- poetry run pytest --version
script:
# Ensure tag is the same as the program version
- test $(poetry version --short) = $CI_COMMIT_TAG
- poetry run pytest --verbosity=2 --exitfirst --cov odoo_openupgrade_wizard
tests/cli_01_init_test.py
tests/cli_02_get_code_test.py
tests/cli_03_docker_build_test.py
tests/cli_04_run_test.py
tests/cli_05_execute_script_python_test.py
tests/cli_06_execute_script_sql_test.py
tests/cli_07_upgrade_test.py
tests/cli_08_estimate_workload_test.py
tests/cli_20_install_from_csv_test.py
tests/cli_21_generate_module_analysis_test.py
check_changelog:
stage: lint
image: python:alpine
rules:
# Run if commit that start with a version number is pushed
- if: $CI_COMMIT_TAG =~ /^[0-9]+\.[0-9]+\.[0-9]+$/
before_script:
- pip install poetry
- poetry --version
script:
# Ensure change log is completed correctly
- cat CHANGES.rst | grep $CI_COMMIT_TAG
pytest:
image:
name: python:$PYTHON_VERSION-alpine
stage: test
tags:
- cie-oow-dind-runner
rules:
# Run if merge request
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
# Run if commit on default branch
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
services:
- name: docker:dind
alias: dind
variables:
ODOO_RPC_URL: dind
DOCKER_HOST: tcp://dind:2375/
DOCKER_DRIVER: overlay2
DOCKER_TLS_CERTDIR: ""
coverage: '/TOTAL\s+\d+\s+\d+\s+(\d+)\%/'
before_script:
- apk add git
- pip install poetry
- poetry --version
- export PATH="$HOME/.local/bin:$PATH"
- poetry install --all-extras
script:
- poetry run pytest -vv -x --cov=odoo_openupgrade_wizard
parallel:
matrix:
- PYTHON_VERSION:
- "3.9"
- "3.13"
build:
stage: build
image: python:alpine
rules:
# Run if merge request
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
# Run if commit on default branch
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
# Run if commit that start with a version number is pushed
- if: $CI_COMMIT_TAG =~ /^[0-9]+\.[0-9]+\.[0-9]+.*/
before_script:
- pip install poetry
- poetry --version
script:
- poetry build
artifacts:
untracked: true
paths:
- dist/
publish:
stage: publish
image: python:alpine
rules:
# Run if commit that start with a version number is pushed
- if: $CI_COMMIT_TAG =~ /^[0-9]+\.[0-9]+\.[0-9]+.*/
before_script:
- pip install poetry
- poetry --version
- ls -l dist
# Uncomment for testing build publication on test.pypi.org
#- poetry config repo.pypitest https://test.pypi.org/legacy/
script:
- poetry publish --skip-existing --username $PYPI_USER --password $PYPI_TOKEN
release:
stage: release
image: registry.gitlab.com/gitlab-org/release-cli:latest
rules:
# Run only for a patch, minor or major release
# This avoid creating a release for alpha, beta, or other special
# releases
- if: $CI_COMMIT_TAG =~ /^[0-9]+\.[0-9]+\.[0-9]+$/
script:
- echo "running release_job for $CI_COMMIT_TAG"
release:
name: "$CI_COMMIT_TAG"
description: "Change log here: ${CI_PROJECT_URL}/-/blob/main/CHANGES.rst"
tag_name: "$CI_COMMIT_TAG"
ref: "$CI_COMMIT_SHA"

View File

@ -1,12 +1,12 @@
---
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
exclude: '^tests/data/output_expected/'
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v3.4.0
rev: v4.5.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
exclude: exclude
- id: debug-statements
- id: mixed-line-ending
- id: name-tests-test
@ -19,15 +19,15 @@ repos:
- id: check-merge-conflict
- id: check-symlinks
- repo: https://github.com/pre-commit/mirrors-isort
rev: v5.7.0
rev: v5.10.1
hooks:
- id: isort
- repo: https://github.com/psf/black
rev: 22.3.0
rev: 23.11.0
hooks:
- id: black
- repo: https://github.com/PyCQA/flake8
rev: "3.9.2"
rev: "6.1.0"
hooks:
- id: flake8
# - repo: https://gitlab.com/smop/pre-commit-hooks

23
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,23 @@
{
// 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"
}
]
}
]
}

3
.vscode/settings.json vendored Normal file
View File

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

285
CHANGES.rst Normal file
View File

@ -0,0 +1,285 @@
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,8 +1,19 @@
# Developers
* Sylvain LE GAL from GRAP (http://www.grap.coop)
* Sylvain LE GAL, from [GRAP](http://www.grap.coop), since March 2022
* Rémy TAYMANS, from [Coop It Easy](https://coopiteasy.be/), since June 2022
* Cyril JEANNERET, from [Camptocamp](https://www.camptocamp.com), since April 2023
* 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
* Sébastien BEAU, from Akretion (https://akretion.com)
* Rémy TAYMANS, from Coop It Easy (https://coopiteasy.be/)

View File

@ -1,19 +1,18 @@
# Tools to understand
# Installation to develop
The library is using many tools. It is recommanded to understand that tools
to contribute to that project.
## Basic installation
* Docker (https://www.docker.com/)
* Dind (Docker In Docker) for running docker in gitlab-ci. (https://docs.gitlab.com/ee/ci/docker/using_docker_build.html#use-docker-in-docker)
* Gitlab CI (https://docs.gitlab.com/ee/ci/quick_start/index.html)
* openupgrade project (https://github.com/oca/openupgrade) and related openupgradelib (https://github.com/oca/openupgradelib)
* poetry (https://python-poetry.org/)
* odoorpc (https://github.com/OCA/odoorpc)
* git-aggregator (https://github.com/acsone/git-aggregator)
* click-odoo (https://github.com/acsone/click-odoo)
```
git clone https://gitlab.com/odoo-openupgrade-wizard/odoo-openupgrade-wizard/
cd odoo-openupgrade-wizard
virtualenv env --python=python3
. ./env/bin/activate
poetry install
```
``odoo-openupgrade-wizard`` commands are now available in your virutalenv.
# Extra Developper Requirements
## Advanced installation
If you want to use this library without installing anything in your
system, execute the following steps, otherwise, go to 'Installation' part.
@ -38,29 +37,9 @@ pipx install virtualenv
pipx install poetry
```
# Installation
```
git clone https://gitlab.com/odoo-openupgrade-wizard/odoo-openupgrade-wizard/
cd odoo-openupgrade-wizard
virtualenv env --python=python3
. ./env/bin/activate
poetry install
```
``odoo-openupgrade-wizard`` commands are now available in your virutalenv.
# Add python dependencies
If you add new dependencies, you have to:
- add the reference in the file ``pyproject.toml``
- run the following command in your virtualenv : ``poetry update``
# Run tests
## Via pytest
## Via pytest (simple)
This will run tests only for the current ``python3.X`` version.
@ -69,17 +48,7 @@ This will run tests only for the current ``python3.X`` version.
poetry run pytest --cov odoo_openupgrade_wizard --verbosity=2 --exitfirst
```
Debug Test
```
poetry run pytest --verbosity=2 --exitfirst --cov odoo_openupgrade_wizard\
tests/cli_01_init_test.py\
tests/cli_02_get_code_test.py\
tests/cli_03_docker_build_test.py\
tests/cli_04_run_test.py\
tests/cli_06_execute_script_sql_test.py
```
## Via Tox
## Via Tox (advanced)
This will run tests for all the python versions put in the ``tox.ini`` folder.
@ -109,7 +78,143 @@ pipx install gitlabci-local
gitlabci-local
```
# Réferences
# Debugging
Some docker command could help you when using / developping this tools.
**Enter the postgres container**
docker exec -it POSTGRES_CONTAINER_NAME /bin/bash
# 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
If you add new dependencies, you have to:
- add the reference in the file ``pyproject.toml``
- run the following command in your virtualenv : ``poetry update``
## Release on Gitlab and publish on PyPI
Building, releasing and publishing a new version works with tags.
Tags that trigger a build and a publication on PyPI must have a name
equal to the version of the program found in `pyproject.toml`.
Tags that matches an change in major, minor or patch version will
trigger a release on gitlab.
Tags that are alpha, beta, pre-release, etc does not trigger a release
on gitlab, but they trigger a publication on PyPI.
Before creating a tag, ensure that the version of the program is
updated. To update the program version you can use the command:
```
poetry version {major,minor,patch}
```
Ensure that the `CHANGES.rst` file contains information about this new
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
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
branch.
When everything is good on the main branch and that tests succeed,
create a new tag with the same name as the version in `pyproject.toml`
file. Tags can be created via Code > Tags > New tag.
To see if the publication on PyPI and the release on gitlab were done
correctly, go in Build > Pipelines. You will find a pipeline for the tag
you just created.
# Understanding the library
## Tools to understand
The library is using many tools. It is recommanded to understand that tools
to contribute to that project:
* Docker (https://www.docker.com/)
* Gitlab CI (https://docs.gitlab.com/ee/ci/quick_start/index.html)
* openupgrade project (https://github.com/oca/openupgrade) and related openupgradelib (https://github.com/oca/openupgradelib)
* poetry (https://python-poetry.org/)
* odoorpc (https://github.com/OCA/odoorpc)
* git-aggregator (https://github.com/acsone/git-aggregator)
Also this project is inspired by the following tools:
* click-odoo-contrib (https://github.com/acsone/click-odoo-contrib)
# Dockerfile information
### From version 5 to 7
There are no plans to make the tool work for these versions.
### From version 8 to 10 (Python2)
Try to create dockerfile, based on the odoo official ones fails. Any help welcome.
### From version 11.0 to latest version. (Python3)
The Dockerfile of the version 11 to the lastest version of Odoo are written this way :
- Copy the content of https://github.com/odoo/odoo/blob/ODOO_VERSION/setup/package.dfsrc
- remove all the part after the big ``apt-get install``
- install debian package ``git`` to have the possibility to pip install from git url.
- install custom debian packages
- install python odoo requirements
- install python ``setuptools-scm`` lib to have the possibility to pip install ``openupgradelib`` from git url.
- install python custom requirements
- makes link between external user and docker odoo user
## Réferences
- how to install gitlab runner locally:
@ -120,3 +225,40 @@ https://docs.gitlab.com/runner/install/linux-manually.html
https://blog.stephane-robert.info/post/gitlab-valider-ci-yml/
https://blog.callr.tech/building-docker-images-with-gitlab-ci-best-practices/
## Python version settings depending on the debian version
This part can help you if you want to change your autogenerated Dockerfiles.
See (https://github.com/odoo/odoo/blob/ODOO_VERSION/setup/package.dfdebian)
### debian:wheezy (V7) (for Odoo: 8.0)
- Ubuntu release : 12.04 xxx, 12.10 xxx, 13.04 xxx, 14.10 xxx
- python2.7
- First release : 04/05/2013
- End LTS : May 2018
### debian:jessie (V8) (for Odoo: 9.0, 10.0)
- Ubuntu release : 14.04 trusty, 14.10 utopic, 15.04 vivid, 15.10 wily
- python2.7
- First release : 26/04/2015
- End LTS : June 2020
### debian:stretch (V9) (for Odoo: 11.0, 12.0)
- Ubuntu releases : 16.04 xenial, 16.10 yakkety, 17.04 zesty, 17.10 artful
- python2.7 and python3.5
- First release : 17/06/2017
- End LTS : June 2022
### debian:buster (13.0, 14.0)
- Ubuntu release : 18.04 bionic, 18.10 cosmic, 19.04 disco, 19.10 eoan
- python2.7 and python3.7
- First release : 06/07/2019
- End LTS : Undefined.
## debian:bullseye (15.0, 16.0)
- Ubuntu release : 20.04 focal, 20.10 groovy, 21.04 hirsute, 21.10 impish
- python3.9
- First release : 14/07/2021
- End LTS : Undefined.

View File

@ -44,3 +44,26 @@ sudo dpkg -i gitlab-runner_amd64.deb
# TODO:
- check dynamic user id with
https://github.com/camptocamp/docker-odoo-project/blob/master/bin/docker-entrypoint.sh
in modules.csv.j2 :
# TODO, this value are usefull for test for analyse between 13 and 14.
# move that values in data/extra_script/modules.csv
# and let this template with only 'base' module.
## Without postgres optimization
2022-07-13 19:42
2022-07-13 21:20
Duration : 1:37 (107)
## With postgres optimization
2022-07-13 21:52
2022-07-14 23:11
duration : 1:19 (79)
16%

19
PATCH.md Normal file
View File

@ -0,0 +1,19 @@
# 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!

1062
README.md

File diff suppressed because it is too large Load Diff

1
newsfragments/.gitignore vendored Normal file
View File

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

View File

@ -1,123 +0,0 @@
import datetime
import logging
import sys
from pathlib import Path
import click
import yaml
from click_loglevel import LogLevel
from loguru import logger
import odoo_openupgrade_wizard
from odoo_openupgrade_wizard.cli_docker_build import docker_build
from odoo_openupgrade_wizard.cli_estimate_workload import estimate_workload
from odoo_openupgrade_wizard.cli_execute_script_python import (
execute_script_python,
)
from odoo_openupgrade_wizard.cli_execute_script_sql import execute_script_sql
from odoo_openupgrade_wizard.cli_generate_module_analysis import (
generate_module_analysis,
)
from odoo_openupgrade_wizard.cli_get_code import get_code
from odoo_openupgrade_wizard.cli_init import init
from odoo_openupgrade_wizard.cli_install_from_csv import install_from_csv
from odoo_openupgrade_wizard.cli_run import run
from odoo_openupgrade_wizard.cli_upgrade import upgrade
from odoo_openupgrade_wizard.tools_system import ensure_folder_exists
@click.group()
@click.version_option(version=odoo_openupgrade_wizard.__version__)
@click.option(
"--env-folder",
default="./",
type=click.Path(
exists=True,
file_okay=False,
writable=True,
resolve_path=True,
),
help="Folder that will contains all the configuration of the wizard"
" and all the Odoo code required to make the migrations. Let empty to"
" use current folder (./).",
)
@click.option(
"--filestore-folder",
type=click.Path(
exists=True, file_okay=False, writable=True, resolve_path=True
),
help="Folder that contains the Odoo filestore of the database(s)"
" to migrate. Let empty to use the subfolder 'filestore' of the"
" environment folder.",
)
@click.option("-l", "--log-level", type=LogLevel(), default=logging.INFO)
@click.pass_context
def main(ctx, env_folder, filestore_folder, log_level):
"""
Provides a command set to perform odoo Community Edition migrations.
"""
date_begin = datetime.datetime.now()
logger.remove()
logger.add(sys.stderr, level=log_level)
logger.debug("Beginning script '%s' ..." % (ctx.invoked_subcommand))
if not isinstance(ctx.obj, dict):
ctx.obj = {}
# Define all the folder required by the tools
env_folder_path = Path(env_folder)
src_folder_path = env_folder_path / Path("./src/")
# Note: postgres folder should be a subfolder, because
# the parent folder will contain a .gitignore file
# that the postgres docker image doesn't like
postgres_folder_path = env_folder_path / Path("./postgres_data/data")
script_folder_path = env_folder_path / Path("./scripts/")
log_folder_path = env_folder_path / Path("./log/")
if not filestore_folder:
filestore_folder_path = env_folder_path / Path("./filestore/")
else:
filestore_folder_path = Path(filestore_folder)
# ensure log folder exists
ensure_folder_exists(log_folder_path, mode="777", git_ignore_content=True)
# Create log file
log_prefix = "{}__{}".format(
date_begin.strftime("%Y_%m_%d__%H_%M_%S"), ctx.invoked_subcommand
)
log_file_path = log_folder_path / Path(log_prefix + ".log")
logger.add(log_file_path)
config_file_path = env_folder_path / Path("config.yml")
module_file_path = env_folder_path / Path("modules.csv")
# Add all global values in the context
ctx.obj["env_folder_path"] = env_folder_path
ctx.obj["src_folder_path"] = src_folder_path
ctx.obj["postgres_folder_path"] = postgres_folder_path
ctx.obj["script_folder_path"] = script_folder_path
ctx.obj["log_folder_path"] = log_folder_path
ctx.obj["log_prefix"] = log_prefix
ctx.obj["filestore_folder_path"] = filestore_folder_path
ctx.obj["config_file_path"] = config_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(docker_build)
main.add_command(estimate_workload)
main.add_command(execute_script_python)
main.add_command(execute_script_sql)
main.add_command(generate_module_analysis)
main.add_command(get_code)
main.add_command(init)
main.add_command(install_from_csv)
main.add_command(run)
main.add_command(upgrade)

View File

@ -0,0 +1,186 @@
#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 logging
import sys
from pathlib import Path
import click
import yaml
from click_loglevel import LogLevel
from loguru import logger
import odoo_openupgrade_wizard
from odoo_openupgrade_wizard.cli.cli_copydb import copydb
from odoo_openupgrade_wizard.cli.cli_docker_build import docker_build
from odoo_openupgrade_wizard.cli.cli_dropdb import dropdb
from odoo_openupgrade_wizard.cli.cli_dumpdb import dumpdb
from odoo_openupgrade_wizard.cli.cli_estimate_workload import estimate_workload
from odoo_openupgrade_wizard.cli.cli_execute_script_python import (
execute_script_python,
)
from odoo_openupgrade_wizard.cli.cli_execute_script_sql import (
execute_script_sql,
)
from odoo_openupgrade_wizard.cli.cli_generate_module_analysis import (
generate_module_analysis,
)
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_install_from_csv import install_from_csv
from odoo_openupgrade_wizard.cli.cli_psql import psql
from odoo_openupgrade_wizard.cli.cli_pull_submodule import pull_submodule
from odoo_openupgrade_wizard.cli.cli_restoredb import restoredb
from odoo_openupgrade_wizard.cli.cli_run import run
from odoo_openupgrade_wizard.cli.cli_upgrade import upgrade
from odoo_openupgrade_wizard.tools.tools_system import ensure_folder_exists
DEFAULT_CONFIG_FILE = "config.yml"
DEFAULT_MODULES_FILE = "modules.csv"
@click.group()
@click.version_option(version=odoo_openupgrade_wizard.__version__)
@click.option(
"--env-folder",
default="./",
type=click.Path(
exists=True,
file_okay=False,
writable=True,
resolve_path=True,
),
help="Directory that will contain all the configuration of the wizard "
"and all the Odoo code required to perform the migrations. Leave "
"empty to use current directory (./).",
)
@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(
"--filestore-folder",
type=click.Path(
exists=True, file_okay=False, writable=True, resolve_path=True
),
help="Directory that contains the Odoo filestore of the database(s) to "
"migrate. Leave empty to use the subdirectory 'filestore' of the "
"environment directory.",
)
@click.option("-l", "--log-level", type=LogLevel(), default=logging.INFO)
@click.pass_context
def main(
ctx, env_folder, config_file, modules_file, filestore_folder, log_level
):
"""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()
logger.remove()
logger.add(sys.stderr, level=log_level)
logger.debug(f"Beginning script '{ctx.invoked_subcommand}'...")
if not isinstance(ctx.obj, dict):
ctx.obj = {}
# Define all the folder required by the tools
env_folder_path = Path(env_folder)
src_folder_path = env_folder_path / Path("./src/")
if filestore_folder:
filestore_folder_path = filestore_folder
else:
filestore_folder_path = env_folder_path / "filestore"
script_folder_path = env_folder_path / Path("./scripts/")
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_folder_exists(log_folder_path, git_ignore_content=True)
# Create log file
log_prefix = "{}__{}".format(
date_begin.strftime("%Y_%m_%d__%H_%M_%S"), ctx.invoked_subcommand
)
log_file_path = log_folder_path / Path(log_prefix + ".log")
logger.add(log_file_path)
ctx.obj["log_prefix"] = log_prefix
elif ctx.invoked_subcommand not in ("init", None):
ctx.fail(
f"Environment not initialized. "
f"Expected config file at: {config_file_path}",
)
# Add all global values in the context
ctx.obj["env_folder_path"] = env_folder_path
ctx.obj["src_folder_path"] = src_folder_path
ctx.obj["filestore_folder_path"] = filestore_folder_path
ctx.obj["script_folder_path"] = script_folder_path
ctx.obj["log_folder_path"] = log_folder_path
ctx.obj["config_file_path"] = config_file_path
ctx.obj["module_file_path"] = module_file_path
main.add_command(copydb)
main.add_command(restoredb)
main.add_command(docker_build)
main.add_command(dropdb)
main.add_command(dumpdb)
main.add_command(estimate_workload)
main.add_command(execute_script_python)
main.add_command(execute_script_sql)
main.add_command(generate_module_analysis)
main.add_command(guess_requirement)
main.add_command(get_code)
main.add_command(init)
main.add_command(install_from_csv)
main.add_command(psql)
main.add_command(pull_submodule)
main.add_command(run)
main.add_command(upgrade)

View File

@ -0,0 +1,29 @@
import click
from odoo_openupgrade_wizard.tools import (
tools_click_odoo_contrib as click_odoo_contrib,
)
@click.command()
@click.option(
"-s",
"--source",
type=str,
help="Name of the source database to copy.",
)
@click.option(
"-d",
"--dest",
type=str,
help="Name of the destination database to create.",
)
@click.pass_context
def copydb(ctx, source, dest):
"""Create a new Odoo database by copying another.
This command duplicates both the PostgreSQL database and its associated
filestore.
"""
click_odoo_contrib.copydb(ctx, source, dest)

View File

@ -0,0 +1,57 @@
import click
from loguru import logger
from odoo_openupgrade_wizard.cli.cli_options import (
get_odoo_versions_from_options,
versions_options,
)
from odoo_openupgrade_wizard.tools.tools_docker import build_image, pull_image
from odoo_openupgrade_wizard.tools.tools_odoo import (
get_docker_image_tag,
get_odoo_env_path,
)
from odoo_openupgrade_wizard.tools.tools_system import get_local_user_id
@click.command()
@versions_options
@click.pass_context
def docker_build(ctx, versions):
"""Build Docker images and pull PostgreSQL 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
logger.info(
"Pulling the PostgreSQL Docker image. This can take a while..."
)
pull_image(ctx.obj["config"]["postgres_image_name"])
# Build images for each odoo version
for odoo_version in get_odoo_versions_from_options(ctx, versions):
odoo_requirement_file_path = (
get_odoo_env_path(ctx, odoo_version) / "src/odoo/requirements.txt"
)
if not odoo_requirement_file_path.exists():
logger.error(
"Cannot build Odoo Docker image for version {odoo_version}, "
"because file {odoo_requirement_file_path} cannot be found. "
"Have you run the get-code command?",
odoo_version=odoo_version,
odoo_requirement_file_path=odoo_requirement_file_path,
)
continue
logger.info(
f"Building Odoo Docker image for version '{odoo_version}'."
" This can take a while..."
)
image = build_image(
get_odoo_env_path(ctx, odoo_version),
get_docker_image_tag(ctx, odoo_version),
{"LOCAL_USER_ID": str(get_local_user_id())},
)
logger.info(f"Docker Image build. '{image[0].tags[0]}'")

View File

@ -0,0 +1,14 @@
import click
from odoo_openupgrade_wizard.cli.cli_options import database_option_required
from odoo_openupgrade_wizard.tools import (
tools_click_odoo_contrib as click_odoo_contrib,
)
@click.command()
@database_option_required
@click.pass_context
def dropdb(ctx, database):
"""Delete a database and its filestore."""
click_odoo_contrib.dropdb(ctx, database)

View File

@ -0,0 +1,122 @@
import pathlib
import shutil
import click
from odoo_openupgrade_wizard.cli.cli_options import database_option_required
from odoo_openupgrade_wizard.tools.tools_postgres import (
check_db_exist,
execute_pg_dump,
)
from odoo_openupgrade_wizard.tools.tools_system import dump_filestore
@click.command()
@database_option_required
@click.option(
"--database-path",
type=click.Path(writable=True, resolve_path=True),
required=True,
help="Destination path for the database dump relative to the project "
"directory.",
)
@click.option(
"--database-format",
type=click.Choice(("p", "c", "d", "t")),
default="c",
help="Database format (see pg_dump options): plain SQL (p), "
"custom format compressed (c), directory (d), or tar file (t).",
)
@click.option(
"--filestore-path",
type=click.Path(writable=True, resolve_path=True),
required=True,
help="Destination path for the filestore backup.",
)
@click.option(
"--filestore-format",
type=click.Choice(("d", "t", "tgz")),
default="tgz",
help="Filestore format: directory (d), tar file (t), "
"or tar file compressed with gzip (tgz)",
)
@click.option(
"--force",
is_flag=True,
default=False,
help="Overwrite files if they already exist.",
)
@click.pass_context
def dumpdb(
ctx,
database,
database_path,
database_format,
filestore_path,
filestore_format,
force,
):
"""Create a dump of an Odoo database and 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)
filestore_path = pathlib.Path(filestore_path)
# Check that database_path is inside the env_folder_path
absolute_database_path = database_path.absolute()
absolute_env_folder_path = ctx.obj["env_folder_path"].resolve().absolute()
if not str(absolute_database_path).startswith(
str(absolute_env_folder_path)
):
ctx.fail(
"database-path should be inside the project path to allow "
"PostgreSQL to write to it."
)
# Fail if dumps already exists and force argument not given
# Remove file if already exists and force is given
if not force and database_path.exists():
ctx.fail(f"{database_path} exists, use --force to overwrite it.")
elif force and database_path.exists():
if database_path.is_dir():
shutil.rmtree(database_path)
else:
database_path.unlink()
if not force and filestore_path.exists():
ctx.fail(f"{filestore_path} exists, use --force to overwrite it.")
elif force and filestore_path.exists():
if filestore_path.is_dir():
shutil.rmtree(filestore_path)
else:
filestore_path.unlink()
# Normalise database_path
database_path = absolute_database_path.relative_to(
absolute_env_folder_path
)
check_db_exist(ctx, database, raise_exception=True)
# dump the database
output = execute_pg_dump(
ctx,
database=database,
dumpformat=database_format,
filename=str(database_path),
)
if output:
click.echo(output)
# dump the filestore
dump_filestore(
ctx,
database=database,
destpath=filestore_path,
copyformat=filestore_format,
)

View File

@ -0,0 +1,92 @@
from datetime import datetime
from pathlib import Path
import click
from odoo_openupgrade_wizard.tools.tools_odoo import 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(
"--analysis-file-path",
type=click.Path(
dir_okay=False,
),
default="./analysis.html",
help="Path where the HTML analysis report will be saved. "
"Default is './analysis.html'",
)
@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.option(
"--time-unit",
type=click.Choice(["hour", "minute", "separator"]),
default="separator",
show_default=True,
help="Format to use for displaying time in the report. "
"*separator* display time as `HHH<sep>MM`, "
"*hour* display time as decimal hour, "
"*min* display time as minutes (rounded).",
)
@click.option(
"--time-separator",
default=":",
help="Character to use as a separator in time output. "
"Used only if --time-unit=separator. Default is ':' (e.g. HHH:MM).",
)
@click.pass_context
def estimate_workload(
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
analysis = Analysis(ctx)
def time_to_text(minutes):
"""Return a text representation for minutes"""
hours, mins = divmod(minutes, 60)
if time_unit == "hour":
result = str(hours)
elif time_unit == "minute":
result = str(minutes)
else:
result = "{}{}{:02d}".format(hours, time_separator, mins)
return result
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()
analysis.analyse_openupgrade_state(ctx)
analysis.estimate_workload(ctx)
# Make some clean to display properly
analysis.modules = sorted(analysis.modules)
# Render html file
ensure_file_exists_from_template(
Path(analysis_file_path),
"analysis.html.j2",
ctx=ctx,
analysis=analysis,
current_date=datetime.now().strftime("%d/%m/%Y %H:%M:%S"),
time_to_text=time_to_text,
)

View File

@ -0,0 +1,52 @@
from pathlib import Path
import click
from odoo_openupgrade_wizard.cli.cli_options import (
database_option_required,
get_migration_step_from_options,
step_option,
)
from odoo_openupgrade_wizard.tools.tools_odoo import (
execute_click_odoo_python_files,
)
@click.command()
@step_option
@database_option_required
@click.option(
"--script-file-path",
multiple=True,
type=click.Path(
exists=True,
dir_okay=False,
),
help="""List of Python files to execute, with either an absolute path
or path relative to the project directory. With either method, the
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
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)
execute_click_odoo_python_files(
ctx, database, migration_step, [Path(x) for x in script_file_path]
)

View File

@ -0,0 +1,44 @@
from pathlib import Path
import click
from odoo_openupgrade_wizard.cli.cli_options import (
database_option_required,
get_migration_step_from_options,
step_option,
)
from odoo_openupgrade_wizard.tools.tools_postgres import (
execute_sql_files_pre_migration,
)
@click.command()
@step_option
@database_option_required
@click.option(
"--script-file-path",
multiple=True,
type=click.Path(
exists=True,
dir_okay=False,
),
help="List of SQL files to execute. Files will be executed in the order "
"listed. If no files are specified, all SQL files (.sql) in the "
"step's directory will be sorted alphabetically and then executed "
"in order.",
)
@click.pass_context
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)
execute_sql_files_pre_migration(
ctx, database, migration_step, [Path(x) for x in script_file_path]
)

View File

@ -1,8 +1,8 @@
import click
from loguru import logger
from odoo_openupgrade_wizard.cli_options import (
database_option,
from odoo_openupgrade_wizard.cli.cli_options import (
database_option_required,
get_migration_steps_from_options,
step_option,
)
@ -12,28 +12,40 @@ from odoo_openupgrade_wizard.configuration_version_dependant import (
get_installable_odoo_modules,
get_upgrade_analysis_module,
)
from odoo_openupgrade_wizard.tools_odoo import (
from odoo_openupgrade_wizard.tools.tools_odoo import (
get_odoo_env_path,
kill_odoo,
run_odoo,
)
from odoo_openupgrade_wizard.tools_odoo_instance import OdooInstance
from odoo_openupgrade_wizard.tools_system import ensure_folder_writable
from odoo_openupgrade_wizard.tools.tools_odoo_instance import OdooInstance
from odoo_openupgrade_wizard.tools.tools_system import ensure_folder_writable
@click.command()
@step_option
@database_option
@database_option_required
@click.option(
"-m",
"--modules",
type=str,
help="Coma-separated list of modules to analysis."
" Let empty to analyse all the Odoo modules.",
help="Comma-separated list of modules to analyse."
" Leave empty to analyse all the Odoo modules.",
)
@click.pass_context
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)
initial_step = migration_steps[0].copy()
@ -42,17 +54,16 @@ def generate_module_analysis(ctx, step, database, modules):
alternative_xml_rpc_port = ctx.obj["config"]["odoo_host_xmlrpc_port"] + 10
if not database:
database = "%s__analysis__" % (
ctx.obj["config"]["project_name"].replace("-", "_"),
database = (
f"{ctx.obj['config']['project_name'].replace('-', '_')}"
"__analysis__"
)
initial_database = "%s_%s" % (
database,
str(initial_step["version"]).replace(".", ""),
initial_database = (
f"{database}_{str(initial_step['version']).replace('.', '')}"
)
final_database = "%s_%s" % (
database,
str(final_step["version"]).replace(".", ""),
final_database = (
f"{database}_{str(final_step['version']).replace('.', '')}"
)
modules = modules and modules.split(",") or []
@ -76,6 +87,7 @@ def generate_module_analysis(ctx, step, database, modules):
database=initial_database,
execution_context="openupgrade",
detached_container=True,
publish_ports=True,
)
# INITIAL : install modules to analyse and generate records
initial_instance = OdooInstance(ctx, initial_database)
@ -96,7 +108,6 @@ def generate_module_analysis(ctx, step, database, modules):
stop_after_init=True,
init=get_upgrade_analysis_module(final_step),
execution_context="openupgrade",
alternative_xml_rpc_port=alternative_xml_rpc_port,
)
# name of the first odoo instance inside the second odoo instance
@ -111,6 +122,7 @@ def generate_module_analysis(ctx, step, database, modules):
alternative_xml_rpc_port=alternative_xml_rpc_port,
execution_context="openupgrade",
links={initial_container.name: odoo_initial_host_name},
publish_ports=True,
)
# FINAL : install modules to analyse and generate records
@ -144,5 +156,5 @@ def generate_module_analysis(ctx, step, database, modules):
except (KeyboardInterrupt, SystemExit):
logger.info("Received Keyboard Interrupt or System Exiting...")
finally:
kill_odoo(ctx, initial_step)
kill_odoo(ctx, final_step)
kill_odoo(ctx, initial_database, initial_step)
kill_odoo(ctx, final_database, final_step)

View File

@ -0,0 +1,35 @@
import click
from odoo_openupgrade_wizard.cli.cli_options import (
get_odoo_versions_from_options,
versions_options,
)
from odoo_openupgrade_wizard.tools.tools_odoo import (
get_odoo_env_path,
get_repo_file_path,
)
from odoo_openupgrade_wizard.tools.tools_system import git_aggregate
@click.command()
@versions_options
@click.option(
"-j",
"--jobs",
type=int,
default=10,
help="Jobs used to call the git-aggregate command."
" Reasonably set to 10 by default.",
)
@click.pass_context
def get_code(ctx, versions, jobs):
"""Get all required source code 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):
folder_path = get_odoo_env_path(ctx, odoo_version)
repo_file_path = get_repo_file_path(ctx, odoo_version)
git_aggregate(folder_path, repo_file_path, jobs)

View File

@ -0,0 +1,61 @@
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

@ -2,15 +2,12 @@ from pathlib import Path
import click
from odoo_openupgrade_wizard import templates
from odoo_openupgrade_wizard.configuration_version_dependant import (
get_odoo_versions,
get_python_libraries,
get_python_major_version,
get_version_options,
)
from odoo_openupgrade_wizard.tools_odoo import get_odoo_env_path
from odoo_openupgrade_wizard.tools_system import (
from odoo_openupgrade_wizard.tools.tools_odoo import get_odoo_env_path
from odoo_openupgrade_wizard.tools.tools_system import (
ensure_file_exists_from_template,
ensure_folder_exists,
)
@ -22,36 +19,58 @@ from odoo_openupgrade_wizard.tools_system import (
required=True,
prompt=True,
type=str,
help="Name of your project without spaces neither special"
" chars or uppercases. exemple 'my-customer-9-12'."
" This will be used to tag with a friendly"
" name the odoo docker images.",
help="Name of your project without spaces, special"
" characters, or uppercases. Example: 'my-customer-9-12'."
" This will be used to tag the Odoo Docker images "
" with a friendly name.",
)
@click.option(
"--initial-version",
required=True,
prompt=True,
type=click.Choice(get_version_options("initial")),
help="Initial Odoo version to migrate from.",
)
@click.option(
"--final-version",
required=True,
prompt=True,
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(
"--extra-repository",
"extra_repository_list",
# TODO, add a callback to check the quality of the argument
help="Coma separated extra repositories to use in the odoo environment."
"Ex: 'OCA/web,OCA/server-tools,GRAP/grap-odoo-incubator'",
help="Comma-separated extra repositories to use in the Odoo environment."
"Example: 'OCA/web,OCA/server-tools,GRAP/grap-odoo-incubator'",
)
@click.pass_context
def init(
ctx, project_name, initial_version, final_version, extra_repository_list
ctx,
project_name,
initial_version,
final_version,
postgresql_version,
extra_repository_list,
):
"""Initialize OpenUpgrade Wizard Environment based on the initial and
the final version of Odoo you want to migrate.
"""Initialize the OOW project environment.
This command sets up the project folder structure, configuration
files, and default templates needed to begin an Odoo migration.
"""
# Handle arguments
@ -70,19 +89,18 @@ def init(
float(initial_version), float(final_version)
)
# 2. Compute Migration Steps
# Compute Migration Steps
# Create initial first step
# Create initial Regular step
steps = [
{
"name": 1,
"execution_context": "regular",
"version": odoo_versions[0],
"complete_name": "step_01__update__%s" % (odoo_versions[0]),
"complete_name": f"step_01__regular__{odoo_versions[0]}",
}
]
# Add all upgrade steps
# Add all Openupgrade steps
step_nbr = 2
for odoo_version in odoo_versions[1:]:
steps.append(
@ -90,90 +108,88 @@ def init(
"name": step_nbr,
"execution_context": "openupgrade",
"version": odoo_version,
"complete_name": "step_%s__upgrade__%s"
% (str(step_nbr).rjust(2, "0"), odoo_version),
"complete_name": (
f"step_{step_nbr:>02}__openupgrade__{odoo_version}"
),
}
)
step_nbr += 1
# add final update step
# add final Regular step
if len(odoo_versions) > 1:
steps.append(
{
"name": step_nbr,
"execution_context": "regular",
"version": odoo_versions[-1],
"complete_name": "step_%s__update__%s"
% (str(step_nbr).rjust(2, "0"), odoo_versions[-1]),
"complete_name": (
f"step_{step_nbr:>02}__regular__{odoo_versions[-1]}"
),
}
)
# 3. ensure src folder exists
# Ensure src folder exists
ensure_folder_exists(ctx.obj["src_folder_path"])
# 4. ensure filestore folder exists
ensure_folder_exists(
ctx.obj["filestore_folder_path"], mode="777", git_ignore_content=True
)
# 5. ensure postgres data folder exists
ensure_folder_exists(
ctx.obj["postgres_folder_path"].parent,
mode="777",
git_ignore_content=True,
)
ensure_folder_exists(
ctx.obj["postgres_folder_path"],
mode="777",
)
# 6. ensure main configuration file exists
# Ensure main configuration file exists
ensure_file_exists_from_template(
ctx.obj["config_file_path"],
templates.CONFIG_YML_TEMPLATE,
"config.yml.j2",
project_name=project_name,
postgresql_version=postgresql_version,
steps=steps,
odoo_versions=odoo_versions,
)
# 7. Ensure module list file exists
# Ensure module list file exists
ensure_file_exists_from_template(
ctx.obj["module_file_path"],
templates.MODULES_CSV_TEMPLATE,
"modules.csv.j2",
project_name=project_name,
steps=steps,
odoo_versions=odoo_versions,
)
# 8. Create one folder per version and add files
# Create one folder per version and add files
for odoo_version in odoo_versions:
# Create main path for each version
path_version = get_odoo_env_path(ctx, odoo_version)
ensure_folder_exists(path_version)
# Create python requirements file
# Create python requirements files
ensure_file_exists_from_template(
path_version / Path("python_requirements.txt"),
templates.PYTHON_REQUIREMENTS_TXT_TEMPLATE,
python_libraries=get_python_libraries(odoo_version),
path_version / Path("extra_python_requirements.txt"),
"odoo/extra_python_requirements.txt.j2",
)
# Create debian requirements file
ensure_file_exists_from_template(
path_version / Path("debian_requirements.txt"),
templates.DEBIAN_REQUIREMENTS_TXT_TEMPLATE,
path_version / Path("addons_python_requirements.txt"),
"odoo/addons_python_requirements.txt.j2",
dependencies={},
)
# Create debian requirements files
ensure_file_exists_from_template(
path_version / Path("extra_debian_requirements.txt"),
"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
ensure_file_exists_from_template(
path_version / Path("odoo.cfg"),
templates.ODOO_CONFIG_TEMPLATE,
path_version / Path("odoo.conf"),
"odoo/odoo.conf.j2",
)
# Create repos.yml file for gitaggregate tools
ensure_file_exists_from_template(
path_version / Path("repos.yml"),
templates.REPO_YML_TEMPLATE,
"odoo/repos.yml.j2",
odoo_version=odoo_version,
orgs=orgs,
)
@ -181,9 +197,7 @@ def init(
# Create Dockerfile file
ensure_file_exists_from_template(
path_version / Path("Dockerfile"),
templates.DOCKERFILE_TEMPLATE,
odoo_version=odoo_version,
python_major_version=get_python_major_version(odoo_version),
f"odoo/{odoo_version}/Dockerfile",
)
# Create 'src' folder that will contain all the odoo code
@ -191,7 +205,7 @@ def init(
path_version / Path("src"), git_ignore_content=True
)
# 9. Create one folder per step and add files
# Create one folder per step and add files
ensure_folder_exists(ctx.obj["script_folder_path"])
for step in steps:
@ -200,10 +214,10 @@ def init(
ensure_file_exists_from_template(
step_path / Path("pre-migration.sql"),
templates.PRE_MIGRATION_SQL_TEMPLATE,
"scripts/pre-migration.sql.j2",
)
ensure_file_exists_from_template(
step_path / Path("post-migration.py"),
templates.POST_MIGRATION_PY_TEMPLATE,
"scripts/post-migration.py.j2",
)

View File

@ -0,0 +1,89 @@
import click
from loguru import logger
from odoo_openupgrade_wizard.cli.cli_options import (
database_option_required,
demo_option,
get_migration_step_from_options,
)
from odoo_openupgrade_wizard.tools.tools_odoo import (
get_odoo_modules_from_csv,
kill_odoo,
run_odoo,
)
from odoo_openupgrade_wizard.tools.tools_odoo_instance import OdooInstance
from odoo_openupgrade_wizard.tools.tools_postgres import ensure_database
@click.command()
@database_option_required
@demo_option
@click.pass_context
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)
ensure_database(ctx, database, state="present")
# Get modules list from the CSV file
module_names = get_odoo_modules_from_csv(ctx.obj["module_file_path"])
module_names.sort()
logger.info(f"Found {len(module_names)} modules.")
logger.debug(module_names)
try:
logger.info(f"Install 'base' module on {database} database...")
run_odoo(
ctx,
migration_step,
database=database,
detached_container=True,
init="base",
demo=with_demo,
publish_ports=True,
)
odoo_instance = OdooInstance(ctx, database)
odoo_default_company = ctx.obj["config"].get(
"odoo_default_company", False
)
if odoo_default_company:
# Then, set correct country to the company of the current user
# Otherwise, due to poor design of Odoo, when installing account
# the US localization will be installed.
# (l10n_us + l10n_generic_coa)
countries = odoo_instance.browse_by_search(
"res.country",
[("code", "=", odoo_default_company["country_code"])],
)
if len(countries) != 1:
raise Exception(
f"Unable to find a country, based on the"
f" code {odoo_default_company['country_code']}."
f" Countries found:"
f" {', '.join([x.name for x in countries])}"
)
vals = {
"country_id": countries[0].id,
"currency_id": countries[0].currency_id.id,
"phone": odoo_default_company.get("phone"),
"email": odoo_default_company.get("email"),
}
logger.info(
f"Configuring main company with values {vals}"
f" (country {countries[0].name}"
)
odoo_instance.env.user.company_id.write(vals)
# Install modules
odoo_instance.install_modules(module_names)
except (KeyboardInterrupt, SystemExit):
logger.info("Received Keyboard Interrupt or System Exiting...")
finally:
kill_odoo(ctx, database, migration_step)

View File

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

View File

@ -0,0 +1,36 @@
import click
from odoo_openupgrade_wizard.cli.cli_options import database_option_required
from odoo_openupgrade_wizard.tools.tools_postgres import execute_psql_command
@click.command(context_settings={"ignore_unknown_options": True})
@database_option_required
@click.option(
"-c",
"--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.pass_context
def psql(ctx, request, database, pager, psql_args):
"""Run a SQL command in the PostgreSQL container.
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)
if pager:
click.echo_via_pager(result)
else:
click.echo(result)

View File

@ -0,0 +1,61 @@
import click
from loguru import logger
from odoo_openupgrade_wizard.cli.cli_options import (
get_odoo_versions_from_options,
versions_options,
)
from odoo_openupgrade_wizard.tools.tools_odoo import get_odoo_env_path
from odoo_openupgrade_wizard.tools.tools_system import execute_check_output
@click.command()
@versions_options
@click.pass_context
def pull_submodule(ctx, versions):
"""Pull the repos.yml file from a Git repository.
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):
version_cfg = (
ctx.obj["config"]["odoo_version_settings"][odoo_version] or {}
)
if version_cfg.get("repo_url"):
logger.info(
f"Pull repos.yml from git repository"
f" for version {odoo_version}..."
)
submodule_path = (
get_odoo_env_path(ctx, odoo_version) / "repo_submodule"
)
if not submodule_path.exists():
execute_check_output(
[
"git",
"submodule",
"add",
"-b",
str(version_cfg["repo_branch"]),
version_cfg["repo_url"],
str(submodule_path),
]
)
else:
execute_check_output(
[
"git",
"pull",
"origin",
str(version_cfg["repo_branch"]),
"--rebase",
],
working_directory=submodule_path,
)
else:
logger.warning(
f"No submodule configuration found"
f" for version {odoo_version}..."
)

View File

@ -0,0 +1,84 @@
from pathlib import Path
import click
from odoo_openupgrade_wizard.cli.cli_options import database_option_required
from odoo_openupgrade_wizard.tools.tools_postgres import execute_pg_restore
from odoo_openupgrade_wizard.tools.tools_system import restore_filestore
@click.command()
@database_option_required
@click.option(
"--database-path",
required=True,
type=click.Path(readable=True, resolve_path=True, exists=True),
help="Path to the database dump (inside the environment folder).",
)
@click.option(
"--database-format",
required=True,
type=click.Choice(("c", "d", "t", "p")),
default="c",
help="Format of the database dump: custom (c), directory (d), tar (t), "
"or plain SQL (p).",
)
@click.option(
"--filestore-path",
required=True,
type=click.Path(readable=True, resolve_path=True, exists=True),
help="Path to the filestore backup (inside the environment folder).",
)
@click.option(
"--filestore-format",
required=True,
type=click.Choice(("d", "t", "tgz")),
default="tgz",
help="Format of the filestore: directory (d), tar (t), or gzip-compressed "
"tar (tgz).",
)
@click.pass_context
def restoredb(
ctx,
database,
database_path,
database_format,
filestore_path,
filestore_format,
):
"""Restore a database and its 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)
filestore_path = Path(filestore_path)
# Check that database_path is inside the env_folder_path
absolute_env_folder_path = ctx.obj["env_folder_path"].resolve().absolute()
if not str(database_path).startswith(str(absolute_env_folder_path)):
ctx.fail(
"database-path should be inside the project path to allow "
"PostgreSQL to read it."
)
# Restore the database
output = execute_pg_restore(
ctx,
database_path.relative_to(absolute_env_folder_path),
database,
database_format,
)
if output:
click.echo(output)
# Restore the filestore
restore_filestore(
ctx,
database,
filestore_path,
filestore_format,
)

View File

@ -0,0 +1,89 @@
import click
from loguru import logger
from odoo_openupgrade_wizard.cli.cli_options import (
database_option_required,
demo_option,
get_migration_step_from_options,
step_option,
)
from odoo_openupgrade_wizard.tools.tools_odoo import kill_odoo, run_odoo
from odoo_openupgrade_wizard.tools.tools_postgres import ensure_database
@click.command()
@step_option
@database_option_required
@demo_option
@click.option(
"--stop-after-init",
is_flag=True,
default=False,
help="Stop after init. Mainly used"
" for test purpose, for commands that are using input()"
" function to stop.",
)
@click.option(
"-i",
"--init-modules",
type=str,
help="List of modules to install. Equivalent to -i Odoo options.",
)
@click.option(
"-u",
"--update-modules",
type=str,
help="List of modules to update. Equivalent to -u Odoo options.",
)
@click.option(
"-e",
"--execution-context",
type=click.Choice(["regular", "openupgrade"]),
help="Force to use an openupgrade (OCA/openupgrade)"
" or a regular (odoo/odoo or OCA/OCB) base code when running odoo."
" Leave empty to use the default execution of the migration step.",
)
@click.pass_context
def run(
ctx,
step,
database,
with_demo,
stop_after_init,
init_modules,
update_modules,
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)
ensure_database(ctx, database, state="present")
try:
run_odoo(
ctx,
migration_step,
database=database,
detached_container=not stop_after_init,
init=init_modules,
update=update_modules,
stop_after_init=stop_after_init,
demo=with_demo,
execution_context=execution_context,
publish_ports=True,
)
if not stop_after_init:
logger.info(
"Odoo is available on your host at http://localhost:"
f"{ctx.obj['config']['odoo_host_xmlrpc_port']}"
)
input("Press 'Enter' to kill the odoo container and exit...")
except (KeyboardInterrupt, SystemExit):
logger.info("Received Keyboard Interrupt or System Exiting...")
finally:
kill_odoo(ctx, database, migration_step)

View File

@ -0,0 +1,69 @@
import click
from loguru import logger
from odoo_openupgrade_wizard.cli.cli_options import (
database_option_required,
demo_option,
first_step_option,
get_migration_steps_from_options,
last_step_option,
)
from odoo_openupgrade_wizard.tools.tools_odoo import (
execute_click_odoo_python_files,
kill_odoo,
run_odoo,
)
from odoo_openupgrade_wizard.tools.tools_postgres import (
execute_sql_files_pre_migration,
)
@click.command()
@first_step_option
@last_step_option
@database_option_required
@demo_option
@click.pass_context
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(
ctx, first_step, last_step
)
for migration_step in migration_steps:
execute_sql_files_pre_migration(ctx, database, migration_step)
if migration_step.get("update", True):
try:
run_odoo(
ctx,
migration_step,
database=database,
detached_container=False,
update="all",
stop_after_init=True,
demo=with_demo,
)
except (KeyboardInterrupt, SystemExit):
logger.info("Received Keyboard Interrupt or System Exiting...")
finally:
kill_odoo(ctx, database, 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)

View File

@ -1,34 +0,0 @@
import click
from loguru import logger
from odoo_openupgrade_wizard.cli_options import (
get_odoo_versions_from_options,
versions_options,
)
from odoo_openupgrade_wizard.tools_docker import build_image, pull_image
from odoo_openupgrade_wizard.tools_odoo import (
get_docker_image_tag,
get_odoo_env_path,
)
@click.command()
@versions_options
@click.pass_context
def docker_build(ctx, versions):
"""Build Odoo Docker Images. (One image per version)"""
# Pull DB image
pull_image(ctx.obj["config"]["postgres_image_name"])
# Build images for each odoo version
for odoo_version in get_odoo_versions_from_options(ctx, versions):
logger.info(
"Building Odoo docker image for version '%s'. "
"This can take a while..." % (odoo_version)
)
image = build_image(
get_odoo_env_path(ctx, odoo_version),
get_docker_image_tag(ctx, odoo_version),
)
logger.info("Docker Image build. '%s'" % image[0].tags[0])

View File

@ -1,56 +0,0 @@
from datetime import datetime
from pathlib import Path
import click
from odoo_openupgrade_wizard import templates
from odoo_openupgrade_wizard.tools_odoo import get_odoo_modules_from_csv
from odoo_openupgrade_wizard.tools_odoo_module import Analysis
from odoo_openupgrade_wizard.tools_system import (
ensure_file_exists_from_template,
)
@click.command()
@click.option(
"--analysis-file-path",
type=click.Path(
dir_okay=False,
),
default="./analysis.html",
)
@click.option(
"--extra-modules",
"extra_modules_list",
# TODO, add a callback to check the quality of the argument
help="Coma separated modules to analyse. If not set, the modules.csv"
" file will be used to define the list of module to analyse."
"Ex: 'account,product,base'",
)
@click.pass_context
def estimate_workload(ctx, analysis_file_path, extra_modules_list):
# 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()
analysis.analyse_openupgrade_state(ctx)
analysis.estimate_workload(ctx)
# Make some clean to display properly
analysis.modules = sorted(analysis.modules)
# Render html file
# TODO, make
ensure_file_exists_from_template(
Path(analysis_file_path),
templates.ANALYSIS_HTML_TEMPLATE,
ctx=ctx,
analysis=analysis,
current_date=datetime.now().strftime("%d/%m/%Y %H:%M:%S"),
)

View File

@ -1,32 +0,0 @@
from pathlib import Path
import click
from odoo_openupgrade_wizard.cli_options import (
database_option_required,
get_migration_step_from_options,
step_option,
)
from odoo_openupgrade_wizard.tools_odoo import execute_click_odoo_python_files
@click.command()
@step_option
@database_option_required
@click.option(
"--script-file-path",
multiple=True,
type=click.Path(
exists=True,
dir_okay=False,
),
help="List of python files that will be executed, replacing the default"
" scripts placed in the migration step folder.",
)
@click.pass_context
def execute_script_python(ctx, step, database, script_file_path):
migration_step = get_migration_step_from_options(ctx, step)
execute_click_odoo_python_files(
ctx, database, migration_step, [Path(x) for x in script_file_path]
)

View File

@ -1,34 +0,0 @@
from pathlib import Path
import click
from odoo_openupgrade_wizard.cli_options import (
database_option_required,
get_migration_step_from_options,
step_option,
)
from odoo_openupgrade_wizard.tools_postgres import (
execute_sql_files_pre_migration,
)
@click.command()
@step_option
@database_option_required
@click.option(
"--script-file-path",
multiple=True,
type=click.Path(
exists=True,
dir_okay=False,
),
help="List of SQL files that will be executed, replacing the default"
" scripts placed in the migration step folder.",
)
@click.pass_context
def execute_script_sql(ctx, step, database, script_file_path):
migration_step = get_migration_step_from_options(ctx, step)
execute_sql_files_pre_migration(
ctx, database, migration_step, [Path(x) for x in script_file_path]
)

View File

@ -1,28 +0,0 @@
import click
from odoo_openupgrade_wizard.cli_options import (
get_odoo_versions_from_options,
versions_options,
)
from odoo_openupgrade_wizard.tools_odoo import get_odoo_env_path
from odoo_openupgrade_wizard.tools_system import git_aggregate
@click.command()
@versions_options
@click.option(
"-j",
"--jobs",
type=int,
default=10,
help="Jobs used to call the git-aggregate command."
" reasonably set to 10 by default.",
)
@click.pass_context
def get_code(ctx, versions, jobs):
"""Get code by running gitaggregate command for each version"""
for odoo_version in get_odoo_versions_from_options(ctx, versions):
folder_path = get_odoo_env_path(ctx, odoo_version)
repo_file_path = folder_path / "repos.yml"
git_aggregate(folder_path, repo_file_path, jobs)

View File

@ -1,75 +0,0 @@
import click
from loguru import logger
from odoo_openupgrade_wizard.cli_options import (
database_option,
get_migration_step_from_options,
)
from odoo_openupgrade_wizard.tools_odoo import (
get_odoo_modules_from_csv,
kill_odoo,
run_odoo,
)
from odoo_openupgrade_wizard.tools_odoo_instance import OdooInstance
from odoo_openupgrade_wizard.tools_postgres import ensure_database
@click.command()
@database_option
@click.pass_context
def install_from_csv(ctx, database):
migration_step = get_migration_step_from_options(ctx, 1)
ensure_database(ctx, database, state="present")
# Get modules list from the CSV file
module_names = get_odoo_modules_from_csv(ctx.obj["module_file_path"])
module_names.sort()
logger.info("Found %d modules." % (len(module_names)))
logger.debug(module_names)
try:
logger.info("Install 'base' module on %s database ..." % (database))
run_odoo(
ctx,
migration_step,
database=database,
detached_container=True,
init="base",
)
odoo_instance = OdooInstance(ctx, database)
default_country_code = ctx.obj["config"].get(
"odoo_default_country_code", False
)
if "account" in module_names and default_country_code:
# Then, set correct country to the company of the current user
# Otherwise, due to poor design of Odoo, when installing account
# the US localization will be installed.
# (l10n_us + l10n_generic_coa)
countries = odoo_instance.browse_by_search(
"res.country",
[("code", "=", default_country_code)],
)
if len(countries) != 1:
raise Exception(
"Unable to find a country, based on the code %s."
" countries found : %s "
% (
default_country_code,
", ".join([x.name for x in countries]),
)
)
logger.info(
"Configuring country of the main company with #%d - %s"
% (countries[0].id, countries[0].name)
)
odoo_instance.env.user.company_id.country_id = countries[0].id
# Install modules
odoo_instance.install_modules(module_names)
except (KeyboardInterrupt, SystemExit):
logger.info("Received Keyboard Interrupt or System Exiting...")
finally:
kill_odoo(ctx, migration_step)

View File

@ -1,63 +0,0 @@
import click
from loguru import logger
from odoo_openupgrade_wizard.cli_options import (
database_option,
get_migration_step_from_options,
step_option,
)
from odoo_openupgrade_wizard.tools_odoo import kill_odoo, run_odoo
from odoo_openupgrade_wizard.tools_postgres import ensure_database
@click.command()
@step_option
@database_option
@click.option(
"--stop-after-init",
is_flag=True,
default=False,
help="Stop after init. Mainly used"
" for test purpose, for commands that are using input()"
" function to stop.",
)
@click.option(
"-i",
"--init-modules",
type=str,
help="List of modules to install. Equivalent to -i odoo options.",
)
@click.option(
"-e",
"--execution-context",
type=click.Choice(["regular", "openupgrade"]),
help="Force to use an openupgrade (OCA/openupgrade)"
" or a regular (odoo/odoo or OCA/OCB) base code when running odoo."
" Let empty to use the defaut execution of the migration step.",
)
@click.pass_context
def run(ctx, step, database, stop_after_init, init_modules, execution_context):
migration_step = get_migration_step_from_options(ctx, step)
ensure_database(ctx, database, state="present")
try:
run_odoo(
ctx,
migration_step,
database=database,
detached_container=not stop_after_init,
init=init_modules,
stop_after_init=stop_after_init,
execution_context=execution_context,
)
if not stop_after_init:
logger.info(
"Odoo is available on your host at"
" http://localhost:%s"
% ctx.obj["config"]["odoo_host_xmlrpc_port"]
)
input("Press 'Enter' to kill the odoo container and exit ...")
except (KeyboardInterrupt, SystemExit):
logger.info("Received Keyboard Interrupt or System Exiting...")
finally:
kill_odoo(ctx, migration_step)

View File

@ -1,41 +0,0 @@
import click
from loguru import logger
from odoo_openupgrade_wizard.cli_options import (
database_option_required,
first_step_option,
get_migration_steps_from_options,
last_step_option,
)
from odoo_openupgrade_wizard.tools_odoo import (
execute_click_odoo_python_files,
kill_odoo,
run_odoo,
)
@click.command()
@first_step_option
@last_step_option
@database_option_required
@click.pass_context
def upgrade(ctx, first_step, last_step, database):
migration_steps = get_migration_steps_from_options(
ctx, first_step, last_step
)
for migration_step in migration_steps:
try:
run_odoo(
ctx,
migration_step,
database=database,
detached_container=False,
update="all",
stop_after_init=True,
)
except (KeyboardInterrupt, SystemExit):
logger.info("Received Keyboard Interrupt or System Exiting...")
finally:
kill_odoo(ctx, migration_step)
execute_click_odoo_python_files(ctx, database, migration_step)

View File

@ -2,75 +2,17 @@ from pathlib import Path
from loguru import logger
_ODOO_VERSION_TEMPLATES = [
{
"version": 8.0,
"python_major_version": "python2",
"python_libraries": [],
},
{
"version": 9.0,
"python_major_version": "python2",
"python_libraries": ["openupgradelib==2.0.0"],
},
{
"version": 10.0,
"python_major_version": "python2",
"python_libraries": ["openupgradelib==2.0.0"],
},
{
"version": 11.0,
"python_major_version": "python3",
"python_libraries": ["openupgradelib==2.0.0"],
},
{
"version": 12.0,
"python_major_version": "python3",
"python_libraries": [
"git+https://github.com/grap/openupgradelib.git"
"@2.0.1#egg=openupgradelib"
],
},
{
"version": 13.0,
"python_major_version": "python3",
"python_libraries": ["openupgradelib"],
},
{
"version": 14.0,
"python_major_version": "python3",
"python_libraries": ["openupgradelib"],
},
{
"version": 15.0,
"python_major_version": "python3",
"python_libraries": ["openupgradelib"],
},
FIRST_ODOO_VERSION_SUPPORTED = 8
LAST_ODOO_VERSION_SUPPORTED = 18
_ALL_ODOO_VERSIONS = [
float(x)
for x in range(
FIRST_ODOO_VERSION_SUPPORTED, LAST_ODOO_VERSION_SUPPORTED + 1
)
]
def get_version_template(version: float) -> dict:
"""return a version template dictionnary according to a version
provided"""
for version_template in _ODOO_VERSION_TEMPLATES:
if version_template["version"] == version:
return version_template
else:
raise ValueError
def get_python_libraries(version: float) -> list:
"""Return a list of python librairies that should be
installed in each docker container for a given version"""
return get_version_template(version)["python_libraries"]
def get_python_major_version(version: float) -> str:
"""Return the major python version (2.0, 3.0) of Odoo for
a given version"""
return get_version_template(version)["python_major_version"]
def get_version_options(mode: str) -> list:
"""Get options available for version click argument.
Arguments:
@ -80,7 +22,7 @@ def get_version_options(mode: str) -> list:
Exemple:
['9.0', '10.0', '11.0']
"""
version_options = [str(x["version"]) for x in _ODOO_VERSION_TEMPLATES]
version_options = [str(version) for version in _ALL_ODOO_VERSIONS]
if mode == "initial":
version_options = version_options[:-1]
if mode == "final":
@ -93,12 +35,9 @@ def get_odoo_versions(initial_version: float, final_version: float) -> list:
version
"""
result = []
for version_template in _ODOO_VERSION_TEMPLATES:
if (
version_template["version"] >= initial_version
and version_template["version"] <= final_version
):
result.append(version_template["version"])
for version in _ALL_ODOO_VERSIONS:
if version >= initial_version and version <= final_version:
result.append(version)
return result
@ -112,7 +51,7 @@ def get_odoo_run_command(migration_step: dict) -> str:
def get_odoo_folder(
migration_step: dict, execution_context: str = False
migration_step: dict, execution_context: str = None
) -> str:
"""return the main odoo folder, depending on the migration step.
(./src/odoo, ./src/openupgrade, ...)"""
@ -140,6 +79,15 @@ def get_base_module_folder(migration_step: dict) -> str:
return "openerp"
def get_manifest_name(migration_step: dict) -> str:
"""return the name of the manifest file present in
each odoo module"""
if migration_step["version"] >= 10.0:
return "__manifest__.py"
return "__openerp__.py"
def skip_addon_path(migration_step: dict, path: Path) -> bool:
"""return a boolean to indicate if the addon_path should be
remove (during the generation of the addons_path).
@ -151,11 +99,14 @@ def skip_addon_path(migration_step: dict, path: Path) -> bool:
) and migration_step["version"] < 14.0
def get_server_wide_modules_upgrade(migration_step: dict) -> list:
def get_server_wide_modules_upgrade(
migration_step: dict, execution_context: str = None
) -> list:
"""return a list of modules to load, depending on the migration step."""
if (
migration_step["version"] >= 14.0
and migration_step["execution_context"] == "openupgrade"
and execution_context != "regular"
):
return ["openupgrade_framework"]
return []
@ -174,8 +125,8 @@ def get_upgrade_analysis_module(migration_step: dict) -> str:
def generate_records(odoo_instance, migration_step: dict):
logger.info(
"Generate Records in version %s ..."
" (It can take a while)" % (migration_step["version"])
"Generate Records in version %s..."
" (This may take a while)" % (migration_step["version"])
)
if migration_step["version"] < 14.0:
wizard = odoo_instance.browse_by_create(
@ -214,7 +165,7 @@ def generate_analysis_files(
):
logger.info(
"Generate analysis files for"
" the modules installed on %s ..." % (initial_database)
" the modules installed on %s..." % (initial_database)
)
proxy_vals = {
"name": "Proxy to Previous version",
@ -225,12 +176,12 @@ def generate_analysis_files(
"password": "admin",
}
if final_step["version"] < 14.0:
logger.info("> Create proxy ...")
logger.info("> Create proxy...")
proxy = final_odoo_instance.browse_by_create(
"openupgrade.comparison.config", proxy_vals
)
logger.info("> Create wizard ...")
logger.info("> Create wizard...")
wizard = final_odoo_instance.browse_by_create(
"openupgrade.analysis.wizard",
{
@ -238,16 +189,16 @@ def generate_analysis_files(
"write_files": True,
},
)
logger.info("> Launch analysis. This can take a while ...")
logger.info("> Launch analysis. This can take a while...")
wizard.get_communication()
else:
logger.info("> Create proxy ...")
logger.info("> Create proxy...")
proxy = final_odoo_instance.browse_by_create(
"upgrade.comparison.config", proxy_vals
)
logger.info("> Create wizard ...")
logger.info("> Create wizard...")
analysis = final_odoo_instance.browse_by_create(
"upgrade.analysis",
{
@ -255,7 +206,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()
@ -316,6 +267,6 @@ def get_openupgrade_analysis_files(
module_name = file.parent.parent.name
result[module_name] = file
logger.debug(
"Version %s : %d analysis files found." % (version, len(result))
"Version %s: %d analysis files found." % (version, len(result))
)
return result

View File

@ -1,322 +0,0 @@
CONFIG_YML_TEMPLATE = """project_name: {{ project_name }}
postgres_image_name: postgres:13
postgres_container_name: {{project_name}}-db
odoo_host_xmlrpc_port: 9069
odoo_default_country_code: FR
odoo_versions:
{%- for odoo_version in odoo_versions %}
- {{ odoo_version }}
{%- endfor %}
migration_steps:
{%- for step in steps %}
- name: {{ step['name'] }}
version: {{ step['version'] }}
execution_context: {{ step['execution_context'] }}
complete_name: {{ step['complete_name'] }}
{% endfor %}
workload_settings:
# porting a module requires 45 minutes minimaly
port_minimal_time: 45
# a migration cost more for each version
port_per_version: 15
# Porting 120 lines of Python code costs 1 hour
port_per_python_line_time: 0.5
# Porting 120 lines of Python code costs 1 hour
port_per_javascript_line_time: 0.5
# Porting 10 lines of XML costs 1 minute
port_per_xml_line_time: 0.10
# Minimal time for Openupgrade PR
open_upgrade_minimal_time: 10
# time for a line of model in the openupgrade_analysis.txt
openupgrade_model_line_time: 10
# Time for a line of field in the openupgrade_analysis.txt
openupgrade_field_line_time: 5
# Time for a line of XML in the openupgrade_analysis.txt
openupgrade_xml_line_time: 0.1
"""
REPO_YML_TEMPLATE = """
##############################################################################
## Odoo Repository
##############################################################################
./src/odoo:
defaults:
depth: 1
remotes:
odoo: https://github.com/odoo/odoo
target: odoo {{ odoo_version }}-target
merges:
- odoo {{ odoo_version }}
##############################################################################
## OpenUpgrade Repository
##############################################################################
./src/openupgrade:
defaults:
depth: 1
remotes:
OCA: https://github.com/OCA/OpenUpgrade
target: OCA {{ odoo_version }}-target
merges:
- OCA {{ odoo_version }}
{% for org_name, repo_list in orgs.items() %}
##############################################################################
## {{ org_name }} Repositories
##############################################################################
{% for repo in repo_list %}
./src/{{ org_name }}/{{ repo }}:
defaults:
depth: 1
remotes:
{{ org_name }}: https://github.com/{{ org_name }}/{{ repo }}
target: {{ org_name }} {{ odoo_version }}-target
merges:
- {{ org_name }} {{ odoo_version }}
{% endfor %}
{% endfor %}
"""
PYTHON_REQUIREMENTS_TXT_TEMPLATE = """
{%- for python_librairy in python_libraries -%}
{{ python_librairy }}
{% endfor %}
odoorpc
click-odoo
"""
DEBIAN_REQUIREMENTS_TXT_TEMPLATE = """
git
"""
ODOO_CONFIG_TEMPLATE = ""
# Technical Notes:
# - We set apt-get update || true, because for some version (at least odoo:10)
# the command update fail, because of obsolete postgresql repository.
DOCKERFILE_TEMPLATE = """
FROM odoo:{{ odoo_version }}
MAINTAINER GRAP, Coop It Easy
# Set User root for installations
USER root
# 1. Make available files in the containers
COPY debian_requirements.txt /debian_requirements.txt
COPY python_requirements.txt /python_requirements.txt
# 2. Install extra debian packages
RUN apt-get update || true &&\
xargs apt-get install -y --no-install-recommends <debian_requirements.txt
# 3. Install extra Python librairies
RUN {{ python_major_version }}\
-m pip install -r python_requirements.txt
# Reset to odoo user to run the container
USER odoo
"""
PRE_MIGRATION_SQL_TEMPLATE = ""
POST_MIGRATION_PY_TEMPLATE = """
import logging
_logger = logging.getLogger(__name__)
_logger.info("Executing post-migration.py script ...")
env = env # noqa: F821
"""
GIT_IGNORE_CONTENT = """
*
!.gitignore
"""
# TODO, this value are usefull for test for analyse between 13 and 14.
# move that values in data/extra_script/modules.csv
# and let this template with only 'base' module.
MODULES_CSV_TEMPLATE = """
base,Base
account,Account Module
web_responsive,Web Responsive Module
"""
ANALYSIS_HTML_TEMPLATE = """
<html>
<body>
<h1>Migration Analysis</h1>
<table border="1" width="100%">
<thead>
<tr>
<th>Initial Version</th>
<th>Final Version</th>
<th>Project Name</th>
<th>Analysis Date</th>
</tr>
</thead>
<tbody>
<tr>
<td>{{ ctx.obj["config"]["odoo_versions"][0] }}</td>
<td>{{ ctx.obj["config"]["odoo_versions"][-1] }}</td>
<td>{{ ctx.obj["config"]["project_name"] }}</td>
<td>{{ current_date }}</td>
</tr>
</tbody>
</table>
<h2>Summary</h2>
<table border="1" width="100%">
<thead>
<tr>
<th>Module Type</th>
<th>Module Quantity</th>
<th>Remaining Hours</th>
</tr>
</thead>
<tbody>
<tr>
<td>Odoo</td>
<td>{{ analysis.get_module_qty("odoo") }}</td>
<td>{{ analysis.workload_hour_text("odoo") }}</td>
</tr>
<tr>
<td>OCA</td>
<td>{{ analysis.get_module_qty("OCA") }}</td>
<td>{{ analysis.workload_hour_text("OCA") }}</td>
</tr>
<tr>
<td>Custom</td>
<td>{{ analysis.get_module_qty("custom") }}</td>
<td>{{ analysis.workload_hour_text("custom") }}</td>
</tr>
</tbody>
<tfood>
<tr>
<th>Total</th>
<td>{{ analysis.get_module_qty() }}</td>
<td>{{ analysis.workload_hour_text() }}</td>
</tr>
</tfood>
</table>
<h2>Details</h2>
<table border="1" width="100%">
<thead>
<tr>
<th>&nbsp;</th>
{%- for odoo_version in ctx.obj["config"]["odoo_versions"] -%}
<th>{{ odoo_version }}</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% set ns = namespace(
current_repository='',
current_module_type='',
) %}
{% for odoo_module in analysis.modules %}
<!-- ---------------------- -->
<!-- Handle New Module Type -->
<!-- ---------------------- -->
{% if (
ns.current_module_type != odoo_module.module_type
and odoo_module.module_type != 'odoo') %}
{% set ns.current_module_type = odoo_module.module_type %}
<tr>
<th colspan="{{1 + ctx.obj["config"]["odoo_versions"]|length}}">
{{ ns.current_module_type}}
</th>
<tr>
{% endif %}
<!-- -------------------- -->
<!-- Handle New Repository-->
<!-- -------------------- -->
{% if ns.current_repository != odoo_module.repository %}
{% set ns.current_repository = odoo_module.repository %}
<tr>
<th colspan="{{1 + ctx.obj["config"]["odoo_versions"]|length}}">
{{ ns.current_repository}}
</th>
<tr>
{% endif %}
<!-- -------------------- -->
<!-- Display Module Line -->
<!-- -------------------- -->
<tr>
<td>{{odoo_module.name}}
</td>
{% for version in odoo_module.analyse.all_version %}
{% 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()}};">
{{module_version.get_text()}}
{% if workload %}
<span style="background-color:lightblue;">
({{ module_version.workload_hour_text()}})
</span>
{% endif %}
{% if size_text %}
<br/>
<span style="color:gray;font-size:11px;font-family:monospace;">
({{ size_text}})
</span>
{% 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>
{% else %}
<td style="background-color:gray;">&nbsp;</td>
{% endif %}
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
</body>
</html>
"""

View File

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

View File

@ -0,0 +1,196 @@
<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>
<h1>Migration Analysis</h1>
<table border="1" width="100%">
<thead>
<tr>
<th>Initial Version</th>
<th>Final Version</th>
<th>Project Name</th>
<th>Analysis Date</th>
</tr>
</thead>
<tbody>
<tr>
<td>{{ ctx.obj['config']['odoo_versions'][0] }}</td>
<td>{{ ctx.obj['config']['odoo_versions'][-1] }}</td>
<td>{{ ctx.obj['config']['project_name'] }}</td>
<td>{{ current_date }}</td>
</tr>
</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>
<h2>Summary</h2>
<table border="1" width="100%">
<thead>
<tr>
<th>Module Type</th>
<th>Module Quantity</th>
<th>Remaining Hours</th>
</tr>
</thead>
<tbody>
<tr>
<td>Odoo</td>
<td>{{ analysis.get_module_qty('odoo') }}</td>
<td>{{ time_to_text(analysis.workload('odoo', False)) }}</td>
</tr>
<tr>
<td>OCA</td>
<td>{{ analysis.get_module_qty('oca') }}</td>
<td>{{ time_to_text(analysis.workload('oca', False)) }}</td>
</tr>
<tr>
<td>Custom</td>
<td>{{ analysis.get_module_qty('custom') }}</td>
<td>{{ time_to_text(analysis.workload('custom', False)) }}</td>
</tr>
{%- if analysis.get_module_qty('not_found') -%}
<tr>
<td>Not Found</td>
<td>{{ analysis.get_module_qty('not_found') }}</td>
<td>&nbsp;</td>
</tr>
{%- endif -%}
</tbody>
<tfood>
<tr>
<th>Total</th>
<td>{{ analysis.get_module_qty() }}</td>
<td>{{ time_to_text(analysis.workload(False, False)) }}</td>
</tr>
</tfood>
</table>
<h2>Details</h2>
<table border="1" width="100%">
<thead>
<tr>
<th>&nbsp;</th>
<th>Total</th>
{%- for odoo_version in ctx.obj['config']['odoo_versions'] %}
<th>{{ odoo_version }}</th>
{%- endfor %}
</tr>
</thead>
<tbody>
{%- set ns = namespace(current_repository='', current_module_type='') %}
{%- for odoo_module in analysis.modules %}
{%- if (ns.current_module_type != odoo_module.module_type) %}
{%- set ns.current_module_type = odoo_module.module_type %}
<!-- ------------------------------------------------ -->
<!-- Handle New Module Type {{ns.current_module_type}}-->
<!-- ------------------------------------------------ -->
<tr>
<th colspan="{{2 + ctx.obj['config']['odoo_versions']|length}}">
<h3>{{ odoo_module.module_type}}</h3>
</th>
<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 %}
</h4>
</th>
<tr>
{%- endif %}
{%- endif %}
<tr class="{{odoo_module.workload == 0 and 'work_done' or ''}}">
<td>{{odoo_module.name}}
{%- if odoo_module.module_type == 'not_found' -%}
{%- set odoo_apps_url = odoo_module.get_odoo_apps_url() -%}
{%- if odoo_apps_url -%}
<a href="{{odoo_apps_url}}" target="_blank">AppsStore</a>
{%- else -%}
{%- set odoo_code_search_url = odoo_module.get_odoo_code_search_url() -%}
{%- if odoo_code_search_url -%}
<a href="{{odoo_code_search_url}}" target="_blank">OdooCodeSearch</a>
{%- endif -%}
{%- endif -%}
{%- endif -%}
</td>
<td>
{%- if odoo_module.workload -%}
{{time_to_text(odoo_module.workload)}}
{%- endif -%}
</td>
{%- 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()}};">
{{module_version.get_text()}}
{%- if workload -%}
<span style="background-color:lightblue;">({{time_to_text(workload)}})</span>
{%- endif -%}
{%- if size_text -%}
<br/>
<span style="color:gray;font-size:11px;font-family:monospace;">({{ size_text}})</span>
{%- 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>
{%- else %}
<td style="background-color:gray;">&nbsp;</td>
{%- endif %}
{%- endfor %}
</tr>
{%- endfor %}
</tbody>
</table>
</body>
</html>

View File

@ -0,0 +1,69 @@
project_name: {{ project_name }}
postgres_image_name: postgres:{{postgresql_version}}
postgres_container_name: {{project_name}}-container-postgres-{{postgresql_version}}
postgres_volume_name: {{project_name}}-volume-postgres-{{postgresql_version}}
postgres_extra_settings:
odoo_rpc_timeout: 3600
odoo_host_xmlrpc_port: 9069
odoo_default_company:
country_code: FR
odoo_versions:
{%- for odoo_version in odoo_versions %}
- {{ odoo_version }}
{%- endfor %}
odoo_version_settings:
{%- for odoo_version in odoo_versions %}
{{odoo_version}}:
{%- endfor %}
migration_steps:
{%- for step in steps %}
- name: {{ step['name'] }}
version: {{ step['version'] }}
execution_context: {{ step['execution_context'] }}
complete_name: {{ step['complete_name'] }}
{%- if step['execution_context'] == 'regular'%}
update: True
{%- endif %}
{% endfor %}
workload_settings:
# Ignored module list
ignored_module_list: []
# porting a module requires 45 minutes minimaly
port_minimal_time: 45
# a migration cost more for each version
port_per_version: 15
# Porting 120 lines of Python code costs 1 hour
port_per_python_line_time: 0.5
# Porting 120 lines of Javascript code costs 1 hour
port_per_javascript_line_time: 0.5
# Porting 10 lines of XML costs 1 minute
port_per_xml_line_time: 0.10
# Minimal time for Openupgrade PR
open_upgrade_minimal_time: 10
# time for a line of model in the openupgrade_analysis.txt
openupgrade_model_line_time: 10
# Time for a line of field in the openupgrade_analysis.txt
openupgrade_field_line_time: 5
# Time for a line of XML in the openupgrade_analysis.txt
openupgrade_xml_line_time: 0.1

View File

@ -0,0 +1 @@
base,Base

View File

@ -0,0 +1,64 @@
# <OOW> : Copy of https://github.com/odoo/odoo/blob/11.0/setup/package.dfsrc
FROM debian:stretch
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 sed -i -- 's/stretch-updates/stretch/g' /etc/apt/**.list
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 \
node-less \
libxml2-dev \
libxslt1-dev \
libldap2-dev \
libsasl2-dev \
libssl-dev \
libjpeg-dev \
zlib1g-dev \
python3-dev \
python3-pip \
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 \
&& 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 /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
ARG LOCAL_USER_ID
RUN useradd --uid $LOCAL_USER_ID --non-unique odoo
USER odoo

View File

@ -0,0 +1,64 @@
# <OOW> : Copy of https://github.com/odoo/odoo/blob/12.0/setup/package.dfsrc
FROM debian:stretch
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 sed -i -- 's/stretch-updates/stretch/g' /etc/apt/**.list
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 \
libsass0 \
libxml2-dev \
libxslt1-dev \
libldap2-dev \
libsasl2-dev \
libssl-dev \
libjpeg-dev \
zlib1g-dev \
python3-dev \
python3-pip \
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 \
&& 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 /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
ARG LOCAL_USER_ID
RUN useradd --uid $LOCAL_USER_ID --non-unique odoo
USER odoo

View File

@ -0,0 +1,63 @@
# <OOW> : Copy of https://github.com/odoo/odoo/blob/13.0/setup/package.dfsrc
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 && \
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 \
libsass1 \
libxml2-dev \
libxslt1-dev \
libldap2-dev \
libsasl2-dev \
libssl-dev \
libjpeg-dev \
zlib1g-dev \
python3-dev \
python3-pip \
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 \
&& 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 /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
ARG LOCAL_USER_ID
RUN useradd --uid $LOCAL_USER_ID --non-unique odoo
USER odoo

View File

@ -0,0 +1,56 @@
# <OOW> : Copy of https://github.com/odoo/odoo/blob/14.0/setup/package.dfsrc
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 && \
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-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 \
&& 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 /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
ARG LOCAL_USER_ID
RUN useradd --uid $LOCAL_USER_ID --non-unique odoo
USER odoo

View File

@ -0,0 +1,56 @@
# <OOW> : Copy of https://github.com/odoo/odoo/blob/15.0/setup/package.dfsrc
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 && \
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-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 \
&& 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 /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
ARG LOCAL_USER_ID
RUN useradd --uid $LOCAL_USER_ID --non-unique odoo
USER odoo

View File

@ -0,0 +1,56 @@
# <OOW> : Copy of https://github.com/odoo/odoo/blob/16.0/setup/package.dfsrc
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 && \
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-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 \
&& 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 /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
ARG LOCAL_USER_ID
RUN useradd --uid $LOCAL_USER_ID --non-unique odoo
USER odoo

View File

@ -0,0 +1,54 @@
# <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

@ -0,0 +1,54 @@
# <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

@ -0,0 +1,8 @@
# 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

@ -0,0 +1,8 @@
# 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

@ -0,0 +1,9 @@
# Mandatory library used in all odoo-openupgrade-wizard
# Note: As the openupgradelib is not allways up to date in pypi,
# we use the github master url.
git+https://github.com/OCA/openupgradelib@master#egg=openupgradelib
# Library used to run generate-module-analysis command
# dependencies of the module OCA/server-tools 'upgrade_analysis'
odoorpc
mako

View File

@ -0,0 +1,13 @@
# This file can be left empty
# The following standard parameters are generated automatically and are
# not required to be found in this file:
# - addons_path
# - connection parameters to the database
# - database name
# - http or xml port
# - etc.
# Only non standard or special options should be specified here.
[options]
# Disable default memory limit of 2560 MiB
limit_memory_hard = 0

View File

@ -0,0 +1,41 @@
##############################################################################
## Odoo Repository
##############################################################################
./src/odoo:
defaults:
depth: 1
remotes:
odoo: https://github.com/odoo/odoo
target: odoo {{ odoo_version }}-target
merges:
- odoo {{ odoo_version }}
##############################################################################
## OpenUpgrade Repository
##############################################################################
./src/openupgrade:
defaults:
depth: 1
remotes:
OCA: https://github.com/OCA/OpenUpgrade
target: OCA {{ odoo_version }}-target
merges:
- OCA {{ odoo_version }}
{% for org_name, repo_list in orgs.items() %}
##############################################################################
## {{ org_name }} Repositories
##############################################################################
{% for repo in repo_list %}
./src/{{ org_name }}/{{ repo }}:
defaults:
depth: 1
remotes:
{{ org_name }}: https://github.com/{{ org_name }}/{{ repo }}
target: {{ org_name }} {{ odoo_version }}-target
merges:
- {{ org_name }} {{ odoo_version }}
{% endfor %}
{% endfor %}

View File

@ -0,0 +1,10 @@
import logging
_logger = logging.getLogger(__name__)
_logger.info("Executing post-migration.py script...")
env = env # noqa: F821
# Write custom script here
env.cr.commit()

View File

@ -0,0 +1,46 @@
import shutil
from loguru import logger
from odoo_openupgrade_wizard.tools.tools_postgres import (
check_db_exist,
ensure_database,
)
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
ensure_database(ctx, dest, state="absent")
# Copy database
ensure_database(ctx, dest, state="present", template=source)
main_path = ctx.obj["filestore_folder_path"] / "filestore"
source_path = main_path / source
dest_path = main_path / dest
# Drop filestore if exist
logger.info(f"Remove filestore of '{dest}' if exists.")
shutil.rmtree(dest_path, ignore_errors=True)
# Copy Filestore
logger.info(f"Copy filestore of '{source}' into '{dest}' directory...")
shutil.copytree(source_path, dest_path)
def dropdb(ctx, database):
"""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
logger.info(f"Drop database '{database}'...")
ensure_database(ctx, database, state="absent")
# Drop filestore
root_filestore_path = ctx.obj["filestore_folder_path"] / "filestore"
filestore_path = root_filestore_path / database
logger.info(f"Remove filestore of '{database}' if exists...")
shutil.rmtree(filestore_path, ignore_errors=True)

View File

@ -0,0 +1,169 @@
import time
import docker
from loguru import logger
def get_docker_client():
return docker.from_env()
def pull_image(image_name):
client = get_docker_client()
client.images.pull(image_name)
def build_image(path, tag, buildargs={}):
logger.debug(
f"Building image named based on {path}/Dockerfile."
" This can take a long time..."
)
debug_docker_command = f"docker build {path} --tag {tag}"
for arg_name, arg_value in buildargs.items():
debug_docker_command += f"\\\n --build-arg {arg_name}={arg_value}"
logger.debug(f"DOCKER COMMAND:\n\n{debug_docker_command}\n")
docker_client = get_docker_client()
try:
image = docker_client.images.build(
path=str(path),
tag=tag,
buildargs=buildargs,
rm=True,
)
logger.debug("Image build done.")
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
def run_container(
image_name,
container_name,
command=None,
ports={},
volumes={},
environments={},
links={},
detach=False,
auto_remove=False,
):
client = get_docker_client()
if not client.images.list(filters={"reference": image_name}):
raise Exception(
f"The image {image_name} is not available on your system."
" Did you run 'odoo-openupgrade-wizard docker-build' ?"
)
logger.debug(f"Launching Docker container named {image_name}...")
debug_docker_command = f"docker run --name {container_name}\\\n"
for k, v in ports.items():
debug_docker_command += f" --publish {k}:{v}\\\n"
for k, v in volumes.items():
debug_docker_command += f" --volume {k}:{v}\\\n"
for k, v in environments.items():
debug_docker_command += f" --env {k}={v}\\\n"
for k, v in links.items():
debug_docker_command += f" --link {k}:{v}\\\n"
if auto_remove:
debug_docker_command += " --rm"
if detach:
debug_docker_command += " --detach"
debug_docker_command += f" {image_name}"
if command:
debug_docker_command += f" \\\n{command}"
logger.debug(f"DOCKER COMMAND:\n{debug_docker_command}")
container = client.containers.run(
image_name,
name=container_name,
command=command,
ports={x: y for y, x in ports.items()},
volumes=[str(k) + ":" + str(v) for k, v in volumes.items()],
environment=environments,
links=links,
detach=detach,
auto_remove=auto_remove,
)
if detach:
logger.debug(f"Container {image_name} launched.")
elif auto_remove:
logger.debug("Container closed.")
return container
def exec_container(container, command):
debug_docker_command = f"docker exec {container.name}"
debug_docker_command += f" \\\n{command}"
logger.debug(f"DOCKER COMMAND:\n{debug_docker_command}")
docker_result = container.exec_run(command)
if docker_result.exit_code != 0:
raise Exception(
f"The command failed in the container {container.name}.\n"
f"- Command: {command}\n"
f"- Exit Code: {docker_result.exit_code}\n"
f"- Output: {docker_result.output}"
)
return docker_result
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()
try:
containers = client.containers.list(
all=True,
filters={"name": container_name},
ignore_removed=True,
)
except docker.errors.NotFound as err:
logger.debug(f"Cannot kill container {container_name}: {err}")
containers = []
for container in containers:
if container.status != "exited":
logger.debug(
"Stop container %s, based on image '%s'."
% (container.name, ",".join(container.image.tags))
)
try:
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:
logger.debug(f"Cannot kill container {container.name}: {err}")

View File

@ -0,0 +1,437 @@
import configparser
import csv
import os
import sys
import traceback
from pathlib import Path
import yaml
from loguru import logger
from odoo_openupgrade_wizard.configuration_version_dependant import (
get_base_module_folder,
get_manifest_name,
get_odoo_folder,
get_odoo_run_command,
get_server_wide_modules_upgrade,
skip_addon_path,
)
from odoo_openupgrade_wizard.tools.tools_docker import (
kill_container,
run_container,
)
from odoo_openupgrade_wizard.tools.tools_postgres import get_postgres_container
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:
"""return the relative path of the repos.yml file
of a given odoo version"""
repo_file = False
# Check if submodule path exists
version_cfg = (
ctx.obj["config"]["odoo_version_settings"][odoo_version] or {}
)
submodule_path = get_odoo_env_path(ctx, odoo_version) / "repo_submodule"
if submodule_path.exists():
repo_file = submodule_path / version_cfg["repo_file_path"]
if repo_file.exists():
return repo_file
else:
logger.warning(f"Unable to find the repo file {repo_file}.")
repo_file = get_odoo_env_path(ctx, odoo_version) / Path("repos.yml")
if not repo_file.exists():
raise Exception(f"Unable to find the repo file {repo_file}.")
return repo_file
def get_odoo_addons_path(
ctx,
odoo_env_path: Path,
migration_step: dict,
execution_context: str = None,
) -> str:
"""Return
- addons_path: a list of Path of that contains odoo module
for the current migration_step,
based on the analysis of the repos.yml file
- empty_addons_path: a list of Path of empty folders.
(without any odoo module)"""
repo_file = get_repo_file_path(ctx, migration_step["version"])
base_module_folder = get_base_module_folder(migration_step)
stream = open(repo_file, "r")
data = yaml.safe_load(stream)
data = data
addons_path = []
empty_addons_path = []
odoo_folder = get_odoo_folder(migration_step, execution_context)
for key in data.keys():
path = Path(key)
if str(path).endswith(odoo_folder):
# Add two folder for odoo folder
addons_path.append(path / Path("addons"))
addons_path.append(
path / Path(base_module_folder) / Path("addons")
)
elif skip_addon_path(migration_step, path):
pass
elif is_addons_path(ctx, odoo_env_path / path, migration_step):
addons_path.append(path)
else:
empty_addons_path.append(path)
return addons_path, empty_addons_path
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()]:
logger.debug(f" Untersuche Pfad: {folder}")
if (folder / "__init__.py").exists() and (
folder / get_manifest_name(migration_step)
).exists():
logger.info(f" ✔️ Odoo-Modul gefunden in: {folder}")
return True
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:
folder_name = "env_%s" % str(odoo_version).rjust(4, "0")
return ctx.obj["src_folder_path"] / folder_name
def get_docker_image_tag(ctx, odoo_version: float) -> str:
"""Return a docker image tag, based on project name and odoo version"""
return "odoo-openupgrade-wizard-image__%s__%s" % (
ctx.obj["config"]["project_name"],
str(odoo_version).rjust(4, "0"),
)
def get_docker_container_name(ctx, database: str, migration_step: dict) -> str:
"""Return a docker container name, based on project name, database name,
odoo version and migration step"""
return "oow-{project}-{database}-{version}-step-{step}".format(
project=ctx.obj["config"]["project_name"],
database=database,
# FIXME: version should be a string, but it is a float
version=str(migration_step["version"]).rjust(4, "0"),
step=str(migration_step["name"]).rjust(2, "0"),
)
def generate_odoo_command_options(
ctx,
migration_step: dict,
execution_context: str,
database: str,
demo: bool = False,
update: str = None,
init: str = None,
stop_after_init: bool = False,
) -> list:
"""
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"])
# Compute 'server_wide_modules'
custom_odoo_config_file = odoo_env_path / "odoo.conf"
parser = configparser.RawConfigParser()
parser.read(custom_odoo_config_file)
server_wide_modules = parser.get(
"options", "server_wide_modules", fallback=[]
)
server_wide_modules += get_server_wide_modules_upgrade(
migration_step, execution_context
)
# Compute 'addons_path'
addons_path_list, empty_addons_path_list = get_odoo_addons_path(
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(
[str(Path("/odoo_env") / x) for x in addons_path_list]
)
for empty_addons_path in empty_addons_path_list:
logger.info(
"Skipping addons path"
f" '{(odoo_env_path / empty_addons_path).resolve()}'"
" because it doesn't contain any odoo module."
)
# Compute 'log_file'
log_file_name = (
f"{ctx.obj['log_prefix']}____{migration_step['complete_name']}.log"
)
log_file_docker_path = f"/env/log/{log_file_name}"
# Build options string
options = [
"--config=/odoo_env/odoo.conf",
"--data-dir=/env/filestore/",
f"--addons-path={addons_path}",
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,
)
base_command = (
Path("/odoo_env")
/ Path(get_odoo_folder(migration_step, execution_context))
/ Path(get_odoo_run_command(migration_step))
)
options_as_string = " ".join(options)
if shell:
return f"{base_command} shell {options_as_string}"
else:
return f"{base_command} {options_as_string}"
def run_odoo(
ctx,
migration_step: dict,
detached_container: bool = False,
database: str = None,
update: str = None,
init: str = None,
stop_after_init: bool = False,
shell: bool = False,
demo: bool = False,
execution_context: str = None,
alternative_xml_rpc_port: int = False,
links: dict = {},
publish_ports: bool = False,
):
# Ensure that Postgres container exist
get_postgres_container(ctx)
logger.info(
"Launching Odoo Container (Version {version}) for {db_text}"
" in {execution_context} mode. Demo Data is {demo_text}"
" {stop_text} {init_text} {update_text}".format(
version=migration_step["version"],
db_text=database and "database '%s'" % database or "any databases",
execution_context=execution_context
or migration_step["execution_context"],
demo_text=demo and "enabled" or "disabled",
stop_text=stop_after_init and " (stop-after-init)" or "",
init_text=init and " (Init: %s)" % init or "",
update_text=update and " (Update: %s)" % update or "",
)
)
command = generate_odoo_command(
ctx,
migration_step,
execution_context,
database,
demo=demo,
update=update,
init=init,
stop_after_init=stop_after_init,
shell=shell,
)
return run_container_odoo(
ctx,
migration_step,
command,
detached_container=detached_container,
database=database,
execution_context=execution_context,
alternative_xml_rpc_port=alternative_xml_rpc_port,
links=links,
publish_ports=publish_ports,
)
def run_container_odoo(
ctx,
migration_step: dict,
command: str,
detached_container: bool = False,
database: str = None,
alternative_xml_rpc_port: int = False,
execution_context: str = None,
links: dict = {},
publish_ports: bool = False,
):
env_path = ctx.obj["env_folder_path"]
odoo_env_path = get_odoo_env_path(ctx, migration_step["version"])
host_xmlrpc_port = (
alternative_xml_rpc_port
and alternative_xml_rpc_port
or ctx.obj["config"]["odoo_host_xmlrpc_port"]
)
links.update({ctx.obj["config"]["postgres_container_name"]: "db"})
if publish_ports:
ports = {host_xmlrpc_port: DEFAULT_ODOO_HTTP_PORT}
else:
ports = {}
return run_container(
get_docker_image_tag(ctx, migration_step["version"]),
get_docker_container_name(ctx, database, migration_step),
command=command,
ports=ports,
volumes={
env_path: "/env/",
odoo_env_path: "/odoo_env/",
},
links=links,
detach=detached_container,
auto_remove=True,
)
def kill_odoo(ctx, database, migration_step: dict):
kill_container(get_docker_container_name(ctx, database, migration_step))
def execute_click_odoo_python_files(
ctx,
database: str,
migration_step: dict,
python_files: list = [],
execution_context: str = None,
):
if not python_files:
# Get post-migration python scripts to execute
script_folder = get_script_folder(ctx, migration_step)
python_files = [
Path("scripts") / Path(migration_step["complete_name"]) / Path(f)
for f in os.listdir(script_folder)
if os.path.isfile(os.path.join(script_folder, f))
and f[-3:] == ".py"
]
python_files = sorted(python_files)
base_command = generate_odoo_command(
ctx,
migration_step,
execution_context,
database,
shell=True,
)
for python_file in python_files:
command = f"/bin/bash -c 'cat /env/{python_file} | {base_command}'"
try:
logger.info(
f"Step {migration_step['complete_name']}."
f" Executing script {python_file}..."
)
run_container_odoo(
ctx,
migration_step,
command,
detached_container=False,
database=database,
)
except Exception as e:
traceback.print_exc()
logger.error(
"An error occured. Exiting. %s\n%s"
% (e, traceback.print_exception(*sys.exc_info()))
)
raise e
finally:
kill_odoo(ctx, database, migration_step)
def get_odoo_modules_from_csv(module_file_path: Path) -> list:
logger.debug(f"Reading '{module_file_path}' file...")
module_names = []
csvfile = open(module_file_path, "r")
spamreader = csv.reader(csvfile, delimiter=",", quotechar='"')
for row in spamreader:
# Try to guess that a line is not correct
if not row:
continue
if not row[0]:
continue
if " " in row[0]:
continue
if any([x.isupper() for x in row[0]]):
continue
module_names.append(row[0])
return module_names

View File

@ -6,13 +6,10 @@ from loguru import logger
# Wait for the launch of odoo instance 60 seconds
_ODOO_RPC_MAX_TRY = 60
# Timeout for odoorpc call is 24 hours
_ODOO_RPC_TIMEOUT = 86400
_ODOO_RPC_URL = "0.0.0.0"
class OdooInstance:
env = False
version = False
@ -23,51 +20,41 @@ class OdooInstance:
or ctx.obj["config"]["odoo_host_xmlrpc_port"]
)
logger.info(
"Connect to Odoo database %s via odoorpc (Port %s)... "
% (database, port)
f"Connect to database {database} via odoorpc (Port {port})..."
)
for x in range(1, _ODOO_RPC_MAX_TRY + 1):
# Connection
try:
rpc_connexion = odoorpc.ODOO(
"0.0.0.0",
_ODOO_RPC_URL,
"jsonrpc",
port=port,
timeout=_ODOO_RPC_TIMEOUT,
timeout=ctx.obj["config"]["odoo_rpc_timeout"],
)
# connexion is OK
break
except (socket.gaierror, socket.error) as e:
if x < _ODOO_RPC_MAX_TRY:
logger.debug(
"%d/%d Unable to connect to the server."
" Retrying in 1 second ..." % (x, _ODOO_RPC_MAX_TRY)
f"{x}/{_ODOO_RPC_MAX_TRY}"
" Unable to connect to the server."
" Retrying in 1 second..."
)
time.sleep(1)
else:
logger.critical(
"%d/%d Unable to connect to the server."
% (x, _ODOO_RPC_MAX_TRY)
f"{x}/{_ODOO_RPC_MAX_TRY}"
" Unable to connect to the server."
)
raise e
# Login
try:
rpc_connexion.login(
database,
"admin",
"admin",
)
rpc_connexion.login(database, "admin", "admin")
except Exception as e:
logger.error(
"Unable to connect to http://localhost:%s"
" with login %s and password %s"
% (
port,
"admin",
"admin",
)
f"Unable to connect to http://localhost:{port}"
" with login 'admin' and password 'admin."
)
raise e
@ -86,41 +73,30 @@ class OdooInstance:
return model.browse(model.create(vals))
def install_modules(self, module_names):
if type(module_names) == str:
if type(module_names) is str:
module_names = [module_names]
installed_modules = []
i = 0
for module_name in module_names:
i += 1
prefix = str(i) + "/" + str(len(module_names))
log_prefix = f"{i}/{len(module_names)} - Module '{module_name}': "
modules = self.browse_by_search(
"ir.module.module", [("name", "=", module_name)]
)
if not len(modules):
logger.error(
"%s - Module '%s': Not found." % (prefix, module_name)
)
logger.error(f"{log_prefix}': Not found.")
continue
module = modules[0]
if module.state == "installed":
logger.info(
"%s - Module %s still installed."
" skipped." % (prefix, module_name)
)
logger.info(f"{log_prefix}': still installed. Skipped.")
elif module.state == "uninstalled":
try_qty = 0
installed = False
while installed is False:
try_qty += 1
logger.info(
"%s - Module '%s': Installing ... %s"
% (
prefix,
module_name,
"(try #%d)" % try_qty if try_qty != 1 else "",
)
)
try_qty_text = f" (try #{try_qty})" if try_qty != 1 else ""
logger.info(f"{log_prefix}': Installing... {try_qty_text}")
try:
module.button_immediate_install()
installed = True
@ -130,20 +106,18 @@ class OdooInstance:
if try_qty <= 5:
sleeping_time = 2 * try_qty * 60
logger.warning(
"Error. Retrying in %d seconds.\n %s"
% (sleeping_time, e)
f"Error. Retrying in {sleeping_time} seconds."
f"\n{e}"
)
time.sleep(sleeping_time)
else:
logger.critical(
"Error after %d try. Exiting.\n %s"
% (try_qty, e)
f"Error after {try_qty} try. Exiting." f"\n{e}"
)
raise e
else:
logger.error(
"%s - Module '%s': In the %s state."
f"{log_prefix}': In the {module.state} state."
" (Unable to install)"
% (prefix, module_name, module.state)
)
return installed_modules

View File

@ -1,9 +1,12 @@
import ast
import importlib
import os
from functools import total_ordering
from pathlib import Path
import requests
from git import Repo
from git.exc import InvalidGitRepositoryError
from loguru import logger
from pygount import SourceAnalysis
@ -12,7 +15,7 @@ from odoo_openupgrade_wizard.configuration_version_dependant import (
get_coverage_relative_path,
get_openupgrade_analysis_files,
)
from odoo_openupgrade_wizard.tools_odoo import (
from odoo_openupgrade_wizard.tools.tools_odoo import (
get_odoo_addons_path,
get_odoo_env_path,
)
@ -62,8 +65,8 @@ class Analysis(object):
).strip()
elif len(splited_line) > 3:
raise ValueError(
"Incorrect value in openupgrade analysis file %s"
" for line %s" % (coverage_path, line)
"Incorrect value in openupgrade analysis"
f" file {coverage_path} for line {line}"
)
for odoo_module in filter(
@ -103,65 +106,65 @@ class Analysis(object):
last_module_version.analyse_missing_module()
def estimate_workload(self, ctx):
logger.info("Estimate workload ...")
logger.info("Estimate workload...")
for odoo_module in self.modules:
for module_version in odoo_module.module_versions.values():
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):
not_found_modules = []
logger.info(
"Analyse version %s. (First version)" % self.initial_version
)
logger.info(f"Analyse version {self.initial_version}. (First version)")
# Instanciate a new odoo_module
for module_name in module_list:
addon_path = OdooModule.get_addon_path(
ctx, module_name, self.initial_version
)
if addon_path:
repository_name = OdooModule.get_repository_name(addon_path)
if (
"%s.%s" % (repository_name, module_name)
not in self.modules
):
if f"{repository_name}.{module_name}" not in self.modules:
logger.debug(
"Discovering module '%s' in %s for version %s"
% (module_name, repository_name, self.initial_version)
f"Discovering module '{module_name}'"
f" in {repository_name}"
f" for version {self.initial_version}"
)
new_odoo_module = OdooModule(
ctx, self, module_name, repository_name
)
new_module_version = OdooModuleVersion(
self.initial_version, new_odoo_module, addon_path
)
new_odoo_module.module_versions.update(
{self.initial_version: new_module_version}
)
self.modules.append(new_odoo_module)
else:
repository_name = False
logger.error(
"Module %s not found for version %s."
% (module_name, self.initial_version)
f"Module {module_name} not found"
f" for version {self.initial_version}."
)
not_found_modules.append(module_name)
if not_found_modules:
raise ValueError(
"The modules %s have not been found in the version %s."
" Analyse can not be done. Please update your repos.yml"
" of your initial version to add repositories that"
" include the modules, then run again the command."
% (",".join(not_found_modules), self.initial_version)
new_odoo_module = OdooModule(
ctx, self, module_name, repository_name
)
new_module_version = OdooModuleVersion(
self.initial_version, new_odoo_module, addon_path
)
new_odoo_module.module_versions.update(
{self.initial_version: new_module_version}
)
self.modules.append(new_odoo_module)
def _generate_module_version_next_version(
self, ctx, previous_version, current_version
):
logger.info(
"Analyse change between %s and %s"
% (previous_version, current_version)
f"Analyse change between {previous_version} and {current_version}"
)
# Get changes between the two versions
(
@ -173,8 +176,8 @@ class Analysis(object):
)
if not apriori_module_path:
raise ValueError(
"Unable to find the path of the module %s for the version %s"
% (apriori_module_name, current_version)
f"Unable to find the path of the module {apriori_module_name}"
f" for the version {current_version}."
)
apriori_absolute_path = (
apriori_module_path
@ -198,25 +201,15 @@ class Analysis(object):
state = "renamed"
new_module_name = renamed_modules[odoo_module.name]
logger.debug(
"%s -> %s : %s renamed into %s"
% (
previous_version,
current_version,
odoo_module.name,
new_module_name,
)
f"{previous_version} -> {current_version}:"
f" {odoo_module.name} renamed into {new_module_name}"
)
elif odoo_module.name in merged_modules:
state = "merged"
new_module_name = merged_modules[odoo_module.name]
logger.debug(
"%s -> %s : %s merged into %s"
% (
previous_version,
current_version,
odoo_module.name,
new_module_name,
)
f"{previous_version} -> {current_version}:"
f" {odoo_module.name} merged into {new_module_name}"
)
# Handle new module
@ -227,9 +220,9 @@ class Analysis(object):
)
if not new_addon_path:
raise ValueError(
"The module %s has not been found in the version %s."
f"The module {new_module_name} has not been found"
f" in the version {current_version}."
" Analyse can not be done."
% (new_module_name, current_version)
)
else:
new_repository_name = OdooModule.get_repository_name(
@ -240,12 +233,9 @@ class Analysis(object):
not in self.modules
):
logger.debug(
"Discovering module '%s' in %s for version %s"
% (
new_module_name,
new_repository_name,
current_version,
)
f"Discovering module '{new_module_name}'"
f" in {new_repository_name}"
f" for version {current_version}"
)
new_odoo_module = OdooModule(
ctx, self, new_module_name, new_repository_name
@ -301,22 +291,27 @@ class Analysis(object):
odoo_modules = self.modules
return len(odoo_modules)
def workload_hour_text(self, module_type=False):
def workload(self, module_type, repository):
odoo_modules = self.modules
if module_type:
odoo_modules = [
x
for x in filter(
lambda x: x.module_type == module_type, self.modules
lambda x: x.module_type == module_type, odoo_modules
)
]
if repository:
odoo_modules = [
x
for x in filter(
lambda x: x.repository == repository, odoo_modules
)
]
else:
odoo_modules = self.modules
total = 0
for odoo_module in odoo_modules:
for module_version in list(odoo_module.module_versions.values()):
total += module_version.workload
return "%d h" % (int(round(total / 60)))
return total
@total_ordering
@ -325,36 +320,77 @@ class OdooModule(object):
self.analyse = analyse
self.name = module_name
self.repository = repository_name
self.unique_name = "%s.%s" % (repository_name, module_name)
self.unique_name = f"{repository_name}.{module_name}"
self.ignored = self.is_ignored(ctx, module_name)
self.module_versions = {}
if repository_name == "odoo/odoo":
if not repository_name:
self.module_type = "not_found"
elif repository_name == "odoo/odoo":
self.module_type = "odoo"
elif repository_name.startswith("OCA"):
self.module_type = "OCA"
elif repository_name.startswith("oca"):
self.module_type = "oca"
else:
self.module_type = "custom"
@property
def workload(self):
return sum(
round(module_version.workload)
for _, module_version in self.module_versions.items()
)
def is_ignored(self, ctx, module_name):
"""Return true if module should be ignored"""
settings = ctx.obj["config"]["workload_settings"]
return module_name in settings["ignored_module_list"]
def get_module_version(self, current_version):
res = self.module_versions.get(current_version, False)
return res
def get_odoo_apps_url(self):
logger.info(f"Searching {self.name} in the Odoo appstore...")
url = (
f"https://apps.odoo.com/apps/modules/"
f"{self.analyse.initial_version}/{self.name}/"
)
try:
response = requests.get(url)
except requests.exceptions.RequestException as err:
logger.warning(f"Error when trying to get {url}: {err}")
return False
if response.status_code == 200:
return url
return False
def get_odoo_code_search_url(self):
logger.info(f"Searching {self.name} in Odoo-Code-Search...")
url = (
f"https://odoo-code-search.com/ocs/search?"
f"q=name%3A%3D{self.name}+version%3A{self.analyse.initial_version}"
)
result = requests.get(url)
if '<td class="code">404</td>' in result.text:
return False
return url
@classmethod
def get_addon_path(cls, ctx, module_name, current_version):
"""Search the module in all the addons path of a given version
and return the addon path of the module, or False if not found.
For exemple find_repository(ctx, 'web_responsive', 12.0)
'/PATH_TO_LOCAL_ENV/src/OCA/web'
'/PATH_TO_LOCAL_ENV/src/oca/web'
"""
# Try to find the repository that contains the module
main_path = get_odoo_env_path(ctx, current_version)
addons_path = get_odoo_addons_path(
addons_path, _ = get_odoo_addons_path(
ctx,
main_path,
{"version": current_version, "execution_context": "openupgrade"},
)
for addon_path in addons_path:
if (addon_path / module_name).exists():
return addon_path
if (main_path / addon_path / module_name).exists():
return main_path / addon_path
return False
@ -363,31 +399,64 @@ class OdooModule(object):
"""Given an addons path that contains odoo modules in a folder
that has been checkouted via git, return a repository name with the
following format org_name/repo_name.
For exemple 'OCA/web' or 'odoo/odoo'
For exemple 'oca/web' or 'odoo/odoo'
"""
# TODO, make the code cleaner and more resiliant
# for the time being, the code will fail for
# - github url set with git+http...
# - gitlab url
# - if odoo code is not in a odoo folder in the repos.yml file...
if str(addon_path).endswith("odoo/odoo/addons") or str(
addon_path
).endswith("openupgrade/odoo/addons"):
odoo_odoo_addons = (
"odoo/odoo/addons",
"odoo/openerp/addons",
"openupgrade/odoo/addons",
"openupgrade/openerp/addons",
)
odoo_addons = (
"odoo/addons",
"openupgrade/addons",
)
if str(addon_path).endswith(odoo_odoo_addons):
path = addon_path.parent.parent
elif str(addon_path).endswith("odoo/addons") or str(
addon_path
).endswith("openupgrade/addons"):
elif str(addon_path).endswith(odoo_addons):
path = addon_path.parent
else:
path = addon_path
repo = Repo(str(path))
repository_name = repo.remotes[0].url.replace(
"https://github.com/", ""
try:
repo = Repo(str(path))
except InvalidGitRepositoryError as err:
logger.critical(f"{path} is not a Git Repository.")
raise err
github_url_prefixes = (
"https://github.com/",
"git@github.com:",
)
if repository_name.lower() == "oca/openupgrade":
repository_names = []
for remote in repo.remotes:
# Standardize all repository_name to lower case
repository_name = remote.url.lower()
for github_url_prefix in github_url_prefixes:
repository_name = repository_name.replace(
github_url_prefix, ""
)
if repository_name.endswith(".git"):
repository_name = repository_name[: -len(".git")]
repository_names.append(repository_name)
# find main repository_name
main_repository_name = next(
(
repo_name
for repo_name in repository_names
if repo_name.startswith("oca")
),
None,
)
if not main_repository_name:
main_repository_name = repository_names[0]
if main_repository_name == "oca/openupgrade":
return "odoo/odoo"
else:
return repository_name
return main_repository_name
def __eq__(self, other):
if isinstance(other, str):
@ -399,7 +468,15 @@ class OdooModule(object):
if self.module_type != other.module_type:
if self.module_type == "odoo":
return True
elif self.module_type == "OCA" and other.module_type == "custom":
elif self.module_type == "oca" and other.module_type in [
"custom",
"not_found",
]:
return True
elif (
self.module_type == "custom"
and other.module_type == "not_found"
):
return True
else:
return False
@ -410,7 +487,6 @@ class OdooModule(object):
class OdooModuleVersion(object):
_exclude_directories = [
"lib",
"demo",
@ -419,7 +495,7 @@ class OdooModuleVersion(object):
"doc",
"description",
]
_exclude_files = ["__openerp__.py", "__manifest__.py"]
_manifest_files = ["__openerp__.py", "__manifest__.py"]
_file_extensions = [".py", ".xml", ".js"]
@ -434,21 +510,89 @@ class OdooModuleVersion(object):
self.version = version
self.odoo_module = odoo_module
self.addon_path = addon_path
self.state = state
self.state = "ignored" if odoo_module.ignored else state
self.target_module = target_module
self.openupgrade_state = ""
self.python_code = 0
self.xml_code = 0
self.javascript_code = 0
self.workload = 0
self._workload = 0
self.analysis_file = False
self.openupgrade_model_lines = 0
self.openupgrade_field_lines = 0
self.openupgrade_xml_lines = 0
@property
def workload(self):
return int(round(self._workload))
@workload.setter
def workload(self, value):
self._workload = int(round(value))
def get_last_existing_version(self):
versions = list(self.odoo_module.module_versions.values())
return [x for x in filter(lambda x: x.addon_path, versions)][-1]
if self.odoo_module.module_type != "not_found":
versions = list(self.odoo_module.module_versions.values())
return [x for x in filter(lambda x: x.addon_path, versions)][-1]
else:
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):
settings = ctx.obj["config"]["workload_settings"]
@ -464,7 +608,7 @@ class OdooModuleVersion(object):
openupgrade_field_line_time = settings["openupgrade_field_line_time"]
openupgrade_xml_line_time = settings["openupgrade_xml_line_time"]
if self.state in ["merged", "renamed", "normal_loss"]:
if self.state in ["merged", "renamed", "normal_loss", "ignored"]:
# The module has been moved, nothing to do
return
@ -475,6 +619,9 @@ class OdooModuleVersion(object):
if self.openupgrade_state and (
self.openupgrade_state.lower().startswith("done")
or self.openupgrade_state.lower().startswith("nothing to do")
or self.openupgrade_state.lower().startswith(
"no db layout changes"
)
):
return
else:
@ -505,6 +652,8 @@ class OdooModuleVersion(object):
return
previous_module_version = self.get_last_existing_version()
if not previous_module_version:
return
self.workload = (
# Minimal port time
port_minimal_time
@ -535,7 +684,7 @@ class OdooModuleVersion(object):
if set(Path(relative_path).parts) & set(self._exclude_directories):
continue
for name in files:
if name in self._exclude_files:
if name in self._manifest_files:
continue
filename, file_extension = os.path.splitext(name)
if file_extension in self._file_extensions:
@ -586,7 +735,7 @@ class OdooModuleVersion(object):
elif line.startswith("---nothing has changed in this module"):
continue
elif line.startswith("---"):
raise Exception("comment %s not undestood" % line)
raise Exception(f"comment {line} not understood")
if line_type == "model":
self.openupgrade_model_lines += 1
@ -633,6 +782,8 @@ class OdooModuleVersion(object):
def analyse_missing_module(self):
last_existing_version = self.get_last_existing_version()
if not last_existing_version:
return
last_existing_version.analyse_size()
def get_bg_color(self):
@ -646,6 +797,9 @@ class OdooModuleVersion(object):
or self.openupgrade_state.lower().startswith(
"nothing to do"
)
or self.openupgrade_state.lower().startswith(
"no db layout changes"
)
):
return "lightgreen"
else:
@ -653,7 +807,7 @@ class OdooModuleVersion(object):
return "lightgreen"
else:
# The module doesn't exist in the current version
if self.state in ["merged", "renamed", "normal_loss"]:
if self.state in ["merged", "renamed", "normal_loss", "ignored"]:
# Normal case, the previous version has been renamed
# or merged
return "lightgray"
@ -664,6 +818,8 @@ class OdooModuleVersion(object):
return "red"
elif self.version != self.odoo_module.analyse.final_version:
return "lightgray"
elif self.odoo_module.module_type == "not_found":
return "lightgray"
else:
return "orange"
@ -673,20 +829,17 @@ class OdooModuleVersion(object):
self.odoo_module.module_type == "odoo"
and self.version != self.odoo_module.analyse.initial_version
):
if self.openupgrade_state.lower().startswith(
"done"
) or self.openupgrade_state.lower().startswith(
"nothing to do"
):
if self.openupgrade_state:
return self.openupgrade_state
else:
return "To analyse"
return ""
else:
if self.state == "merged":
return "Merged into %s" % self.target_module
return f"Merged into {self.target_module}"
elif self.state == "renamed":
return "Renamed into %s" % self.target_module
return f"Renamed into {self.target_module}"
elif self.state == "ignored":
return "Ignored"
elif self.state == "normal_loss":
return ""
@ -694,10 +847,11 @@ class OdooModuleVersion(object):
# A core module disappeared and has not been merged
# or renamed
return "Module lost"
elif self.version != self.odoo_module.analyse.final_version:
return "Unported"
else:
return (
"To port from %s"
% self.get_last_existing_version().version
)
last_existing_version = self.get_last_existing_version()
if not last_existing_version:
return "Unknown"
elif self.version != self.odoo_module.analyse.final_version:
return "Unported"
else:
return f"To port from {last_existing_version.version}"

View File

@ -0,0 +1,317 @@
import os
import shlex
import time
from pathlib import Path
import docker
from loguru import logger
from odoo_openupgrade_wizard.tools.tools_docker import (
exec_container,
get_docker_client,
run_container,
)
from odoo_openupgrade_wizard.tools.tools_system import get_script_folder
def get_postgres_container(ctx):
client = get_docker_client()
image_name = ctx.obj["config"]["postgres_image_name"]
container_name = ctx.obj["config"]["postgres_container_name"]
volume_name = ctx.obj["config"]["postgres_volume_name"]
# Check if container exists
containers = client.containers.list(
all=True,
filters={"name": container_name},
ignore_removed=True,
)
if containers:
container = containers[0]
if container.status == "exited":
logger.warning(
f"Found container {container_name} in a exited status."
" Removing it..."
)
if container.status != "removed":
container.remove()
container.wait(condition="removed")
else:
return container
# Check if volume exists
try:
client.volumes.get(volume_name)
logger.debug(f"Recovering existing PostgreSQL volume: {volume_name}")
except docker.errors.NotFound:
logger.info(f"Creating Postgres volume: {volume_name}")
client.volumes.create(volume_name)
command = "postgres"
postgres_extra_settings = ctx.obj["config"].get("postgres_extra_settings")
if postgres_extra_settings:
for key, value in postgres_extra_settings.items():
command += f" -c {key}={value}"
logger.info(f"Launching PostgreSQL Container. (Image {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(
image_name,
container_name,
command=command,
environments=environments,
volumes={
# Data volume
volume_name: "/var/lib/postgresql/data/pgdata/",
# main folder path (to pass files)
ctx.obj["env_folder_path"].absolute(): "/env/",
},
detach=True,
# ports={"5432/tcp": 5432}, # <--- Port-Mapping hinzugefügt
)
# TODO, improve me.
# Postgres container doesn't seems available immediately.
# check in odoo container, i remember that there is
# some script to do the job
time.sleep(5)
return container
def execute_sql_file(ctx, database, sql_file):
container = get_postgres_container(ctx)
# Recreate relative path to make posible to
# call psql in the container
if str(ctx.obj["env_folder_path"]) not in str(sql_file):
raise Exception(
f"The SQL file {sql_file} is not in the"
f" main directory {ctx.obj['env_folder_path']} available"
" in the PostgreSQL container."
)
relative_path = Path(
str(sql_file).replace(str(ctx.obj["env_folder_path"]), ".")
)
container_path = Path("/env/") / relative_path
command = (
"psql --username=odoo --dbname={database} --file {file_path}"
).format(database=database, file_path=container_path)
logger.info(
f"Executing the script '{relative_path}' in PostgreSQL container"
f" on database {database}"
)
exec_container(container, command)
def execute_sql_request(ctx, request, database="postgres"):
psql_args = ("--tuples-only",)
output = execute_psql_command(ctx, request, database, psql_args)
lines = output.split("\n")
result = []
for line in lines:
if not line:
continue
result.append([x.strip() for x in line.split("|")])
return result
def execute_psql_command(
ctx, request: str, database: str = None, psql_args: tuple = ()
):
"""Execute psql request in postgres container with psql_args on database"""
container = get_postgres_container(ctx)
command = (
"psql"
" --username=odoo"
f" --dbname={database or 'postgres'}"
f" --command {shlex.quote(request)}"
f" {' '.join(psql_args)}"
)
logger.debug(
f"Executing the following command in PostgreSQL container\n{command}"
)
docker_result = exec_container(container, command)
return docker_result.output.decode("utf-8")
def check_db_exist(ctx, database: str, raise_exception=False):
"""
- 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.
- Check if the database exist.
- if doesn't exists and state == 'present', create it.
- if exists and state == 'absent', drop it.
"""
if state == "present":
if check_db_exist(ctx, database):
return
if template:
logger.info(f'Copy database "{template}" into "{database}"...')
request = (
f'CREATE DATABASE "{database}" WITH TEMPLATE "{template}";'
)
else:
logger.info(f"Create database '{database}'...")
request = f'CREATE DATABASE "{database}" OWNER odoo;'
execute_psql_command(ctx, request)
else:
if not check_db_exist(ctx, database):
return
logger.info(f'Drop database "{database}"...')
request = f'DROP DATABASE "{database}";'
execute_psql_command(ctx, request)
def execute_sql_files_pre_migration(
ctx, database: str, migration_step: dict, sql_files: list = []
):
ensure_database(ctx, database, state="present")
if not sql_files:
script_folder = get_script_folder(ctx, migration_step)
sql_files = [
script_folder / Path(f)
for f in os.listdir(script_folder)
if os.path.isfile(os.path.join(script_folder, f))
and f[-4:] == ".sql"
]
sql_files = sorted(sql_files)
for sql_file in sql_files:
execute_sql_file(ctx, database, sql_file)
def chown_to_local_user(ctx, filepath: os.PathLike):
"""Chown a filepath in the postgres container to the local user"""
container = get_postgres_container(ctx)
user_uid = os.getuid()
command = "chown -R {uid}:{uid} {filepath}".format(
uid=user_uid, filepath=filepath
)
logger.debug(
f"Executing the following command in PostgreSQL container:\n{command}"
)
chown_result = exec_container(container, command)
return chown_result.output.decode("utf8")
def execute_pg_dump(
ctx,
database: str,
dumpformat: str,
filename: str,
pg_dump_args="--no-owner",
):
"""Execute pg_dump command on the postgres container and dump the
result to dumpfile.
"""
if pg_dump_args and not isinstance(pg_dump_args, str):
pg_dump_args = " ".join(pg_dump_args)
container = get_postgres_container(ctx)
# Generate path for the output file
filepath = Path("/env") / Path(filename)
# Generate pg_dump command
command = (
"pg_dump"
" --username=odoo"
" --format {dumpformat}"
" --file {filepath}"
" {pg_dump_args}"
" {database}"
).format(
dumpformat=dumpformat,
filepath=filepath,
database=database,
pg_dump_args=pg_dump_args,
)
logger.debug(
f"Executing the following command in PostgreSQL container:\n{command}"
)
pg_dump_result = exec_container(container, command)
chown_to_local_user(ctx, filepath)
return pg_dump_result.output.decode("utf8")
def execute_pg_restore(
ctx,
filepath: Path,
database: str,
database_format: str,
):
"""Execute pg_restore command on the postgres container"""
container = get_postgres_container(ctx)
ensure_database(ctx, database, "absent")
ensure_database(ctx, database, "present")
if database_format == "p":
command = (
"psql"
f" --file='{Path('/env') / filepath}'"
" --username odoo"
f" --dbname={database}"
)
else:
command = (
"pg_restore"
f" {Path('/env') / filepath}"
f" --dbname={database}"
" --schema=public"
" --username=odoo"
" --no-owner"
f" --format {database_format}"
)
logger.info(f"Restoring database '{database}'...")
pg_dump_result = exec_container(container, command)
return pg_dump_result.output.decode("utf8")

View File

@ -0,0 +1,188 @@
import argparse
import os
import shutil
import subprocess
import tarfile
from pathlib import Path
import importlib_resources
from git_aggregator import main as gitaggregate_cmd
from git_aggregator.utils import working_directory_keeper
from jinja2 import Template
from loguru import logger
from plumbum.cmd import chmod, mkdir
from plumbum.commands.processes import ProcessExecutionError
def get_script_folder(ctx, migration_step: dict) -> Path:
return ctx.obj["script_folder_path"] / migration_step["complete_name"]
def ensure_folder_writable(folder_path: Path):
logger.info(f"Make writable the directory '{folder_path}'")
try:
chmod(["--silent", "--recursive", "o+w", str(folder_path)])
except ProcessExecutionError:
pass
def ensure_folder_exists(
folder_path: Path, mode: str = "755", git_ignore_content: bool = False
):
"""Create a local folder.
- directory is created if it doesn't exist.
- mode is applied if defined.
- a log is done at INFO level.
"""
if not folder_path.exists():
cmd = ["-p", folder_path]
cmd = ["-m", mode] + cmd
logger.info(f"Creating directory '{folder_path}'...")
mkdir(cmd)
if git_ignore_content:
ensure_file_exists_from_template(
folder_path / Path(".gitignore"),
".gitignore.j2",
)
def ensure_file_exists_from_template(
file_path: Path, template_name: str, **args
):
template_folder = (
importlib_resources.files("odoo_openupgrade_wizard") / "templates"
)
template_path = template_folder / template_name
if not template_path.exists():
logger.warning(
f"Unable to generate {file_path},"
f" the template {template_name} has not been found."
f" If it's a Dockerfile,"
f" you should maybe contribute to that project ;-)"
)
return
text = template_path.read_text()
template = Template(text)
output = template.render(args)
if file_path.exists():
# Check if content is different
with open(file_path, "r") as file:
data = file.read()
file.close()
if data == output:
return
log_text = f"Updating file '{file_path}' from template..."
else:
log_text = f"Creating file '{file_path}' from template..."
with open(file_path, "w") as f:
logger.info(log_text)
print(output, file=f)
def git_aggregate(folder_path: Path, config_path: Path, jobs: int, env_file: str = ".env"):
# 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(
command="aggregate",
config=str(config_path),
jobs=jobs,
dirmatch=None,
do_push=False,
expand_env=True,
env_file=env_file,
force=True,
)
with working_directory_keeper:
os.chdir(folder_path)
logger.info(
f"Gitaggregate source code for {config_path}."
" This can take a while..."
)
gitaggregate_cmd.run(args)
def get_local_user_id():
return os.getuid()
def execute_check_output(args_list, working_directory=None):
logger.debug(f"Execute {' '.join(args_list)}")
subprocess.check_output(args_list, cwd=working_directory)
def dump_filestore(
ctx,
database: str,
destpath: os.PathLike,
copyformat: str = "d",
):
"""Copy filestore of database to destpath using copyformat.
copyformat can be 'd' for directory, a normal copy, or 't' for a
copy into a tar achive, or 'tgz' to copy to a compressed tar file.
"""
valid_format = ("d", "t", "tgz", "txz")
if copyformat not in valid_format:
raise ValueError(
f"copyformat should be one of the following {valid_format}"
)
filestore_folder_path = ctx.obj["env_folder_path"] / "filestore/filestore"
filestore_path = filestore_folder_path / database
if copyformat == "d":
shutil.copytree(filestore_path, destpath)
elif copyformat.startswith("t"):
wmode = "w"
if copyformat.endswith("gz"):
wmode += ":gz"
elif copyformat.endswith("xz"):
wmode += ":xz"
with tarfile.open(destpath, wmode) as tar:
tar.add(filestore_path, arcname="filestore")
def restore_filestore(
ctx,
database: str,
src_path: Path,
file_format: str = "d",
):
"""Restore filestore of database from src_path using file_format.
file_format can be :
'd' for 'directory': a normal copy,
't' / 'tgz' for 'tar': an extraction from a tar achive
"""
valid_format = ("d", "t", "tgz")
if file_format not in valid_format:
raise ValueError(
f"file_format should be one of the following {valid_format}"
)
filestore_path = (
ctx.obj["env_folder_path"] / "filestore/filestore" / database
)
logger.info(f"Restoring filestore of '{database}'...")
if file_format == "d":
shutil.copytree(src_path, filestore_path)
else: # works for "t" and "tgz"
tar = tarfile.open(src_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)

View File

@ -1,101 +0,0 @@
import docker
from loguru import logger
def get_docker_client():
return docker.from_env()
def pull_image(image_name):
client = get_docker_client()
client.images.pull(image_name)
def build_image(path, tag):
logger.debug(
"Building image named based on %s/Dockerfile."
" This can take a big while ..." % (path)
)
debug_docker_command = "docker build %s --tag %s" % (path, tag)
logger.debug("DOCKER COMMAND:\n %s" % debug_docker_command)
docker_client = get_docker_client()
image = docker_client.images.build(
path=str(path),
tag=tag,
)
logger.debug("Image build.")
return image
def run_container(
image_name,
container_name,
command=None,
ports={},
volumes={},
environments={},
links={},
detach=False,
auto_remove=False,
):
client = get_docker_client()
if not client.images.list(filters={"reference": image_name}):
raise Exception(
"The image %s is not available on your system."
" Did you run 'odoo-openupgrade-wizard docker-build' ?"
% image_name
)
logger.debug("Launching Docker container named %s ..." % (image_name))
debug_docker_command = "docker run --name %s\\\n" % (container_name)
for k, v in ports.items():
debug_docker_command += " --publish {k}:{v}\\\n".format(k=k, v=v)
for k, v in volumes.items():
debug_docker_command += " --volume {k}:{v}\\\n".format(
k=str(k), v=str(v)
)
for k, v in environments.items():
debug_docker_command += " --env {k}={v}\\\n".format(k=k, v=v)
for k, v in links.items():
debug_docker_command += " --link {k}:{v}\\\n".format(k=k, v=v)
if auto_remove:
debug_docker_command += " --rm"
if detach:
debug_docker_command += " --detach"
debug_docker_command += " %s" % (image_name)
if command:
debug_docker_command += " \\\n%s" % (command)
logger.debug("DOCKER COMMAND:\n %s" % debug_docker_command)
container = client.containers.run(
image_name,
name=container_name,
command=command,
ports={x: y for y, x in ports.items()},
volumes=[str(k) + ":" + str(v) for k, v in volumes.items()],
environment=environments,
links=links,
detach=detach,
auto_remove=auto_remove,
)
if detach:
logger.debug("Container %s launched." % image_name)
elif auto_remove:
logger.debug("Container closed.")
return container
def kill_container(container_name):
client = get_docker_client()
containers = client.containers.list(
all=True,
filters={"name": container_name},
)
for container in containers:
logger.debug(
"Stop container %s, based on image '%s'."
% (container.name, ",".join(container.image.tags))
)
container.stop()

View File

@ -1,328 +0,0 @@
import configparser
import csv
import os
import sys
import traceback
from pathlib import Path
import yaml
from loguru import logger
from odoo_openupgrade_wizard.configuration_version_dependant import (
get_base_module_folder,
get_odoo_folder,
get_odoo_run_command,
get_server_wide_modules_upgrade,
skip_addon_path,
)
from odoo_openupgrade_wizard.tools_docker import kill_container, run_container
from odoo_openupgrade_wizard.tools_postgres import get_postgres_container
from odoo_openupgrade_wizard.tools_system import get_script_folder
def get_odoo_addons_path(
ctx, root_path: Path, migration_step: dict, execution_context: str = False
) -> str:
repo_file = get_odoo_env_path(ctx, migration_step["version"]) / Path(
"repos.yml"
)
base_module_folder = get_base_module_folder(migration_step)
stream = open(repo_file, "r")
data = yaml.safe_load(stream)
data = data
addons_path = []
for key in data.keys():
path = root_path / Path(key)
if str(path).endswith(
get_odoo_folder(migration_step, execution_context)
):
# Add two folder for odoo folder
addons_path.append(path / Path("addons"))
addons_path.append(
path / Path(base_module_folder) / Path("addons")
)
elif skip_addon_path(migration_step, path):
pass
else:
addons_path.append(path)
return addons_path
def get_odoo_env_path(ctx, odoo_version: float) -> Path:
folder_name = "env_%s" % str(odoo_version).rjust(4, "0")
return ctx.obj["src_folder_path"] / folder_name
def get_docker_image_tag(ctx, odoo_version: float) -> str:
"""Return a docker image tag, based on project name and odoo version"""
return "odoo-openupgrade-wizard-image__%s__%s" % (
ctx.obj["config"]["project_name"],
str(odoo_version).rjust(4, "0"),
)
def get_docker_container_name(ctx, migration_step: dict) -> str:
"""Return a docker container name, based on project name,
odoo version and migration step"""
return "odoo-openupgrade-wizard-container__%s__%s__step-%s" % (
ctx.obj["config"]["project_name"],
str(migration_step["version"]).rjust(4, "0"),
str(migration_step["name"]).rjust(2, "0"),
)
def generate_odoo_command(
ctx,
migration_step: dict,
execution_context: str,
database: str,
update: str,
init: str,
stop_after_init: bool,
shell: bool,
demo: bool,
) -> str:
database_cmd = database and "--database %s" % database or ""
update_cmd = update and "--update %s" % update or ""
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(get_odoo_folder(migration_step, execution_context))
/ Path(get_odoo_run_command(migration_step))
)
result = (
f" {command}"
f" {shell_cmd}"
f" --config /odoo_env/_auto_generated_odoo.cfg"
f" {demo_cmd}"
f" {database_cmd}"
f" {update_cmd}"
f" {init_cmd}"
f" {stop_after_init_cmd}"
)
return result
def generate_odoo_config_file(
ctx, migration_step, log_file, execution_context
):
"""Create a config file name _auto_generated_odoo.cfg
in the according environment (defined by migration_step)
This configuration file is a merge of the odoo.cfg file that can
contain custom values, and the values required to run the docker container.
"""
odoo_env_path = get_odoo_env_path(ctx, migration_step["version"])
custom_odoo_config_file = odoo_env_path / "odoo.cfg"
auto_generated_odoo_config_file = (
odoo_env_path / "_auto_generated_odoo.cfg"
)
parser = configparser.RawConfigParser()
# Read custom file
parser.read(custom_odoo_config_file)
# compute addons_path
addons_path = ",".join(
[
str(x)
for x in get_odoo_addons_path(
ctx, Path("/odoo_env"), migration_step, execution_context
)
]
)
# compute server wides modules
server_wide_modules = parser.get(
"options", "server_wide_modules", fallback=[]
)
server_wide_modules += get_server_wide_modules_upgrade(migration_step)
# Add required keys
if "options" not in parser:
parser.add_section("options")
parser.set("options", "db_host", "db")
parser.set("options", "db_port", 5432)
parser.set("options", "db_user", "odoo")
parser.set("options", "db_password", "odoo")
parser.set("options", "workers", 0)
parser.set("options", "data_dir", "/env/filestore/")
parser.set("options", "logfile", log_file)
parser.set("options", "addons_path", addons_path)
if server_wide_modules:
parser.set(
"options", "server_wide_modules", ",".join(server_wide_modules)
)
parser.write(open(auto_generated_odoo_config_file, "w"))
def run_odoo(
ctx,
migration_step: dict,
detached_container: bool = False,
database: str = False,
update: str = False,
init: str = False,
stop_after_init: bool = False,
shell: bool = False,
demo: bool = False,
execution_context: str = False,
alternative_xml_rpc_port: int = False,
links: dict = {},
):
# Ensure that Postgres container exist
get_postgres_container(ctx)
logger.info(
"Launching Odoo Container (Version {version}) for {db_text}"
" in {execution_context} mode. Demo Data is {demo_text}"
" {stop_text} {init_text} {update_text}".format(
version=migration_step["version"],
db_text=database and "database '%s'" % database or "any databases",
execution_context=execution_context
or migration_step["execution_context"],
demo_text=demo and "enabled" or "disabled",
stop_text=stop_after_init and " (stop-after-init)" or "",
init_text=init and " (Init : %s)" % init or "",
update_text=update and " (Update : %s)" % update or "",
)
)
env_path = ctx.obj["env_folder_path"]
odoo_env_path = get_odoo_env_path(ctx, migration_step["version"])
log_file = "/env/log/{}____{}.log".format(
ctx.obj["log_prefix"], migration_step["complete_name"]
)
generate_odoo_config_file(ctx, migration_step, log_file, execution_context)
command = generate_odoo_command(
ctx,
migration_step,
execution_context,
database=database,
update=update,
init=init,
stop_after_init=stop_after_init,
shell=shell,
demo=demo,
)
host_xmlrpc_port = (
alternative_xml_rpc_port
and alternative_xml_rpc_port
or ctx.obj["config"]["odoo_host_xmlrpc_port"]
)
links.update({ctx.obj["config"]["postgres_container_name"]: "db"})
return run_container(
get_docker_image_tag(ctx, migration_step["version"]),
get_docker_container_name(ctx, migration_step),
command=command,
ports={
host_xmlrpc_port: 8069,
},
volumes={
env_path: "/env/",
odoo_env_path: "/odoo_env/",
},
links=links,
detach=detached_container,
auto_remove=True,
)
def kill_odoo(ctx, migration_step: dict):
kill_container(get_docker_container_name(ctx, migration_step))
def execute_click_odoo_python_files(
ctx,
database: str,
migration_step: dict,
python_files: list = [],
execution_context: str = False,
):
if not python_files:
# Get post-migration python scripts to execute
script_folder = get_script_folder(ctx, migration_step)
python_files = [
Path("scripts") / Path(migration_step["complete_name"]) / Path(f)
for f in os.listdir(script_folder)
if os.path.isfile(os.path.join(script_folder, f))
and f[-3:] == ".py"
]
python_files = sorted(python_files)
# Prepare data information for docker
links = {ctx.obj["config"]["postgres_container_name"]: "db"}
env_path = ctx.obj["env_folder_path"]
odoo_env_path = get_odoo_env_path(ctx, migration_step["version"])
# Generate odoo config file
log_file = "/env/log/{}____{}__post_migration.log".format(
ctx.obj["log_prefix"], migration_step["complete_name"]
)
generate_odoo_config_file(ctx, migration_step, log_file, execution_context)
for python_file in python_files:
# TODO, check if we should set python2 for old version of Odoo
# or just 'python'
command = (
"click-odoo"
" --database {database}"
" --config /odoo_env/_auto_generated_odoo.cfg"
" /env/{python_file}"
).format(
database=database,
python_file=str(python_file),
)
try:
logger.info(
"Executing script %s / %s"
% (migration_step["complete_name"], python_file)
)
run_container(
get_docker_image_tag(ctx, migration_step["version"]),
get_docker_container_name(ctx, migration_step),
command=command,
ports={},
volumes={
env_path: "/env/",
odoo_env_path: "/odoo_env/",
},
links=links,
detach=False,
auto_remove=True,
)
except Exception as e:
traceback.print_exc()
logger.error(
"An error occured. Exiting. %s\n%s"
% (e, traceback.print_exception(*sys.exc_info()))
)
raise e
finally:
kill_odoo(ctx, migration_step)
def get_odoo_modules_from_csv(module_file_path: Path) -> list:
logger.debug("Reading '%s' file ..." % module_file_path)
module_names = []
csvfile = open(module_file_path, "r")
spamreader = csv.reader(csvfile, delimiter=",", quotechar='"')
for row in spamreader:
# Try to guess that a line is not correct
if not row:
continue
if not row[0]:
continue
if " " in row[0]:
continue
if any([x.isupper() for x in row[0]]):
continue
module_names.append(row[0])
return module_names

View File

@ -1,152 +0,0 @@
import os
import time
from pathlib import Path
from loguru import logger
from odoo_openupgrade_wizard.tools_docker import (
get_docker_client,
run_container,
)
from odoo_openupgrade_wizard.tools_system import get_script_folder
def get_postgres_container(ctx):
client = get_docker_client()
image_name = ctx.obj["config"]["postgres_image_name"]
container_name = ctx.obj["config"]["postgres_container_name"]
containers = client.containers.list(filters={"name": container_name})
if containers:
return containers[0]
logger.info("Launching Postgres Container. (Image %s)" % image_name)
container = run_container(
image_name,
container_name,
environments={
"POSTGRES_USER": "odoo",
"POSTGRES_PASSWORD": "odoo",
"POSTGRES_DB": "postgres",
"PGDATA": "/var/lib/postgresql/data/pgdata",
},
volumes={
ctx.obj["env_folder_path"]: "/env/",
ctx.obj[
"postgres_folder_path"
]: "/var/lib/postgresql/data/pgdata/",
},
detach=True,
)
# TODO, improve me.
# Postgres container doesn't seems available immediately.
# check in odoo container, i remember that there is
# some script to do the job
time.sleep(5)
return container
def execute_sql_file(ctx, database, sql_file):
container = get_postgres_container(ctx)
# Recreate relative path to make posible to
# call psql in the container
if str(ctx.obj["env_folder_path"]) not in str(sql_file):
raise Exception(
"The SQL file %s is not in the"
" main folder %s available"
" in the postgres container."
% (sql_file, ctx.obj["env_folder_path"])
)
relative_path = Path(
str(sql_file).replace(str(ctx.obj["env_folder_path"]), ".")
)
container_path = Path("/env/") / relative_path
docker_command = (
"psql" " --username=odoo" " --dbname={database}" " --file {file_path}"
).format(database=database, file_path=container_path)
logger.info(
"Executing the script '%s' in postgres container"
" on database %s" % (relative_path, database)
)
docker_result = container.exec_run(docker_command)
if docker_result.exit_code != 0:
raise Exception(
"The script '%s' failed on database %s. Exit Code : %d"
% (relative_path, database, docker_result.exit_code)
)
def execute_sql_request(ctx, request, database="postgres"):
container = get_postgres_container(ctx)
docker_command = (
"psql"
" --username=odoo"
" --dbname={database}"
" --tuples-only"
' --command "{request}"'
).format(database=database, request=request)
logger.debug(
"Executing the following command in postgres container"
" on database %s \n %s" % (database, request)
)
docker_result = container.exec_run(docker_command)
if docker_result.exit_code != 0:
raise Exception(
"Request %s failed on database %s. Exit Code : %d"
% (request, database, docker_result.exit_code)
)
lines = docker_result.output.decode("utf-8").split("\n")
result = []
for line in lines:
if not line:
continue
result.append([x.strip() for x in line.split("|")])
return result
def ensure_database(ctx, database: str, state="present"):
"""
- Connect to postgres container.
- Check if the database exist.
- if doesn't exists and state == 'present', create 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 [database] in result:
return
logger.info("Create database '%s' ..." % database)
request = "CREATE DATABASE {database} owner odoo;".format(
database=database
)
execute_sql_request(ctx, request)
else:
if [database] not in result:
return
logger.info("Drop database '%s' ..." % database)
request = "DROP DATABASE {database};".format(database=database)
execute_sql_request(ctx, request)
def execute_sql_files_pre_migration(
ctx, database: str, migration_step: dict, sql_files: list = []
):
ensure_database(ctx, database, state="present")
if not sql_files:
script_folder = get_script_folder(ctx, migration_step)
sql_files = [
script_folder / Path(f)
for f in os.listdir(script_folder)
if os.path.isfile(os.path.join(script_folder, f))
and f[-4:] == ".sql"
]
sql_files = sorted(sql_files)
for sql_file in sql_files:
execute_sql_file(ctx, database, sql_file)

View File

@ -1,91 +0,0 @@
import argparse
import os
from pathlib import Path
from git_aggregator import main as gitaggregate_cmd
from git_aggregator.utils import working_directory_keeper
from jinja2 import Template
from loguru import logger
from plumbum.cmd import chmod, mkdir
from plumbum.commands.processes import ProcessExecutionError
from odoo_openupgrade_wizard import templates
def get_script_folder(ctx, migration_step: dict) -> Path:
return ctx.obj["script_folder_path"] / migration_step["complete_name"]
def ensure_folder_writable(folder_path: Path):
logger.info("Make writable the folder '%s'" % folder_path)
try:
chmod(["--silent", "--recursive", "o+w", str(folder_path)])
except ProcessExecutionError:
pass
def ensure_folder_exists(
folder_path: Path, mode: str = "755", git_ignore_content: bool = False
):
"""Create a local folder.
- directory is created if it doesn't exist.
- mode is applied if defined.
- a log is done at INFO level.
"""
if not folder_path.exists():
cmd = ["--parents", folder_path]
cmd = ["--mode", mode] + cmd
logger.info("Creating folder '%s' ..." % (folder_path))
mkdir(cmd)
if git_ignore_content:
ensure_file_exists_from_template(
folder_path / Path(".gitignore"),
templates.GIT_IGNORE_CONTENT,
)
def ensure_file_exists_from_template(
file_path: Path, template_name: str, **args
):
template = Template(template_name)
output = template.render(args)
if file_path.exists():
# Check if content is different
with open(file_path, "r") as file:
data = file.read()
file.close()
if data == output:
return
log_text = "Updating file '%s' from template ..." % (file_path)
else:
log_text = "Creating file '%s' from template ..." % (file_path)
with open(file_path, "w") as f:
logger.info(log_text)
f.write(output)
f.close()
def git_aggregate(folder_path: Path, config_path: Path, jobs: int):
args = argparse.Namespace(
command="aggregate",
config=str(config_path),
jobs=jobs,
dirmatch=None,
do_push=False,
expand_env=False,
env_file=None,
force=True,
)
with working_directory_keeper:
os.chdir(folder_path)
logger.info(
"Gitaggregate source code for %s. This can take a while ..."
% config_path
)
gitaggregate_cmd.run(args)

1933
poetry.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,51 +1,63 @@
[tool.poetry]
[project]
name = "odoo-openupgrade-wizard"
version = "0.0.2"
version = "1.3.1.1"
description = "CLI tool to manage Odoo Major Upgrades"
authors = [
"GRAP, Groupement Régional Alimentaire de Proximité",
"Coop IT Easy SCRLfs",
{name = "Sylvain LE GAL", email = "sylvain.legal@grap.coop"},
{name = "Rémy TAYMANS", email = "remy@coopiteasy.be"},
{name = "Simon MAILLARD", email = "simon@ogesta.fr"},
]
maintainers = [
"Sylvain LE GAL",
{name = "Sylvain LE GAL", email = "sylvain.legal@grap.coop"},
{name = "Rémy TAYMANS", email = "remy@coopiteasy.be"},
]
license = "AGPLv3+"
readme = "README.md"
repository = "https://gitlab.com/odoo-openupgrade-wizard/odoo-openupgrade-wizard"
keywords = ["cli", "odoo", "openupgrade"]
classifiers = [
"License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)",
"Intended Audience :: Developers",
"Development Status :: 2 - Pre-Alpha",
"Development Status :: 4 - Beta",
"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.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",
]
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)",
]
[tool.poetry.scripts]
odoo-openupgrade-wizard = "odoo_openupgrade_wizard.cli:main"
[project.urls]
Repository = "https://gitlab.com/odoo-openupgrade-wizard/odoo-openupgrade-wizard"
[tool.poetry.dependencies]
python = ">=3.6.3,<4.0.0"
click = "^7.0"
click-loglevel = "^0.4"
odoorpc = "^0.8"
loguru = "^0.6"
plumbum = "^1.7"
single-source = "^0.3"
git-aggregator = "^2.1"
docker = "^5.0"
pyyaml = "5.4.1"
GitPython = "^3.1"
pygount = "^1.4"
[project.scripts]
oow = "odoo_openupgrade_wizard.cli.cli:main"
odoo-openupgrade-wizard = "odoo_openupgrade_wizard.cli.cli:main"
[tool.poetry.dev-dependencies]
[tool.poetry.group.dev.dependencies]
pytest = [
{version = "<=6.1.2", python = "<3.10"},
{version = ">=6.2.5", python = ">=3.10"}
]
pytest-mock = "3.6.1"
pytest-cov = "*"
safety = "*"
pylint = "*"
@ -62,3 +74,36 @@ line-length = 79
[tool.isort]
profile = "black"
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

@ -6,7 +6,8 @@ import yaml
from click.testing import CliRunner
from plumbum.cmd import mkdir
from odoo_openupgrade_wizard.cli import main
from odoo_openupgrade_wizard.cli.cli import main
from odoo_openupgrade_wizard.tools.tools_postgres import execute_sql_request
_logger = logging.getLogger()
@ -24,20 +25,43 @@ def move_to_test_folder():
if os.getcwd().endswith("tests/data/output"):
return
test_folder_path = Path("tests/data/output")
mkdir([test_folder_path, "--parents"])
mkdir(["-p", test_folder_path])
os.chdir(test_folder_path)
def cli_runner_invoke(cmd):
result = CliRunner().invoke(
main,
cmd,
catch_exceptions=False,
)
if not result.exit_code == 0:
_logger.error("exit_code: %s" % result.exit_code)
_logger.error("output: %s" % result.output)
assert result.exit_code == 0
def cli_runner_invoke(cmd, expect_success=True):
if not cmd:
cmd = []
cmd = ["--log-level=DEBUG"] + cmd
try:
result = CliRunner().invoke(
main,
args=cmd,
catch_exceptions=False,
)
if expect_success:
if not result.exit_code == 0:
_logger.error("exit_code: %s" % result.exit_code)
_logger.error("output: %s" % result.output)
assert result.exit_code == 0
else:
assert result.exit_code != 0
except Exception as exception:
if Path("log").exists():
log_files = [
Path("log") / Path(f)
for f in os.listdir(Path("log"))
if f[-4:] == ".log"
]
for log_file in log_files:
print("============================")
print(log_file)
print("============================")
_f = open(log_file)
print(_f.read())
_f.close()
print("============================")
raise exception
def build_ctx_from_config_file() -> dict:
@ -50,7 +74,9 @@ def build_ctx_from_config_file() -> dict:
setattr(ctx, "obj", {})
config_file_path = env_folder_path / "config.yml"
if not config_file_path.exists():
raise Exception("Configuration file not found %s" % config_file_path)
raise Exception(
"Configuration file not found %s" % config_file_path.absolute()
)
with open(config_file_path) as file:
config = yaml.safe_load(file)
ctx.obj["config"] = config
@ -58,7 +84,23 @@ def build_ctx_from_config_file() -> dict:
ctx.obj["env_folder_path"] = env_folder_path
ctx.obj["src_folder_path"] = env_folder_path / Path("src")
ctx.obj["postgres_folder_path"] = env_folder_path / Path(
"postgres_data/data"
)
return ctx
def mock_odoo_rpc_url(mocker):
"""Mock the _ODOO_RPC_URL for testing purpose"""
odoo_rpc_url = os.environ.get("ODOO_RPC_URL")
if odoo_rpc_url:
mocker.patch(
"odoo_openupgrade_wizard.tools.tools_odoo_instance._ODOO_RPC_URL",
odoo_rpc_url,
)
def assert_database(ctx, db_name, state):
request = "select datname FROM pg_database WHERE datistemplate = false;"
results = execute_sql_request(ctx, request)
if state == "present":
assert [db_name] in results
else:
assert [db_name] not in results

View File

@ -6,18 +6,19 @@ from . import cli_runner_invoke, move_to_test_folder
def test_cli_init():
move_to_test_folder()
expected_folder_path = Path("../output_expected").absolute()
cli_runner_invoke(
[
"--log-level=DEBUG",
"init",
"--project-name=test-cli",
"--initial-version=13.0",
"--final-version=14.0",
"--initial-version=14.0",
"--final-version=15.0",
"--postgresql-version=13",
"--extra-repository="
"OCA/web,OCA/server-tools,OCA/bank-statement-import",
]
],
)
assert filecmp.cmp(

View File

@ -5,21 +5,15 @@ from . import cli_runner_invoke, move_to_test_folder
def test_cli_get_code():
move_to_test_folder()
cli_runner_invoke(
[
"--log-level=DEBUG",
"get-code",
]
)
# Check V13
openupgrade_path = Path("./src/env_13.0/src/openupgrade")
assert openupgrade_path.exists()
cli_runner_invoke(["get-code"])
assert (openupgrade_path / Path("odoo")).exists()
# Check V14
web_path = Path("./src/env_14.0/src/OCA/web")
assert web_path.exists()
# check V14
openupgrade_path = Path("./src/env_14.0/src/openupgrade")
# check V15
openupgrade_path = Path("./src/env_15.0/src/openupgrade")
assert openupgrade_path.exists()

View File

@ -1,24 +1,32 @@
from odoo_openupgrade_wizard.tools_docker import get_docker_client
from odoo_openupgrade_wizard.tools.tools_docker import (
get_docker_client,
kill_container,
)
from . import cli_runner_invoke, move_to_test_folder
from . import (
build_ctx_from_config_file,
cli_runner_invoke,
move_to_test_folder,
)
def test_cli_docker_build():
move_to_test_folder()
cli_runner_invoke(
[
"--log-level=DEBUG",
"docker-build",
"--versions=13.0,14.0",
]
)
ctx = build_ctx_from_config_file()
# Drop postgresql container if exist
# (we ensure that the postgres container is removed to
# be sure that the call (environment, etc...) is correct now.)
kill_container(ctx.obj["config"]["postgres_container_name"])
cli_runner_invoke(["docker-build", "--versions=14.0,15.0"])
docker_client = get_docker_client()
assert docker_client.images.get(
"odoo-openupgrade-wizard-image__test-cli__13.0"
"odoo-openupgrade-wizard-image__test-cli__14.0"
)
assert docker_client.images.get(
"odoo-openupgrade-wizard-image__test-cli__14.0"
"odoo-openupgrade-wizard-image__test-cli__15.0"
)

View File

@ -1,7 +1,10 @@
from pathlib import Path
from odoo_openupgrade_wizard.tools_docker import get_docker_client
from odoo_openupgrade_wizard.tools_postgres import execute_sql_request
from odoo_openupgrade_wizard.tools.tools_docker import get_docker_client
from odoo_openupgrade_wizard.tools.tools_postgres import (
ensure_database,
execute_sql_request,
)
from . import (
build_ctx_from_config_file,
@ -12,24 +15,26 @@ from . import (
def test_cli_run():
move_to_test_folder()
ctx = build_ctx_from_config_file()
db_name = "database_test_cli___run"
ensure_database(ctx, db_name, state="absent")
cli_runner_invoke(
[
"--log-level=DEBUG",
"run",
"--step=1",
"--database=%s" % db_name,
f"--database={db_name}",
"--init-modules=base",
"--stop-after-init",
]
],
)
# Ensure that a subfolder filestore/DB_NAME has been created
db_filestore_path = Path("./filestore/filestore/%s" % db_name)
db_filestore_path = Path("./filestore/filestore") / db_name
assert db_filestore_path.exists()
# Ensure that 'base' module is installed
ctx = build_ctx_from_config_file()
request = (
"SELECT id"
" FROM ir_module_module"

View File

@ -2,7 +2,10 @@ from pathlib import Path
from plumbum.cmd import cp
from odoo_openupgrade_wizard.tools_postgres import execute_sql_request
from odoo_openupgrade_wizard.tools.tools_postgres import (
ensure_database,
execute_sql_request,
)
from . import (
build_ctx_from_config_file,
@ -13,29 +16,32 @@ from . import (
def test_cli_execute_script_python():
move_to_test_folder()
extra_script_path = Path("../extra_script/click_odoo_test.py").absolute()
ctx = build_ctx_from_config_file()
extra_script_path = Path(
"../extra_script/01-post-migration-custom_test.py"
).absolute()
cp(
extra_script_path,
Path("click_odoo_test.py"),
Path("01-post-migration-custom_test.py"),
)
db_name = "database_test_cli___execute_script_python"
ensure_database(ctx, db_name, state="absent")
# Install Odoo on V13 with base installed
# Install Odoo on V14 with base installed
cli_runner_invoke(
[
"--log-level=DEBUG",
"run",
"--step=1",
"--database=%s" % db_name,
f"--database={db_name}",
"--init-modules=base",
"--stop-after-init",
]
],
)
# Compute partners quantity
ctx = build_ctx_from_config_file()
request = "SELECT count(*)" " FROM res_partner;"
request = "SELECT count(*) FROM res_partner;"
partner_quantity_before = int(
execute_sql_request(ctx, request, database=db_name)[0][0]
)
@ -43,12 +49,11 @@ def test_cli_execute_script_python():
# Execute Custom Python Script
cli_runner_invoke(
[
"--log-level=DEBUG",
"execute-script-python",
"--step=1",
"--database=%s" % db_name,
"--script-file-path=click_odoo_test.py",
]
f"--database={db_name}",
"--script-file-path=01-post-migration-custom_test.py",
],
)
partner_quantity_after = int(
execute_sql_request(ctx, request, database=db_name)[0][0]

View File

@ -2,7 +2,7 @@ from pathlib import Path
from plumbum.cmd import cp
from odoo_openupgrade_wizard.tools_postgres import (
from odoo_openupgrade_wizard.tools.tools_postgres import (
ensure_database,
execute_sql_request,
)
@ -16,14 +16,15 @@ from . import (
def test_cli_execute_script_sql():
move_to_test_folder()
ctx = build_ctx_from_config_file()
extra_script_path = Path(
"../extra_script/pre-migration-custom_test.sql"
).absolute()
# Deploy SQL Script
destination_path = Path("scripts/step_01__update__13.0")
destination_path = Path("scripts/step_01__regular__14.0")
cp([extra_script_path, destination_path])
ctx = build_ctx_from_config_file()
# Reset database
db_name = "database_test_cli___execute_script_sql"
@ -33,12 +34,7 @@ def test_cli_execute_script_sql():
# TODO call with script-file-path
# to avoid to copy file in scripts/step_xxx folder
cli_runner_invoke(
[
"--log-level=DEBUG",
"execute-script-sql",
"--step=1",
"--database=%s" % db_name,
]
["execute-script-sql", "--step=1", f"--database={db_name}"]
)
# Ensure that the request has been done correctly

View File

@ -1,4 +1,7 @@
from odoo_openupgrade_wizard.tools_postgres import (
from pathlib import Path
from shutil import copy
from odoo_openupgrade_wizard.tools.tools_postgres import (
ensure_database,
execute_sql_request,
)
@ -12,42 +15,31 @@ from . import (
def test_cli_upgrade():
move_to_test_folder()
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
db_name = "database_test_cli___upgrade"
ctx = build_ctx_from_config_file()
ensure_database(ctx, db_name, state="absent")
cli_runner_invoke(
[
"--log-level=DEBUG",
"run",
"--step=1",
"--database=%s" % db_name,
f"--database={db_name}",
"--init-modules=base",
"--stop-after-init",
]
)
# Ensure that 'base' module is installed at 13.0
request = (
"SELECT latest_version"
" FROM ir_module_module"
" WHERE state ='installed'"
" AND name='base';"
)
latest_version = execute_sql_request(ctx, request, database=db_name)
assert latest_version[0][0].startswith("13.")
cli_runner_invoke(
[
"--log-level=DEBUG",
"upgrade",
"--database=%s" % db_name,
"--first-step=1",
"--last-step=3",
]
],
)
# Ensure that 'base' module is installed at 14.0
@ -60,3 +52,43 @@ def test_cli_upgrade():
latest_version = execute_sql_request(ctx, request, database=db_name)
assert latest_version[0][0].startswith("14.")
cli_runner_invoke(
[
"upgrade",
f"--database={db_name}",
"--first-step=1",
"--last-step=3",
],
)
# Ensure that 'base' module is installed at 15.0
request = (
"SELECT latest_version"
" FROM ir_module_module"
" WHERE state ='installed'"
" AND name='base';"
)
latest_version = execute_sql_request(ctx, request, database=db_name)
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,20 +10,17 @@ class TestCliEstimateWorkload(unittest.TestCase):
cli_runner_invoke(
[
"--log-level=DEBUG",
"estimate-workload",
"--extra-modules="
# Done Module
"account"
"base"
# Deleted module (because merged)
",account_analytic_default"
# Deleted module (because renamed)
",account_facturx"
# Deleted module (because lost merge)
",base_gengo"
",account_edi_extended"
# Some modules that are not ported (for the time being)
",l10n_be_invoice_bba,l10n_ch_qriban,l10n_latam_base"
# OCA Portted Modules
# OCA Ported Modules
",web_responsive"
# OCA Unported modules
",web_boolean_button"
@ -32,19 +29,11 @@ class TestCliEstimateWorkload(unittest.TestCase):
",web_view_calendar_list"
",web_widget_child_selector"
",web_widget_one2many_tree_line_duplicate"
",web_widget_dropdown_dynamic_example",
]
",web_widget_dropdown_dynamic_example"
",my_module_that_doesnt_exist",
],
)
# We check file has been created
# parsing this file is a mess, so we don't do it ;-)
assert Path("./analysis.html").exists()
with self.assertRaises(ValueError):
cli_runner_invoke(
[
"--log-level=DEBUG",
"estimate-workload",
"--extra-modules=my_module_that_doesnt_exist",
]
)

View File

@ -0,0 +1,23 @@
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

@ -1,4 +1,4 @@
from odoo_openupgrade_wizard.tools_postgres import (
from odoo_openupgrade_wizard.tools.tools_postgres import (
ensure_database,
execute_sql_request,
)
@ -6,34 +6,41 @@ from odoo_openupgrade_wizard.tools_postgres import (
from . import (
build_ctx_from_config_file,
cli_runner_invoke,
mock_odoo_rpc_url,
move_to_test_folder,
)
def test_cli_install_from_csv():
def test_cli_install_from_csv(mocker):
move_to_test_folder()
mock_odoo_rpc_url(mocker)
# Initialize database
db_name = "database_test_cli___install_from_csv"
ctx = build_ctx_from_config_file()
ensure_database(ctx, db_name, state="absent")
cli_runner_invoke(
[
"--log-level=DEBUG",
"install-from-csv",
"--database=%s" % db_name,
]
)
cli_runner_invoke(["install-from-csv", f"--database={db_name}"])
# Ensure that 'account' is installed
# and also 'product', by dependencies
# Ensure that 'base' is installed
request = (
"SELECT count(*)"
" FROM ir_module_module"
" WHERE state ='installed'"
" AND name in ('product', 'account');"
" AND name in ('base');"
)
module_qty = int(execute_sql_request(ctx, request, database=db_name)[0][0])
assert module_qty == 2
assert module_qty == 1
# Ensure that 'account' is not installed
request = (
"SELECT count(*)"
" FROM ir_module_module"
" WHERE state ='installed'"
" AND name in ('account');"
)
module_qty = int(execute_sql_request(ctx, request, database=db_name)[0][0])
assert module_qty == 0

View File

@ -1,23 +1,26 @@
from pathlib import Path
from odoo_openupgrade_wizard.tools_odoo import get_odoo_env_path
from odoo_openupgrade_wizard.tools.tools_odoo import get_odoo_env_path
from . import (
build_ctx_from_config_file,
cli_runner_invoke,
mock_odoo_rpc_url,
move_to_test_folder,
)
def test_cli_generate_module_analysis():
def test_cli_generate_module_analysis(mocker):
move_to_test_folder()
mock_odoo_rpc_url(mocker)
ctx = build_ctx_from_config_file()
db_name = "database_test_cli___generate_module_analysis"
ctx = build_ctx_from_config_file()
# identify main analysis file of openupgrade
analysis_file_path = get_odoo_env_path(ctx, 14.0) / Path(
analysis_file_path = get_odoo_env_path(ctx, 15.0) / Path(
"src/openupgrade/openupgrade_scripts/scripts"
"/base/14.0.1.3/upgrade_general_log.txt"
"/base/15.0.1.3/upgrade_general_log.txt"
)
# We remove this file and run the analysis
@ -29,12 +32,11 @@ def test_cli_generate_module_analysis():
analysis_file_path
cli_runner_invoke(
[
"--log-level=DEBUG",
"generate-module-analysis",
"--step=2",
"--database=%s" % db_name,
f"--database={db_name}",
"--modules=base",
]
],
)
# The file should has been recreated by the analysis command

166
tests/cli_22_dumpdb_test.py Normal file
View File

@ -0,0 +1,166 @@
import pathlib
import shutil
from odoo_openupgrade_wizard.tools.tools_postgres import ensure_database
from . import (
build_ctx_from_config_file,
cli_runner_invoke,
mock_odoo_rpc_url,
move_to_test_folder,
)
def test_cli_dumpdb(mocker):
move_to_test_folder()
mock_odoo_rpc_url(mocker)
# Initialize database
db_name = "database_test_cli___dumpdb"
ctx = build_ctx_from_config_file()
ensure_database(ctx, db_name, state="absent")
cli_runner_invoke(["install-from-csv", f"--database={db_name}"])
# Dump database and filestore
formatlist = [("p", "d"), ("c", "tgz"), ("t", "t"), ("d", "d")]
for formats in formatlist:
database_path = pathlib.Path("database_test_cli___dumpdb")
filestore_path = pathlib.Path("database_test_clie___dumpdb.filestore")
assert not database_path.exists()
assert not filestore_path.exists()
cli_runner_invoke(
[
"--log-level=DEBUG",
"dumpdb",
f"--database={db_name}",
f"--database-path={database_path}",
f"--database-format={formats[0]}",
f"--filestore-path={filestore_path}",
f"--filestore-format={formats[1]}",
],
)
assert database_path.exists()
assert filestore_path.exists()
# Cleanup files
if database_path.is_dir():
shutil.rmtree(database_path)
else:
database_path.unlink()
if filestore_path.is_dir():
shutil.rmtree(filestore_path)
else:
filestore_path.unlink()
def test_cli_dumpdb_failure(mocker):
move_to_test_folder()
mock_odoo_rpc_url(mocker)
# Initialize database
db_name = "database_test_cli___dumpdb"
ctx = build_ctx_from_config_file()
ensure_database(ctx, db_name, state="absent")
cli_runner_invoke(
[
"--log-level=DEBUG",
"install-from-csv",
f"--database={db_name}",
],
)
# First dump
formats = ("d", "d")
database_path = pathlib.Path("database_test_cli___dumpdb")
filestore_path = pathlib.Path("database_test_clie___dumpdb.filestore")
assert not database_path.exists()
assert not filestore_path.exists()
cli_runner_invoke(
[
"--log-level=DEBUG",
"dumpdb",
f"--database={db_name}",
f"--database-path={database_path}",
f"--database-format={formats[0]}",
f"--filestore-path={filestore_path}",
f"--filestore-format={formats[1]}",
],
)
assert database_path.exists()
assert filestore_path.exists()
# With same name
cli_runner_invoke(
[
"--log-level=DEBUG",
"dumpdb",
f"--database={db_name}",
f"--database-path={database_path}",
f"--database-format={formats[0]}",
f"--filestore-path={filestore_path}",
f"--filestore-format={formats[1]}",
],
expect_success=False,
)
# With --force
cli_runner_invoke(
[
"--log-level=DEBUG",
"dumpdb",
f"--database={db_name}",
f"--database-path={database_path}",
f"--database-format={formats[0]}",
f"--filestore-path={filestore_path}",
f"--filestore-format={formats[1]}",
"--force",
],
)
# With name outside of project path
cli_runner_invoke(
[
"--log-level=DEBUG",
"dumpdb",
f"--database={db_name}",
f"--database-path=/{database_path}",
f"--database-format={formats[0]}",
f"--filestore-path=/{filestore_path}",
f"--filestore-format={formats[1]}",
],
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
if database_path.is_dir():
shutil.rmtree(database_path)
else:
database_path.unlink()
if filestore_path.is_dir():
shutil.rmtree(filestore_path)
else:
filestore_path.unlink()

View File

@ -0,0 +1,85 @@
import pathlib
import shutil
from odoo_openupgrade_wizard.tools.tools_postgres import ensure_database
from . import (
assert_database,
build_ctx_from_config_file,
cli_runner_invoke,
mock_odoo_rpc_url,
move_to_test_folder,
)
def test_cli_restoredb(mocker):
move_to_test_folder()
mock_odoo_rpc_url(mocker)
db_name = "database_test_cli___restoredb"
ctx = build_ctx_from_config_file()
# Ensure environment is clean
ensure_database(ctx, db_name, state="absent")
dest_filestore_path = pathlib.Path(f"./filestore/filestore/{db_name}")
shutil.rmtree(dest_filestore_path, ignore_errors=True)
# Copy database and filestore data in a accessible folder
database_path = pathlib.Path("./restoredb.dump")
filestore_path = pathlib.Path("./restoredb.tar.gz")
shutil.copyfile(pathlib.Path("../restoredb/test.dump"), database_path)
shutil.copyfile(pathlib.Path("../restoredb/test.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
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
assert_database(ctx, db_name, "present")
# Delete filestore and database
shutil.rmtree(dest_filestore_path)
ensure_database(ctx, db_name, state="absent")

70
tests/cli_30_psql_test.py Normal file
View File

@ -0,0 +1,70 @@
from pytest import raises
from odoo_openupgrade_wizard.tools.tools_postgres import (
ensure_database,
execute_psql_command,
)
from . import (
build_ctx_from_config_file,
cli_runner_invoke,
move_to_test_folder,
)
def test_cli_psql():
move_to_test_folder()
ctx = build_ctx_from_config_file()
db_name = "database_test_cli___psql"
ensure_database(ctx, db_name, state="absent")
# initialize database
cli_runner_invoke(
[
"run",
"--step=1",
f"--database={db_name}",
"--init-modules=base",
"--stop-after-init",
],
)
# Test requests from lib
request = (
"SELECT name"
" FROM ir_module_module"
" WHERE state ='installed'"
" AND name='base';"
)
output = execute_psql_command(
ctx,
request,
database=db_name,
psql_args=("--tuples-only",),
)
assert output.strip() == "base"
# test via cli ok
cli_runner_invoke(
[
"psql",
f"--database={db_name}",
f'--command "{request}"',
"--no-pager",
"--tuples-only",
],
)
# test that cli fails with wrong parameters
with raises(Exception):
cli_runner_invoke(
[
"psql",
f"--database={db_name}",
f'--command "{request}"',
"--no-pager",
"--tuples-only",
"---unkwon-argument",
],
)

View File

@ -0,0 +1,48 @@
import pathlib
import shutil
from odoo_openupgrade_wizard.tools.tools_postgres import ensure_database
from . import (
assert_database,
build_ctx_from_config_file,
cli_runner_invoke,
mock_odoo_rpc_url,
move_to_test_folder,
)
def test_cli_copydb(mocker):
move_to_test_folder()
mock_odoo_rpc_url(mocker)
db_name = "database_test_cli___copydb"
db_dest_name = "database_test_cli___copydb__copy"
ctx = build_ctx_from_config_file()
# Ensure environment is clean
ensure_database(ctx, db_name, state="absent")
dest_filestore_path = pathlib.Path(f"./filestore/filestore/{db_dest_name}")
shutil.rmtree(dest_filestore_path, ignore_errors=True)
# Initialize database
cli_runner_invoke(["install-from-csv", f"--database={db_name}"])
# Copy database
cli_runner_invoke(
[
"copydb",
f"--source={db_name}",
f"--dest={db_dest_name}",
],
)
# check filestore exists
assert dest_filestore_path.exists()
# Check database exists
assert_database(ctx, db_dest_name, "present")
# Delete filestore and database
shutil.rmtree(dest_filestore_path)
ensure_database(ctx, db_dest_name, state="absent")

View File

@ -0,0 +1,37 @@
import pathlib
import shutil
from odoo_openupgrade_wizard.tools.tools_postgres import ensure_database
from . import (
assert_database,
build_ctx_from_config_file,
cli_runner_invoke,
mock_odoo_rpc_url,
move_to_test_folder,
)
def test_cli_dropdb(mocker):
move_to_test_folder()
mock_odoo_rpc_url(mocker)
db_name = "database_test_cli___dropdb"
ctx = build_ctx_from_config_file()
# Ensure environment is clean
ensure_database(ctx, db_name, state="absent")
filestore_path = pathlib.Path(f"./filestore/filestore/{db_name}")
shutil.rmtree(filestore_path, ignore_errors=True)
# Initialize database
cli_runner_invoke(["install-from-csv", f"--database={db_name}"])
# Drop database
cli_runner_invoke(["dropdb", f"--database={db_name}"])
# Check database does not exists
assert_database(ctx, db_name, "absent")
# Check filestore does not exists
assert not filestore_path.exists()

View File

@ -0,0 +1,30 @@
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

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

View File

@ -0,0 +1,12 @@
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,11 +0,0 @@
import logging
_logger = logging.getLogger(__name__)
_logger.info("click_odoo_test.py : Begin of script ...")
env = env # noqa: F821
for i in range(0, 10):
env["res.partner"].create({"name": "Partner #%d" % (i)})
_logger.info("click_odoo_test.py : End of script.")

View File

@ -1,46 +0,0 @@
# Unused for the time being
# def _check_orm_usage(self):
# # Classic ORM usage Checks
# partners = self.browse_by_search("res.partner")
# self.browse_by_create("res.partner", {"name": "New Partner"})
# new_partners = self.browse_by_search("res.partner")
# if len(partners) + 1 != len(new_partners):
# raise Exception("Creation of partner failed.")
# def _check_modules(self):
# if self.check_modules_installed("sale"):
# self.uninstall_modules("sale")
# self.install_modules("sale")
# if not self.check_modules_installed("sale"):
# raise Exception("'sale' module should be installed")
# self.uninstall_modules(["product"])
# if self.check_modules_installed("sale"):
# raise Exception(
# "'sale' module should not be installed"
# " after uninstallation of product"
# )
# def _check_models(self):
# if not self.check_models_present("res.partner"):
# raise Exception("'res.partner' model should be present.")
# if self.check_models_present("res.partner.unexisting.model"):
# raise Exception(
# "'res.partner.unexisting.model' model" " should not be present."
# )
# def main(self):
# _check_orm_usage(self)
# _check_modules(self)
# _check_models(self)

View File

@ -1,58 +1,75 @@
project_name: test-cli
postgres_image_name: postgres:13
postgres_container_name: test-cli-db
postgres_image_name: postgres:13
postgres_container_name: test-cli-container-postgres-13
postgres_volume_name: test-cli-volume-postgres-13
postgres_extra_settings:
odoo_rpc_timeout: 3600
odoo_host_xmlrpc_port: 9069
odoo_default_country_code: FR
odoo_default_company:
country_code: FR
odoo_versions:
- 13.0
- 14.0
- 15.0
odoo_version_settings:
14.0:
15.0:
migration_steps:
- name: 1
version: 13.0
version: 14.0
execution_context: regular
complete_name: step_01__update__13.0
complete_name: step_01__regular__14.0
update: True
- name: 2
version: 14.0
version: 15.0
execution_context: openupgrade
complete_name: step_02__upgrade__14.0
complete_name: step_02__openupgrade__15.0
- name: 3
version: 14.0
version: 15.0
execution_context: regular
complete_name: step_03__update__14.0
complete_name: step_03__regular__15.0
update: True
workload_settings:
# porting a module requires 45 minutes minimaly
port_minimal_time: 45
# Ignored module list
ignored_module_list: []
# a migration cost more for each version
port_per_version: 15
# porting a module requires 45 minutes minimaly
port_minimal_time: 45
# Porting 120 lines of Python code costs 1 hour
port_per_python_line_time: 0.5
# a migration cost more for each version
port_per_version: 15
# Porting 120 lines of Python code costs 1 hour
port_per_javascript_line_time: 0.5
# Porting 120 lines of Python code costs 1 hour
port_per_python_line_time: 0.5
# Porting 10 lines of XML costs 1 minute
port_per_xml_line_time: 0.10
# Porting 120 lines of Javascript code costs 1 hour
port_per_javascript_line_time: 0.5
# Minimal time for Openupgrade PR
open_upgrade_minimal_time: 10
# Porting 10 lines of XML costs 1 minute
port_per_xml_line_time: 0.10
# time for a line of model in the openupgrade_analysis.txt
openupgrade_model_line_time: 10
# Minimal time for Openupgrade PR
open_upgrade_minimal_time: 10
# Time for a line of field in the openupgrade_analysis.txt
openupgrade_field_line_time: 5
# time for a line of model in the openupgrade_analysis.txt
openupgrade_model_line_time: 10
# Time for a line of XML in the openupgrade_analysis.txt
openupgrade_xml_line_time: 0.1
# Time for a line of field in the openupgrade_analysis.txt
openupgrade_field_line_time: 5
# Time for a line of XML in the openupgrade_analysis.txt
openupgrade_xml_line_time: 0.1

Some files were not shown because too many files have changed in this diff Show More