From b29630e1b6262b170d13f85b28a2f49879117a6f Mon Sep 17 00:00:00 2001 From: KaySar12 Date: Thu, 3 Apr 2025 16:20:17 +0700 Subject: [PATCH] update --- main.py | 7 +- scripts/clean_up_addons.sh | 101 +++++++++++++++ scripts/dir2file.sh | 10 ++ scripts/gen_config_docker.py | 93 +++++++++++++ scripts/init_config.sh | 245 +++++++++++++++++++++++++++++++++++ scripts/migrate-lang.sh | 33 +++++ scripts/modules_scan.sh | 82 ++++++++++++ services/odoo/connection.py | 2 +- services/odoo/database.py | 0 services/odoo/manager.py | 116 ----------------- services/odoo/module.py | 56 ++++++++ services/odoo/service.py | 73 +++++++++++ 12 files changed, 697 insertions(+), 121 deletions(-) create mode 100644 scripts/clean_up_addons.sh create mode 100644 scripts/dir2file.sh create mode 100644 scripts/gen_config_docker.py create mode 100644 scripts/init_config.sh create mode 100644 scripts/migrate-lang.sh create mode 100644 scripts/modules_scan.sh create mode 100644 services/odoo/database.py delete mode 100644 services/odoo/manager.py create mode 100644 services/odoo/module.py create mode 100644 services/odoo/service.py diff --git a/main.py b/main.py index 13e69bb..66d168a 100644 --- a/main.py +++ b/main.py @@ -2,19 +2,18 @@ import sys import os import argparse -from services.odoo.manager import OdooModuleManager +from services.odoo.service import OdooServiceManager def main(): - print(os.getcwd()) - manager = OdooModuleManager(config_path="utility/config/settings.yaml") + service = OdooServiceManager(config_path="utility/config/settings.yaml") parser = argparse.ArgumentParser( description="Restart Odoo Service", formatter_class=argparse.ArgumentDefaultsHelpFormatter, ) parser.add_argument("instance", help="Instance name") args = parser.parse_args() - manager.restart_service(args.instance) + service.restart_service(args.instance) sys.exit(0) diff --git a/scripts/clean_up_addons.sh b/scripts/clean_up_addons.sh new file mode 100644 index 0000000..8ac385d --- /dev/null +++ b/scripts/clean_up_addons.sh @@ -0,0 +1,101 @@ +#!/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/scripts/dir2file.sh b/scripts/dir2file.sh new file mode 100644 index 0000000..02ab7f8 --- /dev/null +++ b/scripts/dir2file.sh @@ -0,0 +1,10 @@ +#!/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/scripts/gen_config_docker.py b/scripts/gen_config_docker.py new file mode 100644 index 0000000..06ac8b9 --- /dev/null +++ b/scripts/gen_config_docker.py @@ -0,0 +1,93 @@ +#!/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.lower(),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.lower(),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/scripts/init_config.sh b/scripts/init_config.sh new file mode 100644 index 0000000..637df5c --- /dev/null +++ b/scripts/init_config.sh @@ -0,0 +1,245 @@ +#!/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/scripts/migrate-lang.sh b/scripts/migrate-lang.sh new file mode 100644 index 0000000..144cf9e --- /dev/null +++ b/scripts/migrate-lang.sh @@ -0,0 +1,33 @@ +#!/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/scripts/modules_scan.sh b/scripts/modules_scan.sh new file mode 100644 index 0000000..45fcf7e --- /dev/null +++ b/scripts/modules_scan.sh @@ -0,0 +1,82 @@ +#!/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/services/odoo/connection.py b/services/odoo/connection.py index dfabacc..9f04d59 100644 --- a/services/odoo/connection.py +++ b/services/odoo/connection.py @@ -1,7 +1,7 @@ from odoorpc import ODOO import os import sys -from utility.services.config import Config +from services.config import Config class OdooConnection: diff --git a/services/odoo/database.py b/services/odoo/database.py new file mode 100644 index 0000000..e69de29 diff --git a/services/odoo/manager.py b/services/odoo/manager.py deleted file mode 100644 index ca58f9c..0000000 --- a/services/odoo/manager.py +++ /dev/null @@ -1,116 +0,0 @@ -from utility.services.git.handler import GitHandler -from utility.services.odoo.connection import OdooConnection -import subprocess - - -class OdooModuleManager: - def __init__(self, config_path="config/settings.yaml"): - self.config = OdooConnection(config_path) - # Use OdooConnection instead of Config directly - self.git = GitHandler( - repo_url=self.config.config.get("git", "repo_url"), - local_path=self.config.config.get("git", "local_path"), - branch=self.config.config.get("git", "branch", "main"), - ) - - def update_and_upgrade(self, instance_name=None): - """Update and upgrade multiple modules for the specified instance(s).""" - self.config.connect(instance_name) # Connect to the target instance(s) - for instance in self.config.get_instances(): - if instance_name and instance["name"] != instance_name: - continue - print(f"Processing instance: {instance['name']}") - module_names = instance.get("module_names", []) - - if not module_names: - print(f"No modules specified for {instance['name']}, skipping upgrade.") - continue - - for module_name in module_names: - print(f"Upgrading module: {module_name} in {instance['name']}") - try: - # Use OdooConnection.execute to upgrade the module - module_ids = self.config.execute( - instance["name"], - "ir.module.module", - "search", - [("name", "=", module_name), ("state", "=", "installed")], - ) - if module_ids: - self.config.execute( - instance["name"], - "ir.module.module", - "button_upgrade", - [module_ids], - ) - print( - f"Module {module_name} upgraded successfully in {instance['name']}" - ) - else: - print( - f"Module {module_name} not found or not installed in {instance['name']}" - ) - except Exception as e: - print(f"Failed to upgrade {module_name} in {instance['name']}: {e}") - # Continue with other modules - return True - - def restart_service(self, instance_name=None): - """Restart the Odoo service based on the instance type using SSH with private key.""" - for instance in self.config.get_instances(): - if instance_name and instance["name"] != instance_name: - continue - print(f"Restarting service for instance: {instance['name']}") - - service_type = instance.get("type", "systemctl") - host = instance["host"] - service_name = instance.get("service_name", f"odoo_{instance['name']}") - - # Access ssh as a dictionary - ssh_settings = instance.get("ssh", {}) - ssh_user = ssh_settings.get("user", "root") - ssh_key_path = ssh_settings.get("key_path") - # ssh_password = ssh_settings.get("password") # Not used with key - - if service_type == "systemctl": - if host == "localhost" or host == "127.0.0.1": - cmd = f"sudo systemctl restart {service_name}" - else: - if not ssh_key_path: - cmd = f"ssh -t {ssh_user}@{host} 'sudo systemctl restart {service_name}'" - else: - cmd = f"ssh -i {ssh_key_path} {ssh_user}@{host} 'sudo systemctl restart {service_name}'" - elif service_type == "docker": - container_name = instance.get( - "container_name", f"odoo_{instance['name']}" - ) - if host == "localhost" or host == "127.0.0.1": - cmd = f"docker restart {container_name}" - else: - if not ssh_key_path: - raise ValueError( - f"SSH key path not specified for {instance['name']}" - ) - cmd = f"ssh -i {ssh_key_path} {ssh_user}@{host} 'docker restart {container_name}'" - else: - print( - f"Unsupported service type '{service_type}' for {instance['name']}" - ) - continue - - try: - print(f"Executing: {cmd}") - subprocess.run( - cmd, shell=True, check=True, capture_output=True, text=True - ) - print(f"Service restarted successfully for {instance['name']}") - except subprocess.CalledProcessError as e: - print(f"Error restarting service for {instance['name']}: {e}") - raise - - def update_and_restart(self, instance_name=None): - """Combine update, upgrade, and restart.""" - updated = self.update_and_upgrade(instance_name) - if updated: - self.restart_service(instance_name) - return updated diff --git a/services/odoo/module.py b/services/odoo/module.py new file mode 100644 index 0000000..27eec41 --- /dev/null +++ b/services/odoo/module.py @@ -0,0 +1,56 @@ +from services.git.handler import GitHandler +from services.odoo.connection import OdooConnection +import subprocess + + +class OdooModuleManager: + def __init__(self, config_path="config/settings.yaml"): + self.config = OdooConnection(config_path) + # Use OdooConnection instead of Config directly + self.git = GitHandler( + repo_url=self.config.config.get("git", "repo_url"), + local_path=self.config.config.get("git", "local_path"), + branch=self.config.config.get("git", "branch", "main"), + ) + + def update_and_upgrade(self, instance_name=None): + """Update and upgrade multiple modules for the specified instance(s).""" + self.config.connect(instance_name) # Connect to the target instance(s) + for instance in self.config.get_instances(): + if instance_name and instance["name"] != instance_name: + continue + print(f"Processing instance: {instance['name']}") + module_names = instance.get("module_names", []) + + if not module_names: + print(f"No modules specified for {instance['name']}, skipping upgrade.") + continue + + for module_name in module_names: + print(f"Upgrading module: {module_name} in {instance['name']}") + try: + # Use OdooConnection.execute to upgrade the module + module_ids = self.config.execute( + instance["name"], + "ir.module.module", + "search", + [("name", "=", module_name), ("state", "=", "installed")], + ) + if module_ids: + self.config.execute( + instance["name"], + "ir.module.module", + "button_upgrade", + [module_ids], + ) + print( + f"Module {module_name} upgraded successfully in {instance['name']}" + ) + else: + print( + f"Module {module_name} not found or not installed in {instance['name']}" + ) + except Exception as e: + print(f"Failed to upgrade {module_name} in {instance['name']}: {e}") + # Continue with other modules + return True diff --git a/services/odoo/service.py b/services/odoo/service.py new file mode 100644 index 0000000..9583433 --- /dev/null +++ b/services/odoo/service.py @@ -0,0 +1,73 @@ +import subprocess +from services.odoo.connection import OdooConnection + + +class OdooServiceManager: + def __init__(self, config_path="config/settings.yaml"): + self.config = OdooConnection(config_path) + + def _execute_command(self, cmd, instance_name): + """Execute a shell command and handle errors.""" + try: + print(f"Executing: {cmd}") + subprocess.run(cmd, shell=True, check=True, capture_output=True, text=True) + print(f"Service operation successful for {instance_name}") + except subprocess.CalledProcessError as e: + print(f"Error performing service operation for {instance_name}: {e}") + raise + + def _get_command(self, instance, action): + """ + Generate the appropriate command based on instance type and action (stop/restart). + """ + service_type = instance.get("type", "systemctl") + host = instance["host"] + service_name = instance.get("service_name", f"odoo_{instance['name']}") + ssh_settings = instance.get("ssh", {}) + ssh_user = ssh_settings.get("user", "root") + ssh_key_path = ssh_settings.get("key_path") + + local_host = host in ["localhost", "127.0.0.1"] + + if service_type == "systemctl": + cmd = f"sudo systemctl {action} {service_name}" + elif service_type == "docker": + container_name = instance.get("container_name", f"odoo_{instance['name']}") + cmd = f"docker {action} {container_name}" + else: + print(f"Unsupported service type '{service_type}' for {instance['name']}") + return None + + if not local_host: + if not ssh_key_path: + cmd = f"ssh -t {ssh_user}@{host} 'sudo {cmd}'" + else: + cmd = f"ssh -i {ssh_key_path} {ssh_user}@{host} 'sudo {cmd}'" + + return cmd + + def manage_service(self, action, instance_name=None): + """ + Manage the Odoo service (stop or restart) for given instances. + :param action: "stop" or "restart" + :param instance_name: Specific instance name, or None for all instances. + """ + if action not in ["stop", "restart"]: + raise ValueError("Action must be 'stop' or 'restart'.") + + for instance in self.config.get_instances(): + if instance_name and instance["name"] != instance_name: + continue + + print(f"{action.capitalize()}ing service for instance: {instance['name']}") + cmd = self._get_command(instance, action) + if cmd: + self._execute_command(cmd, instance["name"]) + + def stop_service(self, instance_name=None): + """Stop the Odoo service based on the instance type""" + self.manage_service("stop", instance_name) + + def restart_service(self, instance_name=None): + """Restart the Odoo service based on the instance type""" + self.manage_service("restart", instance_name)