Compare commits

...

16 Commits

Author SHA1 Message Date
Stéphan Sainléger
1281517b2f [IMP] avoid directory changes in migration loop
Replace cd into version directories with absolute path execution:

Before:
  cd "${version}.0"
  ./pre_upgrade.sh
  ./upgrade.sh
  ./post_upgrade.sh
  cd ..

After:
  "${SCRIPT_DIR}/${version}.0/pre_upgrade.sh"
  "${SCRIPT_DIR}/${version}.0/upgrade.sh"
  "${SCRIPT_DIR}/${version}.0/post_upgrade.sh"

Benefits:
- No working directory state to track
- More robust: script works regardless of where it's called from
- Easier debugging: no need to remember current directory
- Avoids potential issues if a subscript changes directory
2026-02-02 22:00:31 +01:00
Stéphan Sainléger
7a22648429 [FIX] correct undefined variable FINALE_DB_MODEL_NAME
Replace $FINALE_DB_MODEL_NAME with $FINALE_DB_NAME in the call to
prepare_db.sh.

FINALE_DB_MODEL_NAME was never defined anywhere in the codebase,
causing the script to fail immediately with 'set -u' (unbound variable
error). The intended variable is FINALE_DB_NAME which contains the
target database name (e.g., 'ou16').
2026-02-02 22:00:08 +01:00
Stéphan Sainléger
7b1ce9cc5a [IMP] factor out user confirmation prompts into reusable function
Add confirm_or_exit() function to lib/common.sh to eliminate duplicated
confirmation dialog code in prepare_db.sh.

Before: Two 10-line case statements with identical logic
After: Two single-line function calls

The function provides consistent behavior:
- Displays the question with Y/N options
- Returns 0 on Y/y (continue execution)
- Exits with error on any other input

This follows DRY principle and ensures all confirmation prompts
behave identically across the codebase.
2026-02-02 21:59:47 +01:00
Stéphan Sainléger
c815374cf1 [IMP] use rsync instead of cp for filestore copy
Replace mkdir + rm -rf + cp -a sequence with rsync --delete:

Before (3 commands):
  sudo mkdir -p "$dst_path"
  sudo rm -rf "$dst_path"
  sudo cp -a "$src_path" "$dst_path"

After (2 commands):
  sudo mkdir -p "$(dirname "$dst_path")"
  sudo rsync -a --delete "${src_path}/" "${dst_path}/"

Benefits:
- Incremental copy: only transfers changed files on re-run
- Atomic delete + copy: --delete removes extra files in destination
- Preserves all attributes like cp -a
- Faster for large filestores when re-running migration

Added rsync to required commands check.
2026-02-02 21:59:14 +01:00
Stéphan Sainléger
dd6c41b382 [IMP] combine SQL queries into single transaction with documentation
Merge three separate SQL queries into one for better performance:
- 1 database connection instead of 3
- Atomic execution of all cleanup operations

Added detailed SQL comments explaining each operation:
- DROP SEQUENCE: Why stale sequences prevent Odoo startup
- UPDATE ir_ui_view: Why website templates are reset except pages
- DELETE ir_attachment: Why compiled assets must be purged

Also changed DROP SEQUENCE to DROP SEQUENCE IF EXISTS to avoid
errors if sequences don't exist.
2026-02-02 21:58:46 +01:00
Stéphan Sainléger
0a75c2291b [IMP] simplify migration path construction with seq
Replace manual loop building version array with seq + readarray:

Before (4 lines):
  declare -a versions
  nb_migrations=$((FINAL_VERSION - ORIGIN_VERSION))
  for ((i = 0; i < nb_migrations; i++)); do
      versions[i]=$((ORIGIN_VERSION + 1 + i))
  done

After (1 line):
  readarray -t versions < <(seq $((ORIGIN_VERSION + 1)) "$FINAL_VERSION")

The seq command is purpose-built for generating number sequences,
making the intent clearer and the code more concise.
2026-02-02 21:58:13 +01:00
Stéphan Sainléger
55365df5f9 [IMP] simplify PostgreSQL container detection with readarray
Replace double grep pattern with readarray for cleaner container detection:
- Single grep call instead of two
- Native bash array instead of string manipulation
- Array length check instead of grep -c
- Proper formatting when listing multiple containers

The readarray approach is more idiomatic and avoids edge cases with
empty strings and newline handling.
2026-02-02 21:57:52 +01:00
Stéphan Sainléger
e4d11de009 [IMP] remove redundant SQL query and grep for missing addons
The SQL query already filters on module_origin.state = 'installed',
so the second query to get installed addons and the grep intersection
were completely redundant.

Before: 2 SQL queries + grep + 3 temp files
After: 1 SQL query + variable

This simplifies the code and reduces database round-trips.
2026-02-02 21:57:32 +01:00
Stéphan Sainléger
516b9def3d [IMP] add external command verification at startup
Add check_required_commands() function to verify that all required
external tools are available before the script begins execution:
- docker: Container runtime
- compose: Docker compose wrapper (0k-scripts)
- sudo: Required for filestore operations

Benefits:
- Fails fast with a clear error message listing missing commands
- Prevents cryptic 'command not found' errors mid-execution
- Documents script dependencies explicitly
- Called immediately after argument validation in upgrade.sh
2026-02-02 20:06:27 +01:00
Stéphan Sainléger
20214b4402 [IMP] apply naming conventions for variables
Apply consistent naming conventions throughout upgrade.sh:
- UPPERCASE + readonly for script-level constants (immutable values)
- lowercase for temporary/local variables within the script flow

Constants marked readonly:
- ORIGIN_VERSION, FINAL_VERSION, ORIGIN_DB_NAME, ORIGIN_SERVICE_NAME
- COPY_DB_NAME, FINALE_DB_NAME, FINALE_SERVICE_NAME
- POSTGRES_SERVICE_NAME

Local variables renamed to lowercase:
- postgres_containers, postgres_count (detection phase)
- db_exists, filestore_path (validation phase)

This convention makes it immediately clear which variables are
configuration constants vs runtime values, and prevents accidental
modification of critical values.
2026-02-02 20:06:27 +01:00
Stéphan Sainléger
fa8b1b98f1 [IMP] use mktemp and trap for temporary file cleanup
Replace hardcoded temporary file paths with mktemp -d for secure
temporary directory creation, and add a trap to automatically clean
up on script exit (success, failure, or interruption).

Benefits:
- Automatic cleanup even on Ctrl+C or script errors
- No leftover temporary files in the working directory
- Secure temporary directory creation (proper permissions)
- Files isolated in dedicated temp directory

Added '|| true' to grep command since it returns exit code 1 when
no matches are found, which would trigger set -e otherwise.
2026-02-02 20:06:27 +01:00
Stéphan Sainléger
7c3d5d49c8 [IMP] use heredoc with variable expansion for SQL query
Convert the SQL_404_ADDONS_LIST query from a quoted string to a heredoc
without quotes (<<EOF instead of <<'EOF') to make variable expansion
explicit and consistent with other SQL blocks in the codebase.

Key difference between heredoc variants:
- <<'EOF': Literal content, no variable expansion (use for static SQL)
- <<EOF: Variables like ${FINALE_DB_NAME} are expanded (use when needed)

Also improved SQL formatting for better readability.
2026-02-02 20:06:27 +01:00
Stéphan Sainléger
333360f9f0 [IMP] add structured logging functions
Add logging functions to lib/common.sh for consistent output formatting:
- log_info(): Standard informational messages with [INFO] prefix
- log_warn(): Warning messages to stderr with [WARN] prefix
- log_error(): Error messages to stderr with [ERROR] prefix
- log_step(): Section headers with visual separators

Update upgrade.sh to use these functions throughout, replacing ad-hoc
echo statements. This provides:
- Consistent visual formatting across all scripts
- Clear distinction between info, warnings and errors
- Errors properly sent to stderr
- Easier log parsing and filtering

Also removed redundant '|| exit 1' statements since set -e handles
command failures automatically.
2026-02-02 20:06:27 +01:00
Stéphan Sainléger
4b27955be9 [IMP] centralize common functions in lib/common.sh
Extract shared utility functions into a dedicated library file:
- query_postgres_container: Execute SQL queries in postgres container
- copy_database: Copy database using pgm
- copy_filestore: Copy Odoo filestore directory
- exec_python_script_in_odoo_shell: Run Python scripts in Odoo shell

Benefits:
- Single source of truth for utility functions
- Easier maintenance and testing
- Consistent behavior across all scripts
- Reduced code duplication

Also introduces readonly constants DATASTORE_PATH and FILESTORE_SUBPATH
to avoid hardcoded paths scattered throughout the codebase.
2026-02-02 20:06:27 +01:00
Stéphan Sainléger
a91afa60d3 [IMP] fix undefined variable DB_CONTAINER_NAME
Replace $DB_CONTAINER_NAME with $POSTGRES_SERVICE_NAME which is the
correct variable exported from the parent script (upgrade.sh).

DB_CONTAINER_NAME was never defined, causing the script to fail
immediately with 'set -u' enabled (unbound variable error). The
intended variable is POSTGRES_SERVICE_NAME which contains the name
of the PostgreSQL container detected at runtime.
2026-02-02 20:06:27 +01:00
Stéphan Sainléger
a1508daf24 [IMP] fix return statement outside function
Replace 'return 1' with 'exit 1' in prepare_db.sh.

The 'return' statement is only valid inside functions. When used at
the script's top level, it behaves unpredictably - in some shells it
exits the script, in others it's an error. Using 'exit 1' explicitly
terminates the script with an error status, which is the intended
behavior when the PostgreSQL container is not running.
2026-02-02 20:06:27 +01:00
4 changed files with 215 additions and 264 deletions

View File

@@ -4,42 +4,41 @@ set -euo pipefail
DB_NAME="$1" DB_NAME="$1"
ODOO_SERVICE="$2" ODOO_SERVICE="$2"
FINALE_SQL=$(cat <<'EOF' echo "Running SQL cleanup..."
/*Delete sequences that prevent Odoo to start*/ CLEANUP_SQL=$(cat <<'EOF'
drop sequence base_registry_signaling; -- Drop sequences that prevent Odoo from starting.
drop sequence base_cache_signaling; -- These sequences are recreated by Odoo on startup but stale values
EOF -- from the old version can cause conflicts.
) DROP SEQUENCE IF EXISTS base_registry_signaling;
query_postgres_container "$FINALE_SQL" "$DB_NAME" || exit 1 DROP SEQUENCE IF EXISTS base_cache_signaling;
# Fix duplicated views -- Reset website templates to their original state.
PYTHON_SCRIPT=post_migration_fix_duplicated_views.py -- Views with arch_fs (file source) that have been customized (arch_db not null)
echo "Remove duplicated views with script $PYTHON_SCRIPT ..." -- are reset to use the file version, EXCEPT for actual website pages which
exec_python_script_in_odoo_shell "$DB_NAME" "$DB_NAME" "$PYTHON_SCRIPT" || exit 1 -- contain user content that must be preserved.
# Reset all website templates with custom content
FINALE_SQL_2=$(cat <<'EOF'
UPDATE ir_ui_view UPDATE ir_ui_view
SET arch_db = NULL SET arch_db = NULL
WHERE arch_fs IS NOT NULL WHERE arch_fs IS NOT NULL
AND arch_fs LIKE 'website/%' AND arch_fs LIKE 'website/%'
AND arch_db IS NOT NULL AND arch_db IS NOT NULL
AND id NOT IN (SELECT view_id FROM website_page); AND id NOT IN (SELECT view_id FROM website_page);
EOF
)
query_postgres_container "$FINALE_SQL_2" "$DB_NAME" || exit 1
# Purge QWeb cache from compiled assets -- Purge compiled frontend assets (CSS/JS bundles).
FINALE_SQL_3=$(cat <<'EOF' -- These cached files reference old asset versions and must be regenerated
-- by Odoo after migration to avoid broken stylesheets and scripts.
DELETE FROM ir_attachment DELETE FROM ir_attachment
WHERE name LIKE '/web/assets/%' WHERE name LIKE '/web/assets/%'
OR name LIKE '%.assets_%' OR name LIKE '%.assets_%'
OR (res_model = 'ir.ui.view' AND mimetype = 'text/css'); OR (res_model = 'ir.ui.view' AND mimetype = 'text/css');
EOF EOF
) )
query_postgres_container "$FINALE_SQL_3" "$DB_NAME" || exit 1 query_postgres_container "$CLEANUP_SQL" "$DB_NAME"
# Uninstall obsolette add-ons PYTHON_SCRIPT=post_migration_fix_duplicated_views.py
echo "Remove duplicated views with script $PYTHON_SCRIPT ..."
exec_python_script_in_odoo_shell "$DB_NAME" "$DB_NAME" "$PYTHON_SCRIPT"
# Uninstall obsolete add-ons
PYTHON_SCRIPT=post_migration_cleanup_obsolete_modules.py PYTHON_SCRIPT=post_migration_cleanup_obsolete_modules.py
echo "Uninstall obsolete add-ons with script $PYTHON_SCRIPT ..." echo "Uninstall obsolete add-ons with script $PYTHON_SCRIPT ..."
exec_python_script_in_odoo_shell "$DB_NAME" "$DB_NAME" "$PYTHON_SCRIPT" || exit 1 exec_python_script_in_odoo_shell "$DB_NAME" "$DB_NAME" "$PYTHON_SCRIPT" || exit 1

96
lib/common.sh Normal file
View File

@@ -0,0 +1,96 @@
#!/bin/bash
#
# Common functions for Odoo migration scripts
# Source this file from other scripts: source "$(dirname "$0")/lib/common.sh"
#
set -euo pipefail
readonly DATASTORE_PATH="/srv/datastore/data"
readonly FILESTORE_SUBPATH="var/lib/odoo/filestore"
check_required_commands() {
local missing=()
for cmd in docker compose sudo rsync; do
if ! command -v "$cmd" &>/dev/null; then
missing+=("$cmd")
fi
done
if [[ ${#missing[@]} -gt 0 ]]; then
log_error "Required commands not found: ${missing[*]}"
log_error "Please install them before running this script."
exit 1
fi
}
log_info() { printf "[INFO] %s\n" "$*"; }
log_warn() { printf "[WARN] %s\n" "$*" >&2; }
log_error() { printf "[ERROR] %s\n" "$*" >&2; }
log_step() { printf "\n===== %s =====\n" "$*"; }
confirm_or_exit() {
local message="$1"
local choice
echo ""
echo "$message"
echo "Y - Yes, continue"
echo "N - No, cancel"
read -r -n 1 -p "Your choice: " choice
echo ""
case "$choice" in
[Yy]) return 0 ;;
*) log_error "Cancelled by user."; exit 1 ;;
esac
}
query_postgres_container() {
local query="$1"
local db_name="$2"
if [[ -z "$query" ]]; then
return 0
fi
local result
if ! result=$(docker exec -u 70 "$POSTGRES_SERVICE_NAME" psql -d "$db_name" -t -A -c "$query"); then
printf "Failed to execute SQL query: %s\n" "$query" >&2
printf "Error: %s\n" "$result" >&2
return 1
fi
echo "$result"
}
copy_database() {
local from_db="$1"
local to_service="$2"
local to_db="$3"
docker exec -u 70 "$POSTGRES_SERVICE_NAME" pgm cp -f "$from_db" "${to_db}@${to_service}"
}
copy_filestore() {
local from_service="$1"
local from_db="$2"
local to_service="$3"
local to_db="$4"
local src_path="${DATASTORE_PATH}/${from_service}/${FILESTORE_SUBPATH}/${from_db}"
local dst_path="${DATASTORE_PATH}/${to_service}/${FILESTORE_SUBPATH}/${to_db}"
sudo mkdir -p "$(dirname "$dst_path")"
sudo rsync -a --delete "${src_path}/" "${dst_path}/"
echo "Filestore ${from_service}/${from_db} copied to ${to_service}/${to_db}."
}
exec_python_script_in_odoo_shell() {
local service_name="$1"
local db_name="$2"
local python_script="$3"
compose --debug run "$service_name" shell -d "$db_name" --no-http --stop-after-init < "$python_script"
}
export DATASTORE_PATH FILESTORE_SUBPATH
export -f log_info log_warn log_error log_step confirm_or_exit
export -f check_required_commands
export -f query_postgres_container copy_database copy_filestore exec_python_script_in_odoo_shell

View File

@@ -1,18 +1,20 @@
#!/bin/bash #!/bin/bash
set -euo pipefail set -euo pipefail
# Global variables
ODOO_SERVICE="$1" ODOO_SERVICE="$1"
DB_NAME="$2" DB_NAME="$2"
DB_FINALE_MODEL="$3" DB_FINALE_MODEL="$3"
DB_FINALE_SERVICE="$4" DB_FINALE_SERVICE="$4"
TMPDIR=$(mktemp -d)
trap 'rm -rf "$TMPDIR"' EXIT
echo "Start database preparation" echo "Start database preparation"
# Check POSTGRES container is running # Check POSTGRES container is running
if ! docker ps | grep -q "$DB_CONTAINER_NAME"; then if ! docker ps | grep -q "$POSTGRES_SERVICE_NAME"; then
printf "Docker container %s is not running.\n" "$DB_CONTAINER_NAME" >&2 printf "Docker container %s is not running.\n" "$POSTGRES_SERVICE_NAME" >&2
return 1 exit 1
fi fi
EXT_EXISTS=$(query_postgres_container "SELECT 1 FROM pg_extension WHERE extname = 'dblink'" "$DB_NAME") || exit 1 EXT_EXISTS=$(query_postgres_container "SELECT 1 FROM pg_extension WHERE extname = 'dblink'" "$DB_NAME") || exit 1
@@ -40,71 +42,32 @@ echo "Base neutralized..."
## List add-ons not in final version ## ## List add-ons not in final version ##
####################################### #######################################
# Retrieve add-ons not available on the final Odoo version SQL_MISSING_ADDONS=$(cat <<EOF
SQL_404_ADDONS_LIST=" SELECT module_origin.name
SELECT module_origin.name FROM ir_module_module module_origin
FROM ir_module_module module_origin LEFT JOIN (
LEFT JOIN ( SELECT *
SELECT * FROM dblink('dbname=${FINALE_DB_NAME}','SELECT name, shortdesc, author FROM ir_module_module')
FROM dblink('dbname=$FINALE_DB_NAME','SELECT name, shortdesc, author FROM ir_module_module') AS tb2(name text, shortdesc text, author text)
AS tb2(name text, shortdesc text, author text) ) AS module_dest ON module_dest.name = module_origin.name
) AS module_dest ON module_dest.name = module_origin.name WHERE (module_dest.name IS NULL)
AND (module_origin.state = 'installed')
AND (module_origin.author NOT IN ('Odoo S.A.', 'Lokavaluto', 'Elabore'))
ORDER BY module_origin.name;
EOF
)
echo "Retrieve missing addons..."
missing_addons=$(query_postgres_container "$SQL_MISSING_ADDONS" "$DB_NAME")
WHERE (module_dest.name IS NULL) AND (module_origin.state = 'installed') AND (module_origin.author NOT IN ('Odoo S.A.', 'Lokavaluto', 'Elabore')) log_step "ADD-ONS CHECK"
ORDER BY module_origin.name echo "Installed add-ons not available in final Odoo version:"
; echo "$missing_addons"
" confirm_or_exit "Do you accept to migrate with these add-ons still installed?"
echo "Retrieve 404 addons... "
echo "SQL REQUEST = $SQL_404_ADDONS_LIST"
query_postgres_container "$SQL_404_ADDONS_LIST" "$DB_NAME" > 404_addons || exit 1
# Keep only the installed add-ons
INSTALLED_ADDONS="SELECT name FROM ir_module_module WHERE state='installed';"
query_postgres_container "$INSTALLED_ADDONS" "$DB_NAME" > installed_addons || exit 1
grep -Fx -f 404_addons installed_addons > final_404_addons
rm -f 404_addons installed_addons
# Ask confirmation to uninstall the selected add-ons
echo "
==== ADD-ONS CHECK ====
Installed add-ons not available in final Odoo version:
"
cat final_404_addons
echo "
Do you accept to migrate the database with all these add-ons still installed? (Y/N/R)"
echo "Y - Yes, let's go on with the upgrade."
echo "N - No, stop the upgrade"
read -n 1 -p "Your choice: " choice
case "$choice" in
[Yy] ) echo "
Let's go on!";;
[Nn] ) echo "
Upgrade cancelled!"; exit 1;;
* ) echo "
Please answer by Y or N.";;
esac
# Check the views
PYTHON_SCRIPT=pre_migration_view_checking.py PYTHON_SCRIPT=pre_migration_view_checking.py
echo "Check views with script $PYTHON_SCRIPT ..." echo "Check views with script $PYTHON_SCRIPT ..."
exec_python_script_in_odoo_shell "$DB_NAME" "$DB_NAME" "$PYTHON_SCRIPT" || exit 1 exec_python_script_in_odoo_shell "$DB_NAME" "$DB_NAME" "$PYTHON_SCRIPT"
echo " confirm_or_exit "Do you accept to migrate with the current views state?"
Do you accept to migrate the database with the current views states? (Y/N/R)"
echo "Y - Yes, let's go on with the upgrade."
echo "N - No, stop the upgrade"
read -n 1 -p "Your choice: " choice
case "$choice" in
[Yy] ) echo "
Upgrade confirmed!";;
[Nn] ) echo "
Upgrade cancelled!"; exit 1;;
* ) echo "
Please answer by Y or N.";;
esac
echo "Database successfully prepared!" echo "Database successfully prepared!"

View File

@@ -1,6 +1,9 @@
#!/bin/bash #!/bin/bash
set -euo pipefail set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "${SCRIPT_DIR}/lib/common.sh"
#################### ####################
# USAGE & ARGUMENTS # USAGE & ARGUMENTS
#################### ####################
@@ -22,225 +25,115 @@ EOF
} }
if [[ $# -lt 4 ]]; then if [[ $# -lt 4 ]]; then
echo "ERROR: Missing arguments. Expected 4, got $#." >&2 log_error "Missing arguments. Expected 4, got $#."
usage usage
fi fi
#################### check_required_commands
# GLOBAL VARIABLES #
####################
ORIGIN_VERSION="$1" # "12" for version 12.0 readonly ORIGIN_VERSION="$1"
FINAL_VERSION="$2" # "16" for version 16.0 readonly FINAL_VERSION="$2"
# Path to the database to migrate. Must be a .zip file with the following syntax: {DATABASE_NAME}.zip readonly ORIGIN_DB_NAME="$3"
ORIGIN_DB_NAME="$3" readonly ORIGIN_SERVICE_NAME="$4"
ORIGIN_SERVICE_NAME="$4"
# Get origin database name readonly COPY_DB_NAME="ou${ORIGIN_VERSION}"
COPY_DB_NAME="ou${ORIGIN_VERSION}"
# Define finale database name
export FINALE_DB_NAME="ou${FINAL_VERSION}" export FINALE_DB_NAME="ou${FINAL_VERSION}"
# Define finale odoo service name readonly FINALE_DB_NAME
FINALE_SERVICE_NAME="${FINALE_DB_NAME}" readonly FINALE_SERVICE_NAME="${FINALE_DB_NAME}"
# Service postgres name (dynamically retrieved from running containers) readarray -t postgres_containers < <(docker ps --format '{{.Names}}' | grep postgres || true)
POSTGRES_CONTAINERS=$(docker ps --format '{{.Names}}' | grep postgres)
POSTGRES_COUNT=$(echo "$POSTGRES_CONTAINERS" | grep -c .)
if [[ "$POSTGRES_COUNT" -eq 0 ]]; then if [[ ${#postgres_containers[@]} -eq 0 ]]; then
echo "ERROR: No running PostgreSQL container found. Please start a PostgreSQL container and try again." >&2 log_error "No running PostgreSQL container found. Please start a PostgreSQL container and try again."
exit 1 exit 1
elif [[ "$POSTGRES_COUNT" -gt 1 ]]; then elif [[ ${#postgres_containers[@]} -gt 1 ]]; then
echo "ERROR: Multiple PostgreSQL containers found:" >&2 log_error "Multiple PostgreSQL containers found:"
echo "$POSTGRES_CONTAINERS" >&2 printf ' %s\n' "${postgres_containers[@]}" >&2
echo "Please ensure only one PostgreSQL container is running." >&2 log_error "Please ensure only one PostgreSQL container is running."
exit 1 exit 1
fi fi
export POSTGRES_SERVICE_NAME="$POSTGRES_CONTAINERS" export POSTGRES_SERVICE_NAME="${postgres_containers[0]}"
readonly POSTGRES_SERVICE_NAME
############################################# log_step "INPUT PARAMETERS"
# DISPLAYS ALL INPUTS PARAMETERS log_info "Origin version .......... $ORIGIN_VERSION"
############################################# log_info "Final version ........... $FINAL_VERSION"
log_info "Origin DB name ........... $ORIGIN_DB_NAME"
log_info "Origin service name ..... $ORIGIN_SERVICE_NAME"
echo "===== INPUT PARAMETERS =====" log_step "COMPUTED GLOBAL VARIABLES"
echo "Origin version .......... $ORIGIN_VERSION" log_info "Copy DB name ............. $COPY_DB_NAME"
echo "Final version ........... $FINAL_VERSION" log_info "Finale DB name ........... $FINALE_DB_NAME"
echo "Origin DB name ........... $ORIGIN_DB_NAME" log_info "Finale service name ...... $FINALE_SERVICE_NAME"
echo "Origin service name ..... $ORIGIN_SERVICE_NAME" log_info "Postgres service name .... $POSTGRES_SERVICE_NAME"
echo "
===== COMPUTED GLOBALE VARIABLES ====="
echo "Copy DB name ............. $COPY_DB_NAME"
echo "Finale DB name ........... $FINALE_DB_NAME"
echo "Finale service name ...... $FINALE_SERVICE_NAME"
echo "Postgres service name .... $POSTGRES_SERVICE_NAME"
# Function to launch an SQL request to the postgres container log_step "CHECKS ALL NEEDED COMPONENTS ARE AVAILABLE"
query_postgres_container(){
local QUERY="$1"
local DB_NAME="$2"
if [[ -z "$QUERY" ]]; then
return 0
fi
local result
if ! result=$(docker exec -u 70 "$POSTGRES_SERVICE_NAME" psql -d "$DB_NAME" -t -A -c "$QUERY"); then
printf "Failed to execute SQL query: %s\n" "$query" >&2
printf "Error: %s\n" "$result" >&2
exit 1
fi
echo "$result"
}
export -f query_postgres_container
# Function to copy the postgres databases db_exists=$(docker exec -it -u 70 "$POSTGRES_SERVICE_NAME" psql -tc "SELECT 1 FROM pg_database WHERE datname = '$ORIGIN_DB_NAME'" | tr -d '[:space:]')
copy_database(){ if [[ "$db_exists" ]]; then
local FROM_DB="$1" log_info "Database '$ORIGIN_DB_NAME' found."
local TO_SERVICE="$2"
local TO_DB="$3"
docker exec -u 70 "$POSTGRES_SERVICE_NAME" pgm cp -f "$FROM_DB" "$TO_DB"@"$TO_SERVICE"
}
export -f copy_database
# Function to copy the filetores
copy_filestore(){
local FROM_SERVICE="$1"
local FROM_DB="$2"
local TO_SERVICE="$3"
local TO_DB="$4"
sudo mkdir -p /srv/datastore/data/"$TO_SERVICE"/var/lib/odoo/filestore/"$TO_DB" || exit 1
sudo rm -rf /srv/datastore/data/"$TO_SERVICE"/var/lib/odoo/filestore/"$TO_DB" || exit 1
sudo cp -a /srv/datastore/data/"$FROM_SERVICE"/var/lib/odoo/filestore/"$FROM_DB" /srv/datastore/data/"$TO_SERVICE"/var/lib/odoo/filestore/"$TO_DB" || exit 1
echo "Filestore $FROM_SERVICE/$FROM_DB copied."
}
export -f copy_filestore
# Function to launch python scripts in Odoo Shell
exec_python_script_in_odoo_shell(){
local SERVICE_NAME="$1"
local DB_NAME="$2"
local PYTHON_SCRIPT="$3"
compose --debug run "$SERVICE_NAME" shell -d "$DB_NAME" --no-http --stop-after-init < "$PYTHON_SCRIPT"
}
export -f exec_python_script_in_odoo_shell
##############################################
# CHECKS ALL NEEDED COMPONENTS ARE AVAILABLE #
##############################################
echo "
==== CHECKS ALL NEEDED COMPONENTS ARE AVAILABLE ===="
# Check origin database is in the local postgres
DB_EXISTS=$(docker exec -it -u 70 "$POSTGRES_SERVICE_NAME" psql -tc "SELECT 1 FROM pg_database WHERE datname = '$ORIGIN_DB_NAME'" | tr -d '[:space:]')
if [[ "$DB_EXISTS" ]]; then
echo "UPGRADE: Database '$ORIGIN_DB_NAME' found."
else else
echo "ERROR: Database '$ORIGIN_DB_NAME' not found in the local postgress service. Please add it and restart the upgrade process." log_error "Database '$ORIGIN_DB_NAME' not found in the local postgres service. Please add it and restart the upgrade process."
exit 1 exit 1
fi fi
# Check that the origin filestore exist filestore_path="${DATASTORE_PATH}/${ORIGIN_SERVICE_NAME}/${FILESTORE_SUBPATH}/${ORIGIN_DB_NAME}"
REPERTOIRE="/srv/datastore/data/${ORIGIN_SERVICE_NAME}/var/lib/odoo/filestore/${ORIGIN_DB_NAME}" if [[ -d "$filestore_path" ]]; then
if [[ -d "$REPERTOIRE" ]]; then log_info "Filestore '$filestore_path' found."
echo "UPGRADE: '$REPERTOIRE' filestore found."
else else
echo "ERROR: '$REPERTOIRE' filestore not found, please add it and restart the upgrade process." log_error "Filestore '$filestore_path' not found, please add it and restart the upgrade process."
exit 1 exit 1
fi fi
####################################### log_step "LAUNCH VIRGIN ODOO IN FINAL VERSION"
# LAUNCH VIRGIN ODOO IN FINAL VERSION #
#######################################
# Remove finale database and datastore if already exists (we need a virgin Odoo)
if docker exec -u 70 "$POSTGRES_SERVICE_NAME" pgm ls | grep -q "$FINALE_SERVICE_NAME"; then if docker exec -u 70 "$POSTGRES_SERVICE_NAME" pgm ls | grep -q "$FINALE_SERVICE_NAME"; then
log_info "Removing existing finale database and filestore..."
docker exec -u 70 "$POSTGRES_SERVICE_NAME" pgm rm -f "$FINALE_SERVICE_NAME" docker exec -u 70 "$POSTGRES_SERVICE_NAME" pgm rm -f "$FINALE_SERVICE_NAME"
sudo rm -rf /srv/datastore/data/"$FINALE_SERVICE_NAME"/var/lib/odoo/filestore/"$FINALE_SERVICE_NAME" sudo rm -rf "${DATASTORE_PATH}/${FINALE_SERVICE_NAME}/${FILESTORE_SUBPATH}/${FINALE_SERVICE_NAME}"
fi fi
compose --debug run "$FINALE_SERVICE_NAME" -i base --stop-after-init --no-http compose --debug run "$FINALE_SERVICE_NAME" -i base --stop-after-init --no-http
echo "Model database in final Odoo version created." log_info "Model database in final Odoo version created."
############################ log_step "COPY ORIGINAL COMPONENTS"
# COPY ORIGINAL COMPONENTS #
############################
echo " copy_database "$ORIGIN_DB_NAME" "$COPY_DB_NAME" "$COPY_DB_NAME"
==== COPY ORIGINAL COMPONENTS ====" log_info "Original database copied to ${COPY_DB_NAME}@${COPY_DB_NAME}."
echo "UPGRADE: Start copy"
# Copy database copy_filestore "$ORIGIN_SERVICE_NAME" "$ORIGIN_DB_NAME" "$COPY_DB_NAME" "$COPY_DB_NAME"
copy_database "$ORIGIN_DB_NAME" "$COPY_DB_NAME" "$COPY_DB_NAME" || exit 1 log_info "Original filestore copied."
echo "UPGRADE: original database copied in ${COPY_DB_NAME}@${COPY_DB_NAME}."
# Copy filestore
copy_filestore "$ORIGIN_SERVICE_NAME" "$ORIGIN_DB_NAME" "$COPY_DB_NAME" "$COPY_DB_NAME" || exit 1
echo "UPGRADE: original filestore copied."
##################### log_step "PATH OF MIGRATION"
# PATH OF MIGRATION #
####################
echo " readarray -t versions < <(seq $((ORIGIN_VERSION + 1)) "$FINAL_VERSION")
==== PATH OF MIGRATION ====" log_info "Migration path is ${versions[*]}"
# List all the versions to migrate through
declare -a versions
nb_migrations=$((FINAL_VERSION - ORIGIN_VERSION))
# Build the migration path
for ((i = 0; i < nb_migrations; i++)); do log_step "DATABASE PREPARATION"
versions[i]=$((ORIGIN_VERSION + 1 + i))
./prepare_db.sh "$COPY_DB_NAME" "$COPY_DB_NAME" "$FINALE_DB_NAME" "$FINALE_SERVICE_NAME"
log_step "UPGRADE PROCESS"
for version in "${versions[@]}"; do
log_info "START UPGRADE TO ${version}.0"
"${SCRIPT_DIR}/${version}.0/pre_upgrade.sh"
"${SCRIPT_DIR}/${version}.0/upgrade.sh"
"${SCRIPT_DIR}/${version}.0/post_upgrade.sh"
log_info "END UPGRADE TO ${version}.0"
done done
echo "UPGRADE: Migration path is ${versions[@]}"
log_step "POST-UPGRADE PROCESSES"
######################## ./finalize_db.sh "$FINALE_DB_NAME" "$FINALE_SERVICE_NAME"
# DATABASE PREPARATION #
########################
echo " log_step "UPGRADE PROCESS ENDED WITH SUCCESS"
==== DATABASE PREPARATION ===="
./prepare_db.sh "$COPY_DB_NAME" "$COPY_DB_NAME" "$FINALE_DB_MODEL_NAME" "$FINALE_SERVICE_NAME" || exit 1
###################
# UPGRADE PROCESS #
###################
for version in "${versions[@]}"
do
echo "START UPGRADE TO ${version}.0"
start_version=$((version-1))
end_version="$version"
### Go to the repository holding the upgrate scripts
cd "${end_version}.0"
### Execute pre_upgrade scripts
./pre_upgrade.sh || exit 1
### Start upgrade
./upgrade.sh || exit 1
### Execute post-upgrade scripts
./post_upgrade.sh || exit 1
### Return to parent repository for the following steps
cd ..
echo "END UPGRADE TO ${version}.0"
done
## END UPGRADE LOOP
##########################
# POST-UPGRADE PROCESSES #
##########################
./finalize_db.sh "$FINALE_DB_NAME" "$FINALE_SERVICE_NAME" || exit 1
echo "UPGRADE PROCESS ENDED WITH SUCCESS"