Merge branch 'develop'

- Support ubuntu 23.10
- Script search docker compose
- Improve run_parallel_test
- Update poetry 1.3.1 to 1.5.1
- Update changelog
- Support old docker compose with new syntax
This commit is contained in:
Mathieu Benoit 2024-05-03 23:20:56 -04:00
commit d7f6c257d2
64 changed files with 1450 additions and 1307 deletions

View file

@ -11,6 +11,7 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
- Selenium script to increase open software client interface and automated some actions.
- FAQ about kill git-daemon
- Support Ubuntu 23.10
## Changed
@ -19,6 +20,11 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
- Improve format script to help code-generator
- Improve PyCharm script
- Support OSX for open-terminal
- Remove docker-compose and replace by docker compose
- Update Poetry 1.3.1 to 1.5.1
- Test can be launched with a json configuration and support log/result individually
- Script to search docker compose into the system
- Script search class model can output into json format and support field information
### Fixed

View file

@ -935,15 +935,15 @@ clean_test:
# run docker
.PHONY: docker_run
docker_run:
docker-compose up
docker compose up
.PHONY: docker_run_daemon
docker_run_daemon:
docker-compose up -d
docker compose up -d
.PHONY: docker_stop
docker_stop:
docker-compose down
docker compose down
.PHONY: docker_restart_daemon
docker_restart_daemon:
@ -956,11 +956,11 @@ docker_show_databases:
.PHONY: docker_show_logs_live
docker_show_logs_live:
docker-compose logs -f
docker compose logs -f
.PHONY: docker_show_process
docker_show_process:
docker-compose ps
docker compose ps
.PHONY: docker_exec_erplibre
docker_exec_erplibre:

View file

@ -17,8 +17,7 @@ make install
```
Update your configuration if you need to run from another interface than 127.0.0.1, file `config.conf`
```
#xmlrpc_interface = 127.0.0.1
#netrpc_interface = 127.0.0.1
xmlrpc_interface = 0.0.0.0
```
Ready to execute:
```bash
@ -31,7 +30,7 @@ First, install dependencies to run docker, check script `./script/install/instal
The docker volume is bound to the directory name, therefore create a unique directory name and run:
```bash
wget https://raw.githubusercontent.com/ERPLibre/ERPLibre/v1.5.0/docker-compose.yml
docker-compose up -d
docker compose up -d
```
For more information, read [Docker guide](./docker/README.md).

View file

@ -1,19 +1,21 @@
#!/usr/bin/env bash
source ./.venv/bin/activate
Red='\033[0;31m' # Red
Color_Off='\033[0m' # Text Reset
CONFIG_PATH="./config.conf"
ORIGIN_CONFIG_PATH=CONFIG_PATH
if [ ! -f "${CONFIG_PATH}" ]; then
CONFIG_PATH="/etc/odoo/odoo.conf"
if [ ! -f "${CONFIG_PATH}" ]; then
echo "Cannot find ERPLibre configuration ${ORIGIN_CONFIG_PATH}, did you install ERPLibre? > make install"
echo "${Red}Cannot find${Color_Off} ERPLibre configuration ${ORIGIN_CONFIG_PATH}, did you install ERPLibre? > make install"
exit 1
fi
fi
coverage run -p ./odoo/odoo-bin -c ${CONFIG_PATH} --limit-time-real 99999 --limit-time-cpu 99999 $@
coverage run -p ./odoo/odoo-bin -c "${CONFIG_PATH}" --limit-time-real 99999 --limit-time-cpu 99999 "$@"
retVal=$?
if [[ $retVal -ne 0 ]]; then
echo "Error coverage_run.sh"
echo "${Red}Error${Color_Off} run.sh"
exit 1
fi

View file

@ -148,3 +148,22 @@ Add line at the end of config.conf
```
limit_memory_hard = 0
```
### Docker - All interface bind docker
Create a subnet
```bash
sudo docker network create localnetwork --subnet 10.0.1.0/24
```
And create `docker-compose.override.yml` at root project with
```yaml
version: '3'
networks:
default:
external:
name: localnetwork
```

View file

@ -74,9 +74,8 @@ sudo systemctl -feu [EL_USER]
Comment the following line in `/[EL_USER]/erplibre/config.conf`
```
#xmlrpc_interface = 127.0.0.1
#netrpc_interface = 127.0.0.1
#proxy_mode = True
xmlrpc_interface = 0.0.0.0
proxy_mode = True
```
Add your address ip server_name in nginx config `/etc/nginx/sites-available/[EL_WEBSITE_NAME]`
@ -183,8 +182,8 @@ Note, the goal is to call `env['ir.module.module'].update_list()`.
Restart the docker :
```bash
docker-compose down
docker-compose up -d
docker compose down
docker compose up -d
```
Revert the command in docker-compose.yml.
@ -192,7 +191,7 @@ Revert the command in docker-compose.yml.
You can validate in log the update, you need to find `odoo.modules.loading: updating modules list`, check
```bash
docker-compose logs -f
docker compose logs -f
```
## Update all

View file

@ -38,15 +38,15 @@ docker build -f Dockerfile.prod.pkg -t technolibre/erplibre:12.0-pkg .
Go at the root of this git project.
```bash
cd ERPLibre
docker-compose -f docker-compose.yml up -d
docker compose -f docker-compose.yml up -d
```
### Diagnostic Docker-Compose
Show docker-compose information
```bash
docker-compose ps
docker-compose logs IMAGE_NAME
docker compose ps
docker compose logs IMAGE_NAME
```
Show docker information
@ -90,7 +90,7 @@ docker rmi $(docker images -q)
Delete volumes
```bash
docker-compose rm -v
docker compose rm -v
```
Delete containers

View file

@ -38,7 +38,7 @@ then
elif [[ "$ENV" == "dev" ]] && [ -z ${CURRENT_UID} ]
then
echo 'Please run as follows : CURRENT_UID=$(id -u):$(id -g) docker-compose up'
echo 'Please run as follows : CURRENT_UID=$(id -u):$(id -g) docker compose up'
exit 1
fi

1060
poetry.lock generated

File diff suppressed because it is too large Load diff

View file

@ -19,49 +19,49 @@ authors = [ "Mathieu Benoit <mathben@technolibre.ca>",]
[tool.poetry.dependencies]
python = "^3.7.16"
acme = "^2.6.0"
acme = "2.6.0"
agithub = "^2.2.2"
aioshutil = "<1.2"
altair = "^5.0.1"
argcomplete = "^3.1.1"
altair = "5.0.1"
argcomplete = "3.1.1"
asterisk = "^0.0.8"
astor = "^0.8.1"
azure-identity = "^1.13.0"
azure-storage-blob = "^12.16.0"
azure-identity = "^1.15.0"
azure-storage-blob = "^12.19.0"
babel = "2.9.1"
beautifulsoup4 = "^4.12.2"
bokeh = "1.1.0"
boto3 = ">=1.20.41"
cachetools = ">=2.0.1"
cerberus = "^1.3.4"
cerberus = "^1.3.5"
chardet = "5.0.0"
click = "^8.1.4"
cloudflare = "^2.11.6"
click = "^8.1.7"
cloudflare = "^2.14.3"
code-writer = "^1.1.1"
colorama = "^0.4.6"
configparser = "5.0.2"
coverage = "^7.2.7"
coverage = "7.2.7"
cryptography = "39.0.1"
css-html-prettify = "^2.5.5"
cython = "^0.29.36"
cython = "^3.0.6"
ddt = "1.2.0"
decorator = "4.0.10"
dnspython = "^2.3.0"
dnspython = "2.3.0"
docutils = "0.17.1"
ebaysdk = "2.1.5"
email-validator = "^2.0.0.post2"
emoji = "^2.6.0"
extract-msg = "<0.30"
factur-x = "^2.5"
email-validator = "2.1.0"
emoji = "^2.9.0"
extract-msg = "0.36.1"
factur-x = "^3.1"
feedparser = "6.0.10"
flake8 = "<6.0.0"
formio-data = "^0.5.1"
formio-data = "^1.2.4"
freezegun = "1.2.2"
future = "0.18.3"
geojson = "<3.0.0"
gevent = "1.5.0"
gitpython = "3.1.30"
giturlparse = "^0.10.0"
giturlparse = "0.10.0"
greenlet = "0.4.14"
html2text = "2016.9.19"
invoice2data = "0.3.5"
@ -70,7 +70,7 @@ isort = "<5.12.0"
jinja2 = "2.11.3"
jira = "2.0.0"
jose = "^1.0.0"
josepy = "^1.13.0"
josepy = "^1.14.0"
js2py = "^0.74"
keystoneauth1 = "3.14.0"
lasso = "^0.1.0"
@ -78,7 +78,7 @@ libsass = "0.12.3"
lxml = "4.9.1"
mako = "1.2.2"
markupsafe = "0.23"
mmg = "^1.0.3"
mmg = "1.0.3"
mock = "2.0.0"
mpld3 = "^0.5.9"
mysqlclient = "2.1.1"
@ -86,20 +86,20 @@ num2words = "0.5.6"
numpy = "1.21.1"
oauthlib = "2.1.0"
oca-decorators = "^0.0.1"
odoorpc = "^0.9.0"
odoorpc = "^0.10.1"
ofxparse = "0.16"
openpyxl = "^3.1.2"
openupgradelib = "^3.4.1"
openupgradelib = "^3.5.0"
orjson = "3.6.0"
ovh = "^1.1.0"
paho-mqtt = "^1.6.1"
pandas = "1.3.5"
paramiko = "^3.2.0"
paramiko = "^3.3.1"
passlib = "1.6.5"
pdf2image = "^1.16.3"
pdfminer = "^20191125"
pexpect = "^4.8.0"
phonenumbers = "^8.13.15"
pexpect = "^4.9.0"
phonenumbers = "^8.13.26"
pillow = "9.3.0"
plotly = "4.1.0"
portalocker = "1.7.1"
@ -110,7 +110,7 @@ psycopg2 = "2.9.5"
py-asterisk = "^0.5.18"
py3o-formats = "^0.3"
py3o-template = "^0.10.0"
pycountry = "^22.3.5"
pycountry = "22.3.5"
pydot = "1.2.3"
pygments = "2.7.4"
pygments-csv-lexer = ">=0.1,<1.0"
@ -118,9 +118,9 @@ pygount = ">1.2.1"
pyjsparser = "^2.7.1"
pyjwt = "2.5.0"
pyldap = "2.4.28"
pymssql = "^2.2.7"
pymssql = "^2.2.11"
pymysql = "^1.1.0"
pyotp = "^2.8.0"
pyotp = "^2.9.0"
pyparsing = "2.1.10"
pypdf2 = "1.27.9"
pyproj = "3.2.1"
@ -133,9 +133,9 @@ python-git = "^2018.2.1"
python-jose = "^3.3.0"
python-json-logger = "0.1.5"
python-keystoneclient = "3.22.0"
python-ldap = "^3.4.3"
python-ldap = "^3.4.4"
python-slugify = ">=3.0.2"
python-stdnum = "^1.18"
python-stdnum = "^1.19"
python-swiftclient = "3.9.0"
python-u2flib-server = "^5.0.1"
pytz = ">=2022.7"
@ -149,9 +149,9 @@ requests = "2.28.1"
requests-mock = "^1.11.0"
requests-oauthlib = "1.1.0"
requests-toolbelt = "0.9.1"
responses = "^0.23.1"
responses = "0.23.1"
retrying = "^1.3.4"
sentry-sdk = "^1.27.1"
sentry-sdk = "^1.39.1"
serial = "^0.0.97"
setuptools = "65.5.1"
shapely = "1.8.5"
@ -160,26 +160,26 @@ slugify = "^0.0.1"
soappy = "^0.12.22"
sphinx = "3.0"
sphinx-intl = "^2.1.0"
sphinx-rtd-theme = "^1.2.2"
sqlalchemy = "^2.0.18"
sphinx-rtd-theme = "1.2.2"
sqlalchemy = "^2.0.23"
sqlparse = "^0.4.4"
statsd = "3.2.1"
toml = "^0.10.2"
tornado = "6.1"
tqdm = "^4.65.0"
tqdm = "^4.66.1"
unidecode = "1.0.22"
unidiff = "^0.7.5"
urllib3 = ">=1.26.13"
uvloop = "^0.17.0"
uvloop = "0.17.0"
vcrpy = ">=2.1.1"
vcrpy-unittest = "^0.1.7"
vobject = "0.9.3"
voicent-python = "^1.0"
webcolors = "^1.13"
websocket-client = "^1.6.1"
websocket-client = "1.6.1"
werkzeug = "0.16.1"
wget = "^3.2"
wheel = "^0.40.0"
wheel = "^0.42.0"
win-unicode-console = "^0.5"
xlrd = "1.0.0"
xlsxwriter = "0.9.3"

View file

@ -92,6 +92,25 @@ win_unicode_console
# Security issue, depend on extract-msg 0.36.1
chardet==5.0.0
extract-msg==0.36.1
extract_msg==0.36.1
# Limitation because python3.7
argcomplete==3.1.1
responses==0.23.1
dnspython==2.3.0
acme==2.6.0
altair==5.0.1
pycountry==22.3.5
email-validator==2.1.0
email_validator==2.1.0
sphinx-rtd-theme==1.2.2
sphinx_rtd_theme==1.2.2
uvloop==0.17.0
coverage==7.2.7
websocket-client==1.6.1
websocket_client==1.6.1
mmg==1.0.3
giturlparse==0.10.0
# Force version for security update
pyyaml==6.0

8
run.sh
View file

@ -1,22 +1,24 @@
#!/usr/bin/env bash
source ./.venv/bin/activate
Red='\033[0;31m' # Red
Color_Off='\033[0m' # Text Reset
CONFIG_PATH="./config.conf"
ORIGIN_CONFIG_PATH=CONFIG_PATH
if [ ! -f "${CONFIG_PATH}" ]; then
CONFIG_PATH="/etc/odoo/odoo.conf"
if [ ! -f "${CONFIG_PATH}" ]; then
echo "Cannot find ERPLibre configuration ${ORIGIN_CONFIG_PATH}, did you install ERPLibre? > make install"
echo "${Red}Cannot find${Color_Off} ERPLibre configuration ${ORIGIN_CONFIG_PATH}, did you install ERPLibre? > make install"
exit 1
fi
fi
python3 ./odoo/odoo-bin -c ${CONFIG_PATH} --limit-time-real 99999 --limit-time-cpu 99999 --limit-memory-hard=0 "$@"
python3 ./odoo/odoo-bin -c "${CONFIG_PATH}" --limit-time-real 99999 --limit-time-cpu 99999 --limit-memory-hard=0 "$@"
# When need more memory RAM for instance by force
#python3 ./odoo/odoo-bin -c ${CONFIG_PATH} --limit-time-real 99999 --limit-time-cpu 99999 --limit-memory-soft=8589934592 --limit-memory-hard=10737418240 $@
retVal=$?
if [[ $retVal -ne 0 ]]; then
echo "Error run.sh"
echo "${Red}Error${Color_Off} run.sh"
exit 1
fi

View file

@ -22,6 +22,7 @@ def get_config():
formatter_class=argparse.RawDescriptionHelpFormatter,
description="""\
Check if module exist and is not multiple here to manage conflict.
Return 0 if success, return 1 if missing module, return 2 if multiple same module
""",
epilog="""\
""",
@ -43,6 +44,11 @@ def get_config():
action="store_true",
help="Enable debug output",
)
parser.add_argument(
"--output_path",
action="store_true",
help="Print path if module exist",
)
args = parser.parse_args()
return args
@ -88,8 +94,10 @@ def main():
lst_module_not_exist.append(module)
is_good = True
error_missing_module = False
if lst_module_not_exist:
is_good = False
error_missing_module = True
module_list = "'" + "', '".join(lst_module_not_exist) + "'"
_logger.error(
"Missing"
@ -97,14 +105,19 @@ def main():
)
if dct_module_exist:
for key, lst_value in dct_module_exist.items():
is_print_value = False
if len(lst_value) != 1:
is_print_value = True
is_good = False
module_list = "'" + "', '".join(lst_value) + "'"
_logger.error(f"Conflict modules: {module_list}")
elif lst_value and config.output_path:
is_print_value = True
if is_print_value:
for value in lst_value:
print(value)
if dct_module_exist_empty:
if dct_module_exist_empty and not config.output_path:
for key, lst_value in dct_module_exist_empty.items():
module_list = "'" + "', '".join(lst_value) + "'"
_logger.warning(
@ -112,7 +125,11 @@ def main():
f" {module_list}"
)
return 0 if is_good else -1
if not is_good:
if error_missing_module:
return 1
return 2
return 0
if __name__ == "__main__":

View file

@ -1,4 +1,7 @@
#!/usr/bin/env bash
Red='\033[0;31m' # Red
Color_Off='\033[0m' # Text Reset
# Argument 3 is config
if [[ $# -eq 3 ]]; then
./script/addons/check_addons_exist.py -m "$2" -c "$3"
@ -7,7 +10,7 @@ else
fi
retVal=$?
if [[ $retVal -ne 0 ]]; then
echo "Error check_addons_exist.py into coverage_install_addons.sh"
echo -e "${Red}Error${Color_Off} check_addons_exist.py into coverage_install_addons.sh"
exit 1
fi
@ -21,6 +24,6 @@ fi
retVal=$?
if [[ $retVal -ne 0 ]]; then
echo "Error coverage_run.sh into coverage_install_addons.sh"
echo -e "${Red}Error${Color_Off} coverage_run.sh into coverage_install_addons.sh"
exit 1
fi

View file

@ -1,4 +1,7 @@
#!/usr/bin/env bash
Red='\033[0;31m' # Red
Color_Off='\033[0m' # Text Reset
# Argument 3 is config
if [[ $# -eq 3 ]]; then
./script/addons/check_addons_exist.py -m "$2" -c "$3"
@ -7,7 +10,7 @@ else
fi
retVal=$?
if [[ $retVal -ne 0 ]]; then
echo "Error check_addons_exist.py into coverage_install_addons_dev.sh"
echo -e "${Red}Error${Color_Off} check_addons_exist.py into coverage_install_addons_dev.sh"
exit 1
fi
@ -21,6 +24,6 @@ fi
retVal=$?
if [[ $retVal -ne 0 ]]; then
echo "Error coverage_run.sh into coverage_install_addons_dev.sh"
echo -e "${Red}Error${Color_Off} coverage_run.sh into coverage_install_addons_dev.sh"
exit 1
fi

View file

@ -1,15 +1,18 @@
#!/usr/bin/env bash
Red='\033[0;31m' # Red
Color_Off='\033[0m' # Text Reset
# "$1" code_generator_name
./script/database/db_restore.py --database "$1"
retVal=$?
if [[ $retVal -ne 0 ]]; then
echo "Error ./script/database/db_restore.py into coverage_install_addons_restore_dev.sh"
echo -e "${Red}Error${Color_Off} ./script/database/db_restore.py into coverage_install_addons_restore_dev.sh"
exit 1
fi
./script/addons/coverage_install_addons_dev.sh "$1" "$1"
retVal=$?
if [[ $retVal -ne 0 ]]; then
echo "Error ./script/addons/coverage_install_addons_dev.sh into coverage_install_addons_restore_dev.sh"
echo -e "${Red}Error${Color_Off} ./script/addons/coverage_install_addons_dev.sh into coverage_install_addons_restore_dev.sh"
exit 1
fi

View file

@ -1,4 +1,7 @@
#!/usr/bin/env bash
Red='\033[0;31m' # Red
Color_Off='\033[0m' # Text Reset
# Argument 3 is config
if [[ $# -eq 3 ]]; then
./script/addons/check_addons_exist.py -m "$2" -c "$3"
@ -7,7 +10,7 @@ else
fi
retVal=$?
if [[ $retVal -ne 0 ]]; then
echo "Error check_addons_exist.py into install_addons.sh"
echo -e "${Red}Error${Color_Off} check_addons_exist.py into install_addons.sh"
exit 1
fi
@ -21,6 +24,6 @@ fi
retVal=$?
if [[ $retVal -ne 0 ]]; then
echo "Error run.sh into install_addons.sh"
echo -e "${Red}Error${Color_Off} run.sh into install_addons.sh"
exit 1
fi

View file

@ -1,21 +1,24 @@
#!/usr/bin/env bash
Red='\033[0;31m' # Red
Color_Off='\033[0m' # Text Reset
# "$1" module_name
# "$1" code_generator_template_name
./script/database/db_restore.py --database "$2"
retVal=$?
if [[ $retVal -ne 0 ]]; then
echo "Error ./script/database/db_restore.py into install_addons_cg_template_restore_dev.sh"
echo -e "${Red}Error${Color_Off} ./script/database/db_restore.py into install_addons_cg_template_restore_dev.sh"
exit 1
fi
./script/addons/install_addons_dev.sh "$2" "$1"
retVal=$?
if [[ $retVal -ne 0 ]]; then
echo "Error ./script/addons/install_addons_dev.sh install module into install_addons_cg_template_restore_dev.sh"
echo -e "${Red}Error${Color_Off} ./script/addons/install_addons_dev.sh install module into install_addons_cg_template_restore_dev.sh"
exit 1
fi
./script/addons/install_addons_dev.sh "$2" "$2"
retVal=$?
if [[ $retVal -ne 0 ]]; then
echo "Error ./script/addons/install_addons_dev.sh install template into install_addons_cg_template_restore_dev.sh"
echo -e "${Red}Error${Color_Off} ./script/addons/install_addons_dev.sh install template into install_addons_cg_template_restore_dev.sh"
exit 1
fi

View file

@ -1,4 +1,7 @@
#!/usr/bin/env bash
Red='\033[0;31m' # Red
Color_Off='\033[0m' # Text Reset
# Argument 3 is config
if [[ $# -eq 3 ]]; then
./script/addons/check_addons_exist.py -m "$2" -c "$3"
@ -7,7 +10,7 @@ else
fi
retVal=$?
if [[ $retVal -ne 0 ]]; then
echo "Error check_addons_exist.py into install_addons_dev.sh"
echo -e "${Red}Error${Color_Off} check_addons_exist.py into install_addons_dev.sh"
exit 1
fi
@ -22,6 +25,6 @@ fi
retVal=$?
if [[ $retVal -ne 0 ]]; then
echo "Error run.sh into install_addons_dev.sh"
echo -e "${Red}Error${Color_Off} run.sh into install_addons_dev.sh"
exit 1
fi

View file

@ -1,9 +1,12 @@
#!/usr/bin/env bash
Red='\033[0;31m' # Red
Color_Off='\033[0m' # Text Reset
# $1 BD NAME
# $2 file_path
./script/addons/install_addons.sh "$1" "$(<$2)"
retVal=$?
if [[ $retVal -ne 0 ]]; then
echo "Error ./script/addons/install_addons.sh into install_addons_from_file.sh"
echo -e "${Red}Error${Color_Off} ./script/addons/install_addons.sh into install_addons_from_file.sh"
exit 1
fi

View file

@ -1,15 +1,18 @@
#!/usr/bin/env bash
Red='\033[0;31m' # Red
Color_Off='\033[0m' # Text Reset
# "$1" code_generator_name
./script/database/db_restore.py --database "$1"
retVal=$?
if [[ $retVal -ne 0 ]]; then
echo "Error ./script/database/db_restore.py into install_addons_restore_dev.sh"
echo -e "${Red}Error${Color_Off} ./script/database/db_restore.py into install_addons_restore_dev.sh"
exit 1
fi
./script/addons/install_addons_dev.sh "$1" "$1"
retVal=$?
if [[ $retVal -ne 0 ]]; then
echo "Error ./script/addons/install_addons_dev.sh into install_addons_restore_dev.sh"
echo -e "${Red}Error${Color_Off} ./script/addons/install_addons_dev.sh into install_addons_restore_dev.sh"
exit 1
fi

View file

@ -1,5 +1,8 @@
#!/usr/bin/env bash
# TODO addons website need to be install before to install the theme
Red='\033[0;31m' # Red
Color_Off='\033[0m' # Text Reset
# Argument 3 is config
if [[ $# -eq 3 ]]; then
./script/addons/check_addons_exist.py -m "$2" -c "$3"
@ -8,7 +11,7 @@ else
fi
retVal=$?
if [[ $retVal -ne 0 ]]; then
echo "Error check_addons_exist.py into install_addons.sh"
echo -e "${Red}Error${Color_Off} check_addons_exist.py into install_addons.sh"
exit 1
fi
@ -22,6 +25,6 @@ fi
retVal=$?
if [[ $retVal -ne 0 ]]; then
echo "Error run.sh into install_addons_theme.sh"
echo -e "${Red}Error${Color_Off} run.sh into install_addons_theme.sh"
exit 1
fi

View file

@ -1,4 +1,7 @@
#!/usr/bin/env bash
Red='\033[0;31m' # Red
Color_Off='\033[0m' # Text Reset
# Argument 3 is config
if [[ $# -eq 3 ]]; then
./script/addons/check_addons_exist.py -m "$2" -c "$3"
@ -7,7 +10,7 @@ else
fi
retVal=$?
if [[ $retVal -ne 0 ]]; then
echo "Error check_addons_exist.py into reinstall_addons_dev.sh"
echo -e "${Red}Error${Color_Off} check_addons_exist.py into reinstall_addons_dev.sh"
exit 1
fi
@ -28,6 +31,6 @@ else
fi
retVal=$?
if [[ $retVal -ne 0 ]]; then
echo "Error install_addons_dev.sh into reinstall_addons.sh"
echo -e "${Red}Error${Color_Off} install_addons_dev.sh into reinstall_addons.sh"
exit 1
fi

View file

@ -1,4 +1,7 @@
#!/usr/bin/env bash
Red='\033[0;31m' # Red
Color_Off='\033[0m' # Text Reset
# Argument 3 is config
if [[ $# -eq 3 ]]; then
./script/addons/check_addons_exist.py -m "$2" -c "$3"
@ -7,7 +10,7 @@ else
fi
retVal=$?
if [[ $retVal -ne 0 ]]; then
echo "Error check_addons_exist.py into uninstall_addons.sh"
echo -e "${Red}Error${Color_Off} check_addons_exist.py into uninstall_addons.sh"
exit 1
fi

View file

@ -1,4 +1,6 @@
#!/usr/bin/env bash
Red='\033[0;31m' # Red
Color_Off='\033[0m' # Text Reset
echo "Update all on BD '$1'"
@ -6,6 +8,6 @@ echo "Update all on BD '$1'"
retVal=$?
if [[ $retVal -ne 0 ]]; then
echo "Error run.sh into update_addons_all.sh"
echo -e "${Red}Error${Color_Off} run.sh into update_addons_all.sh"
exit 1
fi

View file

@ -1,4 +1,7 @@
#!/usr/bin/env bash
Red='\033[0;31m' # Red
Color_Off='\033[0m' # Text Reset
# This script will remove mail configuration, remove backup configuration, and force admin user to test/test
echo "Update prod to dev on BD '$1'"
@ -6,6 +9,6 @@ echo "Update prod to dev on BD '$1'"
retVal=$?
if [[ $retVal -ne 0 ]]; then
echo "Error install_addons.sh into update_prod_to_dev.sh"
echo -e "${Red}Error${Color_Off} install_addons.sh into update_prod_to_dev.sh"
exit 1
fi

View file

@ -84,15 +84,15 @@ check_git() {
fi
if [ -z "$output" ]; then
if [ $# -gt 1 ]; then
echo "PASS - ${REP}/${2}"
echo -e "${Green}PASS${Color_Off} - ${REP}/${2}"
else
echo "PASS - ${REP}"
echo -e "${Green}PASS${Color_Off} - ${REP}"
fi
else
if [ $# -gt 1 ]; then
echo -e "${Red}FAIL - ${REP}${2}${Color_Off}"
echo -e "${Red}FAIL${Color_Off} - ${REP}${2}"
else
echo -e "${Red}FAIL - ${REP}${Color_Off}"
echo -e "${Red}FAIL${Color_Off} - ${REP}"
fi
echo -e "${BRed}${output}${Color_Off}"
cd - >/dev/null || exit

View file

@ -4,9 +4,11 @@
# $3 is directory path to check
# $4 is generated module name separate by ,
# $5 optional, the config path
Red='\033[0;31m' # Red
Color_Off='\033[0m' # Text Reset
if [[ $# -lt 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"
echo -e "${Red}Error${Color_Off}, 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
@ -16,14 +18,14 @@ if [[ $# -eq 5 ]]; then
./script/addons/coverage_install_addons_dev.sh "$1" "$2" "$5"
retVal=$?
if [[ $retVal -ne 0 ]]; then
echo "Error ./script/addons/coverage_install_addons_dev.sh ${1} ${2} ${5}"
echo -e "${Red}Error${Color_Off} ./script/addons/coverage_install_addons_dev.sh ${1} ${2} ${5}"
exit 1
fi
else
./script/addons/coverage_install_addons_dev.sh "$1" "$2"
retVal=$?
if [[ $retVal -ne 0 ]]; then
echo "Error ./script/addons/coverage_install_addons_dev.sh ${1} ${2}"
echo -e "${Red}Error${Color_Off} ./script/addons/coverage_install_addons_dev.sh ${1} ${2}"
exit 1
fi
fi
@ -32,7 +34,7 @@ fi
./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}"
echo -e "${Red}Error${Color_Off} ./script/code_generator/test_code_generator_update_module.py ${4} ${3}"
exit 1
fi
@ -54,6 +56,6 @@ echo "TEST ${2}"
./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"
echo -e "${Red}Error${Color_Off} ./script/code_generator/check_git_change_code_generator.sh"
exit 1
fi

View file

@ -4,9 +4,11 @@
# $3 is directory path to check
# $4 is generated module name separate by ,
# $5 optional, the config path
Red='\033[0;31m' # Red
Color_Off='\033[0m' # Text Reset
if [[ $# -lt 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"
echo -e "${Red}Error${Color_Off}, 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
@ -16,14 +18,14 @@ if [[ $# -eq 5 ]]; then
./script/addons/install_addons_dev.sh "$1" "$2" "$5"
retVal=$?
if [[ $retVal -ne 0 ]]; then
echo "Error ./script/addons/install_addons_dev.sh ${1} ${2} ${5}"
echo -e "${Red}Error${Color_Off} ./script/addons/install_addons_dev.sh ${1} ${2} ${5}"
exit 1
fi
else
./script/addons/install_addons_dev.sh "$1" "$2"
retVal=$?
if [[ $retVal -ne 0 ]]; then
echo "Error ./script/addons/install_addons_dev.sh ${1} ${2}"
echo -e "${Red}Error${Color_Off} ./script/addons/install_addons_dev.sh ${1} ${2}"
exit 1
fi
fi
@ -32,7 +34,7 @@ fi
./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}"
echo -e "${Red}Error${Color_Off} ./script/code_generator/test_code_generator_update_module.py ${4} ${3}"
exit 1
fi
@ -64,6 +66,6 @@ echo "TEST ${2}"
./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"
echo -e "${Red}Error${Color_Off} ./script/code_generator/check_git_change_code_generator.sh"
exit 1
fi

View file

@ -1,7 +1,8 @@
#!./.venv/bin/python
import argparse
# import glob
import astor
import json
import ast
import logging
import os
@ -48,6 +49,19 @@ def get_config():
action="store_true",
help="Will search inherit model",
)
parser.add_argument(
"--json",
action="store_true",
help="Return result in json",
)
parser.add_argument(
"--extract_field",
action="store_true",
help=(
"Return list of field for each model, detected. With inherit"
" information"
),
)
parser.add_argument(
"-q",
"--quiet",
@ -108,6 +122,64 @@ def search_and_replace(
return new_file_content
def extract_lambda(node):
result = astor.to_source(node).strip().replace("\n", "")
if result[0] == "(" and result[-1] == ")":
result = result[1:-1]
return result
def fill_search_field(ast_obj, var_name="", py_filename=""):
ast_obj_type = type(ast_obj)
result = None
if ast_obj_type is ast.Str:
result = ast_obj.s
elif ast_obj_type is ast.Lambda:
result = extract_lambda(ast_obj)
elif ast_obj_type is ast.NameConstant:
result = ast_obj.value
elif ast_obj_type is ast.Num:
result = ast_obj.n
elif ast_obj_type is ast.UnaryOp:
if type(ast_obj.op) is ast.USub:
# value is negative
result = ast_obj.operand.n * -1
else:
_logger.warning(
f"Cannot support keyword of variable {var_name} type"
f" {ast_obj_type} operator {type(ast_obj.op)} in filename"
f" {py_filename}."
)
elif ast_obj_type is ast.Name:
result = ast_obj.id
elif ast_obj_type is ast.Attribute:
# Support -> fields.Date.context_today
parent_node = ast_obj
lst_call_lambda = []
while hasattr(parent_node, "value"):
lst_call_lambda.insert(0, parent_node.attr)
parent_node = parent_node.value
lst_call_lambda.insert(0, parent_node.id)
result = ".".join(lst_call_lambda)
elif ast_obj_type is ast.List:
result = [fill_search_field(a, var_name) for a in ast_obj.elts]
elif ast_obj_type is ast.Dict:
result = {
fill_search_field(k, var_name): fill_search_field(
ast_obj.values[i], var_name
)
for (i, k) in enumerate(ast_obj.keys)
}
elif ast_obj_type is ast.Tuple:
result = tuple([fill_search_field(a, var_name) for a in ast_obj.elts])
else:
_logger.warning(
f"Cannot support keyword of variable {var_name} type"
f" {ast_obj_type} in filename {py_filename}."
)
return result
def main():
config = get_config()
if not os.path.exists(config.directory):
@ -116,6 +188,7 @@ def main():
lst_model_name = []
lst_model_inherit_name = []
lst_search_target = ("_name",)
dct_model = {}
lst_search_inherit_target = ("_inherit",) if config.with_inherit else []
@ -135,23 +208,29 @@ def main():
if type(children) == ast.ClassDef:
# Detect good _name
for node in children.body:
# Search models
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 node.targets[0].id in ("_name", "_inherit")
and type(node.value) is ast.Str
):
model_name = ""
is_inherit = False
if (
lst_search_target
and node.targets[0].id in lst_search_target
):
if node.value.s in lst_model_name:
is_duplicated = True
_logger.warning(
"Duplicated model name"
f" {node.value.s} from file {py_file}"
)
else:
model_name = node.value.s
lst_model_name.append(node.value.s)
if (
@ -159,13 +238,54 @@ def main():
and node.targets[0].id
in lst_search_inherit_target
):
is_inherit = True
if node.value.s in lst_model_inherit_name:
_logger.warning(
"Duplicated model inherit name"
f" {node.value.s} from file {py_file}"
)
else:
model_name = node.value.s
lst_model_inherit_name.append(node.value.s)
dct_fields = {}
if model_name:
dct_model[model_name] = {
"fields": dct_fields,
"model_name": model_name,
"is_inherit": is_inherit,
}
# Detect fields
# TODO do it!
if model_name and (
type(node.value) is ast.Str
and node.value.s == model_name
or type(node.value) is ast.List
and model_name
in [a.s for a in node.value.elts]
):
find_children = children
for sequence, node in enumerate(
find_children.body
):
if (
type(node) is ast.Assign
and type(node.value) is ast.Call
and node.value.func.value.id
== "fields"
):
var_name = node.targets[0].id
d = {
"name": var_name,
"type": node.value.func.attr,
"sequence": sequence,
}
dct_fields[var_name] = d
for keyword in node.value.keywords:
value = fill_search_field(
keyword.value, var_name
)
if value is not None:
d[keyword.arg] = value
lst_model_name.sort()
lst_model_inherit_name.sort()
models_name = "; ".join(lst_model_name)
@ -175,12 +295,16 @@ def main():
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 not config.json:
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)
else:
output = json.dumps(dct_model)
print(output)
if config.template_dir:
if not os.path.exists(config.template_dir):

View file

@ -3,6 +3,7 @@ import argparse
import logging
import os
import sys
from colorama import Fore, Style
logging.basicConfig(level=logging.DEBUG)
_logger = logging.getLogger(__name__)
@ -75,12 +76,14 @@ def main():
if lst_result_wrong:
_logger.error(
"FAIL - Some modules wasn't updated, did you execute the code"
f" generator? {lst_result_wrong}"
f"{Fore.RED}FAIL{Style.RESET_ALL} - Some modules wasn't updated,"
f" did you execute the code generator? {lst_result_wrong}"
)
return -1
elif lst_result_good:
_logger.info("SUCCESS - All modules are updated.")
_logger.info(
f"{Fore.GREEN}SUCCESS{Style.RESET_ALL} - All modules are updated."
)
return 0

View file

@ -4,6 +4,7 @@ import logging
import os
import sys
from xml.dom import Node, minidom
from colorama import Fore, Style
from code_writer import CodeWriter
@ -109,7 +110,7 @@ def main():
mydoc = minidom.parse(config.file)
if not mydoc:
print(f"Error, cannot parse {config.file}")
print(f"{Fore.RED}Error{Style.RESET_ALL}, cannot parse {config.file}")
sys.exit(1)
cw.emit("from lxml.builder import E")

View file

@ -1,5 +1,7 @@
#!/usr/bin/env bash
. ./env_var.sh
Red='\033[0;31m' # Red
Color_Off='\033[0m' # Text Reset
ARGS=""
IS_RELEASE=false
@ -50,7 +52,7 @@ echo "Create docker ${ERPLIBRE_DOCKER_PROD_VERSION}"
retVal=$?
if [[ $retVal -ne 0 ]]; then
echo "Error ./script/docker/docker_build.sh when execute docker_update_version.py"
echo -e "${Red}Error${Color_Off} ./script/docker/docker_build.sh when execute docker_update_version.py"
exit 1
fi
@ -67,4 +69,4 @@ docker build ${ARGS} -f Dockerfile.base -t ${ERPLIBRE_DOCKER_BASE_VERSION} .
docker build ${ARGS} -f Dockerfile.prod.pkg -t ${ERPLIBRE_DOCKER_PROD_VERSION} .
cd -
docker-compose up -d
docker compose up -d

View file

@ -7,4 +7,9 @@ BASENAME="${BASENAME//./}"
# Lowercase
BASENAME="${BASENAME,,}"
docker exec -u root -ti ${BASENAME}_ERPLibre_1 bash
docker exec -u root -ti ${BASENAME}-ERPLibre-1 bash
retVal=$?
if [[ $retVal -ne 0 ]]; then
docker exec -u root -ti ${BASENAME}_ERPLibre_1 bash
fi

View file

@ -7,7 +7,15 @@ BASENAME="${BASENAME//./}"
# Lowercase
BASENAME="${BASENAME,,}"
docker exec -u root -ti ${BASENAME}_ERPLibre_1 /bin/bash -c "\
docker exec -u root -ti ${BASENAME}-ERPLibre-1 /bin/bash -c "\
cd /ERPLibre; \
./docker/repo_manifest_gen_org_prefix_path.py /ERPLibre/addons /etc/odoo/odoo.conf /etc/odoo/odoo.conf; \
"
retVal=$?
if [[ $retVal -ne 0 ]]; then
docker exec -u root -ti ${BASENAME}_ERPLibre_1 /bin/bash -c "\
cd /ERPLibre; \
./docker/repo_manifest_gen_org_prefix_path.py /ERPLibre/addons /etc/odoo/odoo.conf /etc/odoo/odoo.conf; \
"
fi

View file

@ -7,7 +7,15 @@ BASENAME="${BASENAME//./}"
# Lowercase
BASENAME="${BASENAME,,}"
docker exec -u root -ti ${BASENAME}_ERPLibre_1 /bin/bash -c "\
docker exec -u root -ti ${BASENAME}-ERPLibre-1 /bin/bash -c "\
cd /ERPLibre; \
time make db_list; \
"
retVal=$?
if [[ $retVal -ne 0 ]]; then
docker exec -u root -ti ${BASENAME}_ERPLibre_1 /bin/bash -c "\
cd /ERPLibre; \
time make db_list; \
"
fi

View file

@ -7,7 +7,15 @@ BASENAME="${BASENAME//./}"
# Lowercase
BASENAME="${BASENAME,,}"
docker exec -u root -ti ${BASENAME}_ERPLibre_1 /bin/bash -c "\
docker exec -u root -ti ${BASENAME}-ERPLibre-1 /bin/bash -c "\
cd /ERPLibre; \
time make test; \
"
retVal=$?
if [[ $retVal -ne 0 ]]; then
docker exec -u root -ti ${BASENAME}_ERPLibre_1 /bin/bash -c "\
cd /ERPLibre; \
time make test; \
"
fi

View file

@ -7,9 +7,19 @@ BASENAME="${BASENAME//./}"
# Lowercase
BASENAME="${BASENAME,,}"
docker exec -u root -ti ${BASENAME}_ERPLibre_1 /bin/bash -c "\
docker exec -u root -ti ${BASENAME}-ERPLibre-1 /bin/bash -c "\
cd /ERPLibre; \
./.venv/repo forall -pc 'git status -s'; \
echo ''; \
git status -s; \
"
retVal=$?
if [[ $retVal -ne 0 ]]; then
docker exec -u root -ti ${BASENAME}_ERPLibre_1 /bin/bash -c "\
cd /ERPLibre; \
./.venv/repo forall -pc 'git status -s'; \
echo ''; \
git status -s; \
"
fi

View file

@ -3,6 +3,7 @@ from __future__ import print_function
import argparse
import os.path
import shutil
from colorama import Fore, Style
import yaml # pip install PyYAML
from agithub.GitHub import GitHub # pip install agithub
@ -169,7 +170,10 @@ def fork_and_clone_repo(
parsed_url.repo
].forks.post(**args)
if status == 404:
print("Error when forking repo %s" % forked_repo)
print(
f"{Fore.RED}FAIL{Style.RESET_ALL} when forking repo"
f" {forked_repo}"
)
exit(1)
else:
print("Forked %s to %s" % (upstream_url, forked_repo["html_url"]))

View file

@ -169,7 +169,6 @@ printf "max_cron_threads = 2\n" >> ${EL_CONFIG_FILE}
if [[ ${EL_INSTALL_NGINX} = "True" ]]; then
printf "workers = 2\n" >> ${EL_CONFIG_FILE}
printf "xmlrpc_interface = 127.0.0.1\n" >> ${EL_CONFIG_FILE}
printf "netrpc_interface = 127.0.0.1\n" >> ${EL_CONFIG_FILE}
printf "proxy_mode = True\n" >> ${EL_CONFIG_FILE}
else
printf "workers = 0\n" >> ${EL_CONFIG_FILE}

View file

@ -3,6 +3,7 @@ import argparse
import logging
import os
import sys
from colorama import Fore, Style
from git import Repo
@ -91,8 +92,8 @@ def main():
path2 = value2.get("@path")
if path1 != path2:
print(
f"WARNING id {i}, path of git are different. "
f"Input1 {path1}, input2 {path2}"
f"{Fore.YELLOW}WARNING{Style.RESET_ALL} id {i}, path of git"
f" are different. Input1 {path1}, input2 {path2}"
)
continue

View file

@ -5,6 +5,7 @@ import os
import webbrowser
from collections import OrderedDict
from typing import List
from colorama import Fore, Style
import git
import xmltodict
@ -307,9 +308,10 @@ class GitTool:
url = repo_root.git.remote("get-url", "origin")
except Exception as e:
print(
"WARNING: Missing origin remote, use default url "
f"{DEFAULT_REMOTE_URL}. Suggest to add a remote origin: \n"
f"> git remote add origin {DEFAULT_REMOTE_URL}"
f"{Fore.YELLOW}WARNING{Style.RESET_ALL}: Missing origin"
f" remote, use default url {DEFAULT_REMOTE_URL}. Suggest"
" to add a remote origin: \n> git remote add origin"
f" {DEFAULT_REMOTE_URL}"
)
url = DEFAULT_REMOTE_URL
url, url_https, url_git = self.get_url(url)
@ -694,7 +696,10 @@ class GitTool:
continue
line_split = line.split(",")
if len(line_split) != 4:
print(f"Error with line {line}, suppose to have only 4 ','.")
print(
f"{Fore.RED}Error{Style.RESET_ALL} with line {line},"
" suppose to have only 4 ','."
)
exit(1)
url, path, revision, clone_depth = line_split
# Validate url
@ -962,7 +967,10 @@ class GitTool:
parsed_url.repo
].forks.post(**args)
if status == 404:
print("Error when forking repo %s" % forked_repo)
print(
f"{Fore.RED}Error{Style.RESET_ALL} when forking repo"
f" {forked_repo}"
)
exit(1)
else:
try:

View file

@ -2,29 +2,7 @@
# https://docs.docker.com/engine/install/debian/
# Docker
sudo apt-get update
sudo apt-get install -y \
apt-transport-https \
ca-certificates \
curl \
gnupg-agent \
software-properties-common
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add -
sudo apt-key fingerprint 0EBFCD88
sudo add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/debian \
$(lsb_release -cs) \
stable"
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io
docker --version
# Docker-compose
sudo curl -L "https://github.com/docker/compose/releases/download/1.27.4/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
docker-compose --version
curl -fsSL https://get.docker.com | sh
# nginx
sudo apt-get install -y nginx

View file

@ -21,6 +21,9 @@ elif [ "18.04" == "${UBUNTU_VERSION}" ]; then
WKHTMLTOX_X64=https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6-1/wkhtmltox_0.12.6-1.bionic_amd64.deb
elif [[ "${OS}" == "Debian" ]]; then
WKHTMLTOX_X64=https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6.1-2/wkhtmltox_0.12.6.1-2.bullseye_amd64.deb
elif [[ "${OS}" == *"Ubuntu"* ]]; then
echo "Your version of Ubuntu is not supported, only support 18.04, 20.04 and 22.04"
WKHTMLTOX_X64=https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6.1-2/wkhtmltox_0.12.6.1-2.jammy_amd64.deb
else
echo "Your version of Ubuntu is not supported, only support 18.04, 20.04 and 22.04"
exit 1

View file

@ -3,18 +3,18 @@
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* || "${VERSION}" == Ubuntu\ 22.04* ]]; then
if [[ "${OS}" == *"Ubuntu"* ]]; then
if [[ "${VERSION}" == Ubuntu\ 18.04* || "${VERSION}" == Ubuntu\ 20.04* || "${VERSION}" == Ubuntu\ 22.04* || "${VERSION}" == Ubuntu\ 22.10* || "${VERSION}" == Ubuntu\ 23.04* || "${VERSION}" == Ubuntu\ 23.10* ]]; then
echo "\n---- linux-gnu installation process started ----"
./script/install/install_debian_dependency.sh
else
echo "Your version is not supported, only support 18.04, 20.04 and 22.04 : ${VERSION}"
echo "Your version is not supported, only support 18.04, 20.04 and 22.04 - 23.10 : ${VERSION}"
fi
elif [[ "${OS}" == "Debian" ]]; then
elif [[ "${OS}" == *"Debian"* ]]; then
./script/install/install_debian_dependency.sh
else
./script/install/install_debian_dependency.sh
echo "Your Linux system is not supported, only support Ubuntu 18.04 or Ubuntu 20.04 or Ubuntu 22.04."
echo "Your Linux system is not supported, only support Ubuntu 18.04 or Ubuntu 20.04 or Ubuntu 22.04 - Ubuntu 23.10."
fi
elif [[ "${OSTYPE}" == "darwin"* ]]; then
echo "\n---- Darwin installation process started ----"

View file

@ -16,24 +16,8 @@ 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
curl -fsSL https://get.docker.com | sh
dockerd-rootless-setuptool.sh install
else
echo "Your Linux system is not supported, only support Ubuntu 18.04 or Ubuntu 20.04."
fi

View file

@ -13,6 +13,9 @@ EL_HOME_ODOO="${EL_HOME}/odoo"
#EL_MINIMAL_ADDONS="False"
#EL_INSTALL_NGINX="True"
Red='\033[0;31m' # Red
Color_Off='\033[0m' # Text Reset
./script/generate_config.sh
#echo -e "\n---- Install Odoo with addons module ----"
@ -33,7 +36,7 @@ VENV_REPO_PATH=${VENV_PATH}/repo
VENV_MULTILINGUAL_MARKDOWN_PATH=${VENV_PATH}/multilang_md.py
#POETRY_PATH=~/.local/bin/poetry
POETRY_PATH=${VENV_PATH}/bin/poetry
POETRY_VERSION=1.3.1
POETRY_VERSION=1.5.1
echo "Python path version home"
echo ${PYENV_VERSION_PATH}
@ -42,6 +45,9 @@ echo ${LOCAL_PYTHON_EXEC}
if [[ ! -d "${PYENV_PATH}" ]]; then
echo -e "\n---- Installing pyenv in ${PYENV_PATH} ----"
# export PYENV_GIT_TAG=v2.3.35
# To change version
# rm ~/.pyenv to uninstall it
curl -L https://raw.githubusercontent.com/pyenv/pyenv-installer/master/bin/pyenv-installer | bash
fi
@ -54,7 +60,7 @@ if [[ ! -d "${PYENV_VERSION_PATH}" ]]; then
echo -e "\n---- Installing python ${PYTHON_VERSION} with pyenv in ${PYENV_VERSION_PATH} ----"
yes n|pyenv install ${PYTHON_VERSION}
if [[ $retVal -ne 0 ]]; then
echo "Error when installing pyenv"
echo -e "${Red}Error${Color_Off} when installing pyenv"
exit 1
fi
fi
@ -119,6 +125,8 @@ if [[ ! -f "${POETRY_PATH}" ]]; then
echo "Poetry installation error."
exit 1
fi
# Fix pip installation missing package
${VENV_PATH}/bin/pip install selenium
fi
# Delete artifacts created by pip, cause error in next "poetry install"

View file

@ -2,10 +2,13 @@
. ./env_var.sh
Red='\033[0;31m' # Red
Color_Off='\033[0m' # Text Reset
./script/install/install_locally.sh
retVal=$?
if [[ $retVal -ne 0 ]]; then
echo "Error ./script/install/install_locally.sh"
echo -e "${Red}Error${Color_Off} ./script/install/install_locally.sh"
exit 1
fi
@ -13,7 +16,7 @@ fi
./script/manifest/update_manifest_local_dev.sh
retVal=$?
if [[ $retVal -ne 0 ]]; then
echo "Error manifest update, check git-repo."
echo -e "${Red}Error${Color_Off} manifest update, check git-repo."
exit 1
fi

View file

@ -2,10 +2,13 @@
. ./env_var.sh
Red='\033[0;31m' # Red
Color_Off='\033[0m' # Text Reset
./script/install/install_locally.sh
retVal=$?
if [[ $retVal -ne 0 ]]; then
echo "Error ./script/install/install_locally.sh"
echo -e "${Red}Error${Color_Off} ./script/install/install_locally.sh"
exit 1
fi
@ -13,6 +16,6 @@ fi
./script/manifest/update_manifest_prod.sh
retVal=$?
if [[ $retVal -ne 0 ]]; then
echo "Error manifest update, check git-repo."
echo -e "${Red}Error${Color_Off} manifest update, check git-repo."
exit 1
fi

View file

@ -1,29 +1,8 @@
#!/usr/bin/env bash
# From https://docs.docker.com/engine/install/ubuntu/
sudo apt-get update
sudo apt-get install -y \
apt-transport-https \
ca-certificates \
curl \
gnupg-agent \
software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo apt-key fingerprint 0EBFCD88
sudo add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) \
stable"
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io
docker --version
# Docker-compose
sudo curl -L "https://github.com/docker/compose/releases/download/1.27.4/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
docker-compose --version
# Docker
curl -fsSL https://get.docker.com | sh
# nginx
sudo apt-get install -y nginx

View file

@ -39,6 +39,27 @@ async def run_command_get_output(*args, cwd=None):
return stdout.decode()
async def run_shell_get_output(cmd, cwd=None):
if cwd is not None:
process = await asyncio.create_subprocess_shell(
cmd,
# stdout must a pipe to be accessible as process.stdout
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
cwd=cwd,
)
else:
process = await asyncio.create_subprocess_shell(
cmd,
# 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()
return stdout.decode()
async def run_command_get_output_and_status(*args, cwd=None):
if cwd is not None:
process = await asyncio.create_subprocess_exec(
@ -60,13 +81,18 @@ async def run_command_get_output_and_status(*args, cwd=None):
return stdout.decode(), stderr.decode(), process.returncode
def print_summary_task(task_list):
for task in task_list:
print(task.cr_code.co_name)
def print_summary_task(task_list, lst_task_name=None):
for i, task in enumerate(task_list):
if lst_task_name:
task_name = lst_task_name[i]
print(f"{i} - {task.cr_code.co_name} - {task_name}")
else:
print(f"{i} - {task.cr_code.co_name}")
def execute(config, lst_task, use_uvloop=False):
error_detected = False
return_value = None
if not config.no_parallel and asyncio.get_event_loop().is_closed():
asyncio.set_event_loop(asyncio.new_event_loop())
@ -85,14 +111,23 @@ def execute(config, lst_task, use_uvloop=False):
if use_uvloop:
uvloop.install()
loop = asyncio.get_event_loop()
# Can fix when cannot attach child to loop
# if sys.platform != "win32":
# policy = asyncio.get_event_loop_policy()
# watcher = asyncio.SafeChildWatcher()
# watcher.attach_loop(loop)
# policy.set_child_watcher(watcher)
if config.debug:
loop.set_debug(True)
try:
commands = asyncio.gather(*lst_task)
return_value = loop.run_until_complete(commands)
except RuntimeError as e:
error_detected = True
print(e)
finally:
loop.close()
return return_value
return return_value, error_detected
class AsyncioPool:

View file

@ -1,4 +1,7 @@
#!/usr/bin/env bash
Red='\033[0;31m' # Red
Color_Off='\033[0m' # Text Reset
# This will format all python file
# argument 1: directory or file to format
NPROC=$(nproc)
@ -6,6 +9,6 @@ source ./script/OCA_maintainer-tools/env/bin/activate
oca-autopep8 -j ${NPROC} --max-line-length 79 -ari $@
retVal=$?
if [[ $retVal -ne 0 ]]; then
echo "Error oca-autopep8 format"
echo -e "${Red}Error${Color_Off} oca-autopep8 format"
exit 1
fi

View file

@ -1,10 +1,13 @@
#!/usr/bin/env bash
Red='\033[0;31m' # Red
Color_Off='\033[0m' # Text Reset
# This will format all python file
# argument 1: directory or file to format
source ./.venv/bin/activate
black -l 79 --preview -t py37 "$@"
retVal=$?
if [[ $retVal -ne 0 ]]; then
echo "Error black format"
echo -e "${Red}Error${Color_Off} black format"
exit 1
fi

View file

@ -1,9 +1,12 @@
#!/usr/bin/env bash
Red='\033[0;31m' # Red
Color_Off='\033[0m' # Text Reset
# This will format all js,css,html
# argument 1: directory or file to format
./node_modules/.bin/prettier --tab-width 4 --print-width 120 --no-bracket-spacing --write "$@"
retVal=$?
if [[ $retVal -ne 0 ]]; then
echo "Error prettier format"
echo -e "${Red}Error${Color_Off} prettier format"
exit 1
fi

View file

@ -1,4 +1,7 @@
#!/usr/bin/env bash
Red='\033[0;31m' # Red
Color_Off='\033[0m' # Text Reset
# This will format all xml
# argument 1: directory or file to format
STR_ARG="'$*'"
@ -12,6 +15,6 @@ else
fi
retVal=$?
if [[ $retVal -ne 0 ]]; then
echo "Error prettier-xml format"
echo -e "${Red}Error${Color_Off} prettier-xml format"
exit 1
fi

View file

@ -1,4 +1,7 @@
#!/usr/bin/env bash
Red='\033[0;31m' # Red
Color_Off='\033[0m' # Text Reset
echo "
===> ${@}
"
@ -10,6 +13,6 @@ echo "
"
if [[ $retVal -ne 0 ]]; then
echo "Error make ${@}"
echo -e "${Red}Error${Color_Off} make ${@}"
exit 1
fi

View file

@ -3,9 +3,9 @@
working_path=$(readlink -f .)
paths=(
"${working_path}/"
# "${working_path}/addons/TechnoLibre_odoo-code-generator"
# "${working_path}/addons/TechnoLibre_odoo-code-generator-template"
"${working_path}/addons/ERPLibre_erplibre_addons"
"${working_path}/addons/TechnoLibre_odoo-code-generator"
"${working_path}/addons/TechnoLibre_odoo-code-generator-template"
# "${working_path}/addons/OCA_server-tools"
)

View file

@ -1,10 +1,13 @@
#!/usr/bin/env bash
Red='\033[0;31m' # Red
Color_Off='\033[0m' # Text Reset
./.venv/bin/poetry add -vv $(grep -v ";" ./.venv/build_dependency.txt | grep -v "*" )
# poetry add -vv $(grep -v ";" ./.venv/build_dependency.txt | grep -v "*" | sed 's/==/@^/' )
# poetry export -f ./.venv/build_dependency.txt --dev | poetry run -- pip install -r /dev/stdin
retVal=$?
if [[ $retVal -ne 0 ]]; then
echo "Error ./script/poetry/poetry_add_build_dependency.sh"
echo -e "${Red}Error${Color_Off} ./script/poetry/poetry_add_build_dependency.sh"
cat ./.venv/build_dependency.txt
exit 1
fi

View file

@ -3,9 +3,9 @@ import argparse
import ast
import logging
import os
import sys
from collections import OrderedDict, defaultdict
from pathlib import Path
from colorama import Fore, Style
import iscompatible
import toml
@ -267,8 +267,8 @@ def combine_requirements(config):
]
)
print(
f"WARNING - Not compatible {str_versions} - "
f"{str_result_choose}."
f"{Fore.YELLOW}WARNING{Style.RESET_ALL} - Not"
f" compatible {str_versions} - {str_result_choose}."
)
elif len(lst_version_requirement) == 1:
result = lst_version_requirement[0][0]

View file

@ -229,7 +229,7 @@ def main():
clone_repo(i, a, config.force_git_fetch)
for i, a in enumerate(lst_repo_url)
]
lst_repo_path = lib_asyncio.execute(
lst_repo_path, has_asyncio_error = lib_asyncio.execute(
config, lst_task_clone, use_uvloop=True
)
@ -268,13 +268,15 @@ def main():
min_i = i * MAX_COROUTINE
max_i = min((i + 1) * MAX_COROUTINE, len(lst_task))
_logger.info(f"Partial execution {min_i} to {max_i}")
tpl_result = lib_asyncio.execute(
tpl_result, has_asyncio_error = lib_asyncio.execute(
config, lst_task[min_i:max_i], use_uvloop=True
)
lst_result += tpl_result
tpl_result = lst_result
else:
tpl_result = lib_asyncio.execute(config, lst_task, use_uvloop=True)
tpl_result, has_asyncio_error = lib_asyncio.execute(
config, lst_task, use_uvloop=True
)
_logger.info("Analyse information")
dct_result = defaultdict(lambda: defaultdict(int))
dct_result_unique = defaultdict(set)

View file

@ -0,0 +1,20 @@
#!/bin/bash
USE_LOCATE=true
if [ "$USE_LOCATE" == false ]
then
find "/" -name "docker-compose.yml" -type f -print 2>/dev/null | grep -v .repo | grep -v /var/lib/docker | xargs -I {} sh -c 'grep -l "ERPLibre" "{}" 2>/dev/null || true'
# SEARCH_PATH="/"
# find "$SEARCH_PATH" -name "docker-compose.yml" -type f 2>/dev/null | grep -v .repo | while read -r file; do
# grep -q "ERPLibre" "$file" && echo "$file"
# done
elif [ "$USE_LOCATE" == true ]
then
locate -b -r "^docker-compose\.yml$" | grep -v .repo | grep -v /var/lib/docker | xargs -I {} sh -c "grep -l \"ERPLibre\" \"{}\" 2>/dev/null || true"
# locate -b -r '^docker-compose\.yml$' | grep -v .repo | while read -r file; do
# if [ -f "$file" ]; then
# grep -q 'ERPLibre' "$file" && echo "$file"
# fi
# done
fi

View file

@ -0,0 +1,149 @@
{
"lst_test": [
{
"run_command": true,
"test_name": "helloworld_test",
"sequence": 10,
"script": "./test/code_generator/hello_world.sh",
"note": "Test helloword_test Will cause conflict with the other because write in code_generator_demo/hooks.py"
},
{
"run_test_exec": true,
"test_name": "code_generator_theme_test",
"path_module_check": "./addons/TechnoLibre_odoo-code-generator-template",
"generated_module": "theme_website_demo_code_generator",
"tested_module": "code_generator_demo_theme_website"
},
{
"run_test_exec": true,
"test_name": "code_generator_demo_test",
"path_module_check": "./addons/TechnoLibre_odoo-code-generator-template",
"generated_module": "code_generator_demo",
"tested_module": "code_generator_demo"
},
{
"run_test_exec": true,
"test_name": "code_generator_data_test",
"path_module_check": "./addons/TechnoLibre_odoo-code-generator-template",
"generated_module": "demo_helpdesk_data",
"tested_module": "code_generator_demo_export_helpdesk"
},
{
"run_test_exec": true,
"test_name": "code_generator_data_part_2_test",
"path_module_check": "./addons/TechnoLibre_odoo-code-generator-template",
"generated_module": "demo_website_data",
"tested_module": "code_generator_demo_export_website",
"note": "Merge to code_generator_data_test when fix double export data."
},
{
"run_test_exec": true,
"test_name": "code_generator_inherit_test",
"path_module_check": "./addons/TechnoLibre_odoo-code-generator-template",
"generated_module": "demo_internal_inherit",
"tested_module": "code_generator_demo_internal_inherit"
},
{
"run_test_exec": true,
"test_name": "code_generator_template_demo_internal",
"path_module_check": "./addons/TechnoLibre_odoo-code-generator-template",
"generated_module": "code_generator_demo_internal",
"tested_module": "code_generator_template_demo_internal",
"search_class_module": "demo_internal",
"init_module_name": "demo_internal"
},
{
"run_test_exec": true,
"test_name": "code_generator_template_demo_portal",
"path_module_check": "./addons/TechnoLibre_odoo-code-generator-template",
"generated_module": "code_generator_demo_portal",
"tested_module": "code_generator_template_demo_portal",
"search_class_module": "demo_portal",
"init_module_name": "demo_portal"
},
{
"run_test_exec": true,
"test_name": "mariadb_test_template",
"path_module_check": "./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",
"init_module_name": "code_generator_portal,demo_mariadb_sql_example_1"
},
{
"run_test_exec": true,
"test_name": "mariadb_test_migrator",
"path_module_check": "./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",
"init_module_name": "code_generator_portal"
},
{
"run_test_exec": true,
"test_name": "mariadb_test_code_generator",
"path_module_check": "./addons/TechnoLibre_odoo-code-generator-template",
"generated_module": "demo_mariadb_sql_example_1",
"tested_module": "code_generator_demo_mariadb_sql_example_1",
"init_module_name": "code_generator_portal"
},
{
"run_test_exec": true,
"test_name": "code_generator_template_demo_internal_inherit",
"path_module_check": "./addons/TechnoLibre_odoo-code-generator-template",
"generated_module": "code_generator_demo_internal_inherit",
"tested_module": "code_generator_template_demo_internal_inherit",
"search_class_module": "demo_internal_inherit",
"init_module_name": "demo_internal_inherit"
},
{
"run_test_exec": true,
"test_name": "code_generator_template_demo_sysadmin_cron",
"path_module_check": "./addons/OCA_server-tools/auto_backup",
"generated_module": "code_generator_auto_backup",
"generated_path": "./addons/OCA_server-tools/",
"tested_module": "code_generator_template_demo_sysadmin_cron",
"search_class_module": "auto_backup",
"init_module_name": "auto_backup",
"install_path": "./addons/TechnoLibre_odoo-code-generator-template"
},
{
"run_test_exec": true,
"test_name": "code_generator_export_website_attachments_test",
"path_module_check": "./addons/TechnoLibre_odoo-code-generator-template",
"generated_module": "demo_website_attachments_data",
"tested_module": "code_generator_demo_export_website_attachments",
"restore_db_image_name": "test_website_attachments"
},
{
"run_test_exec": true,
"test_name": "code_generator_demo_generic_test",
"path_module_check": "./addons/TechnoLibre_odoo-code-generator-template",
"generated_module": "demo_internal,demo_portal",
"tested_module": "code_generator_demo_internal,code_generator_demo_portal"
},
{
"run_test_exec": true,
"test_name": "code_generator_website_snippet_test",
"path_module_check": "./addons/TechnoLibre_odoo-code-generator-template",
"generated_module": "demo_website_leaflet,demo_website_snippet,demo_website_multiple_snippet",
"tested_module": "code_generator_demo_website_leaflet,code_generator_demo_website_snippet,code_generator_demo_website_multiple_snippet",
"file_to_restore": "demo_portal/i18n/demo_portal.pot,demo_portal/i18n/fr_CA.po",
"file_to_restore_origin": true,
"note": "Because code_generator_demo_website_multiple_snippet depend on code_generator_demo_portal, it will execute it and this delete file demo_portal/i18n/demo_portal.pot and demo_portal/i18n/fr_CA.po"
},
{
"run_test_exec": true,
"test_name": "demo_test",
"path_module_check": "./addons/TechnoLibre_odoo-code-generator-template",
"init_module_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"
},
{
"run_test_exec": true,
"test_name": "code_generator_auto_backup_test",
"path_module_check": "./addons/OCA_server-tools/auto_backup",
"generated_module": "auto_backup",
"tested_module": "code_generator_auto_backup"
}
]
}

View file

@ -9,11 +9,13 @@ import sys
import tempfile
import time
import uuid
import json
from typing import Tuple
from collections import defaultdict
import aioshutil
import git
from colorama import Fore
from colorama import Fore, Style
new_path = os.path.normpath(
os.path.join(os.path.dirname(__file__), "..", "..")
@ -26,6 +28,7 @@ logging.basicConfig(level=logging.DEBUG)
_logger = logging.getLogger(__name__)
LOG_FILE = "./.venv/make_test.log"
CONFIG_TESTCASE_JSON = "./script/test/config_testcase.json"
def get_config():
@ -71,6 +74,17 @@ def get_config():
action="store_true",
help="Enable asyncio debugging",
)
parser.add_argument(
"--json_model",
help="Model of data to run test in json",
)
parser.add_argument(
"--output_result_dir",
help=(
"A path of existing directory, will export a file per test with"
" status/result and log."
),
)
args = parser.parse_args()
return args
@ -136,14 +150,14 @@ def check_result(task_list, tpl_result):
status = False
if lst_warning:
print(f"{Fore.YELLOW}{len(lst_warning)} WARNING{Fore.RESET}")
print(f"{Fore.YELLOW}{len(lst_warning)} WARNING{Style.RESET_ALL}")
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}")
print(f"{Fore.RED}{len(lst_error)} ERROR{Style.RESET_ALL}")
i = 0
for error in lst_error:
i += 1
@ -151,13 +165,13 @@ def check_result(task_list, tpl_result):
if lst_error or lst_warning:
str_result = (
f"{Fore.RED}{len(lst_error)} ERROR"
f" {Fore.YELLOW}{len(lst_warning)} WARNING"
f"{Fore.RED}{len(lst_error)} ERROR{Style.RESET_ALL}"
f" {Fore.YELLOW}{len(lst_warning)} WARNING{Style.RESET_ALL}"
)
else:
str_result = f"{Fore.GREEN}SUCCESS 🍰"
str_result = f"{Fore.GREEN}SUCCESS{Style.RESET_ALL} 🍰"
print(f"{Fore.BLUE}Summary TEST {str_result}{Fore.RESET}")
print(f"{Fore.BLUE}Summary TEST {str_result}{Style.RESET_ALL}")
return status
@ -168,7 +182,11 @@ def print_log(lst_task, tpl_result):
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"
status_str = (
f"{Fore.GREEN}PASS{Style.RESET_ALL}"
if not result[1]
else f"{Fore.RED}FAIL{Style.RESET_ALL}"
)
f.write(
f"\nTest execution {i + 1} - {status_str} -"
f" {task.cr_code.co_name}\n\n"
@ -178,6 +196,33 @@ def print_log(lst_task, tpl_result):
f.write("\n")
def print_log_output_into_dir(tpl_result, output_dir):
date_now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
for i, result in enumerate(tpl_result):
log = result[0]
status = result[1]
test_name = result[2]
test_name_file_name = test_name.replace(" ", "")
time_execution = result[3]
log_file = os.path.join(output_dir, test_name_file_name)
with open(log_file, "w") as f:
f.write(f"{status}\n")
f.write(f"{test_name}\n")
f.write(f"{time_execution}\n")
f.write(f"{date_now}\n")
status_str = (
f"{Fore.GREEN}PASS{Style.RESET_ALL}"
if not status
else f"{Fore.RED}FAIL{Style.RESET_ALL}"
)
f.write(
f"\nTest execution {i + 1} - {status_str} - {test_name}\n\n"
)
if log:
f.write(log)
f.write("\n")
async def run_command(*args, test_name=None):
# Create subprocess
start_time = time.time()
@ -195,7 +240,11 @@ async def run_command(*args, test_name=None):
# 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"
status_str = (
f"{Fore.RED}FAIL{Style.RESET_ALL}"
if process.returncode
else f"{Fore.GREEN}PASS{Style.RESET_ALL}"
)
if test_name:
str_output_init = (
f"\n\n{status_str} [{test_name}] [{diff_sec:.3f}s] Execute"
@ -255,7 +304,6 @@ def update_config(
async def test_exec(
config,
path_module_check: str,
generated_module=None,
generate_path=None,
@ -263,14 +311,19 @@ async def test_exec(
search_class_module=None,
script_after_init_check=None,
lst_init_module_name=None,
file_to_restore=None,
file_to_restore_origin=False,
test_name=None,
install_path=None,
run_in_sandbox=False,
run_in_sandbox=True,
restore_db_image_name="erplibre_base",
) -> Tuple[str, int]:
keep_cache=False,
coverage=False,
) -> Tuple[str, int, str, float]:
time_init = datetime.datetime.now()
test_result = ""
test_status = 0
new_destination_path = None
origin_path_module_check = path_module_check
if search_class_module:
if install_path is not None:
path_template_to_generate = os.path.join(
@ -300,7 +353,7 @@ async def test_exec(
destination_path = None
temp_dir_name = None
if run_in_sandbox:
if config.keep_cache:
if keep_cache:
temp_dir = tempfile.mkdtemp()
temp_dir_name = temp_dir
else:
@ -313,9 +366,10 @@ async def test_exec(
lst_path_to_add_config = []
lst_path_to_remove_config = []
if not os.path.exists(path_module_check):
# TODO wrong return
return (
f"Error var path_module_check '{path_module_check}' not"
" exist.",
f"{Fore.RED}Error{Style.RESET_ALL} var path_module_check"
f" '{path_module_check}' not exist.",
-1,
)
@ -369,9 +423,10 @@ async def test_exec(
)
)
if not s_lst_path_tested_module:
# TODO wrong return
return (
f"Error cannot find module '{path_module_check}' not"
" exist.",
f"{Fore.RED}Error{Style.RESET_ALL} cannot find module"
f" '{path_module_check}' not exist.",
-1,
)
else:
@ -431,6 +486,12 @@ async def test_exec(
s_first_path = destination_path
hook_file = os.path.join(new_s_first_path, "hooks.py")
if not os.path.exists(hook_file):
test_status += 1
test_result += f"Hook file '{hook_file}' not exist."
delta = datetime.datetime.now() - time_init
total_time = delta.total_seconds()
return test_result, test_status, test_name, total_time
with open(hook_file) as hook:
hook_line = hook.read()
has_template = (
@ -460,6 +521,7 @@ async def test_exec(
)
break
if nb_space_indentation is None:
# TODO wrong return
return (
f"Cannot find keys '{lst_f_key}' in"
f" {hook_file}",
@ -520,6 +582,12 @@ async def test_exec(
lst_path_to_remove_config=lst_path_to_remove_config,
module_name=generated_module,
)
if not os.path.exists(new_config_path):
test_status += 1
test_result += f"Config file '{new_config_path}' not exist."
delta = datetime.datetime.now() - time_init
total_time = delta.total_seconds()
return test_result, test_status, test_name, total_time
else:
new_config_path = None
@ -555,7 +623,9 @@ async def test_exec(
# Leave when detect anomaly in check before start
if test_status:
return test_result, test_status
delta = datetime.datetime.now() - time_init
total_time = delta.total_seconds()
return test_result, test_status, test_name, total_time
# Execute script before start
if script_after_init_check and not test_status:
@ -582,7 +652,7 @@ async def test_exec(
if not test_status and lst_init_module_name:
# Install required module
str_test = ",".join(lst_init_module_name)
if config.coverage:
if coverage:
script_name = (
"./script/addons/coverage_install_addons_dev.sh"
if tested_module
@ -638,7 +708,7 @@ async def test_exec(
if not test_status and tested_module and generated_module:
cmd = (
"./script/code_generator/coverage_install_and_test_code_generator.sh"
if config.coverage
if coverage
else "./script/code_generator/install_and_test_code_generator.sh"
)
# Finally, the test
@ -677,7 +747,28 @@ async def test_exec(
test_result += res
test_status += status
return test_result, test_status
if file_to_restore:
lst_file_to_ignore = file_to_restore.strip().split(",")
if file_to_restore_origin:
repo_demo_portal = git.Repo(origin_path_module_check)
else:
repo_demo_portal = git.Repo(path_module_check)
status_to_check = repo_demo_portal.git.status("-s")
if all([f"D {a}" in status_to_check for a in lst_file_to_ignore]):
# Revert it
for file_name in lst_file_to_ignore:
repo_demo_portal.git.checkout(file_name)
else:
test_status += 1
test_result += (
f"\n\n{Fore.RED}FAIL{Style.RESET_ALL} - inspect to delete file"
f" {lst_file_to_ignore}"
)
delta = datetime.datetime.now() - time_init
total_time = delta.total_seconds()
return test_result, test_status, test_name, total_time
def check_git_change():
@ -707,534 +798,133 @@ def check_git_change():
return not status
# START TEST
async def run_test_command(
script_path: str, test_name: str
) -> Tuple[str, int, str, float]:
time_init = datetime.datetime.now()
res, status = await run_command(script_path, test_name=test_name)
delta = datetime.datetime.now() - time_init
total_time = delta.total_seconds()
return res, status, test_name, total_time
async def run_demo_test(config) -> 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(
config,
"./addons/TechnoLibre_odoo-code-generator-template",
lst_init_module_name=lst_test_name,
test_name="demo_test",
)
return res, status
async def run_code_generator_migrator_demo_mariadb_sql_example_1_test(
config,
) -> Tuple[str, int]:
# Migrator
res, status = await test_exec(
config,
"./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",
run_in_sandbox=True,
)
return res, status
async def run_code_generator_template_demo_mariadb_sql_example_1_test(
config,
) -> Tuple[str, int]:
# Template
res, status = await test_exec(
config,
"./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",
run_in_sandbox=True,
)
return res, status
async def run_code_generator_demo_mariadb_sql_example_1_test(
config,
) -> Tuple[str, int]:
# Code generator
res, status = await test_exec(
config,
"./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",
run_in_sandbox=True,
)
return res, status
async def run_code_generator_data_test(config) -> Tuple[str, int]:
test_result = ""
test_status = 0
lst_generated_module = [
"demo_helpdesk_data",
# "demo_website_data",
]
lst_tested_module = [
"code_generator_demo_export_helpdesk",
# "code_generator_demo_export_website",
]
# Multiple
res, status = await test_exec(
config,
"./addons/TechnoLibre_odoo-code-generator-template",
generated_module=",".join(lst_generated_module),
tested_module=",".join(lst_tested_module),
test_name="code_generator_data_test",
run_in_sandbox=True,
)
test_result += res
test_status += status
return test_result, test_status
async def run_code_generator_data_test_part_2(config) -> Tuple[str, int]:
# TODO merge this test into run_code_generator_data_test
test_result = ""
test_status = 0
lst_generated_module = [
"demo_website_data",
]
lst_tested_module = [
"code_generator_demo_export_website",
]
# Multiple
res, status = await test_exec(
config,
"./addons/TechnoLibre_odoo-code-generator-template",
generated_module=",".join(lst_generated_module),
tested_module=",".join(lst_tested_module),
test_name="code_generator_data_part_2_test",
run_in_sandbox=True,
)
test_result += res
test_status += status
return test_result, test_status
async def run_code_generator_export_website_attachments_test(
config,
) -> Tuple[str, int]:
test_result = ""
test_status = 0
lst_generated_module = [
"demo_website_attachments_data",
]
lst_tested_module = [
"code_generator_demo_export_website_attachments",
]
# Multiple
res, status = await test_exec(
config,
"./addons/TechnoLibre_odoo-code-generator-template",
generated_module=",".join(lst_generated_module),
tested_module=",".join(lst_tested_module),
test_name="code_generator_export_website_attachments_test",
run_in_sandbox=True,
restore_db_image_name="test_website_attachments",
)
test_result += res
test_status += status
return test_result, test_status
async def run_code_generator_theme_test(config) -> Tuple[str, int]:
test_result = ""
test_status = 0
lst_generated_module = [
"theme_website_demo_code_generator",
]
lst_tested_module = [
"code_generator_demo_theme_website",
]
# Multiple
res, status = await test_exec(
config,
"./addons/TechnoLibre_odoo-code-generator-template",
generated_module=",".join(lst_generated_module),
tested_module=",".join(lst_tested_module),
test_name="code_generator_theme_test",
run_in_sandbox=True,
)
test_result += res
test_status += status
return test_result, test_status
async def run_code_generator_generic_all_test(config) -> Tuple[str, int]:
test_result = ""
test_status = 0
lst_generated_module = [
"demo_internal",
"demo_portal",
"demo_helpdesk_data",
"demo_website_data",
"demo_website_leaflet",
"demo_website_snippet",
"theme_website_demo_code_generator",
]
lst_tested_module = [
"code_generator_demo_internal",
"code_generator_demo_portal",
"code_generator_demo_export_helpdesk",
"code_generator_demo_export_website",
"code_generator_demo_website_leaflet",
"code_generator_demo_website_snippet",
"code_generator_demo_theme_website",
]
# Multiple
res, status = await test_exec(
config,
"./addons/TechnoLibre_odoo-code-generator-template",
generated_module=",".join(lst_generated_module),
tested_module=",".join(lst_tested_module),
test_name="code_generator_generic_all_test",
run_in_sandbox=True,
)
test_result += res
test_status += status
return test_result, test_status
async def run_code_generator_website_snippet_test(config) -> Tuple[str, int]:
test_result = ""
test_status = 0
lst_generated_module = [
"demo_website_leaflet",
"demo_website_snippet",
"demo_website_multiple_snippet",
]
lst_tested_module = [
"code_generator_demo_website_leaflet",
"code_generator_demo_website_snippet",
"code_generator_demo_website_multiple_snippet",
]
# Multiple
res, status = await test_exec(
config,
"./addons/TechnoLibre_odoo-code-generator-template",
generated_module=",".join(lst_generated_module),
tested_module=",".join(lst_tested_module),
test_name="code_generator_website_snippet_test",
run_in_sandbox=True,
)
test_result += res
test_status += status
if "code_generator_demo_website_multiple_snippet" in lst_tested_module:
# Because code_generator_demo_website_multiple_snippet depend on code_generator_demo_portal, it will
# execute it and this delete file demo_portal/i18n/demo_portal.pot and demo_portal/i18n/fr_CA.po
lst_file_is_delete = [
"demo_portal/i18n/demo_portal.pot",
"demo_portal/i18n/fr_CA.po",
]
path_to_check = os.path.join(
"addons", "TechnoLibre_odoo-code-generator-template"
)
repo_demo_portal = git.Repo(path_to_check)
status_to_check = repo_demo_portal.git.status("-s")
if all([f"D {a}" in status_to_check for a in lst_file_is_delete]):
# Revert it
for file_name in lst_file_is_delete:
repo_demo_portal.git.checkout(file_name)
else:
test_status += 1
test_result += (
f"\n\nFAIL - inspect to delete file ${lst_file_is_delete}"
def run_all_test(config) -> bool:
if config.json_model:
json_model = config.json_model
else:
with open(CONFIG_TESTCASE_JSON) as config_json:
json_model = config_json.read()
dct_model_test = json.loads(json_model)
lst_test = dct_model_test.get("lst_test")
if not lst_test:
_logger.error("Model missing attribute 'lst_test'.")
return False
# Sort test by sequence
dct_task = defaultdict(list)
dct_task_name = defaultdict(list)
for dct_test in lst_test:
sequence = dct_test.get("sequence", 0)
cb_coroutine = None
test_name = None
if dct_test.get("run_command"):
test_name = dct_test.get("test_name")
if not test_name:
raise ValueError(
"Missing attribute 'test_name' into the json_model."
)
script = dct_test.get("script")
if not script:
raise ValueError(
"Missing attribute 'script' into the json_model."
)
cb_coroutine = run_test_command(script, test_name)
elif dct_test.get("run_test_exec"):
path_module_check = dct_test.get("path_module_check")
if not path_module_check:
raise ValueError(
"Missing attribute 'path_module_check' into the"
" json_model."
)
generated_module = dct_test.get("generated_module")
generate_path = dct_test.get("generate_path")
tested_module = dct_test.get("tested_module")
search_class_module = dct_test.get("search_class_module")
script_after_init_check = dct_test.get("script_after_init_check")
init_module_name = dct_test.get("init_module_name")
lst_init_module_name = None
if init_module_name:
lst_init_module_name = init_module_name.split(",")
test_name = dct_test.get("test_name")
if not test_name:
raise ValueError(
"Missing attribute 'test_name' into the json_model."
)
file_to_restore = dct_test.get("file_to_restore")
file_to_restore_origin = dct_test.get(
"file_to_restore_origin", False
)
install_path = dct_test.get("install_path")
run_in_sandbox = dct_test.get("run_in_sandbox", True)
restore_db_image_name = dct_test.get(
"restore_db_image_name", "erplibre_base"
)
keep_cache = config.keep_cache
coverage = config.coverage
cb_coroutine = test_exec(
path_module_check,
generated_module=generated_module,
generate_path=generate_path,
tested_module=tested_module,
search_class_module=search_class_module,
script_after_init_check=script_after_init_check,
lst_init_module_name=lst_init_module_name,
file_to_restore=file_to_restore,
file_to_restore_origin=file_to_restore_origin,
test_name=test_name,
install_path=install_path,
run_in_sandbox=run_in_sandbox,
restore_db_image_name=restore_db_image_name,
keep_cache=keep_cache,
coverage=coverage,
)
if cb_coroutine:
dct_task[sequence].append(cb_coroutine)
dct_task_name[sequence].append(test_name)
lst_sequence = sorted(list(dct_task.keys()))
# Print summary
total_tpl_result = []
total_tpl_task = []
# Print summary
for sequence in lst_sequence:
lst_task = dct_task[sequence]
lst_task_name = dct_task_name[sequence]
print(f"sequence {sequence}")
lib_asyncio.print_summary_task(lst_task, lst_task_name=lst_task_name)
# Execute in sequence
for sequence in lst_sequence:
lst_task = dct_task[sequence]
tpl_result, has_asyncio_error = lib_asyncio.execute(config, lst_task)
if tpl_result:
total_tpl_result.extend(tpl_result)
else:
_logger.error("tpl_result is empty.")
if lst_task:
total_tpl_task.extend(lst_task)
else:
_logger.error("tpl_task is empty.")
return test_result, test_status
async def run_code_generator_demo_generic_test(config) -> Tuple[str, int]:
test_result = ""
test_status = 0
lst_generated_module = [
"demo_internal",
"demo_portal",
]
lst_tested_module = [
"code_generator_demo_internal",
"code_generator_demo_portal",
]
# Multiple
res, status = await test_exec(
config,
"./addons/TechnoLibre_odoo-code-generator-template",
generated_module=",".join(lst_generated_module),
tested_module=",".join(lst_tested_module),
test_name="code_generator_demo_generic_test",
run_in_sandbox=True,
)
test_result += res
test_status += status
return test_result, test_status
async def run_code_generator_demo_test(config) -> Tuple[str, int]:
test_result = ""
test_status = 0
lst_generated_module = [
"code_generator_demo",
]
lst_tested_module = [
"code_generator_demo",
]
# Multiple
res, status = await test_exec(
config,
"./addons/TechnoLibre_odoo-code-generator-template",
generated_module=",".join(lst_generated_module),
tested_module=",".join(lst_tested_module),
test_name="code_generator_demo_test",
)
test_result += res
test_status += status
return test_result, test_status
async def run_code_generator_inherit_test(config) -> 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(
config,
"./addons/TechnoLibre_odoo-code-generator-template",
generated_module=",".join(lst_generated_module),
tested_module=",".join(lst_tested_module),
test_name="code_generator_inherit_test",
run_in_sandbox=True,
)
test_result += res
test_status += status
return test_result, test_status
async def run_code_generator_auto_backup_test(config) -> 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(
config,
"./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",
run_in_sandbox=True,
)
test_result += res
test_status += status
return test_result, test_status
async def run_code_generator_template_demo_portal_test(
config,
) -> Tuple[str, int]:
test_result = ""
test_status = 0
# Template
res, status = await test_exec(
config,
"./addons/TechnoLibre_odoo-code-generator-template",
generated_module="code_generator_demo_portal",
tested_module="code_generator_template_demo_portal",
search_class_module="demo_portal",
lst_init_module_name=[
"demo_portal",
],
test_name="code_generator_template_demo_portal",
run_in_sandbox=True,
)
test_result += res
test_status += status
return test_result, test_status
async def run_code_generator_template_demo_internal_test(
config,
) -> Tuple[str, int]:
test_result = ""
test_status = 0
# Template
res, status = await test_exec(
config,
"./addons/TechnoLibre_odoo-code-generator-template",
generated_module="code_generator_demo_internal",
tested_module="code_generator_template_demo_internal",
search_class_module="demo_internal",
lst_init_module_name=[
"demo_internal",
],
test_name="code_generator_template_demo_internal",
run_in_sandbox=True,
)
test_result += res
test_status += status
return test_result, test_status
async def run_code_generator_template_demo_internal_inherit_test(
config,
) -> Tuple[str, int]:
test_result = ""
test_status = 0
# Template
res, status = await test_exec(
config,
"./addons/TechnoLibre_odoo-code-generator-template",
generated_module="code_generator_demo_internal_inherit",
tested_module="code_generator_template_demo_internal_inherit",
search_class_module="demo_internal_inherit",
lst_init_module_name=[
"demo_internal_inherit",
],
test_name="code_generator_template_demo_internal_inherit",
run_in_sandbox=True,
)
test_result += res
test_status += status
return test_result, test_status
async def run_code_generator_template_demo_sysadmin_cron_test(
config,
) -> Tuple[str, int]:
test_result = ""
test_status = 0
# Template
res, status = await test_exec(
config,
"./addons/OCA_server-tools/auto_backup",
generated_module="code_generator_auto_backup",
generate_path="./addons/OCA_server-tools/",
tested_module="code_generator_template_demo_sysadmin_cron",
search_class_module="auto_backup",
lst_init_module_name=[
"auto_backup",
],
test_name="code_generator_template_demo_sysadmin_cron",
install_path="./addons/TechnoLibre_odoo-code-generator-template",
run_in_sandbox=True,
)
test_result += res
test_status += status
return test_result, test_status
async def run_helloworld_test(config) -> 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(config) -> None:
# low in time, at the end for more speed
task_list = [
run_code_generator_migrator_demo_mariadb_sql_example_1_test(config),
run_code_generator_template_demo_mariadb_sql_example_1_test(config),
run_code_generator_demo_mariadb_sql_example_1_test(config),
run_code_generator_auto_backup_test(config),
run_code_generator_template_demo_portal_test(config),
run_code_generator_template_demo_internal_test(config),
run_code_generator_template_demo_internal_inherit_test(config),
run_code_generator_template_demo_sysadmin_cron_test(config),
run_code_generator_demo_test(config),
# Begin to run generic test
# run_code_generator_generic_all_test(config),
run_code_generator_data_test(config),
run_code_generator_data_test_part_2(config),
run_code_generator_export_website_attachments_test(config),
run_code_generator_theme_test(config),
run_code_generator_website_snippet_test(config),
run_code_generator_demo_generic_test(config),
# End run generic test
run_code_generator_inherit_test(config),
run_demo_test(config),
]
task_second_list = [
# TODO Will cause conflict with the other because write in code_generator_demo/hooks.py
run_helloworld_test(config),
]
_logger.info("First list task")
lib_asyncio.print_summary_task(task_list)
_logger.info("Second list task")
lib_asyncio.print_summary_task(task_second_list)
_logger.info("First execution")
tpl_result = lib_asyncio.execute(config, task_list)
_logger.info("Second execution")
tpl_result_second = lib_asyncio.execute(config, task_second_list)
# Extra
tpl_result_total = tpl_result + tpl_result_second
task_list_total = task_list + task_second_list
print_log(task_list_total, tpl_result_total)
status = check_result(task_list_total, tpl_result_total)
print_log(total_tpl_task, total_tpl_result)
status = check_result(total_tpl_task, total_tpl_result)
if status:
log_file_print = LOG_FILE
else:
log_file_print = f"{Fore.RED}{LOG_FILE}{Fore.RESET}"
log_file_print = f"{Fore.RED}{LOG_FILE}{Style.RESET_ALL}"
if config.output_result_dir:
print_log_output_into_dir(total_tpl_result, config.output_result_dir)
print(f"Log file {log_file_print}")
return status
def main():
@ -1250,14 +940,15 @@ def main():
success = check_git_change()
else:
success = True
status = False
if success:
run_all_test(config)
status = run_all_test(config)
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
return int(not status)
if __name__ == "__main__":

View file

@ -1,20 +1,22 @@
#!/usr/bin/env bash
# Usage, ./test.sh -d test_addons_name -i module_name
source ./.venv/bin/activate
Red='\033[0;31m' # Red
Color_Off='\033[0m' # Text Reset
CONFIG_PATH="./config.conf"
ORIGIN_CONFIG_PATH=CONFIG_PATH
if [ ! -f "${CONFIG_PATH}" ]; then
CONFIG_PATH="/etc/odoo/odoo.conf"
if [ ! -f "${CONFIG_PATH}" ]; then
echo "Cannot find ERPLibre configuration ${ORIGIN_CONFIG_PATH}, did you install ERPLibre? > make install"
echo "${Red}Cannot find${Color_Off} ERPLibre configuration ${ORIGIN_CONFIG_PATH}, did you install ERPLibre? > make install"
exit 1
fi
fi
coverage run -p ./odoo/odoo-bin -c ${CONFIG_PATH} --limit-time-real 99999 --limit-time-cpu 99999 --log-level=test --test-enable --stop-after-init $@
coverage run -p ./odoo/odoo-bin -c "${CONFIG_PATH}" --limit-time-real 99999 --limit-time-cpu 99999 --log-level=test --test-enable --stop-after-init "$@"
retVal=$?
if [[ $retVal -ne 0 ]]; then
echo "Error test.sh"
echo "${Red}Error${Color_Off} run.sh"
exit 1
fi