Merge branch 'develop'
- support ubuntu 25.10 - fix s390x by removing wkhtmltopdf (it's missing) - improve selenium_lib.py, support selenium grid project (can be run on docker) - improve code_generator extraction class - support neutralize database - fix repository url - improve postgresql 18 support
This commit is contained in:
commit
2805f94477
32 changed files with 694 additions and 476 deletions
|
|
@ -27,11 +27,13 @@ Recreating the virtual environment, use installation guide from tool `make`.
|
|||
- Support RobotLibre code generator
|
||||
- Support ERPLibre DevOps, automation procedure about DevOps
|
||||
- ERPLibre Home Mobile Application, use TODO to compile, deploy it and personalize it
|
||||
- Support Selenium grid from selenium_lib.py
|
||||
|
||||
## Changed
|
||||
|
||||
- Docker support postgresql 18
|
||||
- Format script search diff file into each repository
|
||||
- Support neutralize database from Odoo
|
||||
|
||||
## [1.6.0] - 2025-04-25
|
||||
|
||||
|
|
|
|||
9
Makefile
9
Makefile
|
|
@ -1,6 +1,11 @@
|
|||
SHELL := /bin/bash
|
||||
LOG_FILE := ./.venv.$(cat ".erplibre-version" | xargs)/make_test.log
|
||||
ODOO_VERSION := $(shell cat .odoo-version | xargs)
|
||||
ERPLIBRE_VERSION := $(strip \
|
||||
$(if $(wildcard .erplibre-version),$(shell cat .erplibre-version),) \
|
||||
)
|
||||
ODOO_VERSION := $(strip \
|
||||
$(if $(wildcard .odoo-version),$(shell cat .odoo-version),) \
|
||||
)
|
||||
LOG_FILE := ./.venv.$(ERPLIBRE_VERSION)/make_test.log
|
||||
#############
|
||||
# General #
|
||||
#############
|
||||
|
|
|
|||
|
|
@ -111,4 +111,4 @@ docker_clean_all:
|
|||
|
||||
.PHONY: docker_compose_clean_all
|
||||
docker_compose_clean_all:
|
||||
./script/terminal/validate_to_continue.sh "⚠️ This will REMOVE docker compose images, volumes and network." && docker-compose down --rmi all -v
|
||||
./script/terminal/validate_to_continue.sh "⚠️ This will REMOVE docker compose images, volumes and network." && docker compose down --rmi all -v
|
||||
|
|
|
|||
|
|
@ -11,6 +11,10 @@ install_dev:
|
|||
# ./script/install/install_locally_dev.sh
|
||||
./script/version/update_env_version.py --install_dev
|
||||
|
||||
.PHONY: install_erplibre
|
||||
install_erplibre:
|
||||
./script/install/install_erplibre.sh
|
||||
|
||||
.PHONY: install_odoo_18
|
||||
install_odoo_18:
|
||||
./script/version/update_env_version.py --erplibre_version odoo18.0_python3.12.10 --install_dev
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
services:
|
||||
ERPLibre:
|
||||
image: technolibre/erplibre:1.6.0_odoo_18.0_4f4f563
|
||||
image: technolibre/erplibre:1.6.0_odoo_18.0_2492ba3
|
||||
ports:
|
||||
- 8069:8069
|
||||
- 8071:8071
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ RUN echo "deb http://apt.postgresql.org/pub/repos/apt/ ${DEBIAN_NAME}-pgdg main"
|
|||
&& gpgconf --kill all \
|
||||
&& rm -rf "$GNUPGHOME" \
|
||||
&& apt-get update \
|
||||
&& apt-get install --no-install-recommends -y postgresql-client-12 libpq-dev \
|
||||
&& apt-get install --no-install-recommends -y postgresql-client-18 libpq-dev \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install npm dependencies
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ services:
|
|||
volumes:
|
||||
# See the volume section at the end of the file
|
||||
- erplibre_data_dir:/home/odoo/.local/share/Odoo
|
||||
- ./addons/addons:/ERPLibre/odoo12.0/addons/addons
|
||||
- ./odoo12.0/addons/addons:/ERPLibre/odoo12.0/addons/addons
|
||||
- ./private:/ERPLibre/private
|
||||
- erplibre_conf:/etc/odoo
|
||||
restart: always
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ services:
|
|||
volumes:
|
||||
# See the volume section at the end of the file
|
||||
- erplibre_data_dir:/home/odoo/.local/share/Odoo
|
||||
- ./addons/addons:/ERPLibre/odoo14.0/addons/addons
|
||||
- ./odoo14.0/addons/addons:/ERPLibre/odoo14.0/addons/addons
|
||||
- ./private:/ERPLibre/private
|
||||
- erplibre_conf:/etc/odoo
|
||||
restart: always
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ services:
|
|||
volumes:
|
||||
# See the volume section at the end of the file
|
||||
- erplibre_data_dir:/home/odoo/.local/share/Odoo
|
||||
- ./addons/addons:/ERPLibre/odoo16.0/addons/addons
|
||||
- ./odoo16.0/addons/addons:/ERPLibre/odoo16.0/addons/addons
|
||||
- ./private:/ERPLibre/private
|
||||
- erplibre_conf:/etc/odoo
|
||||
restart: always
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
services:
|
||||
ERPLibre:
|
||||
image: technolibre/erplibre:1.6.0_odoo_18.0_4f4f563
|
||||
image: technolibre/erplibre:1.6.0_odoo_18.0_2492ba3
|
||||
ports:
|
||||
- 8069:8069
|
||||
- 8071:8071
|
||||
|
|
@ -27,7 +27,7 @@ services:
|
|||
volumes:
|
||||
# See the volume section at the end of the file
|
||||
- erplibre_data_dir:/home/odoo/.local/share/Odoo
|
||||
- ./addons/addons:/ERPLibre/odoo18.0/addons/addons
|
||||
- ./odoo18.0/addons/addons:/ERPLibre/odoo18.0/addons/addons
|
||||
- ./private:/ERPLibre/private
|
||||
- erplibre_conf:/etc/odoo
|
||||
restart: always
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
services:
|
||||
ERPLibre:
|
||||
image: technolibre/erplibre:1.6.0_odoo_18.0_4f4f563
|
||||
image: technolibre/erplibre:1.6.0_odoo_18.0_2492ba3
|
||||
ports:
|
||||
- 8069:8069
|
||||
- 8071:8071
|
||||
|
|
@ -28,7 +28,7 @@ services:
|
|||
volumes:
|
||||
# See the volume section at the end of the file
|
||||
- erplibre_data_dir:/home/odoo/.local/share/Odoo
|
||||
- ./addons/addons:/ERPLibre/odoo18.0/addons/addons
|
||||
- ./odoo18.0/addons/addons:/ERPLibre/odoo18.0/addons/addons
|
||||
- ./private:/ERPLibre/private
|
||||
- erplibre_conf:/etc/odoo
|
||||
restart: always
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
<remote name="TechnoLibre" fetch="https://github.com/TechnoLibre/" />
|
||||
<remote name="ajepe" fetch="https://github.com/ajepe/" />
|
||||
<remote name="bemade" fetch="https://git.bemade.org/bemade/" />
|
||||
<remote name="ERPLibre_origin_bemade" fetch="https://eregion.chezlepro.ca/TechnoLibre/" />
|
||||
<remote name="ERPLibre_origin_bemade" fetch="https://git.bemade.org/technolibre/" />
|
||||
<remote name="camptocamp" fetch="https://github.com/camptocamp/" />
|
||||
<remote name="cetmix" fetch="https://github.com/cetmix/" />
|
||||
<remote name="dhongu" fetch="https://github.com/dhongu/" />
|
||||
|
|
|
|||
1
private/.gitignore
vendored
Normal file
1
private/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
*.kdbx
|
||||
|
|
@ -13,4 +13,6 @@ dataclasses
|
|||
|
||||
pypiwin32
|
||||
|
||||
cadquery
|
||||
cadquery
|
||||
pymysql
|
||||
pymssql
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -21,13 +21,13 @@ authors = [ "Mathieu Benoit <mathben@technolibre.ca>",]
|
|||
python = ">=3.12.10,<3.13"
|
||||
asn1crypto = "1.5.1"
|
||||
astor = "^0.8.1"
|
||||
avalara = "^25.9.0"
|
||||
avalara = "^25.11.0"
|
||||
babel = "2.10.3"
|
||||
bandit = "^1.8.6"
|
||||
bandit = "^1.9.2"
|
||||
base64io = "^1.0.3"
|
||||
beautifulsoup4 = "4.13.5"
|
||||
bokeh = "3.6.3"
|
||||
boto3 = "^1.40.74"
|
||||
boto3 = "^1.42.1"
|
||||
cachetools = "^6.2.2"
|
||||
cairosvg = "^2.8.2"
|
||||
caldav = "1.3.9"
|
||||
|
|
@ -37,7 +37,7 @@ code-writer = "^1.2.0"
|
|||
colorama = "^0.4.6"
|
||||
cryptography = "46.0.3"
|
||||
cssselect = "^1.3.0"
|
||||
cython = "^3.2.1"
|
||||
cython = "^3.2.2"
|
||||
decorator = "5.1.1"
|
||||
docutils = "0.20.1"
|
||||
dropbox = "^12.0.2"
|
||||
|
|
@ -45,7 +45,7 @@ email-validator = "^2.3.0"
|
|||
extract-msg = "^0.55.0"
|
||||
extruct = "^0.18.0"
|
||||
ezdxf = "^1.4.3"
|
||||
factur-x = "^3.13"
|
||||
factur-x = "^3.14"
|
||||
filetype = "^1.2.0"
|
||||
flake8 = "^7.3.0"
|
||||
formio-data = "^2.1.6"
|
||||
|
|
@ -85,7 +85,7 @@ numpy-financial = "<=1.0.0"
|
|||
numpy-stl = "^3.2.0"
|
||||
odoorpc = "^0.10.1"
|
||||
ofxparse = "0.21"
|
||||
openai = "^2.8.0"
|
||||
openai = "^2.8.1"
|
||||
openpyxl = "3.1.2"
|
||||
openupgradelib = "^3.12.0"
|
||||
openwebui-client = ">=0.3.0"
|
||||
|
|
@ -96,11 +96,11 @@ pathspec = "^0.12.1"
|
|||
pdf2image = "^1.17.0"
|
||||
pdfminer = "^20191125"
|
||||
pexpect = "^4.9.0"
|
||||
phonenumbers = "^9.0.18"
|
||||
pillow = "10.2.0"
|
||||
plotly = "^6.4.0"
|
||||
phonenumbers = "^9.0.19"
|
||||
pillow = "12.0.0"
|
||||
plotly = "^6.5.0"
|
||||
polib = "1.1.1"
|
||||
pre-commit = "^4.4.0"
|
||||
pre-commit = "^4.5.0"
|
||||
premailer = "^3.10.0"
|
||||
prometheus-client = "^0.23.1"
|
||||
psutil = "5.9.8"
|
||||
|
|
@ -116,7 +116,6 @@ pyjsparser = "^2.7.1"
|
|||
pyjwt = "^2.10.1"
|
||||
pylint = "3.3.9"
|
||||
pylint-odoo = "^9.3.22"
|
||||
pymysql = "^1.1.2"
|
||||
pyncclient = "^0.7"
|
||||
pyopenssl = "25.3.0"
|
||||
pypdf2 = "2.12.1"
|
||||
|
|
@ -143,9 +142,9 @@ pyzk = "^0.9"
|
|||
qifparse = "^0.5"
|
||||
qrcode = "7.4.2"
|
||||
radon = "^6.0.1"
|
||||
redis = "^7.0.1"
|
||||
redis = "^7.1.0"
|
||||
reportlab = "4.1.0"
|
||||
requests = "2.31.0"
|
||||
requests = ">=2.31.0"
|
||||
requests-toolbelt = "^1.0.0"
|
||||
rjsmin = "1.2.0"
|
||||
roulier = "^1.1.1"
|
||||
|
|
@ -157,7 +156,7 @@ sqlalchemy = "^2.0.44"
|
|||
statsd = "^4.0.1"
|
||||
tldextract = "^5.3.0"
|
||||
tweepy = "^4.16.0"
|
||||
twilio = "^9.8.6"
|
||||
twilio = "^9.8.7"
|
||||
unidecode = "^1.4.0"
|
||||
urllib3 = "2.0.7"
|
||||
uvloop = "^0.22.1"
|
||||
|
|
@ -180,9 +179,6 @@ rev = "24.8.0"
|
|||
extras = [ "s3",]
|
||||
version = "^2025.10.0"
|
||||
|
||||
[tool.poetry.dependencies.pymssql]
|
||||
git = "https://github.com/pymssql/pymssql.git"
|
||||
|
||||
[tool.poetry.dependencies.pysaml2]
|
||||
git = "https://github.com/prauscher/pysaml2.git"
|
||||
rev = "replace-pyopenssl"
|
||||
|
|
|
|||
|
|
@ -66,4 +66,6 @@ pyopenssl==25.3.0
|
|||
git+https://github.com/prauscher/pysaml2.git@replace-pyopenssl
|
||||
|
||||
# Fix compilation, until pymssql==2.3.9 is release
|
||||
git+https://github.com/pymssql/pymssql.git
|
||||
#git+https://github.com/pymssql/pymssql.git
|
||||
|
||||
pillow==12.0.0
|
||||
|
|
|
|||
|
|
@ -319,9 +319,15 @@ def main():
|
|||
"sequence": sequence,
|
||||
}
|
||||
dct_fields[var_name] = d
|
||||
lst_args = [
|
||||
a.value for a in node.value.args
|
||||
]
|
||||
lst_args = []
|
||||
for keyword in node.value.args:
|
||||
value = _fill_search_field(
|
||||
keyword, var_name
|
||||
)
|
||||
# Waste to stock None value
|
||||
if value is not None:
|
||||
lst_args.append(value)
|
||||
|
||||
if lst_args:
|
||||
lst_default_arg_position = (
|
||||
ARGS_TYPE_PARAM.get(
|
||||
|
|
@ -405,6 +411,67 @@ def main():
|
|||
return 0
|
||||
|
||||
|
||||
def extract_lambda(self, node):
|
||||
result = ast.unparse(node)
|
||||
if result[0] == "(" and result[-1] == ")":
|
||||
result = result[1:-1]
|
||||
return result
|
||||
|
||||
|
||||
def _fill_search_field(ast_obj, var_name=""):
|
||||
ast_obj_type = type(ast_obj)
|
||||
result = None
|
||||
if ast_obj_type is ast.Lambda:
|
||||
result = extract_lambda(ast_obj)
|
||||
elif ast_obj_type is ast.Constant:
|
||||
result = ast_obj.value
|
||||
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)}."
|
||||
)
|
||||
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 = []
|
||||
if hasattr(parent_node, "id"):
|
||||
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)
|
||||
# else:
|
||||
# # default=uuid.uuid4().hex
|
||||
# _logger.warning(
|
||||
# f"Cannot support keyword of variable {var_name} type"
|
||||
# f" {ast_obj_type} in filename {self.py_filename}, because"
|
||||
# " parent_node is type ast.Call."
|
||||
# )
|
||||
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}."
|
||||
)
|
||||
return result
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
status = main()
|
||||
sys.exit(status)
|
||||
|
|
|
|||
|
|
@ -74,13 +74,15 @@ def main():
|
|||
no_tab = 0
|
||||
|
||||
# Validate file format
|
||||
out = subprocess.check_output(
|
||||
out = subprocess.run(
|
||||
f"python -m tabnanny {config.file}",
|
||||
stderr=subprocess.STDOUT,
|
||||
shell=True,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
if out:
|
||||
print(out)
|
||||
result = (out.stdout or "") + (out.stderr or "")
|
||||
if result:
|
||||
print(result)
|
||||
sys.exit(1)
|
||||
|
||||
# with tokenize.open(config.file) as f:
|
||||
|
|
|
|||
|
|
@ -12,8 +12,14 @@ _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 ""
|
||||
result = subprocess.run(
|
||||
cmd,
|
||||
shell=True,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
output = (result.stdout or "") + (result.stderr or "")
|
||||
return result.returncode, output.strip()
|
||||
|
||||
|
||||
def get_config():
|
||||
|
|
@ -41,7 +47,7 @@ def get_config():
|
|||
def main():
|
||||
config = get_config()
|
||||
|
||||
out_db = execute_shell("./odoo_bin.sh db --list")
|
||||
status, out_db = execute_shell("./odoo_bin.sh db --list")
|
||||
lst_db = out_db.split("\n")
|
||||
|
||||
lst_database_to_delete = []
|
||||
|
|
|
|||
|
|
@ -60,6 +60,11 @@ SUGGESTION
|
|||
action="store_true",
|
||||
help="Will only drop database if exist.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--neutralize",
|
||||
action="store_true",
|
||||
help="Will disable all cron.",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
return args
|
||||
|
||||
|
|
@ -163,6 +168,8 @@ def main():
|
|||
f"{arg_base} --clone --from_database"
|
||||
f" {cache_database} --database {config.database}"
|
||||
)
|
||||
if config.neutralize:
|
||||
arg += " --neutralize"
|
||||
print(arg)
|
||||
out = check_output(arg.split(" ")).decode()
|
||||
print(out)
|
||||
|
|
|
|||
0
script/database/migrate/process_backup_file.py
Normal file → Executable file
0
script/database/migrate/process_backup_file.py
Normal file → Executable file
64
script/docker/docker_copy_file.sh
Executable file
64
script/docker/docker_copy_file.sh
Executable file
|
|
@ -0,0 +1,64 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# -----------------------------
|
||||
# Parameters
|
||||
# -----------------------------
|
||||
|
||||
if [[ $# -lt 1 ]]; then
|
||||
echo "Usage: $0 <source_file> [destination_directory]"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SRC_FILE="$1"
|
||||
DEST_DIR="${2:-/home/seluser/Downloads}" # default destination inside container
|
||||
|
||||
if [[ ! -f "$SRC_FILE" ]]; then
|
||||
echo "Error: Source file '$SRC_FILE' does not exist."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SRC_BASENAME="$(basename "$SRC_FILE")"
|
||||
|
||||
# -----------------------------
|
||||
# Determine container name
|
||||
# -----------------------------
|
||||
|
||||
CURRENT=$(pwd)
|
||||
BASENAME=$(basename "${CURRENT}")
|
||||
|
||||
# Remove dots and convert to lowercase (same logic as original)
|
||||
BASENAME="${BASENAME//./}"
|
||||
BASENAME="${BASENAME,,}"
|
||||
|
||||
CANDIDATE1="${BASENAME}-ERPLibre-1"
|
||||
CANDIDATE2="${BASENAME}_ERPLibre_1"
|
||||
|
||||
CONTAINER=""
|
||||
|
||||
# Find which container exists
|
||||
if docker ps --format '{{.Names}}' | grep -qx "${CANDIDATE1}"; then
|
||||
CONTAINER="${CANDIDATE1}"
|
||||
elif docker ps --format '{{.Names}}' | grep -qx "${CANDIDATE2}"; then
|
||||
CONTAINER="${CANDIDATE2}"
|
||||
else
|
||||
echo "Error: No matching container found:"
|
||||
echo " - ${CANDIDATE1}"
|
||||
echo " - ${CANDIDATE2}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Detected container: ${CONTAINER}"
|
||||
|
||||
# -----------------------------
|
||||
# Copy file into container
|
||||
# -----------------------------
|
||||
|
||||
echo "Ensuring directory exists inside container: ${DEST_DIR}"
|
||||
docker exec -u root "${CONTAINER}" mkdir -p "${DEST_DIR}"
|
||||
|
||||
echo "Copying '${SRC_FILE}' to '${CONTAINER}:${DEST_DIR}/${SRC_BASENAME}'"
|
||||
docker cp "${SRC_FILE}" "${CONTAINER}:${DEST_DIR}/${SRC_BASENAME}"
|
||||
|
||||
echo "File copy complete."
|
||||
|
|
@ -26,7 +26,7 @@ _logger = logging.getLogger(__name__)
|
|||
|
||||
PROJECT_NAME = os.path.basename(os.getcwd())
|
||||
IDEA_PATH = "./.idea"
|
||||
DEFAULT_ODOO_BIN = "./odoo/odoo-bin" # Will be replaced dynamic
|
||||
DEFAULT_ODOO_BIN = "./odoo/odoo-bin" # Need this to replace static configuration
|
||||
IDEA_MISC = os.path.join(IDEA_PATH, "misc.xml")
|
||||
IDEA_WORKSPACE = os.path.join(IDEA_PATH, "workspace.xml")
|
||||
VCS_WORKSPACE = os.path.join(IDEA_PATH, "vcs.xml")
|
||||
|
|
|
|||
|
|
@ -62,3 +62,7 @@ install_package cmake
|
|||
install_package parallel
|
||||
install_package tk
|
||||
install_package shfmt
|
||||
|
||||
# For erplibre_devops
|
||||
install_package sshpass
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ EL_USER=${USER}
|
|||
UBUNTU_VERSION=$(lsb_release -rs)
|
||||
DEBIAN_VERSION=$(lsb_release -cs)
|
||||
OS=$(lsb_release -si)
|
||||
if [ "25.04" == "${UBUNTU_VERSION}" ] || [ "24.04" == "${UBUNTU_VERSION}" ] || [ "24.10" == "${UBUNTU_VERSION}" ] || [ "23.10" == "${UBUNTU_VERSION}" ] || [ "23.04" == "${UBUNTU_VERSION}" ] || [ "22.10" == "${UBUNTU_VERSION}" ] || [ "22.04" == "${UBUNTU_VERSION}" ]; then
|
||||
if [ "25.10" == "${UBUNTU_VERSION}" ] || [ "25.04" == "${UBUNTU_VERSION}" ] || [ "24.04" == "${UBUNTU_VERSION}" ] || [ "24.10" == "${UBUNTU_VERSION}" ] || [ "23.10" == "${UBUNTU_VERSION}" ] || [ "23.04" == "${UBUNTU_VERSION}" ] || [ "22.10" == "${UBUNTU_VERSION}" ] || [ "22.04" == "${UBUNTU_VERSION}" ]; then
|
||||
WKHTMLTOX_X64=https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6.1-3/wkhtmltox_0.12.6.1-3.jammy_amd64.deb
|
||||
elif [ "20.04" == "${UBUNTU_VERSION}" ]; then
|
||||
WKHTMLTOX_X64=https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6-1/wkhtmltox_0.12.6-1.focal_amd64.deb
|
||||
|
|
@ -39,7 +39,7 @@ fi
|
|||
#--------------------------------------------------
|
||||
if [ "$(uname -m)" = "s390x" ]; then
|
||||
echo "Arch s390x detected"
|
||||
sudo apt install wkhtmltopdf rust-all libqpdf-dev libgeos-dev libproj-dev proj-bin proj-data libgeographiclib-dev freetds-dev freetds-bin libkrb5-dev libssl-dev pkg-config build-essential -y
|
||||
sudo apt install rust-all libqpdf-dev libgeos-dev libproj-dev proj-bin proj-data libgeographiclib-dev freetds-dev freetds-bin libkrb5-dev libssl-dev pkg-config build-essential npm -y
|
||||
fi
|
||||
|
||||
#--------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -3,11 +3,11 @@
|
|||
if [[ "${OSTYPE}" == "linux-gnu" ]]; then
|
||||
source /etc/os-release
|
||||
if [[ "${ID}" == "ubuntu" ]]; then
|
||||
if [[ "${VERSION_ID}" == "18.04" || "${VERSION_ID}" == "20.04" || "${VERSION_ID}" == "22.04" || "${VERSION_ID}" == "22.10" || "${VERSION_ID}" == "23.04" || "${VERSION_ID}" == "23.10" || "${VERSION_ID}" == "24.04" || "${VERSION_ID}" == "25.04" ]]; then
|
||||
if [[ "${VERSION_ID}" == "18.04" || "${VERSION_ID}" == "20.04" || "${VERSION_ID}" == "22.04" || "${VERSION_ID}" == "22.10" || "${VERSION_ID}" == "23.04" || "${VERSION_ID}" == "23.10" || "${VERSION_ID}" == "24.04" || "${VERSION_ID}" == "25.04" || "${VERSION_ID}" == "25.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 - 24.04, 25.04 : ${VERSION_ID}"
|
||||
echo "Your version is not supported, only support 18.04, 20.04 and 22.04 - 24.04, 25.04, 25.10 : ${VERSION_ID}"
|
||||
fi
|
||||
elif [[ "${ID}" == "debian" ]]; then
|
||||
./script/install/install_debian_dependency.sh
|
||||
|
|
@ -15,7 +15,7 @@ if [[ "${OSTYPE}" == "linux-gnu" ]]; then
|
|||
./script/install/install_arch_linux.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 - Ubuntu 23.10 - Ubuntu 24.04, Ubuntu 25.04 ."
|
||||
echo "Your Linux system is not supported, only support Ubuntu 18.04 or Ubuntu 20.04 or Ubuntu 22.04 - Ubuntu 23.10 - Ubuntu 24.04, Ubuntu 25.04, Ubuntu 25.10 ."
|
||||
fi
|
||||
elif [[ "${OSTYPE}" == "darwin"* ]]; then
|
||||
echo "\n---- Darwin installation process started ----"
|
||||
|
|
|
|||
|
|
@ -4,25 +4,36 @@
|
|||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
IGNORE_EXTENSION = []
|
||||
IGNORE_FILE_NAME = ["Makefile"]
|
||||
|
||||
|
||||
def execute_shell(cmd):
|
||||
out = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True)
|
||||
return out.decode().strip() if out else ""
|
||||
result = subprocess.run(
|
||||
cmd,
|
||||
shell=True,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
output = (result.stdout or "") + (result.stderr or "")
|
||||
return result.returncode, output.strip()
|
||||
|
||||
|
||||
def get_modified_files():
|
||||
lst_cmd_git_status = ["git", "status", "--porcelain"]
|
||||
lst_cmd_git_status_repo = [
|
||||
".venv.erplibre/bin/repo",
|
||||
"forall",
|
||||
"-p",
|
||||
"-c",
|
||||
"git status -s",
|
||||
]
|
||||
path_repo_bin = ".venv.erplibre/bin/repo"
|
||||
if os.path.exists(path_repo_bin):
|
||||
lst_cmd_git_status_repo = [
|
||||
path_repo_bin,
|
||||
"forall",
|
||||
"-p",
|
||||
"-c",
|
||||
"git status -s",
|
||||
]
|
||||
else:
|
||||
lst_cmd_git_status_repo = []
|
||||
try:
|
||||
print(" ".join(lst_cmd_git_status))
|
||||
result = subprocess.run(
|
||||
|
|
@ -35,15 +46,18 @@ def get_modified_files():
|
|||
|
||||
lst_lines = [(".", lines_local)]
|
||||
|
||||
print(" ".join(lst_cmd_git_status_repo))
|
||||
if lst_cmd_git_status_repo:
|
||||
print(" ".join(lst_cmd_git_status_repo))
|
||||
|
||||
result = subprocess.run(
|
||||
lst_cmd_git_status_repo,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=True,
|
||||
)
|
||||
lines_project = result.stdout.strip().split("\n\n")
|
||||
result = subprocess.run(
|
||||
lst_cmd_git_status_repo,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=True,
|
||||
)
|
||||
lines_project = result.stdout.strip().split("\n\n")
|
||||
else:
|
||||
lines_project = []
|
||||
|
||||
if os.path.isfile(".odoo-version"):
|
||||
with open(".odoo-version") as txt:
|
||||
|
|
@ -145,4 +159,7 @@ if __name__ == "__main__":
|
|||
has_file = True
|
||||
if has_file:
|
||||
print(cmd_format)
|
||||
execute_shell(cmd_format)
|
||||
status, output = execute_shell(cmd_format)
|
||||
if status != 0:
|
||||
print(output)
|
||||
sys.exit(status)
|
||||
|
|
|
|||
|
|
@ -415,7 +415,7 @@ def combine_requirements(config):
|
|||
print(f"Internal error, missing result for {lst_requirement}.")
|
||||
|
||||
# Support ignored requirements
|
||||
lst_ignore = get_list_ignored()
|
||||
lst_ignore, ignore_requirement_file = get_list_ignored()
|
||||
lst_ignored_key = []
|
||||
for key in dct_requirements.keys():
|
||||
for ignored in lst_ignore:
|
||||
|
|
@ -423,6 +423,9 @@ def combine_requirements(config):
|
|||
lst_ignored_key.append(key)
|
||||
for key in lst_ignored_key:
|
||||
del dct_requirements[key]
|
||||
print(
|
||||
f"{Fore.YELLOW}WARNING{Style.RESET_ALL} - Ignore '{key}' from '{ignore_requirement_file}'"
|
||||
)
|
||||
|
||||
venv_dir = f".venv.{config.set_version_erplibre}"
|
||||
with open(f"./{venv_dir}/build_dependency.txt", "w") as f:
|
||||
|
|
@ -521,9 +524,22 @@ def call_poetry_add_build_dependency():
|
|||
|
||||
|
||||
def get_list_ignored():
|
||||
with open("./requirement/ignore_requirements.txt", "r") as f:
|
||||
lst_ignore_requirements = [a.strip() for a in f.readlines()]
|
||||
return lst_ignore_requirements
|
||||
odoo_erplibre_version = ""
|
||||
with open("./.erplibre-version", "r", encoding="utf-8") as fichier:
|
||||
odoo_erplibre_version = fichier.read().strip()
|
||||
if not odoo_erplibre_version:
|
||||
_logger.warning(
|
||||
"Missing ./.erplibre-version file to determine which requirement/ignore_requirement to use."
|
||||
)
|
||||
return []
|
||||
ignore_requirement_file = (
|
||||
f"./requirement/ignore_requirements.{odoo_erplibre_version}.txt"
|
||||
)
|
||||
with open(ignore_requirement_file, "r") as f:
|
||||
lst_ignore_requirements = [
|
||||
a.strip() for a in f.readlines() if a.strip()
|
||||
]
|
||||
return lst_ignore_requirements, ignore_requirement_file
|
||||
|
||||
|
||||
def main():
|
||||
|
|
|
|||
|
|
@ -4,18 +4,15 @@
|
|||
|
||||
import datetime
|
||||
import getpass
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import random
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import time
|
||||
import tkinter as tk
|
||||
from subprocess import getoutput
|
||||
from tkinter import filedialog
|
||||
|
||||
from pykeepass import PyKeePass
|
||||
|
|
@ -79,7 +76,10 @@ class SeleniumLib(object):
|
|||
self.config_file = config_file.ConfigFile()
|
||||
self.kdbx = None
|
||||
self.video_recorder = None
|
||||
self.default_download_dir_path = tempfile.mkdtemp()
|
||||
if config.use_download_path_default:
|
||||
self.default_download_dir_path = "/home/seluser/Downloads"
|
||||
else:
|
||||
self.default_download_dir_path = tempfile.mkdtemp()
|
||||
if self.config.video_suffix:
|
||||
self.filename_recording = (
|
||||
f"video_{self.config.video_suffix}_"
|
||||
|
|
@ -136,9 +136,10 @@ class SeleniumLib(object):
|
|||
firefox_options.set_preference(
|
||||
"browser.download.manager.showWhenStarting", False
|
||||
)
|
||||
firefox_options.set_preference(
|
||||
"browser.download.dir", self.default_download_dir_path
|
||||
)
|
||||
if self.default_download_dir_path:
|
||||
firefox_options.set_preference(
|
||||
"browser.download.dir", self.default_download_dir_path
|
||||
)
|
||||
firefox_options.set_preference(
|
||||
"browser.helperApps.neverAsk.saveToDisk",
|
||||
"application/octet-stream,application/pdf,application/x-pdf",
|
||||
|
|
@ -248,9 +249,10 @@ class SeleniumLib(object):
|
|||
firefox_options.set_preference(
|
||||
"browser.download.manager.showWhenStarting", False
|
||||
)
|
||||
firefox_options.set_preference(
|
||||
"browser.download.dir", self.default_download_dir_path
|
||||
)
|
||||
if self.default_download_dir_path:
|
||||
firefox_options.set_preference(
|
||||
"browser.download.dir", self.default_download_dir_path
|
||||
)
|
||||
firefox_options.set_preference(
|
||||
"browser.helperApps.neverAsk.saveToDisk",
|
||||
"application/octet-stream,application/pdf,application/x-pdf",
|
||||
|
|
@ -454,6 +456,7 @@ class SeleniumLib(object):
|
|||
value: str = None,
|
||||
timeout=5,
|
||||
wait_clickable=False,
|
||||
wait_is_invisible=False,
|
||||
is_visible=True,
|
||||
):
|
||||
only_one = False
|
||||
|
|
@ -476,6 +479,15 @@ class SeleniumLib(object):
|
|||
)
|
||||
)
|
||||
)
|
||||
elif wait_is_invisible:
|
||||
ele = wait.until(
|
||||
EC.invisibility_of_element_located(
|
||||
(
|
||||
by,
|
||||
value,
|
||||
)
|
||||
)
|
||||
)
|
||||
else:
|
||||
only_one = True
|
||||
ele = wait.until(
|
||||
|
|
@ -813,35 +825,59 @@ class SeleniumLib(object):
|
|||
def scrollto_element(
|
||||
self,
|
||||
element,
|
||||
offset_middle=True,
|
||||
smooth_scroll=True,
|
||||
smooth_speed=50,
|
||||
smooth_fps=1 / 25,
|
||||
viewport_ele=None,
|
||||
):
|
||||
window_y = self.driver.execute_script(
|
||||
"return window.scrollY || window.pageYOffset;"
|
||||
)
|
||||
window_height = self.driver.get_window_size().get("height")
|
||||
element_height = element.rect["height"]
|
||||
viewport_height = self.driver.execute_script(
|
||||
"return window.innerHeight || document.documentElement.clientHeight;"
|
||||
)
|
||||
if viewport_ele:
|
||||
scroll_origin = ScrollOrigin.from_element(viewport_ele, 10, 10)
|
||||
else:
|
||||
scroll_origin = ScrollOrigin.from_viewport(10, 10)
|
||||
delta_y = element.rect["y"]
|
||||
if offset_middle:
|
||||
# TODO check size windows and divide by 2
|
||||
delta_y -= 400
|
||||
target_distance = (
|
||||
delta_y - (viewport_height - element_height) / 2 - window_y
|
||||
)
|
||||
if window_y < delta_y < window_y + viewport_height - 50:
|
||||
# No need to scroll
|
||||
return
|
||||
print(f"scroll to xpath at y:{delta_y} position")
|
||||
if smooth_scroll:
|
||||
actual_pos = 0
|
||||
while delta_y > actual_pos:
|
||||
actual_pos += smooth_speed
|
||||
print(actual_pos)
|
||||
# Detect go top or bot
|
||||
if delta_y > window_y:
|
||||
step = abs(int(smooth_speed)) # to the top
|
||||
condition = lambda a, d: a < d
|
||||
go_down = True
|
||||
else:
|
||||
step = -abs(int(smooth_speed)) # to the bot
|
||||
condition = lambda a, d: a > d
|
||||
go_down = False
|
||||
while condition(actual_pos, target_distance):
|
||||
# TODO detect when cannot scroll, stop scrolling
|
||||
actual_pos += step
|
||||
if go_down and actual_pos > target_distance:
|
||||
step = actual_pos - target_distance
|
||||
elif not go_down and actual_pos < target_distance:
|
||||
step = actual_pos - target_distance
|
||||
|
||||
ActionChains(self.driver).scroll_from_origin(
|
||||
scroll_origin, 0, int(smooth_speed)
|
||||
scroll_origin, 0, int(step)
|
||||
).perform()
|
||||
print(f"Wait {smooth_fps} seconds")
|
||||
time.sleep(smooth_fps)
|
||||
print("end")
|
||||
else:
|
||||
ActionChains(self.driver).scroll_from_origin(
|
||||
scroll_origin, 0, int(delta_y)
|
||||
scroll_origin, 0, int(target_distance)
|
||||
).perform()
|
||||
|
||||
# action = ActionChains(self.driver)
|
||||
|
|
@ -1593,6 +1629,15 @@ class SeleniumLib(object):
|
|||
header.text,
|
||||
) # on retourne aussi le texte exact pour attendre le changement
|
||||
|
||||
def wait_is_invisible(self, by, value):
|
||||
div_field = WebDriverWait(self.driver, 10).until(
|
||||
EC.presence_of_element_located((By.NAME, field_name))
|
||||
)
|
||||
wait = WebDriverWait(driver, 10)
|
||||
wait.until(
|
||||
EC.invisibility_of_element_located((By.ID, "ui-datepicker-div"))
|
||||
)
|
||||
|
||||
def select_date(self, driver, date_to_select):
|
||||
# 1) ouvrir/viser le datepicker (si besoin, clique l'input avant)
|
||||
picker = WebDriverWait(driver, 10).until(
|
||||
|
|
@ -1708,6 +1753,14 @@ def fill_parser(parser):
|
|||
"--use_network",
|
||||
help="Specify the adress, example: http://localhost:4444",
|
||||
)
|
||||
group_browser.add_argument(
|
||||
"--use_download_path_default",
|
||||
action="store_true",
|
||||
help="Actually, the download path is a temporary directory. "
|
||||
"This will enable default path to /home/seluser/Downloads, "
|
||||
"need this with Selenium Grid by network. "
|
||||
"Will delete all file into /home/seluser/Downloads at startup.",
|
||||
)
|
||||
group_browser.add_argument(
|
||||
"--window_size",
|
||||
help="Example 1920,1080",
|
||||
|
|
|
|||
|
|
@ -147,9 +147,8 @@ def run(
|
|||
|
||||
# Open View
|
||||
if config.open_dashboard:
|
||||
selenium_tool.click(
|
||||
"/html/body/header/nav/div/div[1]/div[2]/div/div/div/ul/li[3]/a",
|
||||
timeout=15,
|
||||
selenium_tool.odoo_website_menu_click(
|
||||
"Tableaux de bord"
|
||||
)
|
||||
|
||||
# Open conversation chat
|
||||
|
|
|
|||
|
|
@ -34,9 +34,6 @@ POETRY_LOCK_FILE = os.path.join("poetry.lock")
|
|||
POETRY_LOCK_TEMPLATE_FILE = "poetry.%s.lock"
|
||||
# PIP_REQUIREMENT_FILE = os.path.join("requirements.txt")
|
||||
PIP_REQUIREMENT_TEMPLATE_FILE = "requirements.%s.txt"
|
||||
PIP_IGNORE_REQUIREMENT_FILE = os.path.join(
|
||||
"requirement", "ignore_requirements.txt"
|
||||
)
|
||||
PIP_IGNORE_REQUIREMENT_TEMPLATE_FILE = "ignore_requirements.%s.txt"
|
||||
ADDONS_TEMPLATE_FILE = "odoo%s/addons"
|
||||
ODOO_TEMPLATE_FILE = "odoo%s"
|
||||
|
|
@ -373,12 +370,6 @@ class Update:
|
|||
self.expected_poetry_lock_path,
|
||||
do_delete_source=True,
|
||||
)
|
||||
status &= self.update_link_file(
|
||||
"Pip ignore_requirement.txt",
|
||||
PIP_IGNORE_REQUIREMENT_FILE,
|
||||
self.expected_pip_ignore_requirement_path,
|
||||
do_delete_source=True,
|
||||
)
|
||||
if not os.path.isdir(self.expected_addons_name):
|
||||
status = False
|
||||
return status
|
||||
|
|
|
|||
Loading…
Reference in a new issue