diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 95f94f495..000000000 --- a/Dockerfile +++ /dev/null @@ -1,30 +0,0 @@ -FROM odoo:17.0 -# Overwrite odoo with addons -COPY ./odoo /usr/lib/python3/dist-packages/ -COPY ./addons /usr/lib/python3/dist-packages/odoo -COPY ./deployment/entrypoint.sh / -# Copy entrypoint script and Odoo configuration file -# COPY deployment/entrypoint.sh / -# COPY deployment/odoo.conf /etc/odoo/ - -# Set permissions and Mount /var/lib/odoo to allow restoring filestore and /mnt/extra-addons for users addons and /mnt/backup for backup -RUN chown odoo /etc/odoo/odoo.conf \ - && mkdir -p /mnt/extra-addons \ - && chown -R odoo /mnt/extra-addons \ - && mkdir -p /var/lib/odoo/backup \ - && chown -R odoo /var/lib/odoo/backup -VOLUME ["/var/lib/odoo", "/mnt/extra-addons","/var/lib/odoo/backup"] - -# Expose Odoo services -EXPOSE 8069 8071 8072 - -# Set the default config file -ENV ODOO_RC /etc/odoo/odoo.conf - -# COPY deployment/wait-for-psql.py /usr/local/bin/wait-for-psql.py - -# Set default user when running the container -USER odoo - -ENTRYPOINT ["/entrypoint.sh"] -CMD ["odoo"] \ No newline at end of file diff --git a/Makefile b/Makefile index 6387ed643..0e095cf2b 100644 --- a/Makefile +++ b/Makefile @@ -4,6 +4,9 @@ endif ifneq ("$(wildcard automation/automation.mk)", "") include automation/automation.mk endif +ifneq ("$(wildcard utility/utility.mk)", "") + include utility/utility.mk +endif .SHELLFLAGS += ${SHELLFLAGS} -e PWD = $(shell pwd) UID = $(shell id -u) @@ -18,26 +21,23 @@ DOCKER_PUSH=$(DOCKERCMD) push DOCKER_IMAGE=$(DOCKERCMD) image DOCKER_EXEC=$(DOCKERCMD) exec DEPLOY_PATH=${PWD}/deployment -SETUP_PATH=${PWD}/setup +SCRIPT_PATH=${PWD}/utility/scripts CONFIG=odoo.conf HUB=hub.nextzenos.com CDN=https://cdn.nextzenos.com/CDN/NextERP ORGANIZATION=nexterp -PROJECT := odoo18 +PROJECT := Odoo18-Base ODOO_IMAGE=${HUB}/${ORGANIZATION}/$(PROJECT) TAG := $(shell git rev-parse --abbrev-ref HEAD) CONTAINER_ID=${PROJECT}-${TAG} VERSION := community ADDONS=${PWD}/addons,${PWD}/odoo/addons,${PWD}/extra-addons BACKUP=${CDN}/raw/branch/main/backup/${VERSION}/odoo18-main_2025-01-15_08-05-47.zip - AUTOMATION_PATH=${PWD}/automation - -########### Automation ######### -deploy-native: - @echo "Not implemented" +UPGRADE_SCRIPTS=$(shell cat upgrade_scripts | tr '\n' ',') +push_code: + $(PYTHON) ${SCRIPT_PATH}/interpreter/gen_commit.py $(GIT_USER) $(GIT_PASS) $(PWD) ##### Virtual Environment ##### - check-virtualenv: @if [ "$(VENV)" = "missing" ]; then \ echo "Creating virtual environment..."; \ @@ -52,20 +52,7 @@ install: check-virtualenv sudo apt update -y && \ sudo apt install -y python3-pip libldap2-dev libpq-dev libsasl2-dev postgresql-client libreoffice wkhtmltopdf ansible terraform&& \ ${PIP} install --no-input -r requirements.txt --break-system-packages -download_backup: - @if [ -z "${LINKDB}" ]; then \ - LinkDB=${BACKUP}; \ - read -p "LinkDownload [default: ${BACKUP}]: " InputLinkDB; \ - LinkDB=$${InputLinkDB:-${BACKUP}}; \ - else \ - LinkDB=${LINKDB}; \ - fi; \ - Filename=$$(basename $$LinkDB); \ - echo "Downloading $$Filename from: $$LinkDB"; \ - ${PWD}/setup/download_backup.sh $$LinkDB backup.zip -gen_config: - $(MAKE) download_backup LINKDB=${BACKUP} - ${PWD}/setup/init_config.sh --native ${ADDONS} ${DB_USER} ${DB_PASSWORD} ${DB_SERVER} ${DB_PORT} +#$(MAKE) download_backup LINKDB=${BACKUP} run_server: ${PYTHON} odoo-bin --config=${CONFIG} run_test: @@ -82,22 +69,32 @@ scaffold_module: read -p "addons Path[eg:addons, extra-addons, exercise]: " ModulePath; \ ${PYTHON} odoo-bin scaffold $$ModuleName && \ mv $$ModuleName ${PWD}/$$ModulePath; -cleanup_addons: - @bash ${PWD}/setup/clean_up_addons.sh $(shell echo $(ADDONS) | tr ',' ' ') install_modules: ${PYTHON} odoo-bin --config=${CONFIG} -d ${DATABASE} -i ${MODULES} --xmlrpc-port=${PORT} - +upgrade_modules: + @for script in $$(cat upgrade_scripts); do \ + echo "Running upgrade script: $$script"; \ + ${PYTHON} odoo-bin upgrade_code --script $$script --addons-path=${UPGRADE_DIR} || true; \ + done +delete_records: + ${PYTHON} ${SCRIPT_PATH}/delete_records.py ${DATABASE} ${BASE_MODEL} --force +drop_database: + ${PYTHON} odoo-bin db --config=${CONFIG} drop ${DATABASE} ##### Docker Deployment ######### run_test_docker: sudo ${DOCKER_EXEC} ${CONTAINER_ID} odoo --test-tags :TestAccountMove.test_out_invoice_auto_post_monthly,TestAccountMove.test_included_tax --log-level=test --test-enable -d testdb-${TAG} --stop-after-init --config=/etc/odoo/${CONFIG} --xmlrpc-port=8071 && \ sudo ${DOCKER_EXEC} ${CONTAINER_ID} odoo db --config=/etc/odoo/${CONFIG} drop testdb-${TAG} -gen_config_docker: - $(MAKE) download_backup LINKDB=${BACKUP} - ${PWD}/setup/init_config.sh --docker ${ODOO_IMAGE} ${TAG} ${CONTAINER_ID} +# $(MAKE) download_backup LINKDB=${BACKUP} build_image: - DOCKER_BUILDKIT=1 ${DOCKER_BUILD} . --progress plain --tag ${ODOO_IMAGE}:${TAG} + (cd ${DEPLOY_PATH} && \ + DOCKER_BUILDKIT=1 ${DOCKER_BUILD} . --progress=plain --no-cache --tag $$(echo ${ODOO_IMAGE} | tr A-Z a-z):${TAG}\ + ) +build_image_cache: + (cd ${DEPLOY_PATH} && \ + DOCKER_BUILDKIT=1 ${DOCKER_BUILD} . --progress=plain --tag $$(echo ${ODOO_IMAGE} | tr A-Z a-z):${TAG}\ + ) push_image: - $(DOCKER_PUSH) ${ODOO_IMAGE}:${TAG} + $(DOCKER_PUSH) $(shell echo ${ODOO_IMAGE}| tr A-Z a-z):${TAG} run_server_docker: @if ! docker ps | grep -q "${CONTAINER_ID}"; then \ echo "Container not found. Running docker-compose up -d"; \ @@ -106,8 +103,6 @@ run_server_docker: fi cd ${DEPLOY_PATH} &&\ ${DOCKER_COMPOSE_CMD} up -d -update_tag: - ${SETUP_PATH}/update_tag.sh $(CURR_BRANCH) restore_database_docker: sudo ${DOCKER_EXEC} ${CONTAINER_ID} odoo db -c /etc/odoo/odoo.conf load backup /var/lib/odoo/backup/backup.zip stop_server_docker: @@ -132,6 +127,7 @@ clean_up: ! -name "*.template" \ ! -name "*.py" \ ! -name "*.yml" \ + ! -name "*.txt" \ -print0 | sudo xargs -0 rm -rf {} && \ sudo rm -rf ${DEPLOY_PATH}/etc/* diff --git a/deployment/backup/backup.zip b/deployment/backup/backup.zip deleted file mode 100644 index 36a5b1089..000000000 Binary files a/deployment/backup/backup.zip and /dev/null differ diff --git a/deployment/docker-compose.yml b/deployment/docker-compose.yml deleted file mode 100755 index 8d25ce949..000000000 --- a/deployment/docker-compose.yml +++ /dev/null @@ -1,35 +0,0 @@ -version: '3.8' -services: - app: - image: ${ODOO_IMAGE:-odoo}:${ODOO_TAG:-17} - container_name: ${ODOO_CONTAINER:-odoo} - # user: root - depends_on: - - db - ports: - - ${ODOO_PORT:-8069}:8069 - # - "${ODOO_CHAT_PORT:-8072}:8072" - tty: true - command: -- - environment: - - HOST=db - - USER=${PG_USER:-changeme} - - PASSWORD=${PG_PASS:-password} - volumes: - - ${ODOO_ADDONS:-./addons}:/mnt/extra-addons - - ${ODOO_CONFIG:-./etc}:/etc/odoo - - ${ODOO_BACKUP:-./backup}:/var/lib/odoo/backup - restart: always - db: - image: postgres:16 - # user: root - environment: - - POSTGRES_USER=${PG_USER:-changeme} - - POSTGRES_PASSWORD=${PG_PASS:-password} - - POSTGRES_DB=postgres - ports: - - ${PG_PORT:-5432}:5432 - restart: always - volumes: - - ${PG_DATA:-./postgresql}:/var/lib/postgresql/data - diff --git a/deployment/entrypoint.sh b/deployment/entrypoint.sh deleted file mode 100755 index 939544337..000000000 --- a/deployment/entrypoint.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!/bin/bash - -set -e - -if [ -v PASSWORD_FILE ]; then - PASSWORD="$(< $PASSWORD_FILE)" -fi - -# set the postgres database host, port, user and password according to the environment -# and pass them as arguments to the odoo process if not present in the config file -: ${HOST:=${DB_PORT_5432_TCP_ADDR:='db'}} -: ${PORT:=${DB_PORT_5432_TCP_PORT:=5432}} -: ${USER:=${DB_ENV_POSTGRES_USER:=${POSTGRES_USER:='odoo'}}} -: ${PASSWORD:=${DB_ENV_POSTGRES_PASSWORD:=${POSTGRES_PASSWORD:='odoo'}}} - -DB_ARGS=() -function check_config() { - param="$1" - value="$2" - if grep -q -E "^\s*\b${param}\b\s*=" "$ODOO_RC" ; then - value=$(grep -E "^\s*\b${param}\b\s*=" "$ODOO_RC" |cut -d " " -f3|sed 's/["\n\r]//g') - fi; - DB_ARGS+=("--${param}") - DB_ARGS+=("${value}") -} -check_config "db_host" "$HOST" -check_config "db_port" "$PORT" -check_config "db_user" "$USER" -check_config "db_password" "$PASSWORD" - -case "$1" in - -- | odoo) - shift - if [[ "$1" == "scaffold" ]] ; then - exec odoo "$@" - else - # shellcheck disable=SC2068 - wait-for-psql.py ${DB_ARGS[@]} --timeout=30 - exec odoo "$@" "${DB_ARGS[@]}" - fi - ;; - -*) - # shellcheck disable=SC2068 - wait-for-psql.py ${DB_ARGS[@]} --timeout=30 - exec odoo "$@" "${DB_ARGS[@]}" - ;; - *) - exec "$@" -esac - -exit 1 \ No newline at end of file diff --git a/deployment/env.template b/deployment/env.template deleted file mode 100755 index 009edac12..000000000 --- a/deployment/env.template +++ /dev/null @@ -1,15 +0,0 @@ -COMPOSE_PROJECT_NAME= -#Odoo -ODOO_IMAGE= -ODOO_TAG= -ODOO_PORT=10017 -ODOO_CONTAINER= -# ODOO_CHAT_PORT= -ODOO_ADDONS=./addons -ODOO_CONFIG=./etc -ODOO_BACKUP=./backup -#Database -PG_PORT= -PG_USER= -PG_PASS= -ENTRYPOINT=./entrypoint.sh diff --git a/deployment/etc/odoo.conf b/deployment/etc/odoo.conf deleted file mode 100644 index ca1198737..000000000 --- a/deployment/etc/odoo.conf +++ /dev/null @@ -1,9 +0,0 @@ -[options] -addons_path = /mnt/extra-addons -data_dir = /var/lib/odoo -db_host = db -db_port = 5432 -db_user = default_repo -db_password = smartyourlife -proxy_mode = True - diff --git a/deployment/odoo.conf.template b/deployment/odoo.conf.template deleted file mode 100755 index a295cb26b..000000000 --- a/deployment/odoo.conf.template +++ /dev/null @@ -1,7 +0,0 @@ -[options] -addons_path = /mnt/extra-addons -data_dir = /var/lib/odoo -db_host = -db_port = -db_user = -db_password = \ No newline at end of file diff --git a/deployment/wait-for-psql.py b/deployment/wait-for-psql.py deleted file mode 100755 index 799639741..000000000 --- a/deployment/wait-for-psql.py +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env python3 -import argparse -import psycopg2 -import sys -import time - - -if __name__ == '__main__': - arg_parser = argparse.ArgumentParser() - arg_parser.add_argument('--db_host', required=True) - arg_parser.add_argument('--db_port', required=True) - arg_parser.add_argument('--db_user', required=True) - arg_parser.add_argument('--db_password', required=True) - arg_parser.add_argument('--timeout', type=int, default=5) - - args = arg_parser.parse_args() - - start_time = time.time() - while (time.time() - start_time) < args.timeout: - try: - conn = psycopg2.connect(user=args.db_user, host=args.db_host, port=args.db_port, password=args.db_password, dbname='postgres') - error = '' - break - except psycopg2.OperationalError as e: - error = e - else: - conn.close() - time.sleep(1) - - if error: - print("Database connection failure: %s" % error, file=sys.stderr) - sys.exit(1) \ No newline at end of file diff --git a/setup/clean_up_addons.sh b/setup/clean_up_addons.sh deleted file mode 100755 index 8ac385db3..000000000 --- a/setup/clean_up_addons.sh +++ /dev/null @@ -1,101 +0,0 @@ -#!/bin/bash - -# Check if at least one root folder is provided as an argument -if [ $# -eq 0 ]; then - echo "Usage: $0 [...]" - echo "Please provide at least one root folder path." - exit 1 -fi - -# Define the protected items list file -PROTECTED_LIST="protected.txt" -if [ ! -f "$PROTECTED_LIST" ]; then - echo "Error: '$PROTECTED_LIST' not found." - echo "Please create 'protected.txt' one directory up with a list of protected files/folders (one per line)." - exit 1 -fi - -# Safeguard: Check if any file/folder matching patterns in protected.txt exists in a root folder -check_protected_items() { - local root_dir="$1" - while IFS= read -r pattern; do - # Skip empty lines - [ -z "$pattern" ] && continue - - # Handle wildcards by using find for pattern matching - if [[ "$pattern" == *"*"* ]]; then - # Convert pattern to a find-compatible search - if [[ "$pattern" == /* ]]; then - search_path="${root_dir}${pattern}" - else - search_path="${root_dir}/${pattern}" - fi - - # Use find to check if any files match the pattern - if find "$root_dir" -path "$search_path" 2>/dev/null | grep -q .; then - echo "Error: Protected pattern '$pattern' matches files in '$root_dir'. Aborting execution." - exit 1 - fi - else - # Exact match for non-wildcard entries - if [ -e "$root_dir/$pattern" ]; then - echo "Error: Protected item '$pattern' found in '$root_dir'. Aborting execution." - exit 1 - fi - fi - done < "$PROTECTED_LIST" -} - -# Function to check and delete subfolders -delete_non_manifest_folders() { - local dir="$1" - - # Loop through all immediate subdirectories in the given directory - for subfolder in "$dir"/*/ ; do - # Check if it's a directory - if [ -d "$subfolder" ]; then - # Check if __manifest__.py exists in this subfolder - if [ ! -f "$subfolder/__manifest__.py" ]; then - echo "Deleting '$subfolder' (no __manifest__.py found)" - rm -rf "$subfolder" - else - echo "Keeping '$subfolder' (__manifest__.py found)" - fi - fi - done -} - -# Process each root folder provided as an argument -for ROOT_FOLDER in "$@"; do - # Check if the root folder exists and is a directory - if [ ! -d "$ROOT_FOLDER" ]; then - echo "Error: '$ROOT_FOLDER' is not a valid directory. Skipping." - continue - fi - - # Perform the safeguard check for this root folder - echo "Checking for protected items in '$ROOT_FOLDER' from '$PROTECTED_LIST'..." - check_protected_items "$ROOT_FOLDER" - - # Change to the root folder to handle relative paths cleanly - cd "$ROOT_FOLDER" || { - echo "Error: Could not change to directory '$ROOT_FOLDER'. Skipping." - continue - } - - # Call the function with the current root folder - echo "Processing '$ROOT_FOLDER'..." - delete_non_manifest_folders "." - - # Return to the original directory to process the next root folder - cd - > /dev/null || { - echo "Error: Could not return from '$ROOT_FOLDER'. Exiting." - exit 1 - } - - echo "Cleanup complete for '$ROOT_FOLDER'!" -done - -echo "All root folders processed!" - -exit 0 \ No newline at end of file diff --git a/setup/clean_up_virtualenvs.sh b/setup/clean_up_virtualenvs.sh deleted file mode 100755 index f383c2fb3..000000000 --- a/setup/clean_up_virtualenvs.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/bash - -# Get the current branch name -branch_name=$(git rev-parse --abbrev-ref HEAD) - -# Get a list of all virtual environments, filtering out duplicates and those not containing the branch name -virtualenvs=$(pyenv virtualenvs | awk '{print $1}' | sort -u | grep "$branch_name") - -# Count the number of virtual environments -count=$(echo "$virtualenvs" | wc -l) - -# Calculate how many virtual environments to keep -keep_count=$((count - $1)) - -# If there are more than 3 virtual environments, delete the oldest ones -if (( keep_count > 0 )); then - # Get the oldest virtual environments (assuming they are listed first) - oldest_venvs=$(echo "$virtualenvs" | head -n "$keep_count") - - # Loop through the oldest virtual environments and delete them - for venv in $oldest_venvs; do - echo "Deleting virtual environment: $venv" - pyenv virtualenv-delete -f "$venv" - done -fi - -echo "Old virtual environments containing '$branch_name' deleted." \ No newline at end of file diff --git a/setup/color_log.py b/setup/color_log.py deleted file mode 100644 index dbd92e059..000000000 --- a/setup/color_log.py +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env python3 - -def colorize(text, code): - """Colorizes text using ANSI escape codes.""" - return f"\033[{code}m{text}\033[0m" - -def Show(status, message): - """Displays a message with a status indicator.""" - colors = { - 0: (colorize("[", "90")+colorize(" OK ", "38;5;154") + colorize("]", "90")), # Green, Grey - 1: (colorize("[", "90")+colorize(" FAILED ", "91") + colorize("]", "90")), # Red, Grey - 2: (colorize("[", "90")+colorize(" INFO ", "38;5;154") + colorize("]", "90")), # Green, Grey - 3: (colorize("[", "90")+colorize(" NOTICE ", "33") + colorize("]", "90")), # Yellow, Grey - } - print(f"{colors.get(status, '')} {message}") - if status == 1: - exit(1) - -def Warn(message): - """Displays a warning message in red.""" - print(colorize(message, "91")) - -def GreyStart(): - """Starts a grey-colored output.""" - print(colorize("", "90"), end="") - -def ColorReset(): - """Resets the output color.""" - print("\033[0m", end="") \ No newline at end of file diff --git a/setup/debinstall copy.sh b/setup/debinstall copy.sh deleted file mode 100755 index 0920ec686..000000000 --- a/setup/debinstall copy.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/sh -# -# Install Debian packages needed to run Odoo. - - -if [ "$1" = "-l" -o "$1" = "--list" ]; then - cmd="echo" -else - cmd="apt-get install -y --no-install-recommends" - if [ "$(id -u)" -ne "0" ]; then - echo "\033[0;31mThis script must be run as root to install dependencies, starting a dry run.\033[0m" >&2 - cmd="$cmd -s" - else - apt-get update - fi - if [ "$1" = "-q" -o "$1" = "--quiet" ]; then - cmd="$cmd -qq" - fi -fi - -script_path=$(realpath "$0") -script_dir=$(dirname "$script_path") -control_path=$(realpath "$script_dir/../debian/control") - -sed -n '/^Depends:/,/^[A-Z]/p' "$control_path" \ -| awk '/^ [a-z]/ { gsub(/,/,"") ; gsub(" ", "") ; print $NF }' | sort -u \ -| DEBIAN_FRONTEND=noninteractive xargs $cmd diff --git a/setup/dir2file.sh b/setup/dir2file.sh deleted file mode 100755 index 02ab7f8a0..000000000 --- a/setup/dir2file.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -# Define output file name -input_dir="$1" -output_file="$2" -# Find all directories in root and write to file -# Using find to list only directories (-type d) at depth 1 (-maxdepth 1) -find $input_dir -maxdepth 1 -type d -not -path "$input_dir" -exec basename {} \; | sort >> "$output_file" - -echo "Folder list has been written to $output_file" \ No newline at end of file diff --git a/setup/download_backup.sh b/setup/download_backup.sh deleted file mode 100755 index f213445c4..000000000 --- a/setup/download_backup.sh +++ /dev/null @@ -1,82 +0,0 @@ -#!/usr/bin/bash - -export PATH=/usr/sbin:$PATH -export DEBIAN_FRONTEND=noninteractive -set -euo pipefail -readonly COLOUR_RESET='\e[0m' -readonly aCOLOUR=( - '\e[38;5;154m' # green | Lines, bullets and separators - '\e[1m' # Bold white | Main descriptions - '\e[90m' # Grey | Credits - '\e[91m' # Red | Update notifications Alert - '\e[33m' # Yellow | Emphasis -) -trap 'onCtrlC' INT -onCtrlC() { - echo -e "${COLOUR_RESET}" - exit 1 -} - -Show() { - # OK - if (($1 == 0)); then - echo -e "${aCOLOUR[2]}[$COLOUR_RESET${aCOLOUR[0]} OK $COLOUR_RESET${aCOLOUR[2]}]$COLOUR_RESET $2" - # FAILED - elif (($1 == 1)); then - echo -e "${aCOLOUR[2]}[$COLOUR_RESET${aCOLOUR[3]}FAILED$COLOUR_RESET${aCOLOUR[2]}]$COLOUR_RESET $2" - exit 1 - # INFO - elif (($1 == 2)); then - echo -e "${aCOLOUR[2]}[$COLOUR_RESET${aCOLOUR[0]} INFO $COLOUR_RESET${aCOLOUR[2]}]$COLOUR_RESET $2" - # NOTICE - elif (($1 == 3)); then - echo -e "${aCOLOUR[2]}[$COLOUR_RESET${aCOLOUR[4]}NOTICE$COLOUR_RESET${aCOLOUR[2]}]$COLOUR_RESET $2" - fi -} - -Warn() { - echo -e "${aCOLOUR[3]}$1$COLOUR_RESET" -} - -GreyStart() { - echo -e "${aCOLOUR[2]}\c" -} - -ColorReset() { - echo -e "$COLOUR_RESET\c" -} - - -main() { - DEPLOYMENT_DIR=$(pwd)/deployment - BACKUP_DIR="$DEPLOYMENT_DIR/backup" - DOWNLOAD_URL="$1" - BACKUP_FILE="$BACKUP_DIR/$2" - # Check if the deployment and backup directories exist, create them if not - if [[ ! -d "$BACKUP_DIR" ]]; then - echo "Backup directory does not exist. Creating: $BACKUP_DIR" - mkdir -p "$BACKUP_DIR" - fi - - # Check if the download URL is valid - echo "Checking if the URL is valid: $DOWNLOAD_URL" - if curl --head --silent --fail "$DOWNLOAD_URL" > /dev/null; then - echo "URL is valid. Proceeding with download..." - else - Show 1 "Error: Invalid or inaccessible URL: $DOWNLOAD_URL" - exit 1 - fi - - # Download the file and rename it to backup.zip - wget -O "$BACKUP_FILE" "$DOWNLOAD_URL" - - # Check if the file was downloaded - if [[ -f "$BACKUP_FILE" ]]; then - Show 0 "Backup file successfully downloaded to: $BACKUP_FILE" - else - Show 1 "Error: Backup file was not downloaded." - exit 1 - fi -} - -main "$@" \ No newline at end of file diff --git a/setup/gen_config.py b/setup/gen_config.py deleted file mode 100644 index b0a38390e..000000000 --- a/setup/gen_config.py +++ /dev/null @@ -1,67 +0,0 @@ -#!/usr/bin/env python3 -import argparse -import configparser -import shutil -import os -from dotenv import set_key -from pathlib import Path -import socket -import secrets -import string -import color_log -def find_available_port(start_port=80): - """Finds an available port starting from the given port.""" - with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: - while True: - try: - sock.bind(('0.0.0.0', start_port)) - color_log.Show(3,f" {start_port} is Open") - return start_port - except OSError as e: - if e.errno == 98: # Address already in use - print(f"{start_port} already in use , Try other port ...") - start_port += 1 - else: - raise -def main(): - """ - Generates a random password and finds an available port. - Updates the Odoo configuration file and .env file with these values. - """ - parser = argparse.ArgumentParser(description="Generate Odoo configuration") - parser.add_argument('--db_user', type=str, help='') - parser.add_argument('--db_pass', type=str, help='') - parser.add_argument('--deploy_path', type=str, help='') - parser.add_argument('--addons_path', type=str, help='') - # parser.add_argument('--db_filter', type=str, help='') - parser.add_argument('--db_port', type=int, help='') - parser.add_argument('--db_server', type=str, help='') - args = parser.parse_args() - db_port = args.db_port - db_user = args.db_user - db_pass = args.db_pass - db_server = args.db_server - app_port = find_available_port(8069) - addons_path = args.addons_path - base_dir= args.deploy_path - # db_filter= args.db_filter - # Copy template files - os.makedirs(f"{base_dir}/etc", exist_ok=True) - color_log.Show(3,f"Copy {base_dir}/odoo.conf.template to {base_dir}/etc/odoo.conf") - shutil.copyfile(f'{base_dir}/odoo.conf.template', f'{base_dir}/odoo.conf') - - # Update Odoo configuration file - config = configparser.ConfigParser() - config.read(f'{base_dir}/odoo.conf') - config['options']['db_host'] = str(db_server) - config['options']['db_user'] = db_user - config['options']['db_password'] = db_pass - config['options']['db_port'] = str(db_port) - config['options']['addons_path'] = addons_path - config['options']['xmlrpc_port'] = str(app_port) - config['options']['dbfilter'] = ".*" - config['options']['proxy_mode'] = "True" - with open(f'{base_dir}/odoo.conf', 'w') as configfile: - config.write(configfile) -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/setup/gen_config_docker.py b/setup/gen_config_docker.py deleted file mode 100755 index ce15bc55f..000000000 --- a/setup/gen_config_docker.py +++ /dev/null @@ -1,93 +0,0 @@ -#!/usr/bin/env python3 -import argparse -import configparser -import shutil -import os -from dotenv import set_key -from pathlib import Path -import socket -import secrets -import string -import color_log -def generate_password(length=16): - """Generates a random password of specified length.""" - alphabet = string.ascii_letters + string.digits - return ''.join(secrets.choice(alphabet) for _ in range(length)) - -def find_available_port(start_port=80): - """Finds an available port starting from the given port.""" - with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: - while True: - try: - sock.bind(('0.0.0.0', start_port)) - color_log.Show(3,f" {start_port} is Open") - return start_port - except OSError as e: - if e.errno == 98: # Address already in use - print(f"{start_port} already in use , Try other port ...") - start_port += 1 - else: - raise - -def main(): - """ - Generates a random password and finds an available port. - Updates the Odoo configuration file and .env file with these values. - """ - parser = argparse.ArgumentParser(description="Generate Odoo configuration") - parser.add_argument('--db_port', type=int, help='') - parser.add_argument('--db_user', type=str, help='') - parser.add_argument('--deploy_path', type=str, help='') - parser.add_argument('--image', type=str, help='') - parser.add_argument('--tag', type=str, help='') - parser.add_argument('--addons', type=str, help='') - parser.add_argument('--config', type=str, help='') - parser.add_argument('--container', type=str, help='') - parser.add_argument('--backup', type=str, help='') - args = parser.parse_args() - db_port = args.db_port - db_pass = "smartyourlife" - db_user = args.db_user - base_dir= args.deploy_path - image=args.image - tag=args.tag - container=args.container - addons=args.addons - config_path=args.config - app_port = 10017 - backup = args.backup - # Copy template files - os.makedirs(f"{base_dir}/etc", exist_ok=True) - color_log.Show(3,f"Copy {base_dir}/odoo.conf.template to {base_dir}/etc/odoo.conf") - shutil.copyfile(f'{base_dir}/odoo.conf.template', f'{base_dir}/etc/odoo.conf') - shutil.copyfile(f'{base_dir}/env.template', f'{base_dir}/.env') - - # Update Odoo configuration file - config = configparser.ConfigParser() - config.read(f'{base_dir}/etc/odoo.conf') - config['options']['db_host'] = "db" - config['options']['db_user'] = db_user - config['options']['db_password'] = db_pass - config['options']['db_port'] = str(db_port) - config['options']['addons_path'] = "/mnt/extra-addons" - config['options']['data_dir'] = "/var/lib/odoo" - config['options']['proxy_mode'] = "True" - with open(f'{base_dir}/etc/odoo.conf', 'w') as configfile: - config.write(configfile) - - # Update .env file - env_file_path = Path("deployment/.env") - set_key(dotenv_path=env_file_path, key_to_set="COMPOSE_PROJECT_NAME", value_to_set=f"odoo-{tag}",quote_mode="never") - set_key(dotenv_path=env_file_path, key_to_set="PG_PORT", value_to_set=find_available_port(int(os.getenv('DB_PORT','5432'))+1),quote_mode="never") - set_key(dotenv_path=env_file_path, key_to_set="PG_USER", value_to_set=db_user,quote_mode="never") - set_key(dotenv_path=env_file_path, key_to_set="PG_PASS", value_to_set=db_pass,quote_mode="never") - set_key(dotenv_path=env_file_path, key_to_set="ODOO_CONFIG", value_to_set=config_path,quote_mode="never") - set_key(dotenv_path=env_file_path, key_to_set="ODOO_ADDONS", value_to_set=addons,quote_mode="never") - set_key(dotenv_path=env_file_path, key_to_set="ODOO_PORT", value_to_set=find_available_port(app_port),quote_mode="never") - set_key(dotenv_path=env_file_path, key_to_set="ODOO_IMAGE", value_to_set=image,quote_mode="never") - set_key(dotenv_path=env_file_path, key_to_set="ODOO_TAG", value_to_set=tag,quote_mode="never") - set_key(dotenv_path=env_file_path, key_to_set="ODOO_CONTAINER", value_to_set=container,quote_mode="never") - set_key(dotenv_path=env_file_path, key_to_set="ODOO_BACKUP", value_to_set=backup,quote_mode="never") - -if __name__ == "__main__": - main() diff --git a/setup/init_config.sh b/setup/init_config.sh deleted file mode 100755 index 637df5ce7..000000000 --- a/setup/init_config.sh +++ /dev/null @@ -1,245 +0,0 @@ -#!/usr/bin/bash - -export PATH=/usr/sbin:$PATH -export DEBIAN_FRONTEND=noninteractive -set -euo pipefail -DEPLOY_PATH=$(pwd)/deployment -SETUP_PATH=$(pwd)/setup -PIP=$(pwd)/venv/bin/pip -PYTHON=$(pwd)/venv/bin/python -ODOO_ADDONS=${DEPLOY_PATH}/addons -ODOO_CONFIG=${DEPLOY_PATH}/etc -ODOO_BACKUP=${DEPLOY_PATH}/backup -# System -DEPENDS_PACKAGE=('wget' 'curl' 'git' 'unzip' 'make' 'wkhtmltopdf' 'postgresql-client') -DEPENDS_COMMAND=('wget' 'curl' 'git' 'unzip' 'make' 'wkhtmltopdf' 'psql') -((EUID)) && sudo_cmd="sudo" || sudo_cmd="" -readonly MINIMUM_DOCER_VERSION="20" -UNAME_U="$(uname -s)" -readonly UNAME_U -readonly COLOUR_RESET='\e[0m' -readonly aCOLOUR=( - '\e[38;5;154m' # green | Lines, bullets and separators - '\e[1m' # Bold white | Main descriptions - '\e[90m' # Grey | Credits - '\e[91m' # Red | Update notifications Alert - '\e[33m' # Yellow | Emphasis -) -trap 'onCtrlC' INT -onCtrlC() { - echo -e "${COLOUR_RESET}" - exit 1 -} - -Show() { - # OK - if (($1 == 0)); then - echo -e "${aCOLOUR[2]}[$COLOUR_RESET${aCOLOUR[0]} OK $COLOUR_RESET${aCOLOUR[2]}]$COLOUR_RESET $2" - # FAILED - elif (($1 == 1)); then - echo -e "${aCOLOUR[2]}[$COLOUR_RESET${aCOLOUR[3]}FAILED$COLOUR_RESET${aCOLOUR[2]}]$COLOUR_RESET $2" - exit 1 - # INFO - elif (($1 == 2)); then - echo -e "${aCOLOUR[2]}[$COLOUR_RESET${aCOLOUR[0]} INFO $COLOUR_RESET${aCOLOUR[2]}]$COLOUR_RESET $2" - # NOTICE - elif (($1 == 3)); then - echo -e "${aCOLOUR[2]}[$COLOUR_RESET${aCOLOUR[4]}NOTICE$COLOUR_RESET${aCOLOUR[2]}]$COLOUR_RESET $2" - fi -} - -Warn() { - echo -e "${aCOLOUR[3]}$1$COLOUR_RESET" -} - -GreyStart() { - echo -e "${aCOLOUR[2]}\c" -} - -ColorReset() { - echo -e "$COLOUR_RESET\c" -} - - -Update_Package_Resource() { - GreyStart - if [ -x "$(command -v apk)" ]; then - ${sudo_cmd} apk update - elif [ -x "$(command -v apt)" ]; then - ${sudo_cmd} apt update - elif [ -x "$(command -v dnf)" ]; then - ${sudo_cmd} dnf check-update - elif [ -x "$(command -v zypper)" ]; then - ${sudo_cmd} zypper update - elif [ -x "$(command -v yum)" ]; then - ${sudo_cmd} yum update - fi - ColorReset -} -# 3 Check OS -Check_OS() { - if [[ $UNAME_U == *Linux* ]]; then - Show 0 "Your System is : $UNAME_U" - else - Show 1 "This script is only for Linux." - exit 1 - fi -} -Install_Depends() { - for ((i = 0; i < ${#DEPENDS_COMMAND[@]}; i++)); do - cmd=${DEPENDS_COMMAND[i]} - if [[ ! -x $(${sudo_cmd} which "$cmd") ]]; then - packagesNeeded=${DEPENDS_PACKAGE[i]} - Show 2 "Install the necessary dependencies: \e[33m$packagesNeeded \e[0m" - GreyStart - if [ -x "$(command -v apk)" ]; then - ${sudo_cmd} apk add --no-cache "$packagesNeeded" - elif [ -x "$(command -v apt-get)" ]; then - ${sudo_cmd} apt-get -y -q install "$packagesNeeded" --no-upgrade - elif [ -x "$(command -v dnf)" ]; then - ${sudo_cmd} dnf install "$packagesNeeded" - elif [ -x "$(command -v zypper)" ]; then - ${sudo_cmd} zypper install "$packagesNeeded" - elif [ -x "$(command -v yum)" ]; then - ${sudo_cmd} yum install "$packagesNeeded" - elif [ -x "$(command -v pacman)" ]; then - ${sudo_cmd} pacman -S "$packagesNeeded" - elif [ -x "$(command -v paru)" ]; then - ${sudo_cmd} paru -S "$packagesNeeded" - else - Show 1 "Package manager not found. You must manually install: \e[33m$packagesNeeded \e[0m" - fi - ColorReset - else - Show 2 "\e[33m ${DEPENDS_COMMAND[i]}\e[0m Installed" - fi - done -} - -Check_Dependency_Installation() { - for ((i = 0; i < ${#DEPENDS_COMMAND[@]}; i++)); do - cmd=${DEPENDS_COMMAND[i]} - if [[ ! -x $(${sudo_cmd} which "$cmd") ]]; then - packagesNeeded=${DEPENDS_PACKAGE[i]} - Show 1 "Dependency \e[33m$packagesNeeded \e[0m installation failed, please try again manually!" - exit 1 - fi - done -} -Check_Docker_Install() { - if [[ -x "$(command -v docker)" ]]; then - Docker_Version=$(${sudo_cmd} docker version --format '{{.Server.Version}}') - if [[ $? -ne 0 ]]; then - Install_Docker - elif [[ ${Docker_Version:0:2} -lt "${MINIMUM_DOCER_VERSION}" ]]; then - Show 1 "Recommended minimum Docker version is \e[33m${MINIMUM_DOCER_VERSION}.xx.xx\e[0m,\Current Docker verison is \e[33m${Docker_Version}\e[0m,\nPlease uninstall current Docker and rerun the CasaOS installation script." - exit 1 - else - Show 0 "Current Docker verison is ${Docker_Version}." - fi - else - Install_Docker - fi -} -Install_Docker() { - Show 2 "Install the necessary dependencies: \e[33mDocker \e[0m" - if [[ ! -d "${PREFIX}/etc/apt/sources.list.d" ]]; then - ${sudo_cmd} mkdir -p "${PREFIX}/etc/apt/sources.list.d" - fi - GreyStart - if [[ "${REGION}" = "CN" ]]; then - ${sudo_cmd} curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun - else - ${sudo_cmd} curl -fsSL https://get.docker.com | bash - fi - ColorReset - if [[ $? -ne 0 ]]; then - Show 1 "Installation failed, please try again." - exit 1 - else - Check_Docker_Install_Final - fi -} -Check_Docker_Install_Final() { - if [[ -x "$(command -v docker)" ]]; then - Docker_Version=$(${sudo_cmd} docker version --format '{{.Server.Version}}') - if [[ $? -ne 0 ]]; then - Install_Docker - elif [[ ${Docker_Version:0:2} -lt "${MINIMUM_DOCER_VERSION}" ]]; then - Show 1 "Recommended minimum Docker version is \e[33m${MINIMUM_DOCER_VERSION}.xx.xx\e[0m,\Current Docker verison is \e[33m${Docker_Version}\e[0m,\nPlease uninstall current Docker and rerun the CasaOS installation script." - exit 1 - else - Show 0 "Current Docker verison is ${Docker_Version}." - Check_Docker_Running - fi - else - Show 1 "Installation failed, please run 'curl -fsSL https://get.docker.com | bash' and rerun the CasaOS installation script." - exit 1 - fi -} -Generate_Config_Docker(){ - ODOO_IMAGE=${1:-} - ODOO_TAG=${2:-} - ODOO_CONTAINER=${3:-} - if [[ ! -f "${DEPLOY_PATH}/.env" ]]; then - cp "${DEPLOY_PATH}/env.template" "${DEPLOY_PATH}/.env" - fi - USER="${REPO_NAME:-"default_repo"}" - # Convert to lowercase - USER="${USER,,}" - ${PYTHON} "$SETUP_PATH/gen_config_docker.py" --db_port 5432 --db_user $USER --deploy_path "$DEPLOY_PATH" \ - --image "${ODOO_IMAGE}" --container "${ODOO_CONTAINER}" --tag "${ODOO_TAG:=latest}" \ - --addons "${ODOO_ADDONS}" --config "${ODOO_CONFIG}" --backup "${ODOO_BACKUP}" - Show 0 " Generate Config Complete" -} -Generate_Config_Native(){ - DB_USER=${2:-} - DB_PASSWORD=${3:-} - DB_SERVER=${4:-} - DB_PORT=${5:-} - ADDONS=${1:-} - REPO_NAME=$(basename "$(git rev-parse --show-toplevel)" | sed -E 's/[.-]/_/g') - USER="${REPO_NAME:-"default_repo"}" - # Convert to lowercase - USER="${USER,,}" - PASSWORD="$(openssl rand -hex 24)" - # Check if the user already exists - USER_EXISTS=$(psql "postgresql://${DB_USER}:${DB_PASSWORD}@${DB_SERVER}:${DB_PORT}/postgres" -t -A -c "SELECT COUNT(*) FROM pg_roles WHERE rolname='$USER';") - - if [ $USER_EXISTS -eq 0 ]; then - # User does not exist, create the user - Show 2 "Create the new PostgreSQL username: $USER with password: $PASSWORD" - psql "postgresql://${DB_USER}:${DB_PASSWORD}@${DB_SERVER}:${DB_PORT}/postgres" -c "CREATE USER $USER WITH PASSWORD '$PASSWORD';" - Show 2 "Grant $USER superuser (admin) privileges" - psql "postgresql://${DB_USER}:${DB_PASSWORD}@${DB_SERVER}:${DB_PORT}/postgres" -c "ALTER USER $USER WITH SUPERUSER;" - else - # User exists, update the password (do not try to create) - Show 2 "User $USER already exists, updating password to $PASSWORD" - psql "postgresql://${DB_USER}:${DB_PASSWORD}@${DB_SERVER}:${DB_PORT}/postgres" -c "ALTER USER $USER WITH PASSWORD '$PASSWORD';" - fi - ${PYTHON} "$SETUP_PATH/gen_config.py" --db_user $USER --db_pass $PASSWORD --deploy_path "$(pwd)" \ - --addons_path $ADDONS --db_port $DB_PORT --db_server $DB_SERVER - Show 0 " Generate Config Complete" -} -main(){ - TYPE=${1:-} - Check_OS - # Update_Package_Resource - # Install_Depends - # Check_Dependency_Installation - # Check_Docker_Install - case "$TYPE" in - --native) - Generate_Config_Native $2 $3 $4 $5 $6 - ;; - --docker) - Generate_Config_Docker $2 $3 $4 - ;; - *) - # else - Show 1 "Invalid argument (--docker|--native)" - ;; - esac -} - -main "$@" diff --git a/setup/merge_module.py b/setup/merge_module.py deleted file mode 100755 index 1e7f2edb2..000000000 --- a/setup/merge_module.py +++ /dev/null @@ -1,44 +0,0 @@ -import subprocess -import yaml -import os -import argparse - -# Set up argument parsing -parser = argparse.ArgumentParser( - description="Checkout modules from target branch that are not in source branch." -) -parser.add_argument("yaml_file", help="Path to the YAML file") -parser.add_argument("source_branch", help="The source branch") -parser.add_argument("target_branch", help="The target branch") -parser.add_argument("root_repo", help="Path to the root repository") - -# Parse the arguments -args = parser.parse_args() - -yaml_file = args.yaml_file -source_branch = args.source_branch -target_branch = args.target_branch -root_repo = args.root_repo - -# Change to the repository directory -os.chdir(root_repo) -# Read YAML file -with open(yaml_file, "r") as file: - data = yaml.safe_load(file) - -# Extract module lists for source and target branches -modules_source = data["branches"].get(source_branch, {}).get("modules", []) -modules_target = data["branches"].get(target_branch, {}).get("modules", []) - -# Ensure the latest changes are fetched -subprocess.run(["git", "fetch", "origin"], check=True) - -# Checkout source branch first -print(f"Checking out source branch: {source_branch}") -subprocess.run(["git", "checkout", source_branch], check=True) - -# Checkout modules in target_branch that are not in source_branch -for module in modules_target: - if module not in modules_source: - print(f"Checking out module: {module}") - subprocess.run(["git", "checkout", target_branch, "--", module], check=True) diff --git a/setup/migrate-lang.sh b/setup/migrate-lang.sh deleted file mode 100755 index 144cf9e8a..000000000 --- a/setup/migrate-lang.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash - -# Set source and destination repositories -SRC_REPO="/root/dev/NextERP/dev/Viindoo/odoo-18.0" -DEST_REPO="/root/dev/NextERP/dev/odoo18/Odoo18" -LANG="vi" -# Ensure both paths exist -if [ ! -d "$SRC_REPO" ]; then - echo "Error: Source repository does not exist!" - exit 1 -fi - -if [ ! -d "$DEST_REPO" ]; then - echo "Error: Destination repository does not exist!" - exit 1 -fi - -# Find and copy vi.po files while preserving directory structure -cd "$SRC_REPO" || exit -find . -type f -name "${LANG}.po" | while read -r file; do - # Get the directory path of the file - dir_path=$(dirname "$file") - - # Ensure the destination directory exists - mkdir -p "$DEST_REPO/$dir_path" - - # Copy the file - cp "$file" "$DEST_REPO/$dir_path/" - - echo "Copied: $file -> $DEST_REPO/$dir_path/" -done - -echo "All ${LANG}.po files copied successfully!" \ No newline at end of file diff --git a/setup/modules_scan.sh b/setup/modules_scan.sh deleted file mode 100755 index 45fcf7e30..000000000 --- a/setup/modules_scan.sh +++ /dev/null @@ -1,82 +0,0 @@ -#!/bin/bash - -# Check if required arguments are provided -if [ $# -lt 3 ] || [ $# -gt 4 ]; then - echo "Usage: $0 [list_branch]" - echo "Example: $0 exclude_list.txt /path/to/git/repo /path/to/output.yaml 'branch1 branch2'" - exit 1 -fi - -INPUT_FILE="$1" -ROOT_FOLDER="$2" -OUTPUT_FILE="$3" -LIST_BRANCH="$4" - -# Check if input file exists -if [ ! -f "$INPUT_FILE" ]; then - echo "Error: Input file '$INPUT_FILE' not found" - exit 1 -fi - -# Check if root folder exists -if [ ! -d "$ROOT_FOLDER" ]; then - echo "Error: Root folder '$ROOT_FOLDER' not found" - exit 1 -fi - -# Check if output YAML file exists, if not create it -if [ ! -f "$OUTPUT_FILE" ]; then - echo "Output file does not exist. Creating $OUTPUT_FILE" - touch "$OUTPUT_FILE" -fi - -# Change to root folder -cd "$ROOT_FOLDER" || exit 1 - -# Initialize output file -echo "branches:" > "$OUTPUT_FILE" - -# Get all git branches -git fetch --all -if [ -z "$LIST_BRANCH" ]; then - branches=$(git branch -r | grep -v HEAD | sed 's/origin\///' | sed 's/^[[:space:]]*//') -else - branches=$LIST_BRANCH -fi - -# Process each branch -for branch in $branches; do - echo "Processing branch: $branch" - - # Checkout branch - git checkout "$branch" 2>/dev/null || continue - - # Get all folders in current branch - folders=$(find . -maxdepth 1 -type d -not -path '.' -not -path './.*' | sed 's|./||') - - # Array to store modules not in input file - modules=() - - # Check each folder against input file - while IFS= read -r folder; do - # Skip if folder is empty - [ -z "$folder" ] && continue - - # Check if folder is in input file - if ! grep -Fxq "$folder" "$INPUT_FILE"; then - modules+=("$folder") - fi - done <<< "$folders" - - # Write to yaml if there are modules - if [ ${#modules[@]} -gt 0 ]; then - echo " $branch:" >> "$OUTPUT_FILE" - echo " modules:" >> "$OUTPUT_FILE" - for module in "${modules[@]}"; do - echo " - $module" >> "$OUTPUT_FILE" - done - fi - -done - -echo "Output written to $OUTPUT_FILE" diff --git a/setup/replace_attrs.py b/setup/replace_attrs.py deleted file mode 100644 index 03a7df75f..000000000 --- a/setup/replace_attrs.py +++ /dev/null @@ -1,244 +0,0 @@ -# -*- coding: utf-8 -*- - -import re -from bs4 import formatter, BeautifulSoup as bs -from pathlib import Path - -xml_4indent_formatter = formatter.XMLFormatter(indent=4) -NEW_ATTRS = {'required', 'invisible', 'readonly', 'column_invisible'} -percent_d_regex = re.compile("%\('?\"?[\w\.\d_]+'?\"?\)d") - -def get_files_recursive(path): - return (str(p) for p in Path(path).glob('**/*.xml') if p.is_file()) - -root_dir = input('Enter root directory to check (empty for current directory) : ') -root_dir = root_dir or '.' -all_xml_files = get_files_recursive(root_dir) - -def normalize_domain(domain): - """Normalize Domain, taken from odoo/osv/expression.py -> just the part so that & operators are added where needed. - After that, we can use a part of the def parse() from the same file to manage parenthesis for and/or""" - if len(domain) == 1: - return domain - result = [] - expected = 1 # expected number of expressions - op_arity = {'!': 1, '&': 2, '|': 2} - for token in domain: - if expected == 0: # more than expected, like in [A, B] - result[0:0] = ['&'] # put an extra '&' in front - expected = 1 - if isinstance(token, (list, tuple)): # domain term - expected -= 1 - token = tuple(token) - else: - expected += op_arity.get(token, 0) - 1 - result.append(token) - return result - -def stringify_leaf(leaf): - stringify = '' - switcher = False - # Replace operators not supported in python (=, like, ilike) - operator = str(leaf[1]) - if operator == '=': - operator = '==' - elif 'like' in operator: - if 'not' in operator: - operator = 'not in' - else: - operator = 'in' - switcher = True - # Take left operand, never to add quotes (should be python object / field) - left_operand = leaf[0] - # Take care of right operand, don't add quotes if it's list/tuple/set/boolean/number, check if we have a true/false/1/0 string tho. - right_operand = leaf[2] - if right_operand in ('True', 'False', '1', '0') or type(right_operand) in (list, tuple, set, int, float, bool): - right_operand = str(right_operand) - else: - right_operand = "'"+right_operand+"'" - stringify = "%s %s %s" % (right_operand if switcher else left_operand, operator, left_operand if switcher else right_operand) - return stringify - -def stringify_attr(stack): - if stack in (True, False, 'True', 'False', 1, 0, '1', '0'): - return stack - last_parenthesis_index = max(index for index, item in enumerate(stack[::-1]) if item not in ('|', '!')) - stack = normalize_domain(stack) - stack = stack[::-1] - result = [] - for index, leaf_or_operator in enumerate(stack): - if leaf_or_operator == '!': - expr = result.pop() - result.append('(not (%s))' % expr) - elif leaf_or_operator == '&' or leaf_or_operator == '|': - left = result.pop() - # In case of a single | or single & , we expect that it's a tag that have an attribute AND a state - # the state will be added as OR in states management - try: - right = result.pop() - except IndexError: - res = left + ('%s' % ' and' if leaf_or_operator=='&' else ' or') - result.append(res) - continue - form = '(%s %s %s)' - if index > last_parenthesis_index: - form = '%s %s %s' - result.append(form % (left, 'and' if leaf_or_operator=='&' else 'or', right)) - else: - result.append(stringify_leaf(leaf_or_operator)) - result = result[0] - return result - -def get_new_attrs(attrs): - new_attrs = {} - attrs_dict = eval(attrs.strip()) - for attr in NEW_ATTRS: - if attr in attrs_dict.keys(): - new_attrs[attr] = stringify_attr(attrs_dict[attr]) - ordered_attrs = {attr: new_attrs[attr] for attr in NEW_ATTRS if attr in new_attrs} - return ordered_attrs - -# Prettify puts on three lines (1/ opening tag, 2/ text, 3/ closing tag), not very cool. -# Taken from https://stackoverflow.com/questions/55962146/remove-line-breaks-and-spaces-around-span-elements-with-python-regex -# And changed to avoid putting ALL one line, and only manage , as it's the only one messing stuff here -# Kinda ugly to use the 3 types of tags but tbh I keep it like this while I have no time for a regex replace keeping the name="x" :p -def prettify_output(html): - for attr in NEW_ATTRS: - html = re.sub(f'[ \n]+',f'', html) - html = re.sub(f'[ \n]+',f'', html) - html = re.sub(r'[ \n]+', r'', html) - html = re.sub(r'[ \n]+', r'', html) - return html - -autoreplace = input('Do you want to auto-replace attributes ? (y/n) (empty == no) (will not ask confirmation for each file) : ') or 'n' -nofilesfound = True -ok_files = [] -nok_files = [] - -for xml_file in all_xml_files: - try: - with open(xml_file, 'rb') as f: - contents = f.read().decode('utf-8') - f.close() - if not 'attrs' in contents and not 'states' in contents: - continue - counter_for_percent_d_replace = 1 - percent_d_results = {} - for percent_d in percent_d_regex.findall(contents): - contents = contents.replace(percent_d, "'REPLACEME%s'" % counter_for_percent_d_replace) - percent_d_results[counter_for_percent_d_replace] = percent_d - counter_for_percent_d_replace += 1 - soup = bs(contents, 'xml') - tags_with_attrs = soup.select('[attrs]') - attribute_tags_name_attrs = soup.select('attribute[name="attrs"]') - tags_with_states = soup.select('[states]') - attribute_tags_name_states = soup.select('attribute[name="states"]') - if not (tags_with_attrs or attribute_tags_name_attrs or\ - tags_with_states or attribute_tags_name_states): - continue - print('\n################################################################') - print('##### Taking care of file -> %s' % xml_file) - print('\n########### Current tags found ###\n') - for t in tags_with_attrs + attribute_tags_name_attrs + tags_with_states + attribute_tags_name_states: - print(t) - - nofilesfound = False - # Management of tags that have attrs="" - for tag in tags_with_attrs: - attrs = tag['attrs'] - new_attrs = get_new_attrs(attrs) - del tag['attrs'] - for new_attr in new_attrs.keys(): - tag[new_attr] = new_attrs[new_attr] - # Management of attributes name="attrs" - attribute_tags_after = [] - for attribute_tag in attribute_tags_name_attrs: - new_attrs = get_new_attrs(attribute_tag.text) - for new_attr in new_attrs.keys(): - new_tag = soup.new_tag('attribute') - new_tag['name'] = new_attr - new_tag.append(str(new_attrs[new_attr])) - attribute_tags_after.append(new_tag) - attribute_tag.insert_after(new_tag) - attribute_tag.decompose() - # Management ot tags that have states="" - for state_tag in tags_with_states: - base_invisible = '' - if 'invisible' in state_tag.attrs and state_tag['invisible']: - base_invisible = state_tag['invisible'] - if not (base_invisible.endswith('or') or base_invisible.endswith('and')): - base_invisible = base_invisible + ' or ' - else: - base_invisible = base_invisible + ' ' - invisible_attr = "state not in [%s]" % ','.join(("'" + state.strip() + "'") for state in state_tag['states'].split(',')) - state_tag['invisible'] = base_invisible + invisible_attr - del state_tag['states'] - # Management of attributes name="states" - attribute_tags_states_after = [] - for attribute_tag_states in attribute_tags_name_states: - states = attribute_tag_states.text - existing_invisible_tag = False - # I don't know why, looking for attribute[name="invisible"] does not work, - # but if it exists, I can find it with findAll attribute -> loop to name="invisible" - for tag in attribute_tag_states.parent.findAll('attribute'): - if tag['name'] == 'invisible': - existing_invisible_tag = tag - break - if not existing_invisible_tag: - existing_invisible_tag = soup.new_tag('attribute') - existing_invisible_tag['name'] = 'invisible' - if existing_invisible_tag.text: - states_to_add = 'state not in [%s]' % ( - ','.join(("'" + state.strip() + "'") for state in states.split(',')) - ) - if existing_invisible_tag.text.endswith('or') or existing_invisible_tag.text.endswith('and'): - new_invisible_text = '%s %s' % (existing_invisible_tag.text, states_to_add) - else: - new_invisible_text = ' or '.join([existing_invisible_tag.text, states_to_add]) - else: - new_invisible_text = 'state not in [%s]' % ( - ','.join(("'" + state.strip() + "'") for state in states.split(',')) - ) - existing_invisible_tag.string = new_invisible_text - attribute_tag_states.insert_after(existing_invisible_tag) - attribute_tag_states.decompose() - attribute_tags_states_after.append(existing_invisible_tag) - - print('\n########### Will be replaced by ###\n') - for t in tags_with_attrs + attribute_tags_after + tags_with_states + attribute_tags_states_after: - print(t) - print('################################################################\n') - if autoreplace.lower()[0] == 'n': - confirm = input('Do you want to replace? (y/n) (empty == no) : ') or 'n' - else: - confirm = 'y' - if confirm.lower()[0] == 'y': - with open(xml_file, 'wb') as rf: - html = soup.prettify(formatter=xml_4indent_formatter) - html = prettify_output(html) - for percent_d_result in percent_d_results.keys(): - html = html.replace("'REPLACEME%s'" % percent_d_result, percent_d_results[percent_d_result]) - rf.write(html.encode('utf-8')) - ok_files.append(xml_file) - except Exception as e: - nok_files.append((xml_file, e)) - -print('\n################################################') -print('################## Run Debug ##################') -print('################################################') - -if nofilesfound: - print('No XML Files with "attrs" or "states" found in dir " %s "' % root_dir) - -print('Succeeded on files') -for file in ok_files: - print(file) -if not ok_files: - print('No files') -print('') -print('Failed on files') -for file in nok_files: - print(file[0]) - print('Reason: ', file[1]) -if not nok_files: - print('No files')