[UPD] makefile: improve test

- prevent makefile cache to ignore test
- update image_db_create_erplibre_base
- add mariadb migrator test
- add helloworld test
- fix other test
- add isort format
- add show log of test
- add clean to remove temporary file
This commit is contained in:
Mathieu Benoit 2022-01-24 14:00:47 -05:00
parent a9eeb562e6
commit 24e5cba22b
30 changed files with 5686 additions and 82 deletions

302
Makefile
View file

@ -1,4 +1,5 @@
SHELL := /bin/bash
LOG_FILE := ./.venv/make_test.log
#############
# General #
#############
@ -112,6 +113,10 @@ db_drop_db_code_generator:
db_drop_db_template:
./.venv/bin/python3 ./odoo/odoo-bin db --drop --database template
.PHONY: db_drop_all
db_drop_all:
./script/database/db_drop_all.py
.PHONY: db_clean_cache
db_clean_cache:
./script/db_restore.py --clean_cache
@ -121,7 +126,8 @@ db_restore_erplibre_base_db_test:
./script/db_restore.py --database test
.PHONY: db_restore_erplibre_base_db_test_module_test
db_restore_erplibre_base_db_test_module_test: db_restore_erplibre_base_db_test
db_restore_erplibre_base_db_test_module_test:
./script/make.sh db_restore_erplibre_base_db_test
./script/addons/install_addons.sh test test
.PHONY: db_restore_erplibre_base_db_test_image_test
@ -157,7 +163,8 @@ db_restore_erplibre_base_db_template:
./script/db_restore.py --database template
.PHONY: db_create_db_test
db_create_db_test: db_drop_db_test
db_create_db_test:
./script/make.sh db_drop_db_test
./.venv/bin/python3 ./odoo/odoo-bin db --create --database test
########################
@ -166,7 +173,7 @@ db_create_db_test: db_drop_db_test
.PHONY: image_db_create_erplibre_base
image_db_create_erplibre_base:
./script/make.sh db_create_db_test
./script/addons/install_addons.sh test web_responsive,disable_odoo_online,remove_odoo_enterprise,auth_user_case_insensitive,muk_web_theme,muk_utils,muk_branding,muk_mail_branding,muk_web_branding,muk_web_theme_mail,muk_web_utils,fetchmail_notify_error_to_sender,mail_debrand,partner_quebec_tz,erplibre_info
./script/addons/install_addons.sh test web_responsive,disable_odoo_online,remove_odoo_enterprise,auth_user_case_insensitive,muk_web_theme,muk_utils,muk_branding,muk_mail_branding,muk_web_branding,muk_web_theme_mail,muk_web_utils,fetchmail_notify_error_to_sender,mail_debrand,partner_quebec_tz,erplibre_info,web_timeline,web_diagram_position
./.venv/bin/python3 ./odoo/odoo-bin db --backup --database test --restore_image erplibre_base
.PHONY: image_db_create_erplibre_website
@ -216,15 +223,18 @@ image_diff_base_website:
# Addons installation #
#########################
.PHONY: addons_install_code_generator_basic
addons_install_code_generator_basic: db_restore_erplibre_base_db_code_generator
addons_install_code_generator_basic:
./script/make.sh db_restore_erplibre_base_db_code_generator
./script/addons/install_addons_dev.sh code_generator code_generator
.PHONY: addons_install_code_generator_featured
addons_install_code_generator_featured: db_restore_erplibre_base_db_code_generator
addons_install_code_generator_featured:
./script/make.sh db_restore_erplibre_base_db_code_generator
./script/addons/install_addons_dev.sh code_generator code_generator_cron,code_generator_hook,code_generator_portal
.PHONY: addons_install_code_generator_full
addons_install_code_generator_full: db_restore_erplibre_base_db_code_generator
addons_install_code_generator_full:
./script/make.sh db_restore_erplibre_base_db_code_generator
./script/addons/install_addons_dev.sh code_generator code_generator_cron,code_generator_hook,code_generator_portal,code_generator_db_servers,code_generator_website_snippet,code_generator_geoengine,code_generator_theme_website,code_generator_website_leaflet
.PHONY: addons_install_code_generator_demo
@ -236,27 +246,40 @@ addons_uninstall_code_generator_demo:
./script/addons/uninstall_addons.sh code_generator code_generator_demo
.PHONY: addons_reinstall_code_generator_demo
addons_reinstall_code_generator_demo: addons_uninstall_code_generator_demo addons_install_code_generator_demo
addons_reinstall_code_generator_demo:
./script/make.sh addons_uninstall_code_generator_demo
./script/make.sh addons_install_code_generator_demo
.PHONY: addons_install_all_code_generator_demo
addons_install_all_code_generator_demo: db_restore_erplibre_base_db_code_generator
./script/addons/install_addons_dev.sh code_generator code_generator_demo,code_generator_demo_export_helpdesk,code_generator_demo_internal,code_generator_demo_portal,code_generator_demo_theme_website,code_generator_demo_website_leaflet,code_generator_demo_website_snippet
addons_install_all_code_generator_demo:
./script/make.sh db_restore_erplibre_base_db_code_generator
./script/addons/install_addons_dev.sh code_generator code_generator_demo,code_generator_demo_export_helpdesk,code_generator_demo_internal,code_generator_demo_internal_inherit,code_generator_demo_portal,code_generator_demo_theme_website,code_generator_demo_website_leaflet,code_generator_demo_website_snippet,code_generator_auto_backup
# ./script/addons/install_addons_dev.sh code_generator code_generator_demo
# ./script/addons/install_addons_dev.sh code_generator code_generator_demo_export_helpdesk
# ./script/addons/install_addons_dev.sh code_generator code_generator_demo_internal
# ./script/addons/install_addons_dev.sh code_generator code_generator_demo_internal_inherit
# ./script/addons/install_addons_dev.sh code_generator code_generator_demo_portal
# ./script/addons/install_addons_dev.sh code_generator code_generator_auto_backup
# ./script/addons/install_addons_dev.sh code_generator code_generator_demo_theme_website
# ./script/addons/install_addons_dev.sh code_generator code_generator_demo_website_leaflet
# ./script/addons/install_addons_dev.sh code_generator code_generator_demo_website_snippet
.PHONY: addons_install_all_code_generator_template
addons_install_all_code_generator_template:
./script/addons/install_addons_dev.sh template demo_portal,auto_backup
./script/addons/install_addons_dev.sh template code_generator_template_demo_portal code_generator_template_demo_sysadmin_cron
./script/make.sh db_restore_erplibre_base_db_template
./script/addons/install_addons_dev.sh template demo_portal,auto_backup,demo_internal_inherit
./script/addons/install_addons_dev.sh template code_generator_template_demo_portal,code_generator_template_demo_sysadmin_cron,code_generator_template_demo_internal_inherit
#./script/addons/install_addons_dev.sh template demo_portal
#./script/addons/install_addons_dev.sh template code_generator_template_demo_portal
#./script/addons/install_addons_dev.sh template auto_backup
#./script/addons/install_addons_dev.sh template code_generator_template_demo_sysadmin_cron
#./script/addons/install_addons_dev.sh template demo_internal_inherit
#./script/addons/install_addons_dev.sh template code_generator_template_demo_internal_inherit
.PHONY: addons_install_all_generated_demo
addons_install_all_generated_demo:
./script/addons/install_addons_dev.sh template demo_export_helpdesk,demo_internal,demo_portal,demo_website_leaflet,demo_website_snippet
./script/make.sh db_restore_erplibre_base_db_template
./script/addons/install_addons_dev.sh template demo_helpdesk_data,demo_internal,demo_internal_inherit,demo_portal,demo_website_leaflet,demo_website_snippet,auto_backup
# TODO support installation theme with cli
#./script/addons/install_addons_dev.sh template theme_website_demo_code_generator
@ -264,51 +287,196 @@ addons_install_all_generated_demo:
addons_install_all_code_generator:
./script/addons/install_addons_dev.sh code_generator code_generator_auto_backup
##################
# Code generator #
##################
.PHONY: addons_install_code_generator_template_code_generator
addons_install_code_generator_template_code_generator:
./script/db_restore.py --database template
./script/code_generator/search_class_model.py --quiet -d addons/TechnoLibre_odoo-code-generator/code_generator -t addons/TechnoLibre_odoo-code-generator-template/code_generator_template_code_generator --with_inherit
./script/maintenance/black.sh ./addons/TechnoLibre_odoo-code-generator-template/code_generator_template_code_generator
./script/addons/install_addons_dev.sh template code_generator
./script/addons/install_addons_dev.sh template code_generator_template_code_generator
./script/git/remote_code_generation_git_compare.py --quiet --git_gui --clear --replace_directory --directory1 ./addons/TechnoLibre_odoo-code-generator-template/code_generator_code_generator --directory2 ./addons/TechnoLibre_odoo-code-generator/code_generator_code_generator
.PHONY: addons_install_code_generator_code_generator
addons_install_code_generator_code_generator:
./script/db_restore.py --database code_generator
./script/addons/install_addons_dev.sh code_generator code_generator_code_generator
./script/git/remote_code_generation_git_compare.py --quiet --git_gui --clear --replace_directory --directory1 ./addons/TechnoLibre_odoo-code-generator/code_generator --directory2 ./addons/TechnoLibre_odoo-code-generator-template/code_generator
.PHONY: meld_code_generator_template_code_generator
meld_code_generator_template_code_generator:
./script/make.sh clean
meld ./addons/TechnoLibre_odoo-code-generator/code_generator_code_generator ./addons/TechnoLibre_odoo-code-generator-template/code_generator_code_generator
.PHONY: meld_code_generator_code_generator
meld_code_generator_code_generator:
./script/make.sh clean
meld ./addons/TechnoLibre_odoo-code-generator-template/code_generator ./addons/TechnoLibre_odoo-code-generator/code_generator
########################
# Extra migrator sql #
########################
.PHONY: addons_install_code_generator_mariadb_sql_example_1
addons_install_code_generator_mariadb_sql_example_1:
./script/database/restore_mariadb_sql_example_1.sh
./script/db_restore.py --database code_generator
./script/addons/install_addons_dev.sh code_generator code_generator_portal
./script/addons/install_addons_dev.sh code_generator code_generator_migrator_demo_mariadb_sql_example_1
./script/db_restore.py --database template
./script/addons/install_addons_dev.sh template code_generator_portal,demo_mariadb_sql_example_1
./script/code_generator/search_class_model.py --quiet -d addons/TechnoLibre_odoo-code-generator-template/demo_mariadb_sql_example_1 -t addons/TechnoLibre_odoo-code-generator-template/code_generator_template_demo_mariadb_sql_example_1
./script/addons/install_addons_dev.sh template code_generator_template_demo_mariadb_sql_example_1
./script/db_restore.py --database code_generator
./script/addons/install_addons_dev.sh code_generator code_generator_portal
./script/addons/install_addons_dev.sh code_generator code_generator_demo_mariadb_sql_example_1
##########
# test #
##########
.PHONY: test
test: test_format test_code_generator_generation test_code_generator_generation_extra test_code_generator_code_i18n test_code_generator_code_i18n_extra
test:
./script/make.sh clean
-rm ${LOG_FILE}
./script/make.sh test_base |& tee -a ${LOG_FILE}
./script/test/check_result_test.sh ${LOG_FILE}
.PHONY: test_full
test_full:
./script/make.sh clean
-rm ${LOG_FILE}
./script/make.sh test_base |& tee -a ${LOG_FILE}
./script/make.sh test_extra |& tee -a ${LOG_FILE}
#./script/make.sh doc |& tee -a ${LOG_FILE}
./script/test/check_result_test.sh ${LOG_FILE}
.PHONY: test_full_fast
test_full_fast:
./script/make.sh clean
./script/test/run_parallel_test.py
.PHONY: test_base
test_base:
./script/make.sh test_format
./script/make.sh test_code_generator_hello_world
./script/make.sh test_installation_demo
./script/make.sh test_code_generator_generation
./script/make.sh test_code_generator_generation_template
.PHONY: test_extra
test_extra:
./script/make.sh test_code_generator_migrator_demo_mariadb_sql_example_1
./script/make.sh test_code_generator_template_demo_mariadb_sql_example_1
./script/make.sh test_code_generator_demo_mariadb_sql_example_1
.PHONY: test_format
test_format:
./script/maintenance/black.sh --check ./addons/TechnoLibre_odoo-code-generator/
./script/maintenance/black.sh --check ./addons/TechnoLibre_odoo-code-generator-template/
.PHONY: test_code_generator_hello_world
test_code_generator_hello_world:
./test/code_generator/hello_world.sh
.PHONY: test_installation_demo
test_installation_demo:
./script/code_generator/check_git_change_code_generator.sh ./addons/TechnoLibre_odoo-code-generator-template
./script/db_restore.py --database test_demo
./script/addons/install_addons.sh test_demo demo_helpdesk_data,demo_internal,demo_internal_inherit,demo_mariadb_sql_example_1,demo_portal,demo_website_data,demo_website_leaflet,demo_website_snippet
# TODO cannot install from command line (CLI)
#./script/addons/install_addons.sh test_demo theme_website_demo_code_generator ./addons/TechnoLibre_odoo-code-generator-template
.PHONY: test_code_generator_generation
test_code_generator_generation:
./script/code_generator/check_git_change_code_generator.sh ./addons/TechnoLibre_odoo-code-generator-template
./script/db_restore.py --database code_generator
./script/code_generator/install_and_test_code_generator.sh code_generator code_generator_demo,code_generator_demo_export_helpdesk,code_generator_demo_internal,code_generator_demo_portal,code_generator_demo_theme_website,code_generator_demo_website_leaflet,code_generator_demo_website_snippet ./addons/TechnoLibre_odoo-code-generator-template
# ./script/code_generator/install_and_test_code_generator.sh code_generator code_generator_demo ./addons/TechnoLibre_odoo-code-generator-template
# ./script/code_generator/install_and_test_code_generator.sh code_generator code_generator_demo_export_helpdesk ./addons/TechnoLibre_odoo-code-generator-template
# ./script/code_generator/install_and_test_code_generator.sh code_generator code_generator_demo_internal ./addons/TechnoLibre_odoo-code-generator-template
# ./script/code_generator/install_and_test_code_generator.sh code_generator code_generator_demo_portal ./addons/TechnoLibre_odoo-code-generator-template
# ./script/code_generator/install_and_test_code_generator.sh code_generator code_generator_demo_theme_website ./addons/TechnoLibre_odoo-code-generator-template
# ./script/code_generator/install_and_test_code_generator.sh code_generator code_generator_demo_website_leaflet ./addons/TechnoLibre_odoo-code-generator-template
# ./script/code_generator/install_and_test_code_generator.sh code_generator code_generator_demo_website_snippet ./addons/TechnoLibre_odoo-code-generator-template
.PHONY: test_code_generator_generation_extra
test_code_generator_generation_extra:
./script/code_generator/check_git_change_code_generator.sh ./addons/OCA_server-tools/auto_backup
./script/db_restore.py --database code_generator
./script/code_generator/install_and_test_code_generator.sh code_generator code_generator_auto_backup ./addons/OCA_server-tools/auto_backup
# Multiple
./script/db_restore.py --database test_code_generator
./script/code_generator/install_and_test_code_generator.sh test_code_generator code_generator_demo,code_generator_demo_export_helpdesk,code_generator_demo_export_website,code_generator_demo_internal,code_generator_demo_portal,code_generator_demo_theme_website,code_generator_demo_website_leaflet,code_generator_demo_website_snippet ./addons/TechnoLibre_odoo-code-generator-template code_generator_demo,demo_helpdesk_data,demo_website_data,demo_internal,demo_portal,theme_website_demo_code_generator,demo_website_leaflet,demo_website_snippet
# inherit
# TODO should be in multiple list, need to support it
./script/db_restore.py --database test_code_generator
./script/code_generator/install_and_test_code_generator.sh test_code_generator code_generator_demo_internal_inherit ./addons/TechnoLibre_odoo-code-generator-template demo_internal_inherit
# auto_backup
./script/db_restore.py --database test_code_generator
./script/code_generator/install_and_test_code_generator.sh test_code_generator code_generator_auto_backup ./addons/OCA_server-tools/auto_backup auto_backup
.PHONY: test_code_generator_code_i18n
test_code_generator_code_i18n:
# Single
#./script/db_restore.py --database test_code_generator
#./script/code_generator/install_and_test_code_generator.sh test_code_generator code_generator_demo ./addons/TechnoLibre_odoo-code-generator-template code_generator_demo
#./script/code_generator/install_and_test_code_generator.sh test_code_generator code_generator_demo_export_helpdesk ./addons/TechnoLibre_odoo-code-generator-template demo_helpdesk_data
#./script/code_generator/install_and_test_code_generator.sh test_code_generator code_generator_demo_export_website ./addons/TechnoLibre_odoo-code-generator-template demo_website_data
#./script/code_generator/install_and_test_code_generator.sh test_code_generator code_generator_demo_internal ./addons/TechnoLibre_odoo-code-generator-template demo_internal
#./script/code_generator/install_and_test_code_generator.sh test_code_generator code_generator_demo_portal ./addons/TechnoLibre_odoo-code-generator-template demo_portal
#./script/code_generator/install_and_test_code_generator.sh test_code_generator code_generator_demo_theme_website ./addons/TechnoLibre_odoo-code-generator-template theme_website_demo_code_generator
#./script/code_generator/install_and_test_code_generator.sh test_code_generator code_generator_demo_website_leaflet ./addons/TechnoLibre_odoo-code-generator-template demo_website_leaflet
#./script/code_generator/install_and_test_code_generator.sh test_code_generator code_generator_demo_website_snippet ./addons/TechnoLibre_odoo-code-generator-template demo_website_snippet
#./script/db_restore.py --database test_code_generator
#./script/code_generator/install_and_test_code_generator.sh test_code_generator code_generator_demo_internal_inherit ./addons/TechnoLibre_odoo-code-generator-template demo_internal_inherit
#./script/db_restore.py --database test_code_generator
#./script/code_generator/install_and_test_code_generator.sh test_code_generator code_generator_auto_backup ./addons/OCA_server-tools/auto_backup auto_backup
.PHONY: test_code_generator_generation_template
test_code_generator_generation_template:
./script/make.sh test_code_generator_code_template_demo_portal
./script/make.sh test_code_generator_code_template_demo_internal_inherit
./script/make.sh test_code_generator_code_template_demo_sysadmin_cron
.PHONY: test_code_generator_code_template_demo_portal
test_code_generator_code_template_demo_portal:
./script/code_generator/check_git_change_code_generator.sh ./addons/TechnoLibre_odoo-code-generator-template
./script/db_restore.py --database template
./script/addons/install_addons_dev.sh template demo_portal
./script/code_generator/install_and_test_code_generator.sh template code_generator_template_demo_portal ./addons/TechnoLibre_odoo-code-generator-template
./script/db_restore.py --database test_template
./script/addons/install_addons_dev.sh test_template demo_portal
#./script/addons/install_addons_dev.sh test_template code_generator_template_demo_portal
./script/code_generator/search_class_model.py --quiet -d addons/TechnoLibre_odoo-code-generator-template/demo_portal -t addons/TechnoLibre_odoo-code-generator-template/code_generator_template_demo_portal --with_inherit
./script/code_generator/install_and_test_code_generator.sh test_template code_generator_template_demo_portal ./addons/TechnoLibre_odoo-code-generator-template code_generator_demo_portal
.PHONY: test_code_generator_code_i18n_extra
test_code_generator_code_i18n_extra:
./script/code_generator/check_git_change_code_generator.sh ./addons/OCA_server-tools/auto_backup
./script/db_restore.py --database template
./script/addons/install_addons_dev.sh template auto_backup
./script/code_generator/install_and_test_code_generator.sh template code_generator_template_demo_sysadmin_cron ./addons/TechnoLibre_odoo-code-generator-template
# To support i18n in auto_backup
.PHONY: test_code_generator_code_template_demo_internal_inherit
test_code_generator_code_template_demo_internal_inherit:
./script/code_generator/check_git_change_code_generator.sh ./addons/TechnoLibre_odoo-code-generator-template
./script/db_restore.py --database test_template
./script/addons/install_addons_dev.sh test_template demo_internal_inherit
#./script/addons/install_addons_dev.sh test_template code_generator_template_demo_internal_inherit
./script/code_generator/search_class_model.py --quiet -d addons/TechnoLibre_odoo-code-generator-template/demo_internal_inherit -t addons/TechnoLibre_odoo-code-generator-template/code_generator_template_demo_internal_inherit --with_inherit
./script/code_generator/install_and_test_code_generator.sh test_template code_generator_template_demo_internal_inherit ./addons/TechnoLibre_odoo-code-generator-template code_generator_demo_internal_inherit
.PHONY: test_code_generator_code_template_demo_sysadmin_cron
test_code_generator_code_template_demo_sysadmin_cron:
./script/code_generator/check_git_change_code_generator.sh ./addons/OCA_server-tools/auto_backup
./script/db_restore.py --database test_template
./script/addons/install_addons_dev.sh test_template auto_backup
#./script/addons/install_addons_dev.sh test_template code_generator_template_demo_sysadmin_cron
./script/code_generator/search_class_model.py --quiet -d addons/OCA_server-tools/auto_backup -t addons/TechnoLibre_odoo-code-generator-template/code_generator_template_demo_sysadmin_cron --with_inherit
./script/code_generator/install_and_test_code_generator.sh test_template code_generator_template_demo_sysadmin_cron ./addons/TechnoLibre_odoo-code-generator-template code_generator_auto_backup
.PHONY: test_code_generator_migrator_demo_mariadb_sql_example_1
test_code_generator_migrator_demo_mariadb_sql_example_1:
./script/code_generator/check_git_change_code_generator.sh ./addons/TechnoLibre_odoo-code-generator-template
./script/database/restore_mariadb_sql_example_1.sh
./script/db_restore.py --database test_code_generator
./script/addons/install_addons_dev.sh test_code_generator code_generator_portal
#./script/addons/install_addons_dev.sh test_code_generator code_generator_migrator_demo_mariadb_sql_example_1
./script/code_generator/install_and_test_code_generator.sh test_code_generator code_generator_migrator_demo_mariadb_sql_example_1 ./addons/TechnoLibre_odoo-code-generator-template demo_mariadb_sql_example_1
.PHONY: test_code_generator_template_demo_mariadb_sql_example_1
test_code_generator_template_demo_mariadb_sql_example_1:
./script/code_generator/check_git_change_code_generator.sh ./addons/TechnoLibre_odoo-code-generator-template
./script/db_restore.py --database test_template
./script/addons/install_addons_dev.sh test_template code_generator_portal,demo_mariadb_sql_example_1
./script/code_generator/search_class_model.py --quiet -d addons/TechnoLibre_odoo-code-generator-template/demo_mariadb_sql_example_1 -t addons/TechnoLibre_odoo-code-generator-template/code_generator_template_demo_mariadb_sql_example_1 --with_inherit
#./script/addons/install_addons_dev.sh test_template code_generator_template_demo_mariadb_sql_example_1
./script/code_generator/install_and_test_code_generator.sh test_template code_generator_template_demo_mariadb_sql_example_1 ./addons/TechnoLibre_odoo-code-generator-template code_generator_demo_mariadb_sql_example_1
.PHONY: test_code_generator_demo_mariadb_sql_example_1
test_code_generator_demo_mariadb_sql_example_1:
./script/code_generator/check_git_change_code_generator.sh ./addons/TechnoLibre_odoo-code-generator-template
./script/db_restore.py --database test_code_generator
./script/addons/install_addons_dev.sh test_code_generator code_generator_portal
#./script/addons/install_addons_dev.sh test_code_generator code_generator_demo_mariadb_sql_example_1
./script/code_generator/install_and_test_code_generator.sh test_code_generator code_generator_demo_mariadb_sql_example_1 ./addons/TechnoLibre_odoo-code-generator-template demo_mariadb_sql_example_1
##############
# tag #
@ -328,22 +496,39 @@ open_terminal:
# format #
############
.PHONY: format
format: format_code_generator format_code_generator_template format_script
format:
./script/make.sh format_code_generator
./script/make.sh format_code_generator_template
./script/make.sh format_script
.PHONY: format_code_generator
format_code_generator:
.venv/bin/isort --profile black -l 79 ./addons/TechnoLibre_odoo-code-generator/
./script/maintenance/black.sh ./addons/TechnoLibre_odoo-code-generator/
#./script/maintenance/prettier_xml.sh ./addons/TechnoLibre_odoo-code-generator/
.PHONY: format_code_generator_template
format_code_generator_template:
.venv/bin/isort --profile black -l 79 ./addons/TechnoLibre_odoo-code-generator-template/
./script/maintenance/black.sh ./addons/TechnoLibre_odoo-code-generator-template/
#./script/maintenance/prettier_xml.sh ./addons/TechnoLibre_odoo-code-generator-template/
.PHONY: format_script
format_script:
#.venv/bin/isort --profile black -l 79 ./script/ --gitignore
./script/maintenance/black.sh ./script/
.PHONY: format_script_isort_only
format_script_isort_only:
.venv/bin/isort --profile black -l 79 ./script/ --gitignore
#########
# log #
#########
.PHONY: log_show_test
log_show_test:
vim ${LOG_FILE}
###########
# clean #
###########
@ -372,7 +557,9 @@ docker_stop:
docker-compose down
.PHONY: docker_restart_daemon
docker_restart_daemon: docker_stop docker_run_daemon
docker_restart_daemon:
./script/make.sh docker_stop
./script/make.sh docker_run_daemon
.PHONY: docker_show_databases
docker_show_databases:
@ -500,21 +687,41 @@ config_gen_image_db:
i18n_generate_demo_portal:
./.venv/bin/python3 ./odoo/odoo-bin i18n --database code_generator --module demo_portal --addons_path addons/TechnoLibre_odoo-code-generator
###########
# Clean #
###########
.PHONY: clean
clean:
find . -type f -name '*.py[co]' -delete -o -type d -name __pycache__ -delete
###################
# Documentation #
###################
# documentation all
.PHONY: doc
doc: doc_dev doc_migration doc_test doc_user
doc:
./script/make.sh doc_dev
./script/make.sh doc_migration
./script/make.sh doc_test
./script/make.sh doc_user
./script/make.sh doc_markdown
# documentation clean all
.PHONY: doc_clean
doc_clean: doc_clean_dev doc_clean_migration doc_clean_test doc_clean_user
doc_clean:
./script/make.sh doc_clean_dev
./script/make.sh doc_clean_migration
./script/make.sh doc_clean_test
./script/make.sh doc_clean_user
# documentation dev
.PHONY: doc_dev
doc_dev:
source ./.venv/bin/activate && make -C doc/itpp-labs_odoo-development/docs html || exit 1
.PHONY: open_doc_dev
open_doc_dev:
-$(BROWSER) doc/itpp-labs_odoo-development/docs/_build/html/index.html
.PHONY: doc_clean_dev
@ -525,6 +732,9 @@ doc_clean_dev:
.PHONY: doc_migration
doc_migration:
source ./.venv/bin/activate && make -C doc/itpp-labs_odoo-port-docs/docs html || exit 1
.PHONY: open_doc_migration
open_doc_migration:
-$(BROWSER) doc/itpp-labs_odoo-port-docs/docs/_build/html/index.html
.PHONY: doc_clean_migration
@ -535,6 +745,9 @@ doc_clean_migration:
.PHONY: doc_test
doc_test:
source ./.venv/bin/activate && make -C doc/itpp-labs_odoo-test-docs/doc-src html || exit 1
.PHONY: open_doc_test
open_doc_test:
-$(BROWSER) doc/itpp-labs_odoo-test-docs/doc-src/_build/html/index.html
.PHONY: doc_clean_test
@ -545,6 +758,9 @@ doc_clean_test:
.PHONY: doc_user
doc_user:
source ./.venv/bin/activate && make -C doc/odoo_documentation-user html || exit 1
.PHONY: open_doc_user
open_doc_user:
-$(BROWSER) doc/odoo_documentation-user/_build/html/index.html
.PHONY: doc_clean_user

View file

@ -2,6 +2,14 @@
requires = [ "poetry>=1.0.0",]
build-backend = "poetry.masonry.api"
[tool.isort]
profile = "black"
py_version = 37
[tool.black]
line-length = 79
target-version = [ "py37",]
[tool.poetry]
name = "ERPLibre"
version = "1.2.1"
@ -65,6 +73,7 @@ html2text = "2016.9.19"
html5print = "^0.1.2"
invoice2data = "^0.3.6"
iscompatible = "^0.1.1"
isort = "^5.10.1"
jira = "2.0.0"
js2py = "^0.71"
keystoneauth1 = "3.14.0"

View file

@ -43,6 +43,7 @@ html5print
css-html-prettify
BeautifulSoup4
git+https://github.com/psf/black.git@21.6b0
isort
# For updating poetry
toml

View file

@ -0,0 +1,115 @@
#!./.venv/bin/python
import argparse
import configparser
import logging
import os
import sys
from collections import defaultdict
CONFIG_PATH = "./config.conf"
logging.basicConfig(
format=(
"%(asctime)s,%(msecs)d %(levelname)-8s [%(filename)s:%(lineno)d]"
" %(message)s"
),
datefmt="%Y-%m-%d:%H:%M:%S",
level=logging.INFO,
)
_logger = logging.getLogger(__name__)
def get_config():
parser = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter,
description="""\
Check if module exist and is not multiple here to manage conflict.
""",
epilog="""\
""",
)
parser.add_argument(
"-m",
"--module",
required=True,
help="Module name to search, a list can be use separate by ,",
)
parser.add_argument(
"--debug",
action="store_true",
help="Enable debug output",
)
args = parser.parse_args()
return args
def main():
config = get_config()
if config.debug:
logging.getLogger().setLevel(logging.DEBUG)
config_parser = configparser.ConfigParser()
config_parser.read(CONFIG_PATH)
if "options" in config_parser:
if "addons_path" in config_parser["options"]:
addons_path = config_parser["options"]["addons_path"]
else:
_logger.error(
"Missing item 'addons_path' in section 'options' in"
f" '{CONFIG_PATH}'"
)
return -1
else:
_logger.error(f"Missing section 'options' in '{CONFIG_PATH}'")
return -1
lst_addons_path = addons_path.split(",")
lst_module = config.module.split(",")
dct_module_exist = defaultdict(list)
dct_module_exist_empty = defaultdict(list)
lst_module_not_exist = []
for module in lst_module:
for path in lst_addons_path:
module_path = os.path.join(path, module)
manifest_file_path = os.path.join(module_path, "__manifest__.py")
if os.path.isdir(module_path):
if os.path.isfile(manifest_file_path):
dct_module_exist[module].append(module_path)
else:
dct_module_exist_empty[module].append(module_path)
if module not in dct_module_exist.keys():
lst_module_not_exist.append(module)
is_good = True
if lst_module_not_exist:
is_good = False
module_list = "'" + "', '".join(lst_module_not_exist) + "'"
_logger.error(
"Missing"
f" module{'s' if len(lst_module_not_exist) > 1 else ''} {module_list}"
)
if dct_module_exist:
for key, lst_value in dct_module_exist.items():
if len(lst_value) != 1:
is_good = False
module_list = "'" + "', '".join(lst_value) + "'"
_logger.error(f"Conflict modules: {module_list}")
for value in lst_value:
print(value)
if dct_module_exist_empty:
for key, lst_value in dct_module_exist_empty.items():
module_list = "'" + "', '".join(lst_value) + "'"
_logger.warning(
"Found this directory, but missing __manifest__.py:"
f" {module_list}"
)
return 0 if is_good else -1
if __name__ == "__main__":
sys.exit(main())

View file

@ -1,5 +1,11 @@
#!/usr/bin/env bash
./run.sh --no-http --stop-after-init -d $1 -i $2 -u $2
./script/addons/check_addons_exist.py -m "$2"
retVal=$?
if [[ $retVal -ne 0 ]]; then
echo "Error install_addons.sh"
exit 1
fi
./run.sh --no-http --stop-after-init -d "$1" -i "$2" -u "$2"
retVal=$?
if [[ $retVal -ne 0 ]]; then
echo "Error install_addons.sh"

View file

@ -1,5 +1,11 @@
#!/usr/bin/env bash
./run.sh --no-http --stop-after-init --dev qweb -d $1 -i $2 -u $2
./script/addons/check_addons_exist.py -m "$2"
retVal=$?
if [[ $retVal -ne 0 ]]; then
echo "Error install_addons.sh"
exit 1
fi
./run.sh --no-http --stop-after-init --dev qweb -d "$1" -i "$2" -u "$2"
retVal=$?
if [[ $retVal -ne 0 ]]; then
echo "Error install_addons_dev.sh"

View file

@ -1,6 +1,12 @@
#!/usr/bin/env bash
./script/addons/uninstall_addons.sh $1 $2
./script/addons/install_addons_dev.sh $1 $2
./script/addons/check_addons_exist.py -m "$2"
retVal=$?
if [[ $retVal -ne 0 ]]; then
echo "Error install_addons.sh"
exit 1
fi
./script/addons/uninstall_addons.sh "$1" "$2"
./script/addons/install_addons_dev.sh "$1" "$2"
retVal=$?
if [[ $retVal -ne 0 ]]; then
echo "Error reinstall_addons.sh"

View file

@ -1,2 +1,8 @@
#!/usr/bin/env bash
./run.sh --no-http --stop-after-init -d $1 --uninstall $2
./script/addons/check_addons_exist.py -m "$2"
retVal=$?
if [[ $retVal -ne 0 ]]; then
echo "Error install_addons.sh"
exit 1
fi
./run.sh --no-http --stop-after-init -d "$1" --uninstall "$2"

View file

@ -73,27 +73,41 @@ On_IPurple='\033[0;105m' # Purple
On_ICyan='\033[0;106m' # Cyan
On_IWhite='\033[0;107m' # White
# TODO support argument to test only specified path
check_git() {
REP=$1
cd ${REP}
cd "${REP}" || exit
output=$(git status --porcelain)
if [ -z "$output" ]; then
echo "PASS - ${REP}"
if [ $# -gt 1 ]; then
output=$(git status --porcelain "$2")
else
echo -e "${Red}FAIL - ${REP}${Color_Off}"
output=$(git status --porcelain)
fi
if [ -z "$output" ]; then
if [ $# -gt 1 ]; then
echo "PASS - ${REP}/${2}"
else
echo "PASS - ${REP}"
fi
else
if [ $# -gt 1 ]; then
echo -e "${Red}FAIL - ${REP}${2}${Color_Off}"
else
echo -e "${Red}FAIL - ${REP}${Color_Off}"
fi
echo -e "${BRed}${output}${Color_Off}"
cd - >/dev/null
cd - >/dev/null || exit
exit 1
fi
cd - >/dev/null
cd - >/dev/null || exit
}
if [ $# -gt 0 ]; then
check_git "$1"
if [ $# -gt 1 ]; then
check_git "$1" "$2"
else
check_git "$1"
fi
else
# check_git "./addons/TechnoLibre_odoo-code-generator"
check_git "./addons/TechnoLibre_odoo-code-generator-template"

View file

@ -0,0 +1,60 @@
#!./.venv/bin/python
import argparse
import logging
import os
import sys
logging.basicConfig(level=logging.DEBUG)
_logger = logging.getLogger(__name__)
# TODO Check if exist, (A) master[template], (B) replicator[code_generator], (C) module
# TODO if force, recreate from C
# TODO if c is code_generator_demo with a different name, execute it.
# TODO if a exist, execute it, and execute b.
# TODO open commit view
def get_config():
parser = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter,
description="""\
Get the list of updating module in a working path,
for each module, search where it's suppose to generate new code,
compare if the date change of directory is after the starting date.
""",
epilog="""\
""",
)
parser.add_argument(
"-d",
"--directory",
required=True,
help="Directory of the module.",
)
parser.add_argument(
"-m",
"--module_name",
required=True,
help="Module name to create",
)
parser.add_argument(
"-f",
"--force",
required=True,
help="Force override directory and module.",
)
args = parser.parse_args()
return args
def main():
config = get_config()
if not os.path.exists(config.directory):
_logger.error(f"Path directory {config.directory} not exist.")
return -1
return 0
if __name__ == "__main__":
sys.exit(main())

View file

@ -1,29 +1,47 @@
#!/usr/bin/env bash
# $1 is database name
# $2 is module name
# $2 is module name separate by ,
# $3 is directory path to check
# $4 is generated module name separate by ,
./script/addons/install_addons_dev.sh $1 $2
if (($# < 4)); then
echo "ERROR, need 4 arguments: 1-database name, 2-list of module to install, 3-directory to check difference, 4-list of generated module"
exit 1
fi
INIT_DATETIME=$(date +%s)
./script/addons/install_addons_dev.sh "$1" "$2"
retVal=$?
if [[ $retVal -ne 0 ]]; then
echo "Error ./script/addons/install_addons_dev.sh ${1} ${2}"
exit 1
fi
# Check if the code was updated
./script/code_generator/test_code_generator_update_module.py -m "$4" -d "$3" --datetime "${INIT_DATETIME}"
retVal=$?
if [[ $retVal -ne 0 ]]; then
echo "Error ./script/code_generator/test_code_generator_update_module.py ${4} ${3}"
exit 1
fi
# TODO check output when got warning
./script/repo_revert_git_diff_date_from_code_generator.py
# Remove pot and po diff
cd $3
cd "$3" || exit 1
# git 2.22 and more, else use next command
#BRANCH=$(git branch --show-current)
BRANCH=$(git rev-parse --abbrev-ref HEAD)
#BRANCH=$(git rev-parse --abbrev-ref HEAD)
# Support old version git < 2.23.0
# git restore --source="${BRANCH}" "*.po*"
git checkout -- "*.po*"
cd -
./script/maintenance/black.sh $3
cd - || exit 1
./script/maintenance/black.sh "$3"
echo "TEST ${2}"
./script/code_generator/check_git_change_code_generator.sh $3
./script/code_generator/check_git_change_code_generator.sh "$3"
retVal=$?
if [[ $retVal -ne 0 ]]; then
echo "Error ./script/code_generator/check_git_change_code_generator.sh"

View file

@ -0,0 +1,498 @@
#!./.venv/bin/python
import argparse
import logging
import os
import sys
import uuid
from git import Repo
from git.exc import InvalidGitRepositoryError, NoSuchPathError
CODE_GENERATOR_DIRECTORY = "./addons/TechnoLibre_odoo-code-generator-template/"
CODE_GENERATOR_DEMO_NAME = "code_generator_demo"
KEY_REPLACE_CODE_GENERATOR_DEMO = 'MODULE_NAME = "%s"'
logging.basicConfig(
format=(
"%(asctime)s,%(msecs)d %(levelname)-8s [%(filename)s:%(lineno)d]"
" %(message)s"
),
datefmt="%Y-%m-%d:%H:%M:%S",
level=logging.INFO,
)
_logger = logging.getLogger(__name__)
# TODO Check if exist DONE
# TODO change name into code_generator_demo DONE
# TODO Create code generator empty module with demo DONE
# TODO revert code_generator_demo DONE
# TODO execute create_code_generator_from_existing_module.sh with force option
# TODO open web interface on right database already selected locally with make run
def get_config():
parser = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter,
description="""\
Create new project for a single module with code generator suite.
""",
epilog="""\
""",
)
parser.add_argument(
"-d",
"--directory",
required=True,
help="Directory of the module, need to be a git root directory.",
)
parser.add_argument(
"-m",
"--module",
required=True,
help="Module name to create",
)
parser.add_argument(
"--directory_code_generator",
help="The directory of the code_generator to use.",
)
parser.add_argument(
"--code_generator_name",
help="The name of the code_generator to use.",
)
parser.add_argument(
"--directory_template_name",
help="The directory of the template to use.",
)
parser.add_argument(
"--template_name",
help="The name of the template to use.",
)
parser.add_argument(
"-f",
"--force",
action="store_true",
help="Force override directory and module.",
)
parser.add_argument(
"--do_not_update_config",
action="store_true",
help=(
"Ignore updating config file. This is a patch for a bug for"
" duplicate path, but 1 relative and the other is absolute."
),
)
parser.add_argument(
"--keep_bd_alive",
action="store_true",
help="By default, the bd is cleaned after a run.",
)
parser.add_argument(
"--debug",
action="store_true",
help="Enable debug output",
)
args = parser.parse_args()
return args
class ProjectManagement:
def __init__(
self,
module_name,
module_directory,
cg_name="",
cg_directory="",
template_name="",
template_directory="",
force=False,
ignore_config=False,
keep_bd_alive=False,
):
self.force = force
self.ignore_config = ignore_config
self.keep_bd_alive = keep_bd_alive
self.msg_error = ""
self.origin_config_txt = ""
self.has_config_update = False
self.module_directory = module_directory
if not os.path.exists(self.module_directory):
self.msg_error = (
f"Path directory '{self.module_directory}' not exist."
)
_logger.error(self.msg_error)
return
self.cg_directory = cg_directory if cg_directory else module_directory
if not os.path.exists(self.cg_directory):
self.msg_error = (
f"Path cg directory '{self.cg_directory}' not exist."
)
_logger.error(self.msg_error)
return
self.template_directory = (
template_directory if template_directory else module_directory
)
if not os.path.exists(self.template_directory):
self.msg_error = (
f"Path template directory '{self.template_directory}' not"
" exist."
)
_logger.error(self.msg_error)
return
if not module_name:
self.msg_error = "Module name is missing."
_logger.error(self.msg_error)
return
# Get module name
self.module_name = module_name
# Get code_generator name
self.cg_name = self._generate_cg_name(default=cg_name)
# Get template name
self.template_name = self._generate_template_name(
default=template_name
)
def _generate_cg_name(self, default=""):
if default:
return default
return f"code_generator_{self.module_name}"
def _generate_template_name(self, default=""):
if default:
return default
return f"code_generator_template_{self.module_name}"
def search_and_replace_file(self, filepath, lst_search_and_replace):
"""
lst_search_and_replace is a list of tuple, first item is search, second is replace
"""
with open(filepath, "r") as file:
txt = file.read()
for search, replace in lst_search_and_replace:
if search not in txt:
self.msg_error = (
f"Cannot find '{search}' in file '{filepath}'"
)
_logger.error(self.msg_error)
return False
txt = txt.replace(search, replace)
with open(filepath, "w") as file:
file.write(txt)
return True
@staticmethod
def validate_path_ready_to_be_override(name, directory, path=""):
if not path:
path = os.path.join(directory, name)
if not os.path.exists(path):
return True
# Check if in git
try:
git_repo = Repo(directory)
except NoSuchPathError:
_logger.error(f"Directory not existing '{directory}'")
return False
except InvalidGitRepositoryError:
_logger.error(
f"The path '{path}' exist, but no git repo, use force to"
" ignore it."
)
return False
status = git_repo.git.status(name, porcelain=True)
if status:
_logger.error(
f"The directory '{path}' has git difference, use force to"
" ignore it."
)
print(status)
return False
return True
@staticmethod
def restore_git_code_generator_demo(
code_generator_demo_path, relative_path
):
try:
git_repo = Repo(code_generator_demo_path)
except NoSuchPathError:
_logger.error(
f"Directory not existing '{code_generator_demo_path}'"
)
return False
except InvalidGitRepositoryError:
_logger.error(
f"The path '{code_generator_demo_path}' exist, but no git repo"
)
return False
git_repo.git.restore(relative_path)
def generate_module(self):
module_path = os.path.join(self.module_directory, self.module_name)
if not self.force and not self.validate_path_ready_to_be_override(
self.module_name, self.module_directory, path=module_path
):
self.msg_error = f"Cannot generate on module path '{module_path}'"
_logger.error(self.msg_error)
return False
cg_path = os.path.join(self.cg_directory, self.cg_name)
if not self.force and not self.validate_path_ready_to_be_override(
self.cg_name, self.cg_directory, path=cg_path
):
self.msg_error = f"Cannot generate on cg path '{cg_path}'"
_logger.error(self.msg_error)
return False
template_path = os.path.join(
self.template_directory, self.template_name
)
template_hooks_py = os.path.join(template_path, "hooks.py")
if not self.force and not self.validate_path_ready_to_be_override(
self.template_name, self.template_directory, path=template_path
):
self.msg_error = (
f"Cannot generate on template path '{template_path}'"
)
_logger.error(self.msg_error)
return False
# Validate code_generator_demo
code_generator_demo_path = os.path.join(
CODE_GENERATOR_DIRECTORY, CODE_GENERATOR_DEMO_NAME
)
code_generator_demo_hooks_py = os.path.join(
code_generator_demo_path, "hooks.py"
)
code_generator_hooks_path_relative = os.path.join(
CODE_GENERATOR_DEMO_NAME, "hooks.py"
)
if not os.path.exists(code_generator_demo_path):
self.msg_error = (
"code_generator_demo is not accessible"
f" '{code_generator_demo_path}'"
)
_logger.error(self.msg_error)
return False
if not (
self.validate_path_ready_to_be_override(
CODE_GENERATOR_DEMO_NAME, CODE_GENERATOR_DIRECTORY
)
and self.search_and_replace_file(
code_generator_demo_hooks_py,
[
(
KEY_REPLACE_CODE_GENERATOR_DEMO
% CODE_GENERATOR_DEMO_NAME,
KEY_REPLACE_CODE_GENERATOR_DEMO % self.template_name,
),
(
'value["enable_sync_template"] = False',
'value["enable_sync_template"] = True',
),
(
"# path_module_generate ="
" os.path.normpath(os.path.join(os.path.dirname(__file__),"
" '..'))",
f'path_module_generate = "{self.module_directory}"',
),
(
'# "path_sync_code": path_module_generate,',
'"path_sync_code": path_module_generate,',
),
(
'# value["template_module_path_generated_extension"]'
' = "."',
'value["template_module_path_generated_extension"] ='
f' "{self.cg_directory}"',
),
],
)
):
return False
self.update_config()
bd_name_demo = f"new_project_code_generator_demo_{uuid.uuid4()}"[:63]
cmd = f"./script/db_restore.py --database {bd_name_demo}"
_logger.info(cmd)
os.system(cmd)
_logger.info("========= GENERATE code_generator_demo =========")
cmd = (
f"./script/addons/install_addons_dev.sh {bd_name_demo}"
" code_generator_demo"
)
os.system(cmd)
if not self.keep_bd_alive:
cmd = (
"./.venv/bin/python3 ./odoo/odoo-bin db --drop --database"
f" {bd_name_demo}"
)
_logger.info(cmd)
os.system(cmd)
# Revert code_generator_demo
self.restore_git_code_generator_demo(
CODE_GENERATOR_DIRECTORY, code_generator_hooks_path_relative
)
# Validate
if not os.path.exists(template_path):
_logger.error(f"Module template not exists '{template_path}'")
self.revert_config()
return False
else:
_logger.info(f"Module template exists '{template_path}'")
self.search_and_replace_file(
template_hooks_py,
[
(
'value["enable_template_wizard_view"] = False',
'value["enable_template_wizard_view"] = True',
),
],
)
# Execute all
bd_name_template = (
f"new_project_code_generator_template_{uuid.uuid4()}"[:63]
)[:63]
cmd = f"./script/db_restore.py --database {bd_name_template}"
os.system(cmd)
_logger.info(cmd)
_logger.info(f"========= GENERATE {self.template_name} =========")
# TODO maybe the module exist somewhere else
if os.path.exists(module_path):
# Install module before running code generator
cmd = (
"./script/code_generator/search_class_model.py --quiet -d"
f" {module_path} -t {template_path}"
)
_logger.info(cmd)
os.system(cmd)
cmd = (
f"./script/addons/install_addons_dev.sh {bd_name_template}"
f" {self.module_name}"
)
_logger.info(cmd)
os.system(cmd)
cmd = (
f"./script/addons/install_addons_dev.sh {bd_name_template}"
f" {self.template_name}"
)
_logger.info(cmd)
os.system(cmd)
if not self.keep_bd_alive:
cmd = (
"./.venv/bin/python3 ./odoo/odoo-bin db --drop --database"
f" {bd_name_template}"
)
_logger.info(cmd)
os.system(cmd)
# Validate
if not os.path.exists(cg_path):
_logger.error(f"Module cg not exists '{cg_path}'")
self.revert_config()
return False
else:
_logger.info(f"Module cg exists '{cg_path}'")
bd_name_generator = f"new_project_code_generator_{uuid.uuid4()}"[:63]
cmd = f"./script/db_restore.py --database {bd_name_generator}"
_logger.info(cmd)
os.system(cmd)
_logger.info(f"========= GENERATE {self.cg_name} =========")
cmd = (
f"./script/addons/install_addons_dev.sh {bd_name_generator}"
f" {self.cg_name}"
)
_logger.info(cmd)
os.system(cmd)
if not self.keep_bd_alive:
cmd = (
"./.venv/bin/python3 ./odoo/odoo-bin db --drop --database"
f" {bd_name_generator}"
)[:63]
_logger.info(cmd)
os.system(cmd)
# Validate
if not os.path.exists(template_path):
_logger.error(f"Module not exists '{module_path}'")
self.revert_config()
return False
else:
_logger.info(f"Module exists '{module_path}'")
self.revert_config()
return True
def update_config(self):
if self.ignore_config:
return
# Backup config and restore it after, check if path exist or add it temporary
with open("./config.conf") as config:
config_txt = config.read()
self.origin_config_txt = config_txt
lst_directory = list(
{
self.cg_directory,
self.module_directory,
self.template_directory,
}
)
lst_directory_to_add = []
for directory in lst_directory:
if directory not in config_txt:
self.has_config_update = True
lst_directory_to_add.append(directory)
if lst_directory_to_add:
new_str = "addons_path = " + ",".join(lst_directory_to_add) + ","
config_txt = config_txt.replace("addons_path = ", new_str)
with open("./config.conf", "w") as config:
config.write(config_txt)
def revert_config(self):
if self.ignore_config:
return
with open("./config.conf", "w") as config:
config.write(self.origin_config_txt)
def main():
config = get_config()
if config.debug:
logging.getLogger().setLevel(logging.DEBUG)
project = ProjectManagement(
config.module,
config.directory,
cg_name=config.code_generator_name,
template_name=config.template_name,
force=config.force,
ignore_config=config.do_not_update_config,
keep_bd_alive=config.keep_bd_alive,
)
if project.msg_error:
return -1
if not project.generate_module():
return -1
return 0
if __name__ == "__main__":
sys.exit(main())

View file

@ -0,0 +1,223 @@
#!./.venv/bin/python
import argparse
# import glob
import ast
import logging
import os
import sys
from pathlib import Path
logging.basicConfig(level=logging.DEBUG)
_logger = logging.getLogger(__name__)
def get_config():
"""Parse command line arguments, extracting the config file name,
returning the union of config file and command line arguments
:return: dict of config file settings and command line arguments
"""
parser = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter,
description="""\
Search all method class and give a list separate by ;
""",
epilog="""\
""",
)
parser.add_argument(
"-d",
"--directory",
dest="directory",
required=True,
help="Directory to execute search in recursive.",
)
parser.add_argument(
"-t",
"--template_dir",
dest="template_dir",
help=(
"Overwrite value template_model_name in template module, give only"
" template source directory, will update file hooks.py"
),
)
parser.add_argument(
"--with_inherit",
action="store_true",
help="Will search inherit model",
)
parser.add_argument(
"-q",
"--quiet",
action="store_true",
help="Don't show output of found model.",
)
args = parser.parse_args()
return args
def search_and_replace(
f_lines, hooks_file_path, models_name, search_word="template_model_name"
):
if not models_name:
return f_lines
t_index = f_lines.find(search_word)
if t_index == -1:
_logger.error(f"Cannot find {search_word} in file {hooks_file_path}")
return -1
t_index_equation = f_lines.index("=", t_index + 1)
if t_index_equation == -1:
_logger.error(f"Cannot find {search_word} = in file {hooks_file_path}")
return -1
# find next character
i = 1
while f_lines[t_index_equation + i] in (" ", "\n"):
i += 1
first_char = f_lines[t_index_equation + i]
if first_char == "(":
second_char = ")"
elif f_lines[t_index_equation + i : t_index_equation + i + 3] == '"""':
first_char = '"""'
second_char = '"""'
else:
second_char = first_char
# t_index_first_quote = f_lines.index(first_char, t_index + 1)
t_index_second_quote = f_lines.index(first_char, t_index_equation + i)
if t_index_second_quote == -1:
_logger.error(
f'Cannot find {search_word} = "##" in file {hooks_file_path}'
)
return -1
t_index_third_quote = f_lines.index(second_char, t_index_second_quote + 1)
if t_index_third_quote == -1:
_logger.error(f"Cannot find third quote in file {hooks_file_path}")
return -1
# if "\n" in models_name:
# new_file_content = (
# f'{f_lines[:t_index_second_quote]}"""{models_name}"""{f_lines[t_index_third_quote + len(second_char):]}'
# )
# else:
# new_file_content = (
# f'{f_lines[:t_index_second_quote]}"{models_name}"{f_lines[t_index_third_quote + len(second_char):]}'
# )
new_file_content = (
f'{f_lines[:t_index_second_quote]}"{models_name}"{f_lines[t_index_third_quote + len(second_char):]}'
)
return new_file_content
def main():
config = get_config()
if not os.path.exists(config.directory):
_logger.error(f"Path directory {config.directory} not exist.")
return -1
lst_model_name = []
lst_model_inherit_name = []
lst_search_target = ("_name",)
lst_search_inherit_target = ("_inherit",) if config.with_inherit else []
# lst_py_file = glob.glob(os.path.join(config.directory, "***", "*.py"))
lst_py_file = Path(config.directory).rglob("*.py")
for py_file in lst_py_file:
if py_file == "__init__.py":
continue
with open(py_file, "r") as source:
f_lines = source.read()
try:
f_ast = ast.parse(f_lines)
except Exception as e:
_logger.error(f"Cannot parse file {py_file}")
continue
for children in f_ast.body:
if type(children) == ast.ClassDef:
# Detect good _name
for node in children.body:
if (
type(node) is ast.Assign
and node.targets
and type(node.targets[0]) is ast.Name
# and node.targets[0].id in ("_name",)
and type(node.value) is ast.Str
):
if (
lst_search_target
and node.targets[0].id in lst_search_target
):
if node.value.s in lst_model_name:
_logger.warning(
"Duplicated model name"
f" {node.value.s} from file {py_file}"
)
else:
lst_model_name.append(node.value.s)
if (
lst_search_inherit_target
and node.targets[0].id
in lst_search_inherit_target
):
if node.value.s in lst_model_inherit_name:
_logger.warning(
"Duplicated model inherit name"
f" {node.value.s} from file {py_file}"
)
else:
lst_model_inherit_name.append(node.value.s)
lst_model_name.sort()
lst_model_inherit_name.sort()
models_name = "; ".join(lst_model_name)
# TODO temporary fix, remove this when it's supported
lst_ignored_inherit = ["portal.mixin", "mail.thread"]
for ignored_inherit in lst_ignored_inherit:
if ignored_inherit in lst_model_inherit_name:
lst_model_inherit_name.remove(ignored_inherit)
models_inherit_name = "; ".join(lst_model_inherit_name)
if not models_name:
_logger.warning(f"Missing models class in {config.directory}")
elif not config.quiet:
# _logger.info(models_name)
print(models_name)
print(models_inherit_name)
if config.template_dir:
if not os.path.exists(config.template_dir):
_logger.error(
f"Path template dir {config.template_dir} not exist."
)
return -1
hooks_file_path = os.path.join(config.template_dir, "hooks.py")
if not os.path.exists(hooks_file_path):
_logger.error(
f"Path template hooks.py file {hooks_file_path} not exist."
)
return -1
with open(hooks_file_path, "r") as source:
f_lines = source.read()
# Throw exception if not found
new_file_content = search_and_replace(
f_lines, hooks_file_path, models_name
)
if models_inherit_name:
new_file_content = search_and_replace(
new_file_content,
hooks_file_path,
models_inherit_name,
search_word="template_inherit_model_name",
)
with open(hooks_file_path, "w") as source:
source.write(new_file_content)
# Call black
os.system(f"./script/maintenance/black.sh {hooks_file_path}")
return 0
if __name__ == "__main__":
status = main()
sys.exit(status)

View file

@ -0,0 +1,89 @@
#!./.venv/bin/python
import argparse
import logging
import os
import sys
logging.basicConfig(level=logging.DEBUG)
_logger = logging.getLogger(__name__)
def get_config():
parser = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter,
description="""\
Get the list of updating module in a working path,
for each module, search where it's suppose to generate new code,
compare if the date change of directory is after the starting date.
""",
epilog="""\
""",
)
parser.add_argument(
"-d",
"--directory",
dest="directory",
required=True,
help="Directory of the module to check.",
)
parser.add_argument(
"-m",
"--module_list",
dest="module_list",
required=True,
help="List of module, separate by ','",
)
parser.add_argument(
"--datetime",
dest="datetime",
required=True,
help="The datetime to check if the generated module is after.",
)
args = parser.parse_args()
return args
def main():
config = get_config()
if not os.path.exists(config.directory):
_logger.error(f"Path directory {config.directory} not exist.")
return -1
lst_module = [a for a in config.module_list.split(",") if a.split()]
if not lst_module:
_logger.error(f"No module was selected, be sure to use --module_list")
return -1
lst_result_good = []
lst_result_wrong = []
for module_name in lst_module:
if config.directory.endswith(module_name):
module_path = config.directory
else:
module_path = os.path.join(config.directory, module_name)
if not os.path.exists(module_path):
_logger.error(
f"Module '{module_name}' not existing in path"
f" '{config.directory}'."
)
return -1
stat = os.stat(module_path)
result = stat.st_mtime > float(config.datetime)
if result:
lst_result_good.append(module_name)
else:
lst_result_wrong.append(module_name)
if lst_result_wrong:
_logger.error(
"FAIL - Some modules wasn't updated, did you execute the code"
f" generator? {lst_result_wrong}"
)
return -1
elif lst_result_good:
_logger.info("SUCCESS - All modules are updated.")
return 0
if __name__ == "__main__":
sys.exit(main())

14
script/database/README.md Normal file
View file

@ -0,0 +1,14 @@
# Database
This section is for code generator database migrator.
This configuration is for development environnement.
You need to install script `./script/install_dev_extra_ubuntu.sh`.
## Restore database
Run script to restore database:
```bash
./script/database/restore_mariadb_sql_example_1.sh
```

44
script/database/db_drop_all.py Executable file
View file

@ -0,0 +1,44 @@
#!./.venv/bin/python
import argparse
import logging
import subprocess
import sys
logging.basicConfig(level=logging.DEBUG)
_logger = logging.getLogger(__name__)
def execute_shell(cmd):
out = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True)
return out.decode().strip() if out else ""
def get_config():
parser = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter,
description="""\
Drop all database, caution!
""",
epilog="""\
""",
)
args = parser.parse_args()
return args
def main():
config = get_config()
out_db = execute_shell("./.venv/bin/python3 ./odoo/odoo-bin db --list")
lst_db = out_db.split("\n")
for db_name in lst_db:
execute_shell(
"./.venv/bin/python3 ./odoo/odoo-bin db --drop --database"
f" {db_name}"
)
print(f"{db_name} deleted")
return 0
if __name__ == "__main__":
sys.exit(main())

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,29 @@
#!/usr/bin/env bash
SQL_PATH=./script/database/mariadb_sql_example_1.sql
# You need to set no password to mysql root user
# SET PASSWORD FOR root@localhost='';
# FLUSH PRIVILEGES;
# Second solution
# ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '';
# FLUSH PRIVILEGES;
echo "Create database and user"
mysql -u root << EOF
DROP DATABASE IF EXISTS mariadb_sql_example_1;
CREATE USER IF NOT EXISTS 'organization'@'localhost' IDENTIFIED BY 'organization';
GRANT ALL PRIVILEGES ON *.* TO 'organization'@'localhost' IDENTIFIED BY 'organization';
FLUSH PRIVILEGES;
CREATE DATABASE mariadb_sql_example_1;
EOF
echo "Fix SQL file"
sed -i "s/'0000-00-00'/NULL/g" ${SQL_PATH}
echo "Import SQL file"
mysql -u organization -porganization mariadb_sql_example_1 < ${SQL_PATH}
echo "Fix SQL in database"
./.venv/bin/python ./script/database/fix_mariadb_sql_example_1.py

View file

@ -0,0 +1,104 @@
#!./.venv/bin/python
# © 2021 TechnoLibre (http://www.technolibre.ca)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import argparse
import logging
import os
import shutil
import subprocess
import tempfile
from git import Repo
_logger = logging.getLogger(__name__)
def get_config():
"""Parse command line arguments, extracting the config file name,
returning the union of config file and command line arguments
:return: dict of config file settings and command line arguments
"""
# TODO update description
parser = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter,
description="""Copy a directory in a temporary directory to compare with a generated code to understand the difference.""",
epilog="""\
""",
)
parser.add_argument(
"--directory1",
required=True,
help="Compare input1 to input2. Input1 is older config.",
)
parser.add_argument(
"--directory2",
required=True,
help="The generated code directory",
)
parser.add_argument(
"--replace_directory",
action="store_true",
help="Erase after first commit to see removing file.",
)
parser.add_argument(
"-q",
"--quiet",
action="store_true",
help="Don't show output of difference.",
)
parser.add_argument("--git_gui", action="store_true", help="Open git gui.")
parser.add_argument("--meld", action="store_true", help="Open meld.")
parser.add_argument(
"--clear",
action="store_true",
help="Delete temporary directory at the end of execution.",
)
args = parser.parse_args()
return args
def main():
config = get_config()
# path = tempfile.mkdtemp()
path = tempfile.NamedTemporaryFile().name
if os.path.exists(config.directory1) and os.path.exists(config.directory2):
if config.git_gui:
shutil.copytree(config.directory1, path)
shutil.copy2("./.gitignore", path)
# repo = Repo(path)
repo = Repo.init(path=path)
repo.git.add(".")
repo.git.commit("-m", "First commit")
# shutil.copy2(config.directory2, path)
if config.replace_directory:
subprocess.call(f"rm -r {path}/*", shell=True)
subprocess.call(f"cp -r {config.directory2}/* {path}", shell=True)
status = repo.git.diff()
if not config.quiet:
print(status)
try:
subprocess.call(f"cd {path};git gui", shell=True)
except:
pass
if config.clear:
shutil.rmtree(path, ignore_errors=True)
elif config.meld:
try:
subprocess.call(f"make clean", shell=True)
subprocess.call(
f"meld {config.directory1} {config.directory2}", shell=True
)
except:
pass
elif not os.path.exists(config.directory1):
_logger.error(f"Path is not existing {config.directory1}")
# elif not os.path.exists(config.directory2):
else:
_logger.error(f"Path is not existing {config.directory2}")
if __name__ == "__main__":
main()

View file

@ -49,8 +49,8 @@ curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poet
# Install Wkhtmltopdf if needed
#--------------------------------------------------
echo "\n---- Installing Wkhtmltopdf if needed ----"
if [ ! -f "wkhtmltox-0.12.6-1.macos-cocoa.pkg" ]; then
sudo wget https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6-1/wkhtmltox-0.12.6-1.macos-cocoa.pkg
sudo sudo installer -pkg wkhtmltox-0.12.6-1.macos-cocoa.pkg -target /
if [ ! -f "wkhtmltox-0.12.6-2.macos-cocoa.pkg" ]; then
sudo wget https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6-2/wkhtmltox-0.12.6-2.macos-cocoa.pkg
sudo sudo installer -pkg wkhtmltox-0.12.6-2.macos-cocoa.pkg -target /
else echo "Wkhtmltopdf already installed"
fi

View file

@ -53,7 +53,9 @@ sudo apt-get install libmariadbd-dev -y
sudo apt-get install make libssl-dev zlib1g-dev libreadline-dev libsqlite3-dev curl llvm libncurses5-dev libncursesw5-dev xz-utils tk-dev liblzma-dev -y
echo -e "\n---- Installing nodeJS NPM and rtlcss for LTR support ----"
sudo apt-get install nodejs npm -y
curl -fsSL https://deb.nodesource.com/setup_current.x | sudo -E bash -
sudo apt-get install -y nodejs
sudo npm install npm@latest -g
sudo npm install -g rtlcss
sudo npm install -g less

View file

@ -1,16 +1,17 @@
#!/usr/bin/env bash
################################################################################
# Script for installing ERPLibre locally for dev
# Author: Alexandre Ferreira Benevides
################################################################################
if [[ "${OSTYPE}" == "linux-gnu" ]]; then
OS=$(lsb_release -si)
VERSION=$(cat /etc/issue)
if [[ "${OS}" == "Ubuntu" ]]; then
echo "\n---- linux-gnu installation process started ----"
./script/install_debian_dependency.sh
if [[ "${VERSION}" == Ubuntu\ 18.04* || "${VERSION}" == Ubuntu\ 20.04* ]]; then
echo "\n---- linux-gnu installation process started ----"
./script/install_debian_dependency.sh
else
echo "Your version is not supported, only support 18.04 and 20.04 : ${VERSION}"
fi
else
echo "Your Linux system is not supported."
echo "Your Linux system is not supported, only support Ubuntu 18.04 or Ubuntu 20.04."
fi
elif [[ "${OSTYPE}" == "darwin"* ]]; then
echo "\n---- Darwin installation process started ----"

View file

@ -0,0 +1,40 @@
#!/usr/bin/env bash
# Need this for test extra
if [[ "${OSTYPE}" == "linux-gnu" ]]; then
OS=$(lsb_release -si)
VERSION=$(cat /etc/issue)
if [[ "${OS}" == "Ubuntu" ]]; then
if [[ "${VERSION}" == Ubuntu\ 18.04* || "${VERSION}" == Ubuntu\ 20.04* ]]; then
# Install mariadb
sudo apt install mariadb-client mariadb-server
echo "This is not for production, this is for development. Mysql user root will be accessible without password."
sudo mysql -u root << EOF
SET PASSWORD FOR root@localhost='';
FLUSH PRIVILEGES;
EOF
# Install docker
sudo apt-get update
sudo apt-get install ca-certificates curl gnupg lsb-release
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io
# Run without root
#sudo groupadd docker
sudo usermod -aG docker "$USER"
#newgrp docker
# Install docker-compose
sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
else
echo "Your version is not supported, only support 18.04 and 20.04 : ${VERSION}"
fi
else
echo "Your Linux system is not supported, only support Ubuntu 18.04 or Ubuntu 20.04."
fi
fi

View file

@ -1,4 +1,15 @@
#!/usr/bin/env bash
echo "==$&%== Begin ${@}"
echo "
===> ${@}
"
time make $@
echo "==$&%== End ${@}"
retVal=$?
echo "
<=== ${@}
"
if [[ $retVal -ne 0 ]]; then
echo "Error make ${@}"
exit 1
fi

View file

@ -5,6 +5,7 @@ paths=(
"${working_path}/"
"${working_path}/addons/TechnoLibre_odoo-code-generator"
"${working_path}/addons/TechnoLibre_odoo-code-generator-template"
"${working_path}/addons/OCA_server-tools"
)
cmd_before="cd "
@ -13,7 +14,7 @@ cmd_after=";gnome-terminal --tab -- bash -c 'git status;bash';"
#cmd_after=";gnome-terminal --tab;"
LONGCMD=""
for t in ${paths[@]}; do
for t in "${paths[@]}"; do
LONGCMD+=${cmd_before}${t}${cmd_after}
done

View file

@ -1,15 +1,20 @@
#!./.venv/bin/python
import os
import sys
import argparse
import logging
import os
import re
import sys
import git
from unidiff import PatchSet
import re
new_path = os.path.normpath(os.path.join(os.path.dirname(__file__), ".."))
sys.path.append(new_path)
logging.basicConfig(
format="%(asctime)s %(levelname)s: %(message)s",
datefmt="%Y-%m-%d %I:%M:%S",
)
_logger = logging.getLogger(__name__)
@ -62,6 +67,7 @@ def main():
with open(file_real_path, "r") as file:
lst_data = file.readlines()
for data in lst_data:
# TODO this remove code begin with this string, why do we remove it?
if data.startswith("#: code:addons/addons/"):
is_modified = True
else:

View file

@ -0,0 +1,77 @@
#!/usr/bin/env bash
if (($# < 1)); then
echo "ERROR, need 1 arguments: log file path"
exit 1
fi
Color_Off='\033[0m'
Red='\033[0;31m'
Blue='\033[0;34m'
Yellow='\033[0;33m'
LOG_FILE="${1}"
echo "== RESULT from ${LOG_FILE} =="
WARNING_MESSAGE=$(grep -i warning "${LOG_FILE}")
WARNING_MESSAGE=$(echo "${WARNING_MESSAGE}"|grep -v "have the same label:")
WARNING_MESSAGE=$(echo "${WARNING_MESSAGE}"|grep -v "odoo.addons.code_generator.extractor_module_file: Ignore next error about ALTER TABLE DROP CONSTRAINT.")
# Remove empty line
if [[ -z "${WARNING_MESSAGE// }" ]]; then
COUNT_WARNING=0
else
COUNT_WARNING=$(echo "${WARNING_MESSAGE}"|wc -l)
fi
ERROR_MESSAGE=$(grep -i error "${LOG_FILE}")
ERROR_MESSAGE=$(echo "${ERROR_MESSAGE}"|grep -v "fetchmail_notify_error_to_sender")
ERROR_MESSAGE=$(echo "${ERROR_MESSAGE}"|grep -v "odoo.sql_db: bad query: ALTER TABLE \"db_backup\" DROP CONSTRAINT \"db_backup_db_backup_name_unique\"")
ERROR_MESSAGE=$(echo "${ERROR_MESSAGE}"|grep -v "ERROR: constraint \"db_backup_db_backup_name_unique\" of relation \"db_backup\" does not exist")
ERROR_MESSAGE=$(echo "${ERROR_MESSAGE}"|grep -v "odoo.sql_db: bad query: ALTER TABLE \"db_backup\" DROP CONSTRAINT \"db_backup_db_backup_days_to_keep_positive\"")
ERROR_MESSAGE=$(echo "${ERROR_MESSAGE}"|grep -v "ERROR: constraint \"db_backup_db_backup_days_to_keep_positive\" of relation \"db_backup\" does not exist")
ERROR_MESSAGE=$(echo "${ERROR_MESSAGE}"|grep -v "odoo.addons.code_generator.extractor_module_file: Ignore next error about ALTER TABLE DROP CONSTRAINT.")
# Remove empty line
if [[ -z "${ERROR_MESSAGE// }" ]]; then
COUNT_ERROR=0
else
COUNT_ERROR=$(echo "${ERROR_MESSAGE}"|wc -l)
fi
echo_count_warnings () {
if (("${COUNT_WARNING}" > 1)); then
echo -e "${Yellow}${COUNT_WARNING} WARNINGS${Color_Off}"
else
echo -e "${Yellow}${COUNT_WARNING} WARNING${Color_Off}"
fi
}
echo_count_errors () {
if (("${COUNT_ERROR}" > 1)); then
echo -e "${Red}${COUNT_ERROR} ERRORS${Color_Off}"
else
echo -e "${Red}${COUNT_ERROR} ERROR${Color_Off}"
fi
}
echo -e "${Blue}Summary of check result${Color_Off}"
if (("${COUNT_WARNING}" > 0)); then
echo_count_warnings
fi
if (("${COUNT_ERROR}" > 0)); then
echo_count_errors
fi
echo -e "${Blue}Log result${Color_Off}"
if (("${COUNT_WARNING}" > 0)); then
if (("${COUNT_ERROR}" > 0)); then
echo_count_warnings
fi
echo -e "${WARNING_MESSAGE}"
fi
if (("${COUNT_ERROR}" > 0)); then
if (("${COUNT_WARNING}" > 0)); then
echo_count_errors
fi
echo -e "${ERROR_MESSAGE}"
fi
echo -e "${Blue}End of check result${Color_Off}"

607
script/test/run_parallel_test.py Executable file
View file

@ -0,0 +1,607 @@
#!./.venv/bin/python
import argparse
import asyncio
import datetime
import logging
import os
import sys
import time
import uuid
from typing import Tuple
from colorama import Fore
logging.basicConfig(level=logging.DEBUG)
_logger = logging.getLogger(__name__)
LOG_FILE = "./.venv/make_test.log"
def get_config():
parser = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter,
description="""\
Run code generator test in parallel.
""",
epilog="""\
""",
)
parser.add_argument(
"--ignore_init_check_git",
action="store_true",
help="Will not stop or init check if contain git change.",
)
args = parser.parse_args()
return args
lst_ignore_warning = [
"have the same label:",
"odoo.addons.code_generator.extractor_module_file: Ignore next error about"
" ALTER TABLE DROP CONSTRAINT.",
]
lst_ignore_error = [
"fetchmail_notify_error_to_sender",
'odoo.sql_db: bad query: ALTER TABLE "db_backup" DROP CONSTRAINT'
' "db_backup_db_backup_name_unique"',
'ERROR: constraint "db_backup_db_backup_name_unique" of relation'
' "db_backup" does not exist',
'odoo.sql_db: bad query: ALTER TABLE "db_backup" DROP CONSTRAINT'
' "db_backup_db_backup_days_to_keep_positive"',
'ERROR: constraint "db_backup_db_backup_days_to_keep_positive" of relation'
' "db_backup" does not exist',
"odoo.addons.code_generator.extractor_module_file: Ignore next error about"
" ALTER TABLE DROP CONSTRAINT.",
]
def extract_result(result, test_name, lst_error, lst_warning):
lst_log = result[0].split("\n")
for log_line in lst_log:
is_ignore_error = False
if "error" in log_line.lower():
# Remove ignore error
for ignore_error in lst_ignore_error:
if ignore_error in log_line:
is_ignore_error = True
break
if not is_ignore_error:
lst_error.append(log_line)
is_ignore_warning = False
if "warning" in log_line.lower():
# Remove ignore warning
for ignore_warning in lst_ignore_warning:
if ignore_warning in log_line:
is_ignore_warning = True
break
if not is_ignore_warning:
lst_warning.append(log_line)
if result[1]:
lst_error.append(f"Return status error for test {test_name}")
def check_result(task_list, tpl_result):
lst_error = []
lst_warning = []
for i, result in enumerate(tpl_result):
extract_result(
result, task_list[i].cr_code.co_name, lst_error, lst_warning
)
if lst_warning:
print(f"{Fore.YELLOW}{len(lst_warning)} WARNING{Fore.RESET}")
i = 0
for warning in lst_warning:
i += 1
print(f"[{i}]{warning}")
if lst_error:
print(f"{Fore.RED}{len(lst_error)} ERROR{Fore.RESET}")
i = 0
for error in lst_error:
i += 1
print(f"[{i}]{error}")
if lst_error or lst_warning:
str_result = (
f"{Fore.RED}{len(lst_error)} ERROR"
f" {Fore.YELLOW}{len(lst_warning)} WARNING"
)
else:
str_result = f"{Fore.GREEN}SUCCESS 🍰"
print(f"{Fore.BLUE}Summary TEST {str_result}{Fore.RESET}")
def print_log(lst_task, tpl_result):
if len(lst_task) != len(tpl_result):
_logger.error("Different length for log... What happen?")
return
with open(LOG_FILE, "w") as f:
for i, task in enumerate(lst_task):
result = tpl_result[i]
status_str = "PASS" if not result[1] else "FAIL"
f.write(
f"\nTest execution {i + 1} - {status_str} -"
f" {task.cr_code.co_name}\n\n"
)
if result[0]:
f.write(result[0])
f.write("\n")
print(f"Log file {LOG_FILE}")
async def run_command(*args, test_name=None):
# Create subprocess
start_time = time.time()
cmd_str = " ".join(args)
process = await asyncio.create_subprocess_exec(
*args,
# stdout must a pipe to be accessible as process.stdout
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
)
# Wait for the subprocess to finish
stdout, stderr = await process.communicate()
end_time = time.time()
diff_sec = end_time - start_time
# Return stdout + stderr, returncode
str_out = "\n" + stdout.decode().strip() + "\n" if stdout else ""
str_err = "\n" + stderr.decode().strip() + "\n" if stderr else ""
status_str = "FAIL" if process.returncode else "PASS"
if test_name:
str_output_init = (
f"\n\n{status_str} [{test_name}] [{diff_sec:.3f}s] Execute"
f' "{cmd_str}"\n\n'
)
else:
str_output_init = (
f'\n\n{status_str} [{diff_sec:.3f}s] Execute "{cmd_str}"\n\n'
)
all_output = str_out + str_err
print(str_output_init)
if process.returncode:
lst_error = []
lst_warning = []
extract_result(
(all_output, process.returncode), test_name, lst_error, lst_warning
)
for error in lst_error:
print(f"\t{error}")
for warning in lst_warning:
print(f"\t{warning}")
return str_output_init + all_output, process.returncode
async def test_exec(
path_module_check: str,
generated_module=None,
tested_module=None,
search_class_module=None,
script_after_init_check=None,
lst_init_module_name=None,
test_name=None,
install_path=None,
) -> Tuple[str, int]:
test_result = ""
test_status = 0
if install_path is None:
install_path = path_module_check
# Check code, init module to install
if lst_init_module_name:
for module_name in lst_init_module_name:
res, status = await run_command(
"./script/code_generator/check_git_change_code_generator.sh",
path_module_check,
module_name,
test_name=test_name,
)
test_result += res
test_status += status
# Check code, module to generate
if tested_module:
res, status = await run_command(
"./script/code_generator/check_git_change_code_generator.sh",
path_module_check,
tested_module,
test_name=test_name,
)
test_result += res
test_status += status
# Leave when detect anomaly in check before start
if test_status:
return test_result, test_status
# Execute script before start
if script_after_init_check and not test_status:
res, status = await run_command(script_after_init_check)
test_result += res
test_status += status
is_db_create = False
unique_database_name = f"test_demo_{uuid.uuid4()}"[:63]
if not test_status:
res, status = await run_command(
"./script/db_restore.py",
"--database",
unique_database_name,
test_name=test_name,
)
test_result += res
test_status += status
is_db_create = not status
if not test_status and lst_init_module_name:
# Parallel execution here
# No parallel execution here
str_test = ",".join(lst_init_module_name)
script_name = (
"./script/addons/install_addons_dev.sh"
if tested_module
else "./script/addons/install_addons.sh"
)
res, status = await run_command(
script_name,
unique_database_name,
str_test,
test_name=test_name,
)
test_result += res
test_status += status
if not test_status and search_class_module and generated_module:
path_template_to_generate = os.path.join(
path_module_check, tested_module
)
path_module_to_generate = os.path.join(
path_module_check, search_class_module
)
# Parallel execution here
# No parallel execution here
res, status = await run_command(
"./script/code_generator/search_class_model.py",
"--quiet",
"-d",
path_module_to_generate,
"-t",
path_template_to_generate,
test_name=test_name,
)
test_result += res
test_status += status
if not test_status and tested_module and generated_module:
# Parallel execution here
# No parallel execution here
res, status = await run_command(
"./script/code_generator/install_and_test_code_generator.sh",
unique_database_name,
tested_module,
install_path,
generated_module,
test_name=test_name,
)
test_result += res
test_status += status
if is_db_create:
res, status = await run_command(
"./.venv/bin/python3",
"./odoo/odoo-bin",
"db",
"--drop",
"--database",
unique_database_name,
test_name=test_name,
)
test_result += res
test_status += status
return test_result, test_status
def check_git_change():
"""
return True if success
"""
loop = asyncio.get_event_loop()
task_list = [
run_command(
"./script/code_generator/check_git_change_code_generator.sh",
"./addons/TechnoLibre_odoo-code-generator-template",
test_name="Init check_git_change",
)
]
commands = asyncio.gather(*task_list)
tpl_result = loop.run_until_complete(commands)
status = any([a[1] for a in tpl_result])
loop.close()
return not status
def print_summary_task(task_list):
for task in task_list:
print(task.cr_code.co_name)
# START TEST
async def run_demo_test() -> Tuple[str, int]:
lst_test_name = [
"demo_helpdesk_data",
"demo_internal",
"demo_internal_inherit",
"demo_mariadb_sql_example_1",
"demo_portal",
"demo_website_data",
"demo_website_leaflet",
"demo_website_snippet",
]
res, status = await test_exec(
"./addons/TechnoLibre_odoo-code-generator-template",
lst_init_module_name=lst_test_name,
test_name="demo_test",
)
return res, status
async def run_mariadb_test() -> Tuple[str, int]:
test_result = ""
test_status = 0
# Migrator
res, status = await test_exec(
"./addons/TechnoLibre_odoo-code-generator-template",
generated_module="demo_mariadb_sql_example_1",
tested_module="code_generator_migrator_demo_mariadb_sql_example_1",
script_after_init_check=(
"./script/database/restore_mariadb_sql_example_1.sh"
),
lst_init_module_name=[
"code_generator_portal",
],
test_name="mariadb_test-migrator",
)
test_result += res
test_status += status
# Template
res, status = await test_exec(
"./addons/TechnoLibre_odoo-code-generator-template",
generated_module="code_generator_demo_mariadb_sql_example_1",
tested_module="code_generator_template_demo_mariadb_sql_example_1",
search_class_module="demo_mariadb_sql_example_1",
lst_init_module_name=[
"code_generator_portal",
"demo_mariadb_sql_example_1",
],
test_name="mariadb_test-template",
)
test_result += res
test_status += status
# Code generator
res, status = await test_exec(
"./addons/TechnoLibre_odoo-code-generator-template",
generated_module="demo_mariadb_sql_example_1",
lst_init_module_name=[
"code_generator_portal",
],
tested_module="code_generator_demo_mariadb_sql_example_1",
test_name="mariadb_test-code-generator",
)
test_result += res
test_status += status
return test_result, test_status
async def run_code_generator_multiple_test() -> Tuple[str, int]:
test_result = ""
test_status = 0
lst_generated_module = [
"code_generator_demo",
"demo_helpdesk_data",
"demo_website_data",
"demo_internal",
"demo_portal",
"theme_website_demo_code_generator",
"demo_website_leaflet",
"demo_website_snippet",
]
lst_tested_module = [
"code_generator_demo",
"code_generator_demo_export_helpdesk",
"code_generator_demo_export_website",
"code_generator_demo_internal",
"code_generator_demo_portal",
"code_generator_demo_theme_website",
"code_generator_demo_website_leaflet",
"code_generator_demo_website_snippet",
]
# Multiple
res, status = await test_exec(
"./addons/TechnoLibre_odoo-code-generator-template",
generated_module=",".join(lst_generated_module),
tested_module=",".join(lst_tested_module),
test_name="code_generator_multiple_test",
)
test_result += res
test_status += status
return test_result, test_status
async def run_code_generator_inherit_test() -> Tuple[str, int]:
# TODO can be merge into code_generator_multiple
test_result = ""
test_status = 0
lst_generated_module = [
"demo_internal_inherit",
]
lst_tested_module = [
"code_generator_demo_internal_inherit",
]
# Inherit
res, status = await test_exec(
"./addons/TechnoLibre_odoo-code-generator-template",
generated_module=",".join(lst_generated_module),
tested_module=",".join(lst_tested_module),
test_name="code_generator_inherit_test",
)
test_result += res
test_status += status
return test_result, test_status
async def run_code_generator_auto_backup_test() -> Tuple[str, int]:
test_result = ""
test_status = 0
lst_generated_module = [
"auto_backup",
]
lst_tested_module = [
"code_generator_auto_backup",
]
# Auto-backup
res, status = await test_exec(
"./addons/OCA_server-tools/auto_backup",
generated_module=",".join(lst_generated_module),
tested_module=",".join(lst_tested_module),
test_name="code_generator_auto_backup_test",
)
test_result += res
test_status += status
return test_result, test_status
async def run_code_generator_template_demo_portal_test() -> Tuple[str, int]:
test_result = ""
test_status = 0
# Template
res, status = await test_exec(
"./addons/TechnoLibre_odoo-code-generator-template",
generated_module="code_generator_demo_portal",
tested_module="code_generator_template_demo_portal",
# search_class_module="demo_mariadb_sql_example_1",
lst_init_module_name=[
"demo_portal",
],
test_name="code_generator_template_demo_portal",
)
test_result += res
test_status += status
return test_result, test_status
async def run_code_generator_template_demo_internal_inherit_test() -> Tuple[
str, int
]:
test_result = ""
test_status = 0
# Template
res, status = await test_exec(
"./addons/TechnoLibre_odoo-code-generator-template",
generated_module="code_generator_demo_internal_inherit",
tested_module="code_generator_template_demo_internal_inherit",
# search_class_module="code_generator_demo_internal_inherit",
lst_init_module_name=[
"demo_internal_inherit",
],
test_name="code_generator_template_demo_internal_inherit",
)
test_result += res
test_status += status
return test_result, test_status
async def run_code_generator_template_demo_sysadmin_cron_test() -> Tuple[
str, int
]:
test_result = ""
test_status = 0
# Template
res, status = await test_exec(
"./addons/OCA_server-tools/auto_backup",
generated_module="code_generator_auto_backup",
tested_module="code_generator_template_demo_sysadmin_cron",
# search_class_module="code_generator_demo_internal_inherit",
lst_init_module_name=[
"auto_backup",
],
test_name="code_generator_template_demo_sysadmin_cron",
install_path="./addons/TechnoLibre_odoo-code-generator-template",
)
test_result += res
test_status += status
return test_result, test_status
async def run_helloworld_test() -> Tuple[str, int]:
res, status = await run_command(
"./test/code_generator/hello_world.sh", test_name="helloworld_test"
)
return res, status
def run_all_test() -> None:
task_list = []
task_list.append(run_demo_test())
task_list.append(run_helloworld_test())
task_list.append(run_mariadb_test())
task_list.append(run_code_generator_multiple_test())
task_list.append(run_code_generator_inherit_test())
task_list.append(run_code_generator_auto_backup_test())
task_list.append(run_code_generator_template_demo_portal_test())
task_list.append(run_code_generator_template_demo_internal_inherit_test())
task_list.append(run_code_generator_template_demo_sysadmin_cron_test())
print_summary_task(task_list)
if asyncio.get_event_loop().is_closed():
asyncio.set_event_loop(asyncio.new_event_loop())
loop = asyncio.get_event_loop()
commands = asyncio.gather(*task_list)
tpl_result = loop.run_until_complete(commands)
loop.close()
print_log(task_list, tpl_result)
check_result(task_list, tpl_result)
def main():
config = get_config()
start_time = time.time()
if not config.ignore_init_check_git:
success = check_git_change()
else:
success = True
if success:
run_all_test()
end_time = time.time()
diff_sec = end_time - start_time
# print(f"Time execution {diff_sec:.3f}s")
print(f"Time execution {datetime.timedelta(seconds=diff_sec)}")
return 0
if __name__ == "__main__":
sys.exit(main())

View file

@ -0,0 +1,18 @@
#!/usr/bin/env bash
Color_Off='\033[0m' # Text Reset
Red='\033[0;31m' # Red
Green='\033[0;32m' # Green
tmp_dir=$(mktemp -d -t "erplibre_code_generator_helloworld-$(date +%Y-%m-%d-%H-%M-%S)-XXXXXXXXXX")
./script/code_generator/new_project.py -m test -d "${tmp_dir}"
MODULE_PATH="${tmp_dir}/test"
if [[ -d "${MODULE_PATH}" ]]; then
echo -e "${Green}SUCCESS${Color_Off} ${MODULE_PATH} exists."
else
echo -e "${Red}ERROR${Color_Off} ${MODULE_PATH} is missing."
fi
rm -rf "${tmp_dir}"