[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 SHELL := /bin/bash
LOG_FILE := ./.venv/make_test.log
############# #############
# General # # General #
############# #############
@ -112,6 +113,10 @@ db_drop_db_code_generator:
db_drop_db_template: db_drop_db_template:
./.venv/bin/python3 ./odoo/odoo-bin db --drop --database 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 .PHONY: db_clean_cache
db_clean_cache: db_clean_cache:
./script/db_restore.py --clean_cache ./script/db_restore.py --clean_cache
@ -121,7 +126,8 @@ db_restore_erplibre_base_db_test:
./script/db_restore.py --database test ./script/db_restore.py --database test
.PHONY: db_restore_erplibre_base_db_test_module_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 ./script/addons/install_addons.sh test test
.PHONY: db_restore_erplibre_base_db_test_image_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 ./script/db_restore.py --database template
.PHONY: db_create_db_test .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 ./.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 .PHONY: image_db_create_erplibre_base
image_db_create_erplibre_base: image_db_create_erplibre_base:
./script/make.sh db_create_db_test ./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 ./.venv/bin/python3 ./odoo/odoo-bin db --backup --database test --restore_image erplibre_base
.PHONY: image_db_create_erplibre_website .PHONY: image_db_create_erplibre_website
@ -216,15 +223,18 @@ image_diff_base_website:
# Addons installation # # Addons installation #
######################### #########################
.PHONY: addons_install_code_generator_basic .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 ./script/addons/install_addons_dev.sh code_generator code_generator
.PHONY: addons_install_code_generator_featured .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 ./script/addons/install_addons_dev.sh code_generator code_generator_cron,code_generator_hook,code_generator_portal
.PHONY: addons_install_code_generator_full .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 ./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 .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 ./script/addons/uninstall_addons.sh code_generator code_generator_demo
.PHONY: addons_reinstall_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 .PHONY: addons_install_all_code_generator_demo
addons_install_all_code_generator_demo: db_restore_erplibre_base_db_code_generator addons_install_all_code_generator_demo:
./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 ./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
# ./script/addons/install_addons_dev.sh code_generator code_generator_demo_export_helpdesk # ./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
# ./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_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_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_leaflet
# ./script/addons/install_addons_dev.sh code_generator code_generator_demo_website_snippet # ./script/addons/install_addons_dev.sh code_generator code_generator_demo_website_snippet
.PHONY: addons_install_all_code_generator_template .PHONY: addons_install_all_code_generator_template
addons_install_all_code_generator_template: addons_install_all_code_generator_template:
./script/addons/install_addons_dev.sh template demo_portal,auto_backup ./script/make.sh db_restore_erplibre_base_db_template
./script/addons/install_addons_dev.sh template code_generator_template_demo_portal code_generator_template_demo_sysadmin_cron ./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 .PHONY: addons_install_all_generated_demo
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 # TODO support installation theme with cli
#./script/addons/install_addons_dev.sh template theme_website_demo_code_generator #./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: addons_install_all_code_generator:
./script/addons/install_addons_dev.sh code_generator code_generator_auto_backup ./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 # # test #
########## ##########
.PHONY: 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 .PHONY: test_format
test_format: test_format:
./script/maintenance/black.sh --check ./addons/TechnoLibre_odoo-code-generator/ ./script/maintenance/black.sh --check ./addons/TechnoLibre_odoo-code-generator/
./script/maintenance/black.sh --check ./addons/TechnoLibre_odoo-code-generator-template/ ./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 .PHONY: test_code_generator_generation
test_code_generator_generation: test_code_generator_generation:
./script/code_generator/check_git_change_code_generator.sh ./addons/TechnoLibre_odoo-code-generator-template ./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/code_generator/check_git_change_code_generator.sh ./addons/OCA_server-tools/auto_backup
./script/db_restore.py --database code_generator # Multiple
./script/code_generator/install_and_test_code_generator.sh code_generator code_generator_auto_backup ./addons/OCA_server-tools/auto_backup ./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 # Single
test_code_generator_code_i18n: #./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/code_generator/check_git_change_code_generator.sh ./addons/TechnoLibre_odoo-code-generator-template
./script/db_restore.py --database template ./script/db_restore.py --database test_template
./script/addons/install_addons_dev.sh template demo_portal ./script/addons/install_addons_dev.sh test_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/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 .PHONY: test_code_generator_code_template_demo_internal_inherit
test_code_generator_code_i18n_extra: test_code_generator_code_template_demo_internal_inherit:
./script/code_generator/check_git_change_code_generator.sh ./addons/OCA_server-tools/auto_backup ./script/code_generator/check_git_change_code_generator.sh ./addons/TechnoLibre_odoo-code-generator-template
./script/db_restore.py --database template ./script/db_restore.py --database test_template
./script/addons/install_addons_dev.sh template auto_backup ./script/addons/install_addons_dev.sh test_template demo_internal_inherit
./script/code_generator/install_and_test_code_generator.sh template code_generator_template_demo_sysadmin_cron ./addons/TechnoLibre_odoo-code-generator-template #./script/addons/install_addons_dev.sh test_template code_generator_template_demo_internal_inherit
# To support i18n in auto_backup ./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/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 # # tag #
@ -328,22 +496,39 @@ open_terminal:
# format # # format #
############ ############
.PHONY: 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 .PHONY: format_code_generator
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/black.sh ./addons/TechnoLibre_odoo-code-generator/
#./script/maintenance/prettier_xml.sh ./addons/TechnoLibre_odoo-code-generator/ #./script/maintenance/prettier_xml.sh ./addons/TechnoLibre_odoo-code-generator/
.PHONY: format_code_generator_template .PHONY: format_code_generator_template
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/black.sh ./addons/TechnoLibre_odoo-code-generator-template/
#./script/maintenance/prettier_xml.sh ./addons/TechnoLibre_odoo-code-generator-template/ #./script/maintenance/prettier_xml.sh ./addons/TechnoLibre_odoo-code-generator-template/
.PHONY: format_script .PHONY: format_script
format_script: format_script:
#.venv/bin/isort --profile black -l 79 ./script/ --gitignore
./script/maintenance/black.sh ./script/ ./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 # # clean #
########### ###########
@ -372,7 +557,9 @@ docker_stop:
docker-compose down docker-compose down
.PHONY: docker_restart_daemon .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 .PHONY: docker_show_databases
docker_show_databases: docker_show_databases:
@ -500,21 +687,41 @@ config_gen_image_db:
i18n_generate_demo_portal: i18n_generate_demo_portal:
./.venv/bin/python3 ./odoo/odoo-bin i18n --database code_generator --module demo_portal --addons_path addons/TechnoLibre_odoo-code-generator ./.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 #
################### ###################
# documentation all # documentation all
.PHONY: doc .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 # documentation clean all
.PHONY: doc_clean .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 # documentation dev
.PHONY: doc_dev .PHONY: doc_dev
doc_dev: doc_dev:
source ./.venv/bin/activate && make -C doc/itpp-labs_odoo-development/docs html || exit 1 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 -$(BROWSER) doc/itpp-labs_odoo-development/docs/_build/html/index.html
.PHONY: doc_clean_dev .PHONY: doc_clean_dev
@ -525,6 +732,9 @@ doc_clean_dev:
.PHONY: doc_migration .PHONY: doc_migration
doc_migration: doc_migration:
source ./.venv/bin/activate && make -C doc/itpp-labs_odoo-port-docs/docs html || exit 1 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 -$(BROWSER) doc/itpp-labs_odoo-port-docs/docs/_build/html/index.html
.PHONY: doc_clean_migration .PHONY: doc_clean_migration
@ -535,6 +745,9 @@ doc_clean_migration:
.PHONY: doc_test .PHONY: doc_test
doc_test: doc_test:
source ./.venv/bin/activate && make -C doc/itpp-labs_odoo-test-docs/doc-src html || exit 1 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 -$(BROWSER) doc/itpp-labs_odoo-test-docs/doc-src/_build/html/index.html
.PHONY: doc_clean_test .PHONY: doc_clean_test
@ -545,6 +758,9 @@ doc_clean_test:
.PHONY: doc_user .PHONY: doc_user
doc_user: doc_user:
source ./.venv/bin/activate && make -C doc/odoo_documentation-user html || exit 1 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 -$(BROWSER) doc/odoo_documentation-user/_build/html/index.html
.PHONY: doc_clean_user .PHONY: doc_clean_user

View file

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

View file

@ -43,6 +43,7 @@ html5print
css-html-prettify css-html-prettify
BeautifulSoup4 BeautifulSoup4
git+https://github.com/psf/black.git@21.6b0 git+https://github.com/psf/black.git@21.6b0
isort
# For updating poetry # For updating poetry
toml 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 #!/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=$? retVal=$?
if [[ $retVal -ne 0 ]]; then if [[ $retVal -ne 0 ]]; then
echo "Error install_addons.sh" echo "Error install_addons.sh"

View file

@ -1,5 +1,11 @@
#!/usr/bin/env bash #!/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=$? retVal=$?
if [[ $retVal -ne 0 ]]; then if [[ $retVal -ne 0 ]]; then
echo "Error install_addons_dev.sh" echo "Error install_addons_dev.sh"

View file

@ -1,6 +1,12 @@
#!/usr/bin/env bash #!/usr/bin/env bash
./script/addons/uninstall_addons.sh $1 $2 ./script/addons/check_addons_exist.py -m "$2"
./script/addons/install_addons_dev.sh $1 $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=$? retVal=$?
if [[ $retVal -ne 0 ]]; then if [[ $retVal -ne 0 ]]; then
echo "Error reinstall_addons.sh" echo "Error reinstall_addons.sh"

View file

@ -1,2 +1,8 @@
#!/usr/bin/env bash #!/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_ICyan='\033[0;106m' # Cyan
On_IWhite='\033[0;107m' # White On_IWhite='\033[0;107m' # White
# TODO support argument to test only specified path
check_git() { check_git() {
REP=$1 REP=$1
cd ${REP} cd "${REP}" || exit
output=$(git status --porcelain) if [ $# -gt 1 ]; then
if [ -z "$output" ]; then output=$(git status --porcelain "$2")
echo "PASS - ${REP}"
else 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}" echo -e "${BRed}${output}${Color_Off}"
cd - >/dev/null cd - >/dev/null || exit
exit 1 exit 1
fi fi
cd - >/dev/null cd - >/dev/null || exit
} }
if [ $# -gt 0 ]; then if [ $# -gt 0 ]; then
check_git "$1" if [ $# -gt 1 ]; then
check_git "$1" "$2"
else
check_git "$1"
fi
else else
# check_git "./addons/TechnoLibre_odoo-code-generator" # check_git "./addons/TechnoLibre_odoo-code-generator"
check_git "./addons/TechnoLibre_odoo-code-generator-template" 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 #!/usr/bin/env bash
# $1 is database name # $1 is database name
# $2 is module name # $2 is module name separate by ,
# $3 is directory path to check # $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=$? retVal=$?
if [[ $retVal -ne 0 ]]; then if [[ $retVal -ne 0 ]]; then
echo "Error ./script/addons/install_addons_dev.sh ${1} ${2}" echo "Error ./script/addons/install_addons_dev.sh ${1} ${2}"
exit 1 exit 1
fi 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 ./script/repo_revert_git_diff_date_from_code_generator.py
# Remove pot and po diff # Remove pot and po diff
cd $3 cd "$3" || exit 1
# git 2.22 and more, else use next command # git 2.22 and more, else use next command
#BRANCH=$(git branch --show-current) #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 # Support old version git < 2.23.0
# git restore --source="${BRANCH}" "*.po*" # git restore --source="${BRANCH}" "*.po*"
git checkout -- "*.po*" git checkout -- "*.po*"
cd - cd - || exit 1
./script/maintenance/black.sh $3 ./script/maintenance/black.sh "$3"
echo "TEST ${2}" echo "TEST ${2}"
./script/code_generator/check_git_change_code_generator.sh $3 ./script/code_generator/check_git_change_code_generator.sh "$3"
retVal=$? retVal=$?
if [[ $retVal -ne 0 ]]; then if [[ $retVal -ne 0 ]]; then
echo "Error ./script/code_generator/check_git_change_code_generator.sh" 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 # Install Wkhtmltopdf if needed
#-------------------------------------------------- #--------------------------------------------------
echo "\n---- Installing Wkhtmltopdf if needed ----" echo "\n---- Installing Wkhtmltopdf if needed ----"
if [ ! -f "wkhtmltox-0.12.6-1.macos-cocoa.pkg" ]; then if [ ! -f "wkhtmltox-0.12.6-2.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 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-1.macos-cocoa.pkg -target / sudo sudo installer -pkg wkhtmltox-0.12.6-2.macos-cocoa.pkg -target /
else echo "Wkhtmltopdf already installed" else echo "Wkhtmltopdf already installed"
fi 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 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 ----" 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 rtlcss
sudo npm install -g less sudo npm install -g less

View file

@ -1,16 +1,17 @@
#!/usr/bin/env bash #!/usr/bin/env bash
################################################################################
# Script for installing ERPLibre locally for dev
# Author: Alexandre Ferreira Benevides
################################################################################
if [[ "${OSTYPE}" == "linux-gnu" ]]; then if [[ "${OSTYPE}" == "linux-gnu" ]]; then
OS=$(lsb_release -si) OS=$(lsb_release -si)
VERSION=$(cat /etc/issue)
if [[ "${OS}" == "Ubuntu" ]]; then if [[ "${OS}" == "Ubuntu" ]]; then
echo "\n---- linux-gnu installation process started ----" if [[ "${VERSION}" == Ubuntu\ 18.04* || "${VERSION}" == Ubuntu\ 20.04* ]]; then
./script/install_debian_dependency.sh 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 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 fi
elif [[ "${OSTYPE}" == "darwin"* ]]; then elif [[ "${OSTYPE}" == "darwin"* ]]; then
echo "\n---- Darwin installation process started ----" 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 #!/usr/bin/env bash
echo "==$&%== Begin ${@}" echo "
===> ${@}
"
time make $@ 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}/"
"${working_path}/addons/TechnoLibre_odoo-code-generator" "${working_path}/addons/TechnoLibre_odoo-code-generator"
"${working_path}/addons/TechnoLibre_odoo-code-generator-template" "${working_path}/addons/TechnoLibre_odoo-code-generator-template"
"${working_path}/addons/OCA_server-tools"
) )
cmd_before="cd " cmd_before="cd "
@ -13,7 +14,7 @@ cmd_after=";gnome-terminal --tab -- bash -c 'git status;bash';"
#cmd_after=";gnome-terminal --tab;" #cmd_after=";gnome-terminal --tab;"
LONGCMD="" LONGCMD=""
for t in ${paths[@]}; do for t in "${paths[@]}"; do
LONGCMD+=${cmd_before}${t}${cmd_after} LONGCMD+=${cmd_before}${t}${cmd_after}
done done

View file

@ -1,15 +1,20 @@
#!./.venv/bin/python #!./.venv/bin/python
import os
import sys
import argparse import argparse
import logging import logging
import os
import re
import sys
import git import git
from unidiff import PatchSet from unidiff import PatchSet
import re
new_path = os.path.normpath(os.path.join(os.path.dirname(__file__), "..")) new_path = os.path.normpath(os.path.join(os.path.dirname(__file__), ".."))
sys.path.append(new_path) 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__) _logger = logging.getLogger(__name__)
@ -62,6 +67,7 @@ def main():
with open(file_real_path, "r") as file: with open(file_real_path, "r") as file:
lst_data = file.readlines() lst_data = file.readlines()
for data in lst_data: for data in lst_data:
# TODO this remove code begin with this string, why do we remove it?
if data.startswith("#: code:addons/addons/"): if data.startswith("#: code:addons/addons/"):
is_modified = True is_modified = True
else: 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}"