Introduce a new `generate-module-report` command to generate report templates for Odoo modules. Extract common variable export logic into reusable `export-module-vars` and `export-module-inherit-vars` functions to reduce duplication across generate-module-* commands. Add `model_templates.xml` template for report generation. Update help table to reflect the new command order and include the report command.
3068 lines
101 KiB
Bash
Executable File
3068 lines
101 KiB
Bash
Executable File
#!/bin/bash
|
|
cd "$(dirname "$0")" || exit
|
|
# Commands such as git pull return exit code if there are no changes
|
|
# set -e
|
|
|
|
if [[ -a ".env" ]]; then
|
|
source .env
|
|
fi
|
|
|
|
help-table() {
|
|
local -a rows
|
|
local max_cmd=7 max_opt=6 max_desc=11
|
|
|
|
rows+=("Command|Option|Description")
|
|
rows+=("<<<ENV>>>| | ")
|
|
rows+=("backup-env-files|[path]|Archive and copy ,env files to target location.")
|
|
rows+=("copy-dotenv|[env][env]|Copy .env file.")
|
|
rows+=("create-dotenv|[env][type]|Create .env file of type nextcloud, odoo, upgrade.")
|
|
rows+=("edit-dotenv|[env]|Open .env file in default editor.")
|
|
rows+=("list-dotenv||List .env files.")
|
|
rows+=("load-dotenv||Restore content of .env from pass entry.")
|
|
rows+=("load-dotenv|[env]|Load and export .env file.")
|
|
rows+=("remove-dotenv|[env]|Remove environment config.")
|
|
rows+=("rename-dotenv|[env][env]|Rename .env file.")
|
|
rows+=("restore-dotenv-files|[path]|Extract and copy .env files from backup file.")
|
|
rows+=("save-dotenv||Store content of .env in pass entry.")
|
|
rows+=("show-dotenv|[env]|Output content of the .env file.")
|
|
rows+=("<<<GIT>>>| | ")
|
|
rows+=("aggregate-git-folders|[path]|Run gitaggregate for the repos.yaml.")
|
|
rows+=("add-git-folder|[branch][url][path]|Add git folder to .gitmodules and .gitmodule.csv.")
|
|
rows+=("checkout|[version]|Checkout Odoo version.")
|
|
rows+=("checkout-git-folder|[branch][path]|Checkout git commit from .gitmodules.csv.")
|
|
rows+=("commit-git-folder|[message][path]|Commit all changes in path.")
|
|
rows+=("clean-git-folder|[path]|Clean git folder.")
|
|
rows+=("clone-git-folder|[branch][path][version]|Clone git folder listed in the .gitmodules file.")
|
|
rows+=("download-git-folder|[grep]|Download git folder listed in the .gitmodules file.")
|
|
rows+=("status-git-folder|[path]|Show status for git folder in path.")
|
|
rows+=("list-git-folder|[path]|List path and url of git folders.")
|
|
rows+=("pull-git-folder|[branch][path]|Pull all git folders listed in the .gitmodules file.")
|
|
rows+=("push-git-folder||Push all git folders in path.")
|
|
rows+=("remove-git-folder|[path]|Remove git folders in path.")
|
|
rows+=("ls-git-folder|[grep]|List git folders path space separated.")
|
|
rows+=("reset-git-folder|[path]|Abort rebase and reset git folder.")
|
|
rows+=("stage-git-folder|[path]|Stage all files in git folders in path.")
|
|
rows+=("switch-git-folder|[branch][path]|Switch branch for all git folders from the .gitmodules file.")
|
|
rows+=("sync-git-folder|[branch][path]|Switch, stash and pull all git folders.")
|
|
rows+=("<<<DOCKER>>>| | ")
|
|
rows+=("archive-docker-tags||Archive Docker image tags on hub older than 1 year.")
|
|
rows+=("build|[folder][output]|Build Docker image from target folder.")
|
|
rows+=("login-docker|[user][token]|Setup Docker Hub login credentials.")
|
|
rows+=("login-podman|[user][token]|Login into registry with podman.")
|
|
rows+=("container-ps||List container processes.")
|
|
rows+=("push|[folder]|Publish Docker image from target folder.")
|
|
rows+=("remove|[name]|Remove containers and volumes.")
|
|
rows+=("test-image|[folder]|Test Docker image from target folder.")
|
|
rows+=("test-odoo-version-regex||Test the Odoo module version regex filter.")
|
|
rows+=("<<<VUEPRESS>>>| | ")
|
|
rows+=("build-vuepress||Create Vuepress build.")
|
|
rows+=("install-vuepress||Install Node build dependencies.")
|
|
rows+=("dev-vuepress||Start Vuepress development server.")
|
|
rows+=("serve-vuepress||Serve Vuepress build.")
|
|
rows+=("<<<DATABASE>>>| | ")
|
|
rows+=("change-uuid|[env]|Change database uuid via xmlrpc.")
|
|
rows+=("clear-assets|[db]|Clear all assets of Odoo database.")
|
|
rows+=("clear-filestore|[db]|Clear local filestore folder.")
|
|
rows+=("clear-views|[db]|Clear all views of Odoo database.")
|
|
rows+=("cloc-odoo|[db]|Count custom line of codes.")
|
|
rows+=("drop-db|[db]|Drop target Odoo database.")
|
|
rows+=("init-db|[db]|Initialize the Odoo database.")
|
|
rows+=("load-language|[db][lang]|Install language package in Odoo db.")
|
|
rows+=("patch-database|[db][path]|Apply sql file to database.")
|
|
rows+=("reset-views|[db][key]|Execute hard reset on views matching keys.")
|
|
rows+=("set-admin|[db]|Sets the password for the first user in database.")
|
|
rows+=("update-module|[db][name,path]|Update target Odoo module.")
|
|
rows+=("update-module-list|[db]|Update module list of Odoo database.")
|
|
rows+=("<<<REVISION>>>| | ")
|
|
rows+=("checkout-latest-revision|[version]|Checkout the latest revision of the Odoo version.")
|
|
rows+=("checkout-revision|[revision]|Load Odoo revision env var and checkout git folders.")
|
|
rows+=("commit-and-push-revision|[revision]|Commit all changes and tag with current revision.")
|
|
rows+=("create-revision|[revision]|Create new Odoo revision.")
|
|
rows+=("list-revision||List available Odoo revisions.")
|
|
rows+=("load-latest-revision|[version]|Load the latest revision of the Odoo version.")
|
|
rows+=("load-revision|[revision]|Load env var from specified revision.")
|
|
rows+=("show-revision|[revision]|Show references of Odoo revision.")
|
|
rows+=("<<<LLM>>>| | ")
|
|
rows+=("commit-with-llm||Commit with llm generated commit message.")
|
|
rows+=("update-with-llm|[glob][prompt]|Feed module files with prompt to LLM and apply file changes.")
|
|
rows+=("<<<SNIPPETS>>>| | ")
|
|
rows+=("create-snippet|[xml-id]|Create snippet from template.")
|
|
rows+=("install-snippet|[env][path]|Install snippet definition.")
|
|
rows+=("disable-snippet|[env][path]|Disable snippet by path or xml-id.")
|
|
rows+=("enable-snippet|[env][path]|Enable snippet by path or xml-id.")
|
|
rows+=("lint-snippets||Run checks for all snippets.")
|
|
rows+=("remove-snippet|[env][path]|Remove snippet by path or xml-id.")
|
|
rows+=("update-snippet|[env][path]|Update snippet definition.")
|
|
rows+=("<<<INSTALL>>>| | ")
|
|
rows+=("activate-venv||Activate virtualenv.")
|
|
rows+=("init-venv||Initialize python virtual env.")
|
|
rows+=("disable-mailserver|[env]|Disable mail server settings via xmlrpc.")
|
|
rows+=("install||Install Odoo requirements in source folder.")
|
|
rows+=("install-ansible-build-scripts|[role]|Install scripts of the specified Ansbile role.")
|
|
rows+=("install-requirements|[db][path]|Install python packages from requirements.txt.")
|
|
rows+=("list-packages||List installed python packages.")
|
|
rows+=("init-module|[db][path,module]|Initialize Odoo module.")
|
|
rows+=("set-addons-path||Set Odoo addons path env variable.")
|
|
rows+=("<<<MIGRATION>>>| | ")
|
|
rows+=("export-website-data|[env]|Export website data from Odoo database.")
|
|
rows+=("import-csv|[db][path]|Import data from csv. Filename is PostgreSQL table name.")
|
|
rows+=("import-website-data|[env]|Import website data to Odoo database.")
|
|
rows+=("<<<GENERATE>>>| | ")
|
|
rows+=("create-module|[path]|Create new Odoo module from template.")
|
|
rows+=("generate-module-docs|[path]|Generate readme file for module with OCA tools.")
|
|
rows+=("generate-module-model|[path][model]|Generate model in module folder.")
|
|
rows+=("generate-module-inherit|[path][model]|Generate inherited model in module folder.")
|
|
rows+=("generate-module-views|[path][model]|Generate model views in module folder.")
|
|
rows+=("generate-module-security|[path][model]|Generate model access file.")
|
|
rows+=("generate-module-migration|[path]|Generate migration script for currentmodule version.")
|
|
rows+=("generate-module-snippet|[path][ref][model]|Generate snippet for referefenced view.")
|
|
rows+=("generate-module-report|[path][model]|Generate report for a model in module folder.")
|
|
rows+=("generate-module-repo|[path]|Initialize Odoo module repo from template.")
|
|
rows+=("generate-module-wizard|[path][model]|Generate wizard for a model in module folder.")
|
|
rows+=("<<<MODULE>>>| | ")
|
|
rows+=("list-modules|[path]|Get modules in path as bash array.")
|
|
rows+=("pytest-module|[path]|Run module tests with pytest.")
|
|
rows+=("release-module|[path]|Create GitHub release for a module.")
|
|
rows+=("remove-module|[db][name]|Remove target Odoo module.")
|
|
rows+=("test-module|[path]|Test target Odoo module.")
|
|
rows+=("test-modules|[path]|Test target Odoo modules in repo folder.")
|
|
rows+=("translate-module|[path][lang][db]|Generate translation for Odoo module.")
|
|
rows+=("upload-module|[env][path]|Zip and upload Odoo module.")
|
|
rows+=("visualize-dependencies|[path]|Generate visualizations of module dependencies.")
|
|
rows+=("zip-module|[path]|Create zip file for module.")
|
|
rows+=("<<<HELPER>>>| | ")
|
|
rows+=("generate-admin-password|[pass]|Generate hash for Odoo master password.")
|
|
rows+=("generate-ssh-keys||Generate ssh key pair.")
|
|
rows+=("generate-pg-ssl-keys||Generate PostgreSQL SSL key material.")
|
|
rows+=("get-addons-path||Output addons path.")
|
|
rows+=("get-modules|[path][option]|Get list of modules in path. 'basename' or 'dirname'.")
|
|
rows+=("get-module-version|[path]|Get module version from manifest.")
|
|
rows+=("get-module-checksum|[path]|Get module checksum.")
|
|
rows+=("help|[grep]|Show help for commands.")
|
|
rows+=("info||Show values of project env vars.")
|
|
rows+=("load-ssh-key||Load SSH private key from env var.")
|
|
rows+=("update-module-license|[grep]|Update LICENSE file for each matching module.")
|
|
rows+=("<<<RUN>>>| | ")
|
|
rows+=("debug|[name]|Debugg application.")
|
|
rows+=("exec|[name][cmd]|Run command in container.")
|
|
rows+=("logs|[name]|Tail container logs. Default is '\''odoo'\''.")
|
|
rows+=("manifestoo|[param]|Execute manifestoo cli.")
|
|
rows+=("odoocli|[param]|Execute odoocli cli.")
|
|
rows+=("psql|[db]|Start interactive psql shell.")
|
|
rows+=("restart|[name]|Restart container.")
|
|
rows+=("run|[name][cmd]|Run container with command.")
|
|
rows+=("shell|[db][code]|Start interactive odoo shell or run code.")
|
|
rows+=("source||Source the Python virtual env.")
|
|
rows+=("start|[name][db]|Start service. Use list to show services.")
|
|
rows+=("stop|[name]|Stop containers.")
|
|
rows+=("test-xmlrpc|[env]|Test json rpc connection.")
|
|
rows+=("<<<PROJECT>>>| | ")
|
|
rows+=("lint||Run precommit for this project.")
|
|
rows+=("list-versions||List available Odoo versions.")
|
|
rows+=("check-version||Check if Odoo source is the correct version.")
|
|
rows+=("load-version|[version]|Load git refs from version folder.")
|
|
rows+=("save-version||Save git folder refs to version folder.")
|
|
rows+=("template-compose||Template the Docker compose file.")
|
|
rows+=("template-odoo-rc||Template the Odoo config file.")
|
|
rows+=("template-repo|[path]|Update the repo folder from template.")
|
|
rows+=("test-project|[cleanup]|Run tests for this project.")
|
|
rows+=("version||Show version of required tools.")
|
|
rows+=("<<<PERFORMANCE>>>| | ")
|
|
rows+=("record-with-memray|[name]|Record application memory usage with memray.")
|
|
rows+=("record-with-py-spy|[pid]|Record and create flamechart for a process.")
|
|
rows+=("<<<DOCS>>>| | ")
|
|
rows+=("update-docs||Update all project docs.")
|
|
rows+=("update-modules-doc||Update modules docs file.")
|
|
rows+=("update-revisions-doc||Update revisions doc file.")
|
|
rows+=("update-snippets-doc||Update snippets doc file.")
|
|
rows+=("<<<UPGRADE>>>| | ")
|
|
rows+=("migrate-module|[path]|Migrate module code from to target Odoo version.")
|
|
rows+=("upgrade-odoo|[env][step]|Run the Odoo upgrade for a target environment.")
|
|
|
|
for row in "${rows[@]}"; do
|
|
IFS='|' read -r cmd opt desc <<< "$row"
|
|
(( ${#cmd} > max_cmd )) && max_cmd=${#cmd}
|
|
(( ${#opt} > max_opt )) && max_opt=${#opt}
|
|
(( ${#desc} > max_desc )) && max_desc=${#desc}
|
|
done
|
|
|
|
printf '| %-*s | %-*s | %-*s |\n' \
|
|
"$max_cmd" "Command" \
|
|
"$max_opt" "Option" \
|
|
"$max_desc" "Description"
|
|
|
|
printf '|-%*s-|-%*s-|-%*s-|\n' \
|
|
"$max_cmd" "$(printf '%*s' "$max_cmd" '' | tr ' ' '-')" \
|
|
"$max_opt" "$(printf '%*s' "$max_opt" '' | tr ' ' '-')" \
|
|
"$max_desc" "$(printf '%*s' "$max_desc" '' | tr ' ' '-')"
|
|
|
|
local first_row=true
|
|
for row in "${rows[@]:1}"; do
|
|
IFS='|' read -r cmd opt desc <<< "$row"
|
|
if [[ "$cmd" =~ ^\<\<\<.*\>\>\>$ ]]; then
|
|
group_name="${cmd/\<\<\</}"
|
|
group_name="${group_name/\>\>\>/}"
|
|
|
|
if [ "$first_row" = true ]; then
|
|
first_row=false
|
|
else
|
|
printf '|-%*s-|-%*s-|-%*s-|\n' \
|
|
"$max_cmd" "$(printf '%*s' "$max_cmd" '' | tr ' ' '-')" \
|
|
"$max_opt" "$(printf '%*s' "$max_opt" '' | tr ' ' '-')" \
|
|
"$max_desc" "$(printf '%*s' "$max_desc" '' | tr ' ' '-')"
|
|
fi
|
|
|
|
local left_pad=$(( (max_cmd - ${#group_name}) / 2 ))
|
|
local right_pad=$(( max_cmd - left_pad - ${#group_name} ))
|
|
printf '| %*s%s%*s | %*s | %*s |\n' \
|
|
"$left_pad" "" \
|
|
"$group_name" \
|
|
"$right_pad" "" \
|
|
"$max_opt" "" \
|
|
"$max_desc" ""
|
|
|
|
printf '|-%*s-|-%*s-|-%*s-|\n' \
|
|
"$max_cmd" "$(printf '%*s' "$max_cmd" '' | tr ' ' '-')" \
|
|
"$max_opt" "$(printf '%*s' "$max_opt" '' | tr ' ' '-')" \
|
|
"$max_desc" "$(printf '%*s' "$max_desc" '' | tr ' ' '-')"
|
|
else
|
|
first_row=false
|
|
printf '| %-*s | %-*s | %-*s |\n' \
|
|
"$max_cmd" "$cmd" \
|
|
"$max_opt" "$opt" \
|
|
"$max_desc" "$desc"
|
|
fi
|
|
done
|
|
}
|
|
|
|
help() {
|
|
echo
|
|
if [[ -n "$1" ]]; then
|
|
help-table | grep -i "$1" | column -t -s'|'
|
|
else
|
|
echo 'task <command> [options]'
|
|
echo
|
|
echo 'commands:'
|
|
echo
|
|
help-table
|
|
fi
|
|
echo
|
|
}
|
|
|
|
|
|
# Static env vars
|
|
|
|
PASS_ENTRY="$(echo "env/${PWD##*/}" | tr '[:upper:]' '[:lower:]')"
|
|
ODOO_RC="odoo.conf"
|
|
TASK_DOTENV_DIR="$PWD/vault"
|
|
export PYTHONPATH="$PWD/odoo" # Required so upgrade-util does not overwrite the odoo namespace
|
|
ODOO_VERSION=$(echo "$ODOO_REVISION" | cut -d'.' -f1-2)
|
|
CONTAINER_CONFIG="$HOME/.docker/$CONTAINER_REGISTRY"
|
|
|
|
# Updateable env vars
|
|
|
|
ORG_NAME=${ORG_NAME:=Mint-System}
|
|
DB_NAME=${DB_NAME:="$ODOO_VERSION"}
|
|
WITHOUT_DEMO=${WITHOUT_DEMO:=False}
|
|
ODOO_LANGUAGE=${ODOO_LANGUAGE:=de_CH}
|
|
ODOO_PORT=${ODOO_PORT:=8069}
|
|
NGINX_PORT=${NGINX_PORT:=8080}
|
|
WORKERS=${WORKERS:=0}
|
|
POSTGRES_IMAGE=${POSTGRES_IMAGE:=postgres:16-alpine}
|
|
POSTGRES_PORT=${POSTGRES_PORT:=5432}
|
|
POSTGRES_SSL=${POSTGRES_SSL:=on}
|
|
PGSSLMODE=${PGSSLMODE:=prefer}
|
|
LOG_LEVEL=${LOG_LEVEL:=info}
|
|
CONTAINER_REGISTRY=${CONTAINER_REGISTRY:=mintsystem}
|
|
PLATFORM=${PLATFORM:=linux/amd64}
|
|
CONTAINER_ENGINE=${CONTAINER_ENGINE:=docker}
|
|
COMPOSE_COMMAND=${COMPOSE_COMMAND:=docker compose}
|
|
LLM_MODEL=${LLM_MODEL:=llama}
|
|
SMTP_SERVER=${SMTP_SERVER:=localhost}
|
|
SMTP_PORT=${SMTP_PORT:=1025}
|
|
SMTP_SSL=${SMTP_SSL:=False}
|
|
EMAIL_FROM=${EMAIL_FROM:=info@yourcompany.com}
|
|
MAIL_ALIAS_DOMAIN=${MAIL_ALIAS_DOMAIN:=yourcompany.com}
|
|
LIST_DB=${LIST_DB:=False}
|
|
CONTAINER_TAG="odoo:$ODOO_REVISION"
|
|
ODOO_INIT_LOGIN=${ODOO_INIT_LOGIN:=admin}
|
|
ODOO_INIT_PASSWORD=${ODOO_INIT_PASSWORD:=admin}
|
|
SNIPPET_PREFIX=${SNIPPET_PREFIX:=mint_system}
|
|
|
|
# Conditional env vars
|
|
|
|
if [[ "$CONTAINER_ENGINE" == "podman" ]]; then
|
|
COMPOSE_COMMAND="podman-compose"
|
|
fi
|
|
|
|
if [[ "$(uname)" == "Darwin" ]]; then
|
|
OS_RELEASE="Darwin"
|
|
PYTHON_NOTIFY=""
|
|
else
|
|
OS_RELEASE=$(awk -F= '/^NAME/{print $2}' /etc/os-release | tr -d '"')
|
|
PYTHON_NOTIFY="inotify"
|
|
fi
|
|
|
|
if [ "$WITHOUT_DEMO" = "True" ]; then
|
|
ODOO_PARAM="--without-demo=all"
|
|
fi
|
|
|
|
# Import commands
|
|
|
|
clone-taskfile(){
|
|
if [[ ! -d "$HOME/taskfile.build" ]]; then
|
|
echo -e '\033[38;5;214mGit\033[0m: Clone taskfile repo'
|
|
git clone https://git.taskfile.build "$HOME/taskfile.build"
|
|
else
|
|
echo -e '\033[38;5;214mGit\033[0m: Pull taskfile repo'
|
|
git -C "$HOME/taskfile.build" pull
|
|
fi
|
|
}
|
|
|
|
if [[ -d "$HOME/taskfile.build/bin" ]]; then
|
|
for file in "$HOME/taskfile.build/bin/"*; do
|
|
if [[ -f "$file" ]]; then
|
|
source "$file"
|
|
fi
|
|
done
|
|
fi
|
|
|
|
# Help commands
|
|
|
|
info() {
|
|
set-addons-path
|
|
echo "OS Release: $OS_RELEASE"
|
|
echo "Pass Entry: $PASS_ENTRY"
|
|
echo "Odoo Revision: $ODOO_REVISION"
|
|
echo "Odoo Version: $ODOO_VERSION"
|
|
echo "Odoo Port: $ODOO_PORT"
|
|
echo "Postgres Port: $POSTGRES_PORT"
|
|
echo "Odoo Language: $ODOO_LANGUAGE"
|
|
echo "Container Registry: $CONTAINER_REGISTRY"
|
|
echo "Container Config: $CONTAINER_CONFIG"
|
|
echo "Addons Path: $ADDONS_PATH"
|
|
}
|
|
|
|
version() {
|
|
uv --version
|
|
activate-venv
|
|
python -V
|
|
wkhtmltopdf -V
|
|
if [[ "$CONTAINER_ENGINE" == "docker" ]]; then
|
|
docker -v
|
|
docker compose version
|
|
else
|
|
podman --version
|
|
podman-compose --version
|
|
fi
|
|
}
|
|
|
|
generate-admin-password() {
|
|
if [[ -z "$1" ]]; then echo '\$1 is empty.'; exit; fi
|
|
export ODOO_PASSWORD="$1"
|
|
bin/hash-password
|
|
}
|
|
|
|
# Env commands
|
|
|
|
init-venv() {
|
|
echo "Ensure Python version $(cat .python-version) is installed."
|
|
uv python install
|
|
|
|
if [[ ! -d ".venv$ODOO_VERSION" ]]; then
|
|
echo "Init .venv$ODOO_VERSION with $(uv --version)."
|
|
uv venv ".venv$ODOO_VERSION"
|
|
fi
|
|
}
|
|
|
|
activate-venv() {
|
|
if [[ -f ".venv$ODOO_VERSION/bin/activate" ]]; then
|
|
source ".venv$ODOO_VERSION/bin/activate"
|
|
else
|
|
echo "Could not find .venv$ODOO_VERSION/bin/active. Use init-venv to initalize."
|
|
fi
|
|
}
|
|
|
|
get-addons-path() {
|
|
set-addons-path
|
|
echo "$ADDONS_PATH"
|
|
}
|
|
|
|
template-odoo-rc() {
|
|
echo "Template $ODOO_RC"
|
|
|
|
# Load and export vars defined in odoo conf template
|
|
for var in $(grep -oE '\$([A-Z_][A-Z0-9_]*)|\$\{([A-Z_][A-Z0-9_]*)\}' odoo.conf.template); do
|
|
var=${var/\$/}
|
|
var=${var//\{\//}
|
|
export $var
|
|
done
|
|
|
|
local ir_config_parameter=${IR_CONFIG_PARAMETER:-""}
|
|
|
|
if [[ -n "$GIT_SSH_PRIVATE_KEY" ]]; then
|
|
local git_ssh_private_key=$(echo -e "$GIT_SSH_PRIVATE_KEY" | base64 -w0)
|
|
ir_config_parameter+=$'\n'"git.ssh_private_key = \"$git_ssh_private_key\""
|
|
fi
|
|
|
|
if [[ -n "$GIT_SSH_PUBLIC_KEY" ]]; then
|
|
ir_config_parameter+=$'\n'"git.ssh_public_key = \"$GIT_SSH_PUBLIC_KEY\""
|
|
fi
|
|
|
|
if [[ -n "$BASE_URL_WEBSITE" ]]; then
|
|
ir_config_parameter+=$'\n'"base_url_website = \"$BASE_URL_WEBSITE\""
|
|
fi
|
|
|
|
if [[ -n "$BASE_URL_APP" ]]; then
|
|
ir_config_parameter+=$'\n'"base_url_app = \"$BASE_URL_APP\""
|
|
fi
|
|
|
|
if [[ -n "$UPLOADCARE_PUBLIC_KEY" ]]; then
|
|
ir_config_parameter+=$'\n'"uploadcare.public_key = \"$UPLOADCARE_PUBLIC_KEY\""
|
|
fi
|
|
|
|
if [[ -n "$UPLOADCARE_SECRET_KEY" ]]; then
|
|
ir_config_parameter+=$'\n'"uploadcare.secret_key = \"$UPLOADCARE_SECRET_KEY\""
|
|
fi
|
|
|
|
if [[ -n "$KEYCLOAK_BASE_URL" ]]; then
|
|
ir_config_parameter+=$'\n'"keycloak.base_url = \"$KEYCLOAK_BASE_URL\""
|
|
fi
|
|
|
|
if [[ -n "$KEYCLOAK_CLIENT_ID" ]]; then
|
|
ir_config_parameter+=$'\n'"keycloak.client_id = \"$KEYCLOAK_CLIENT_ID\""
|
|
fi
|
|
|
|
if [[ -n "$KEYCLOAK_CLIENT_SECRET" ]]; then
|
|
ir_config_parameter+=$'\n'"keycloak.client_secret = \"$KEYCLOAK_CLIENT_SECRET\""
|
|
fi
|
|
|
|
if [[ -n "$KEYCLOAK_REALM" ]]; then
|
|
ir_config_parameter+=$'\n'"keycloak.realm = \"$KEYCLOAK_REALM\""
|
|
fi
|
|
|
|
export IR_CONFIG_PARAMETER="$ir_config_parameter"
|
|
|
|
envsubst < "odoo.conf.template" > "$ODOO_RC"
|
|
}
|
|
|
|
set-addons-path() {
|
|
# Read addons path from git module
|
|
local git_folders_path="$(git config --file .gitmodules --get-regexp path | awk '{ print $2 }' | sort | tr '\n' ',' | sed 's/,*$//')"
|
|
|
|
# Remove excluded addons
|
|
local excludes="addons/theme_mint_system addons/company addons/web"
|
|
for exclude in $excludes; do
|
|
git_folders_path=$(echo "$git_folders_path" | sed "s|${exclude},||g; s|^${exclude}$||")
|
|
done
|
|
|
|
# Filter valid directories
|
|
local valid_paths=""
|
|
while IFS= read -r addon; do
|
|
addon=$(echo "$addon" | xargs)
|
|
[[ -z "$addon" ]] && continue
|
|
if [[ -d "$addon" ]]; then
|
|
valid_paths="${valid_paths:+$valid_paths,}$addon"
|
|
else
|
|
echo "Set Addons Path: Path '$addon' does not exist." >&2
|
|
fi
|
|
done < <(echo "$git_folders_path" | tr ',' '\n')
|
|
git_folders_path="$valid_paths"
|
|
|
|
# Convert to source paths (add /addons where needed)
|
|
local addons_path="$(echo "$git_folders_path" | sed 's|,odoo|,odoo/addons|g')"
|
|
|
|
# Append paths from config env var
|
|
if [[ -n "$ODOO_ADDONS_PATH" ]]; then
|
|
addons_path="${ODOO_ADDONS_PATH},${addons_path}"
|
|
fi
|
|
|
|
# Export the addons path
|
|
export ADDONS_PATH="$addons_path"
|
|
}
|
|
|
|
load-ssh-key() {
|
|
if [[ -n "$GIT_SSH_PRIVATE_KEY" ]]; then
|
|
echo 'Setup SSH key from env var.'
|
|
local decoded_git_ssh_private_key=$(echo -e "$GIT_SSH_PRIVATE_KEY" | base64 -d)
|
|
mkdir -p ~/.ssh
|
|
echo "$decoded_git_ssh_private_key" > ~/.ssh/id_ed25519
|
|
chmod 600 ~/.ssh/id_ed25519
|
|
eval "$(ssh-agent -s)"
|
|
ssh-add ~/.ssh/id_ed25519 || (echo 'Dumping ~/.ssh/id_ed25519 content:' && cat ~/.ssh/id_ed25519)
|
|
else
|
|
echo 'The SSH key env var is empty.'
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# Docs commands
|
|
|
|
lint() {
|
|
echo "Run pre-commit in $PWD"
|
|
pre-commit run
|
|
}
|
|
|
|
update-snippets-doc() {
|
|
./bin/update-snippets-doc
|
|
}
|
|
|
|
update-revisions-doc() {
|
|
|
|
echo 'Update revisions doc file.'
|
|
|
|
rm -f "revisions.md"
|
|
echo -e "# Odoo Revisions \n\
|
|
A Odoo revision is a snapshot of git references of the Odoo source and modules at a specific date.\n\
|
|
For each major release there are multiple revisions.\n" > "revisions.md"
|
|
|
|
declare -A revisions
|
|
local path_url="https://github.com/${ORG_NAME}/Odoo-Build/tree/main"
|
|
|
|
for file in $(ls revisions/ | sort -rV); do
|
|
local revision="$file"
|
|
local version=$(echo "$revision" | cut -d'.' -f1-2)
|
|
|
|
if [[ -z "${revisions[$version]}" ]]; then
|
|
revisions[$version]="## $version"
|
|
fi
|
|
|
|
revisions[$version]+=$'\n\n'
|
|
revisions[$version]+="#### $revision"
|
|
revisions[$version]+=$'\n\n```bash\n'
|
|
revisions[$version]+=$(cat "revisions/$file")
|
|
revisions[$version]+=$'\n```'
|
|
done
|
|
|
|
for revision in $(echo "${!revisions[@]}" | tr ' ' '\n' | sort -rV); do
|
|
echo -e "${revisions[$revision]}\n" >> "revisions.md"
|
|
done
|
|
}
|
|
|
|
update-modules-doc() {
|
|
echo 'Update module doc file.'
|
|
./bin/update-modules-doc
|
|
}
|
|
|
|
update-docs() {
|
|
update-modules-doc
|
|
update-revisions-doc
|
|
update-snippets-doc
|
|
}
|
|
|
|
update-module-license() {
|
|
if [[ -z "$1" ]]; then echo '\$1 is empty.'; exit; fi
|
|
local git_folders=$(list-module "$1")
|
|
|
|
for git_folder in $git_folders; do
|
|
echo "Update license file for $git_folder."
|
|
cp templates/module/LICENSE "$git_folder/"
|
|
done
|
|
}
|
|
|
|
template-repo(){
|
|
if [[ -z "$1" ]]; then echo '\$1 is empty.'; exit; fi
|
|
local repo_path="$1"
|
|
|
|
echo "Copy task file to $repo_path."
|
|
cp "templates/task" "$repo_path"
|
|
|
|
echo "Template README.md to $repo_path."
|
|
export REPO_FOLDER=$(basename "$repo_path")
|
|
export REPO_MODEL=${REPO_FOLDER%%_*}
|
|
export REPO_TITLE=$(echo "$REPO_FOLDER" | sed 's/_/ /g' | sed 's/\b\(.\)/\u\1/g')
|
|
export REPO_NAME=$(echo "$REPO_FOLDER" | sed 's/_/-/g' | sed 's/\b\(.\)/\u\1/g')
|
|
envsubst < "templates/README.md" > "$repo_path/README.md"
|
|
|
|
echo "Copy $ODOO_VERSION template files to $repo_path."
|
|
cp -r "templates/$ODOO_VERSION/." "$repo_path"
|
|
|
|
echo 'Remove deprecated repo files.'
|
|
rm -f "$repo_path/.flake8"
|
|
rm -f "$repo_path/.isort.cfg"
|
|
rm -f "$repo_path/.pylintrc-mandatory"
|
|
}
|
|
|
|
# Module commands
|
|
|
|
get-modules() {
|
|
if [[ -z "$1" ]]; then echo '\$1 is empty.'; exit; fi
|
|
|
|
local option="$2"
|
|
if [[ -z "$option" ]]; then
|
|
option="dirname"
|
|
fi
|
|
|
|
local modules
|
|
if [[ "$option" == "dirname" ]]; then
|
|
modules=$(echo "$1" | tr "," "\n" | xargs -I {} find {} -type f -name "__manifest__.py" | xargs grep -l -iE "version.*([0-9]+\.[0-9]+(\.[0-9]+)?|${ODOO_VERSION}(\.[0-9]+)*)" | xargs -r dirname | sort -u | tr "\n" "," | sed 's/,$//')
|
|
fi
|
|
if [[ "$option" == "basename" ]]; then
|
|
modules=$(echo "$1" | tr "," "\n" | xargs -I {} find {} -type f -name "__manifest__.py" | xargs grep -l -iE "version.*([0-9]+\.[0-9]+(\.[0-9]+)?|${ODOO_VERSION}(\.[0-9]+)*)" | xargs -r dirname | xargs -r -I {} basename {} | sort -u | tr "\n" "," | sed 's/,$//')
|
|
fi
|
|
echo "$modules"
|
|
}
|
|
|
|
get-module-version() {
|
|
if [[ -z "$1" ]]; then
|
|
echo '\$1 is empty.'
|
|
else
|
|
# Get version of module
|
|
local version=$(grep -m 1 \"version "$1/__manifest__.py" | sed "s;';\";g" | sed "s/,//g" | sed 's/#.*//g')
|
|
version=$(echo "{ $version }" | jq .version | sed 's/"//g' | sed 's/null//g')
|
|
|
|
# Set default version
|
|
[[ -z "$version" ]] && version=0.0
|
|
|
|
local count_dots=$(echo "$version" | grep -o "\." | wc -l)
|
|
|
|
# Check if oca version or enterprise version
|
|
if [[ $count_dots == 2 ]]; then
|
|
version="$ODOO_VERSION.$version"
|
|
fi
|
|
if [[ $count_dots == 1 ]]; then
|
|
version="$ODOO_VERSION.$version"
|
|
fi
|
|
|
|
echo "$version"
|
|
fi
|
|
}
|
|
|
|
get-module-checksum() {
|
|
if [[ -z "$1" ]]; then
|
|
echo '\$1 is empty.'
|
|
else
|
|
bin/get-module-checksum "$1"
|
|
fi
|
|
}
|
|
|
|
list-modules() {
|
|
if [[ -z "$1" ]]; then echo '\$1 is empty.'; exit; fi
|
|
local modules=$(get-modules "$1" dirname)
|
|
echo "$modules" | tr ',' '\n'
|
|
}
|
|
|
|
# Container commands
|
|
|
|
archive-docker-tags() {
|
|
export CONTAINER_CONFIG
|
|
python bin/archive-docker-tags
|
|
}
|
|
|
|
template-compose() {
|
|
echo 'Template compose.yml'
|
|
|
|
# Load and export vars defined in compose template
|
|
for var in $(grep -oE '\$\{?[A-Z_][A-Z0-9_]*\}?' compose.yml.template); do
|
|
# Strip $, ${, }
|
|
var=${var#\$\{} # Remove leading ${
|
|
var=${var%\}} # Remove trailing }
|
|
var=${var#\$} # Remove leading $
|
|
export "$var"
|
|
done
|
|
|
|
# Add volume mounts
|
|
VOLUME_MOUNTS=""
|
|
if [[ -d "./tmp/postgres-odoo" ]]; then
|
|
VOLUME_MOUNTS="${VOLUME_MOUNTS} - ./tmp/postgres-odoo:/mnt/postgres-odoo"$'\n'
|
|
fi
|
|
if [[ -d "./addons" ]]; then
|
|
VOLUME_MOUNTS="${VOLUME_MOUNTS} - ./addons:/mnt/addons"$'\n'
|
|
fi
|
|
if [[ -d "./oca" ]]; then
|
|
VOLUME_MOUNTS="${VOLUME_MOUNTS} - ./oca:/mnt/oca"$'\n'
|
|
fi
|
|
if [[ -d "./enterprise" ]]; then
|
|
VOLUME_MOUNTS="${VOLUME_MOUNTS} - ./enterprise:/mnt/enterprise"$'\n'
|
|
fi
|
|
if [[ -d "./themes" ]]; then
|
|
VOLUME_MOUNTS="${VOLUME_MOUNTS} - ./themes:/mnt/themes"$'\n'
|
|
fi
|
|
VOLUME_MOUNTS="${VOLUME_MOUNTS%$'\n'}"
|
|
export VOLUME_MOUNTS
|
|
|
|
envsubst < "compose.yml.template" > "compose.yml"
|
|
}
|
|
|
|
container-ps() {
|
|
$CONTAINER_ENGINE ps
|
|
}
|
|
|
|
build() {
|
|
if [[ -z "$1" ]]; then
|
|
echo '$1 is empty.'
|
|
exit
|
|
fi
|
|
|
|
# Set container tag based on Dockerfile
|
|
local manifest_file
|
|
local latest_tag
|
|
|
|
if [[ "$1" == "images/odoo" ]]; then
|
|
manifest_file="images/odoo/Dockerfile"
|
|
latest_tag="odoo:$ODOO_VERSION"
|
|
fi
|
|
if [[ "$1" == "images/odoo-mailgate" ]]; then
|
|
manifest_file="images/odoo-mailgate/Dockerfile"
|
|
CONTAINER_TAG="odoo-mailgate"
|
|
latest_tag="odoo-mailgate:latest"
|
|
elif [[ "$1" == "images/odoo-upgrade" ]]; then
|
|
manifest_file="images/odoo-upgrade/Dockerfile"
|
|
CONTAINER_TAG="odoo-upgrade"
|
|
latest_tag="odoo-upgrade:latest"
|
|
elif [[ "$1" == "images/odoocli" ]]; then
|
|
manifest_file="images/odoocli/Dockerfile"
|
|
CONTAINER_TAG="odoocli"
|
|
latest_tag="odoocli:latest"
|
|
fi
|
|
|
|
# Load Odoo revision
|
|
local odoo_date=$(echo "$ODOO_REVISION" | cut -d'.' -f3-)
|
|
source "revisions/$ODOO_REVISION"
|
|
|
|
# Select Python version
|
|
case "$ODOO_VERSION" in
|
|
"15.0")
|
|
local python_version="3.9"
|
|
PLATFORM="linux/amd64"
|
|
;;
|
|
"16.0")
|
|
local python_version="3.11"
|
|
;;
|
|
"17.0")
|
|
local python_version="3.11"
|
|
;;
|
|
"18.0")
|
|
local python_version="3.12"
|
|
;;
|
|
"19.0")
|
|
local python_version="3.13"
|
|
;;
|
|
*)
|
|
local python_version="3.12"
|
|
;;
|
|
esac
|
|
|
|
local docker_output="--load"
|
|
if [[ -n "$2" ]]; then
|
|
docker_output="$2"
|
|
fi
|
|
|
|
# Output configuration
|
|
echo "Python Version: $python_version"
|
|
echo "Platform: $PLATFORM"
|
|
echo "Docker Output: $docker_output"
|
|
|
|
echo "Remove container image with tag ${CONTAINER_REGISTRY}/${CONTAINER_TAG}"
|
|
docker image rm -f "${CONTAINER_REGISTRY}/${CONTAINER_TAG}"
|
|
|
|
if ! docker buildx inspect odoo_builder >/dev/null 2>&1; then
|
|
docker buildx create --name odoo_builder --use
|
|
docker buildx inspect --bootstrap
|
|
else
|
|
docker buildx use odoo_builder
|
|
fi
|
|
|
|
echo "Run Docker build ${CONTAINER_REGISTRY}/${CONTAINER_TAG} for ${PLATFORM}"
|
|
SOURCE_DATE_EPOCH=$(git -C odoo log -1 --pretty=%ct) docker buildx build --platform "$PLATFORM" . \
|
|
--file "$manifest_file" \
|
|
--build-arg PYTHON_VERSION="$python_version" \
|
|
--build-arg ODOO_VERSION="$ODOO_VERSION" \
|
|
--build-arg ODOO_DATE="$odoo_date" \
|
|
--build-arg ODOO_ENTERPRISE_REF="$ODOO_ENTERPRISE_REF" \
|
|
--tag "${CONTAINER_REGISTRY}/${CONTAINER_TAG}" \
|
|
--tag "${CONTAINER_REGISTRY}/${latest_tag}" \
|
|
"$docker_output"
|
|
}
|
|
|
|
load-latest-revision() {
|
|
if [[ -n "$1" ]]; then
|
|
ODOO_VERSION="$1"
|
|
fi
|
|
|
|
ODOO_REVISION=$(list-revision | grep "$ODOO_VERSION" | tail -n1)
|
|
load-revision "$ODOO_REVISION"
|
|
}
|
|
|
|
checkout-latest-revision() {
|
|
if [[ -n "$1" ]]; then
|
|
ODOO_VERSION="$1"
|
|
fi
|
|
|
|
ODOO_REVISION=$(list-revision | grep "$ODOO_VERSION" | tail -n1)
|
|
checkout-revision "$ODOO_REVISION"
|
|
check-version
|
|
}
|
|
|
|
container-login() {
|
|
if [[ -n "$1" ]]; then
|
|
CONTAINER_REGISTRY_USERNAME="$1"
|
|
fi
|
|
if [[ -n "$2" ]]; then
|
|
CONTAINER_REGISTRY_PASSWORD="$2"
|
|
fi
|
|
|
|
echo "Docker login with username $CONTAINER_REGISTRY_USERNAME."
|
|
echo "$CONTAINER_REGISTRY_PASSWORD" | docker --config "$CONTAINER_CONFIG" login --username "$CONTAINER_REGISTRY_USERNAME" --password-stdin
|
|
echo "$CONTAINER_REGISTRY_PASSWORD" | docker --config "$CONTAINER_CONFIG" login dhi.io --username "$CONTAINER_REGISTRY_USERNAME" --password-stdin
|
|
}
|
|
|
|
login-podman() {
|
|
if [[ -n "$1" ]]; then
|
|
CONTAINER_REGISTRY_USERNAME="$1"
|
|
fi
|
|
if [[ -n "$2" ]]; then
|
|
CONTAINER_REGISTRY_PASSWORD="$2"
|
|
fi
|
|
|
|
echo "Podman login with username $CONTAINER_REGISTRY_USERNAME."
|
|
podman login --username "$CONTAINER_REGISTRY_USERNAME" --password "$CONTAINER_REGISTRY_PASSWORD" docker.io
|
|
}
|
|
|
|
test-odoo-version-regex() {
|
|
|
|
# If ODOO_VERSION=19.0 then
|
|
# 1.0 Match
|
|
# 1.1 Match
|
|
# 2.5 Match
|
|
# 2.0.0 Match
|
|
# 13.0.1.0.0 No match
|
|
# 14.0.2.1.1 No match
|
|
# 13.0.1.0.0 No match
|
|
# 19.0.0.0.0 Match
|
|
|
|
# Variations of the version key:
|
|
# 'version': '19.0.x',
|
|
# "version": "1.0",
|
|
# 'version': '19.0.1.0.0',
|
|
|
|
echo -e "\033[38;5;214mTEST\033[0m: Find and check versions in module"
|
|
|
|
versions=$(grep -r --include="__manifest__.py" '[\"'\'']version[\"'\'']:.[\"'\'']' addons enterprise oca odoo themes |
|
|
sed -E 's/.+[\"'\'']version[\"'\'']:.[\"'\'']([0-9].+)[\"'\''].+/\1/g' |
|
|
sort -u)
|
|
|
|
ODOO_VERSION="19.0"
|
|
for version in $versions; do
|
|
echo -n "Check version $version: "
|
|
# 1. regex: version.[0-9]\.[0-9]$
|
|
# 2. regex: version.[0-9]\.[0-9]\.[0-9]$
|
|
# 3. regex: version.${ODOO_VERSION}\.[0-9]\.[0-9]\.[0-9]$
|
|
if [[ $version =~ ^[0-9]+\.[0-9]+$ ||
|
|
$version =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ||
|
|
$version =~ ^${ODOO_VERSION}\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
|
echo "✓ Match"
|
|
else
|
|
echo "✗ No match"
|
|
fi
|
|
done
|
|
|
|
echo -e "\033[38;5;214mTEST\033[0m: Run set-addons-path"
|
|
export PATH=./images/odoo/bin/:$PATH
|
|
export TEST_ADDONS_DIR="./templates,./themes,./enterprise,/gugus,./oca,./addons"
|
|
export ODOO_VERSION="19.0"
|
|
source ./images/odoo/bin/set-addons-path
|
|
echo "ADDONS_PATH: $ADDONS_PATH"
|
|
}
|
|
|
|
test-image() {
|
|
# Test the scripts of the Docker images without starting the enviroment.
|
|
set -e
|
|
|
|
if [[ -z "$1" ]]; then
|
|
echo '$1 is empty.'
|
|
exit
|
|
fi
|
|
|
|
if [[ "$1" == "images/odoo-upgrade" ]]; then
|
|
CONTAINER_TAG="odoo-upgrade"
|
|
elif [[ "$1" == "images/odoocli" ]]; then
|
|
CONTAINER_TAG="odoocli"
|
|
fi
|
|
|
|
if [[ "$CONTAINER_TAG" =~ ^odoo: ]]; then
|
|
generate-ssh-keys
|
|
export GIT_SSH_PRIVATE_KEY
|
|
export GIT_SSH_PUBLIC_KEY
|
|
export PATH=./images/odoo/bin/:$PATH
|
|
export LOCAL_PATH="./tmp"
|
|
mkdir -p "$LOCAL_PATH"
|
|
|
|
echo -e "\033[38;5;214mTEST\033[0m: Run git clone addons"
|
|
local server_tools_git_ref=$(curl -s "https://api.github.com/repos/OCA/server-tools/branches/$ODOO_VERSION" | grep '"sha":' | head -n 1 | awk -F '"' '{print $4}')
|
|
export ADDONS_GIT_REPOS="https://github.com/OCA/server-tools.git#${ODOO_VERSION}#${server_tools_git_ref}"
|
|
# This will remove all SSH keys at the end
|
|
./images/odoo/bin/clone-git-addons
|
|
rm -rf "$LOCAL_PATH/github.com" # Cleanup for next run
|
|
|
|
echo -e "\033[38;5;214mTEST\033[0m: Install python package."
|
|
export PYTHON_INSTALL="prometheus-client,pyjwt"
|
|
./images/odoo/bin/install-python-packages
|
|
|
|
echo -e "\033[38;5;214mTEST\033[0m: Sync python project."
|
|
cat > tmp/pyproject.toml << 'EOF'
|
|
[project]
|
|
name = "test-project"
|
|
version = "0.1.0"
|
|
description = "Test project for uv sync"
|
|
dependencies = [
|
|
"requests>=2.28.0",
|
|
"click>=8.0.0",
|
|
]
|
|
|
|
[build-system]
|
|
requires = ["setuptools>=61.0", "wheel"]
|
|
build-backend = "setuptools.build_meta"
|
|
|
|
[tool.uv]
|
|
dev-dependencies = [
|
|
"pytest>=7.0.0",
|
|
"black>=23.0.0",
|
|
]
|
|
EOF
|
|
./images/odoo/bin/sync-python-project
|
|
uv pip list
|
|
|
|
|
|
echo -e "\033[38;5;214mTEST\033[0m: Parse url."
|
|
git_proto=$(./images/odoo/bin/parse-url "git@github.com:OCA/server-tools.git" proto)
|
|
[[ "$git_proto" == "ssh://" ]] && log-success "Test success."
|
|
fi
|
|
|
|
if [[ "$CONTAINER_TAG" =~ ^odoo-upgrade ]]; then
|
|
echo -e "\033[38;5;214mTEST\033[0m: Show help of odoo-upgrade."
|
|
docker run -it mintsystem/odoo-upgrade --help
|
|
fi
|
|
|
|
if [[ "$CONTAINER_TAG" =~ ^odoocli ]]; then
|
|
echo -e "\033[38;5;214mTEST\033[0m: Show help of odoocli."
|
|
docker_odoocli() {
|
|
docker run -it \
|
|
-e ODOO_URL="$ODOO_URL" -e DB_NAME="$DB_NAME" -e ODOO_USERNAME="$ODOO_USERNAME" -e ODOO_PASSWORD="$ODOO_PASSWORD" \
|
|
mintsystem/odoocli "$@"
|
|
}
|
|
docker_odoocli --help
|
|
fi
|
|
}
|
|
|
|
push() {
|
|
if [[ -z "$1" ]]; then
|
|
echo '$1 is empty.'
|
|
exit
|
|
fi
|
|
local latest_tag
|
|
|
|
if [[ "$1" == "images/odoo" ]]; then
|
|
latest_tag="odoo:$ODOO_VERSION"
|
|
elif [[ "$1" == "images/odoo-mailgate" ]]; then
|
|
CONTAINER_TAG="odoo-mailgate"
|
|
latest_tag="odoo-mailgate:latest"
|
|
elif [[ "$1" == "images/odoo-upgrade" ]]; then
|
|
CONTAINER_TAG="odoo-upgrade"
|
|
latest_tag="odoo-upgrade:latest"
|
|
elif [[ "$1" == "images/odoocli" ]]; then
|
|
CONTAINER_TAG="odoocli"
|
|
latest_tag="odoocli:latest"
|
|
fi
|
|
|
|
echo "Tag container image ${CONTAINER_REGISTRY}/${CONTAINER_TAG} with ${CONTAINER_REGISTRY}/${latest_tag}"
|
|
$CONTAINER_ENGINE tag "${CONTAINER_REGISTRY}/${CONTAINER_TAG}" "${CONTAINER_REGISTRY}/${latest_tag}"
|
|
|
|
if [[ "$CONTAINER_ENGINE" == "docker" ]]; then
|
|
echo "Push container image to ${CONTAINER_REGISTRY}/${CONTAINER_TAG}"
|
|
docker --config "$CONTAINER_CONFIG" push "${CONTAINER_REGISTRY}/${CONTAINER_TAG}"
|
|
docker --config "$CONTAINER_CONFIG" push "${CONTAINER_REGISTRY}/${latest_tag}"
|
|
fi
|
|
|
|
if [[ "$CONTAINER_ENGINE" == "podman" ]]; then
|
|
echo "Push container image to ${CONTAINER_REGISTRY}/${CONTAINER_TAG}"
|
|
podman push "${CONTAINER_REGISTRY}/${CONTAINER_TAG}"
|
|
podman push "${CONTAINER_REGISTRY}/${latest_tag}"
|
|
fi
|
|
}
|
|
|
|
upload-module() {
|
|
if [[ -z "$1" ]]; then
|
|
echo '$1 is empty.'
|
|
exit
|
|
fi
|
|
if [[ -z "$2" ]]; then
|
|
echo '$2 is empty.'
|
|
exit
|
|
fi
|
|
|
|
# Check if path is root folder
|
|
local modules="$2"
|
|
if [[ ! -f "$2/__manifest__.py" ]]; then
|
|
echo 'Identified as root folder'
|
|
modules="$modules/*"
|
|
fi
|
|
|
|
for module in $modules; do
|
|
|
|
if [[ -f "$module/__manifest__.py" ]]; then
|
|
|
|
# Create module zip
|
|
zip-module "$module"
|
|
|
|
# Get path to zip file and upload url
|
|
local version=$(get-module-version "$module")
|
|
local module_name=$(basename "$module")
|
|
local file_path="tmp/$module_name-$version.zip"
|
|
load-dotenv "$1"
|
|
local nextcloud_upload_url="$NEXTCLOUD_URL/remote.php/dav/files/$NEXTCLOUD_USERNAME/Odoo-Apps/"
|
|
|
|
# Upload zip file
|
|
curl -u "$NEXTCLOUD_USERNAME:$NEXTCLOUD_PASSWORD" -T "$file_path" "$nextcloud_upload_url"
|
|
echo "File $file_path uploaded to $nextcloud_upload_url."
|
|
|
|
UPLOADS+=" - name: $module_name\n"
|
|
UPLOADS+=" version: $version\n"
|
|
fi
|
|
done
|
|
|
|
echo 'odoo_apps:'
|
|
echo -e "$UPLOADS"
|
|
}
|
|
|
|
logs() {
|
|
local container="$1"
|
|
container=${container:="odoo"}
|
|
$CONTAINER_ENGINE logs --follow "$container"
|
|
}
|
|
|
|
# Install commands
|
|
|
|
install() {
|
|
init-venv
|
|
activate-venv
|
|
|
|
if [[ ! -d "$HOME/taskfile.build" ]]; then
|
|
echo -e '\033[38;5;214mGit\033[0m: Clone taskfile repo'
|
|
git clone https://git.taskfile.build "$HOME/taskfile.build"
|
|
else
|
|
echo -e '\033[38;5;214mGit\033[0m: Pull taskfile repo'
|
|
git -C "$HOME/taskfile.build" pull
|
|
fi
|
|
|
|
# OS specific installations
|
|
if [[ "$OS_RELEASE" =~ ^(Debian|Debian GNU/Linux|Ubuntu|Pop!_OS)$ ]]; then
|
|
|
|
echo -e "\033[38;5;214mApt\033[0m: Update apt repo"
|
|
sudo apt update
|
|
|
|
echo -e "\033[38;5;214mApt\033[0m: Install python-ldap and psycopg2 build dependencies"
|
|
sudo apt-get install -y libsasl2-dev libldap2-dev libssl-dev libpq-dev python3-dev
|
|
|
|
echo -e "\033[38;5;214mApt\033[0m: Install xmllint, ripgrep and gettext"
|
|
sudo apt install -y libxml2-utils ripgrep gettext
|
|
|
|
elif [[ "$OS_RELEASE" =~ ^(Fedora Linux)$ ]]; then
|
|
|
|
echo -e "\033[38;5;214mDnf\033[0m: Update dnf repo"
|
|
sudo dnf update
|
|
|
|
echo -e "\033[38;5;214mDnf\033[0m: Install psycopg2 build dependencies"
|
|
sudo dnf install -y postgresql-devel
|
|
|
|
echo -e "\033[38;5;214mApt\033[0m: Install ripgrep and gettext"
|
|
sudo dnf install -y ripgrep gettext
|
|
|
|
elif [[ "$OS_RELEASE" =~ ^(Darwin)$ ]]; then
|
|
|
|
echo 'Welcome daring Mac user. Let'\''s try our best to get this working.'
|
|
echo -e "\033[38;5;214mBrew\033[0m: Install required packages with brew."
|
|
brew install gettext libsasl2 openldap openssl libxml2 grep ripgrep
|
|
|
|
elif [[ "$OS_RELEASE" =~ ^(Arch Linux|SteamOS)$ ]]; then
|
|
|
|
echo 'Arch is the best'
|
|
|
|
echo -e "\033[38;5;214mPacman\033[0m: Install python-ldap and psycopg2 build dependencies"
|
|
sudo pacman -S --noconfirm base-devel postgresql-devel
|
|
|
|
echo -e "\033[38;5;214mApt\033[0m: Install ripgrep"
|
|
sudo pacman -S --noconfirm ripgrep
|
|
|
|
else
|
|
echo "The operating system $OS_RELEASE is not supported."
|
|
exit 1
|
|
fi
|
|
|
|
# Install wkhtmltopdf
|
|
if [[ "$OS_RELEASE" =~ ^(Arch Linux|Darwin|Debian|Debian GNU/Linux|Fedora Linux|Ubuntu|Pop!_OS|SteamOS)$ ]]; then
|
|
|
|
# Source: https://gist.github.com/faniska/37f896d5e9de5fee925925d7caf3cb9e
|
|
local installed_version=$(wkhtmltopdf --version 2>&1 | sed -E 's/^wkhtmltopdf +([0-9.]+).*/\1/')
|
|
if [[ "$installed_version" != "0.12.4" ]]; then
|
|
echo -e "\033[38;5;214mCurl\033[0m: Install wkhtmltopdf"
|
|
curl -L -O https://github.com/wkhtmltopdf/wkhtmltopdf/releases/download/0.12.4/wkhtmltox-0.12.4_linux-generic-amd64.tar.xz
|
|
tar xvf wkhtmltox-0.12.4_linux-generic-amd64.tar.xz
|
|
sudo mv wkhtmltox/bin/wkhtmlto* /usr/bin/
|
|
rm -rf wkhtmltox wkhtmltox-0.12.4_linux-generic-amd64.tar.xz
|
|
else
|
|
echo 'wkhtmltopdf version is already 0.12.4'
|
|
fi
|
|
fi
|
|
|
|
# Odoo Build requirements
|
|
echo -e "\033[38;5;214mPython\033[0m: Install Odoo Build development tools"
|
|
uv pip install -r requirements.txt
|
|
|
|
# Odoo requirements
|
|
echo -e "\033[38;5;214mPython\033[0m: Install Odoo and dependencies."
|
|
cd odoo
|
|
uv pip install -e .
|
|
grep -v "python-ldap" requirements.txt | uv pip install -r -
|
|
cd ..
|
|
|
|
# Install inotify
|
|
echo -e "\033[38;5;214mPython\033[0m: Install $PYTHON_NOTIFY."
|
|
if [[ -n "$PYTHON_NOTIFY" ]]; then
|
|
uv pip install "$PYTHON_NOTIFY"
|
|
fi
|
|
if [[ "$PYTHON_NOTIFY" == "inotify" ]] && ! sudo grep -q "fs.inotify.max_user_watches=524288" /etc/sysctl.d/99-sysctl.conf; then
|
|
sudo bash -c 'echo "fs.inotify.max_user_watches=524288" >> /etc/sysctl.d/99-sysctl.conf'
|
|
sudo sysctl -p /etc/sysctl.d/99-sysctl.conf
|
|
echo -e "\033[38;5;214mSystem\033[0m: Updated fs.inotify.max_user_watches to 524288"
|
|
fi
|
|
|
|
# Fixes for Odoo versions
|
|
if [[ "$ODOO_VERSION" == "14.0" ]] ; then
|
|
echo -e "\033[38;5;214mPython\033[0m: Fix shebang for odoo binary."
|
|
sed -i '1s|.*|#!'"$PWD"'/.venv14.0/bin/python|' .venv14.0/bin/odoo
|
|
fi
|
|
|
|
}
|
|
|
|
# Process commands
|
|
|
|
start() {
|
|
set-addons-path
|
|
if [[ "$POSTGRES_SSL" == "on" ]]; then
|
|
generate-pg-ssl-keys
|
|
fi
|
|
ODOO_COMMAND="odoo --dev=all"
|
|
if [[ "$1" =~ "odoo-nginx" ]]; then
|
|
ODOO_COMMAND="odoo-nginx --dev=all"
|
|
fi
|
|
export ODOO_COMMAND
|
|
if [[ "$1" =~ "odoo" && -a ".env.odoo" ]]; then
|
|
source .env.odoo
|
|
fi
|
|
|
|
template-compose
|
|
|
|
# Use default database if second param is not given
|
|
local database="$2"
|
|
if [[ -z "$2" ]]; then
|
|
database="$DB_NAME"
|
|
fi
|
|
|
|
if [[ -z "$1" ]]; then
|
|
echo "Start all container ${CONTAINER_REGISTRY}/${CONTAINER_TAG}"
|
|
echo 'Open http://localhost:8069 url in your browser.'
|
|
$COMPOSE_COMMAND up -d
|
|
fi
|
|
|
|
if [[ "$1" = "list" ]]; then
|
|
echo "Options:"
|
|
echo "admin"
|
|
echo "db"
|
|
echo "mailgate"
|
|
echo "mailpit"
|
|
echo "source"
|
|
echo "hatch"
|
|
echo "odoo"
|
|
echo "odoo-nginx"
|
|
fi
|
|
|
|
if [[ "$1" =~ "admin" ]]; then
|
|
echo 'Open http://localhost:8000 url in your browser.'
|
|
open-url-with-delay "http://localhost:8000" 2 & $COMPOSE_COMMAND up -d admin
|
|
fi
|
|
|
|
if [[ "$1" =~ "db" ]]; then
|
|
$COMPOSE_COMMAND up -d db
|
|
fi
|
|
|
|
if [[ "$1" =~ "mailgate" ]]; then
|
|
echo 'Send emails to: smtp://localhost:587'
|
|
$COMPOSE_COMMAND up -d mailgate
|
|
fi
|
|
|
|
if [[ "$1" =~ "mailpit" ]]; then
|
|
echo 'Open http://localhost:8025 url in your browser.'
|
|
open-url-with-delay "http://localhost:8025" 2 & $COMPOSE_COMMAND up -d mailpit
|
|
fi
|
|
|
|
if [[ "$1" =~ "source" ]]; then
|
|
template-odoo-rc
|
|
local port=8069
|
|
while [[ $port -le 8079 ]]; do
|
|
if ! lsof -i:$port >/dev/null 2>&1; then
|
|
break
|
|
fi
|
|
port=$((port + 1))
|
|
done
|
|
|
|
echo "Open http://localhost:$port url in your browser."
|
|
open-url-with-delay "http://localhost:$port/web?debug=1&db=$database" 2 &
|
|
odoo --database="$database" --config "$ODOO_RC" \
|
|
--http-port="$port" --addons-path="$ADDONS_PATH" --dev=all --log-level="$LOG_LEVEL"
|
|
fi
|
|
|
|
if [[ "$1" =~ "hatch" ]]; then
|
|
template-odoo-rc
|
|
local port=8069
|
|
while [[ $port -le 8079 ]]; do
|
|
if ! lsof -i:$port >/dev/null 2>&1; then
|
|
break
|
|
fi
|
|
port=$((port + 1))
|
|
done
|
|
|
|
echo "Open http://localhost:$port url in your browser."
|
|
open-url-with-delay "http://localhost:$port/web?debug=1&db=$database" 2 &
|
|
odoo --database="$database" --config "$ODOO_RC" \
|
|
--http-port="$port" --dev=all --log-level="$LOG_LEVEL"
|
|
fi
|
|
|
|
if [[ "$1" =~ "odoo" ]]; then
|
|
echo "Start Odoo container ${CONTAINER_REGISTRY}/${CONTAINER_TAG}"
|
|
echo 'Open http://localhost:8069 url in your browser.'
|
|
open-url-with-delay "http://localhost:8069" 2 & $COMPOSE_COMMAND up -d odoo
|
|
fi
|
|
|
|
if [[ "$1" =~ "odoo-nginx" ]]; then
|
|
echo "Start Odoo container ${CONTAINER_REGISTRY}/${CONTAINER_TAG} with nginx proxy."
|
|
echo 'Open http://localhost:8080 url in your browser.'
|
|
open-url-with-delay "http://localhost:8080" 2 & $COMPOSE_COMMAND up -d odoo
|
|
fi
|
|
}
|
|
|
|
debug() {
|
|
if [[ -z "$1" ]]; then
|
|
echo '$1 is empty.'
|
|
exit
|
|
fi
|
|
set-addons-path
|
|
|
|
local port=8069
|
|
|
|
if [[ "$1" =~ "source" ]]; then
|
|
# Disable file validation to avoid frozen modules warning
|
|
export PYDEVD_DISABLE_FILE_VALIDATION=1
|
|
|
|
echo 'Starting Odoo with the debugger on port 5678.'
|
|
echo "Attach a debugpy debugger and open http://localhost:$port url in your browser."
|
|
|
|
python -m debugpy --listen 5678 --wait-for-client $(which odoo) --database="$DATABASE" --config "$ODOO_RC" \
|
|
--http-port="$port" --addons-path="$ADDONS_PATH" --dev=all --log-level="$LOG_LEVEL"
|
|
fi
|
|
}
|
|
|
|
run() {
|
|
if [[ -z "$1" ]]; then
|
|
echo '$1 is empty.'
|
|
exit
|
|
fi
|
|
if [[ -z "$2" ]]; then
|
|
echo '$2 is empty.'
|
|
exit
|
|
fi
|
|
|
|
template-compose
|
|
|
|
echo "Run '$1' container with command '$2'."
|
|
$COMPOSE_COMMAND run --rm "$1" "$2" "$3"
|
|
}
|
|
|
|
exec() {
|
|
if [[ -z "$1" ]]; then
|
|
echo '$1 is empty.'
|
|
exit
|
|
fi
|
|
local container="$1"
|
|
shift
|
|
local cmd="$@"
|
|
|
|
echo "Execute command '$cmd' with $CONTAINER_EXEC_PARAM in container '$container'."
|
|
$CONTAINER_ENGINE exec $CONTAINER_EXEC_PARAM "$container" $cmd
|
|
}
|
|
|
|
record-with-py-spy() {
|
|
if [[ -z "$1" ]]; then
|
|
echo '$1 is empty.'
|
|
exit
|
|
fi
|
|
|
|
local pid="$1"
|
|
local output_file="tmp/speedscope-capture.json"
|
|
local speedscope_url="https://www.speedscope.app/"
|
|
|
|
mkdir -p tmp
|
|
echo "source task source; py-spy record --pid $pid --output $output_file --format speedscope" | sudo bash --
|
|
|
|
open-with-os "$speedscope_url"
|
|
}
|
|
|
|
record-with-memray() {
|
|
if [[ -z "$1" ]]; then
|
|
echo '$1 is empty.'
|
|
exit
|
|
fi
|
|
set-addons-path
|
|
|
|
local port=8069
|
|
local output_file="tmp/memray-capture.bin"
|
|
|
|
if [[ "$1" =~ "source" ]]; then
|
|
|
|
echo 'Starting Odoo with memray.'
|
|
echo "Open http://localhost:$port url in your browser."
|
|
|
|
mkdir -p tmp
|
|
rm "$output_file"
|
|
python3 -m memray run -o "$output_file" $(which odoo) --database="$DATABASE" --config "$ODOO_RC" \
|
|
--http-port="$port" --addons-path="$ADDONS_PATH" --dev=all --log-level="$LOG_LEVEL"
|
|
python3 -m memray flamegraph "$output_file"
|
|
open-with-os "tmp/memray-flamegraph-output.html"
|
|
fi
|
|
}
|
|
|
|
remove() {
|
|
template-compose
|
|
|
|
if [[ -z "$1" ]]; then
|
|
$COMPOSE_COMMAND down -v
|
|
fi
|
|
|
|
if [[ "$1" =~ "db" ]]; then
|
|
$COMPOSE_COMMAND rm -f -s -v db
|
|
fi
|
|
|
|
if [[ "$1" =~ "admin" ]]; then
|
|
$COMPOSE_COMMAND rm -f -s -v admin
|
|
fi
|
|
|
|
if [[ "$1" =~ "odoo" ]]; then
|
|
$COMPOSE_COMMAND rm -f -s -v odoo
|
|
fi
|
|
|
|
if [[ "$1" =~ "mailpit" ]]; then
|
|
$COMPOSE_COMMAND rm -f -s -v mail
|
|
fi
|
|
|
|
if [[ "$1" =~ "mailgate" ]]; then
|
|
$COMPOSE_COMMAND rm -f -s -v mailgate
|
|
fi
|
|
}
|
|
|
|
restart() {
|
|
template-compose
|
|
|
|
if [[ -z "$1" ]]; then
|
|
$COMPOSE_COMMAND restart
|
|
fi
|
|
|
|
if [[ "db|mail|odoo" =~ $1 ]]; then
|
|
$COMPOSE_COMMAND restart "$1"
|
|
fi
|
|
}
|
|
|
|
stop() {
|
|
template-compose
|
|
|
|
if [[ -z "$1" ]]; then
|
|
$COMPOSE_COMMAND stop
|
|
fi
|
|
|
|
$COMPOSE_COMMAND stop "$1"
|
|
}
|
|
|
|
generate-ssh-keys() {
|
|
if [[ -z "$GIT_SSH_PRIVATE_KEY" ]]; then
|
|
echo 'Generate SSH private and public keys.'
|
|
mkdir -p tmp
|
|
if [[ ! -f "tmp/ed25519" ]]; then
|
|
ssh-keygen -t ed25519 -C admin@example.com -f tmp/ed25519 -N ''
|
|
fi
|
|
GIT_SSH_PRIVATE_KEY=$(cat tmp/ed25519 | base64 -w0)
|
|
GIT_SSH_PUBLIC_KEY=$(cat tmp/ed25519.pub)
|
|
fi
|
|
}
|
|
|
|
generate-pg-ssl-keys() {
|
|
local keymaterial_path="tmp/postgres-odoo"
|
|
local postgres_uid=70
|
|
local postgres_gid=70
|
|
|
|
if [[ "$POSTGRES_IMAGE" == "pgvector/pgvector"* ]]; then
|
|
postgres_uid=999
|
|
postgres_gid=999
|
|
fi
|
|
|
|
if [[ -z "$PG_SSL_CA_CRT" ]] || [[ -z "$PG_SSL_SERVER_CRT" ]] || [[ -z "$PG_SSL_SERVER_KEY" ]]; then
|
|
echo 'Generate PostgreSQL SSL/TLS certificates.'
|
|
mkdir -p "$keymaterial_path"
|
|
|
|
if [[ ! -f "$keymaterial_path/ca.crt" ]]; then
|
|
openssl genrsa -out "$keymaterial_path/ca.key" 4096
|
|
chmod 600 "$keymaterial_path/ca.key"
|
|
openssl req -x509 -new -nodes -key "$keymaterial_path/ca.key" -sha256 -days 3650 \
|
|
-out "$keymaterial_path/ca.crt" -subj "/C=CH/ST=State/L=Locality/O=Organization/CN=Root CA"
|
|
fi
|
|
|
|
if [[ ! -f "$keymaterial_path/server.crt" ]]; then
|
|
openssl genrsa -out "$keymaterial_path/server.key" 2048
|
|
openssl req -new -key "$keymaterial_path/server.key" -out "$keymaterial_path/server.csr" \
|
|
-subj "/C=CH/ST=State/L=Locality/O=Organization/CN=localhost"
|
|
openssl x509 -req -in "$keymaterial_path/server.csr" -CA "$keymaterial_path/ca.crt" \
|
|
-CAkey "$keymaterial_path/ca.key" -CAcreateserial -out "$keymaterial_path/server.crt" \
|
|
-days 365 -sha256
|
|
fi
|
|
|
|
local current_uid=$(stat -c '%u' "$keymaterial_path/server.key" 2>/dev/null || stat -f '%u' "$keymaterial_path/server.key" 2>/dev/null)
|
|
|
|
if [[ "$current_uid" != "$postgres_uid" ]]; then
|
|
chmod 600 "$keymaterial_path/server.key"
|
|
chmod 644 "$keymaterial_path/server.crt"
|
|
chmod 644 "$keymaterial_path/ca.crt"
|
|
sudo chown "$postgres_uid:$postgres_gid" "$keymaterial_path/server.key" "$keymaterial_path/server.crt" "$keymaterial_path/ca.crt"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
test-project() {
|
|
# This starts the Docker environment and uses internal and external tools to run commands.
|
|
|
|
if [[ "$1" == "cleanup" ]]; then
|
|
echo -e "\033[38;5;214mTEST\033[0m: Cleanup test environment."
|
|
drop-db test
|
|
remove odoo
|
|
git -C oca/partner-contact clean -df
|
|
git -C oca/partner-contact checkout .
|
|
remove-dotenv test
|
|
else
|
|
version
|
|
info
|
|
set -e
|
|
|
|
echo -e "\033[38;5;214mTEST\033[0m: Clone module repo with https"
|
|
(git clone -b "$ODOO_VERSION" https://github.com/OCA/partner-contact.git oca/partner-contact) || true
|
|
|
|
echo -e "\033[38;5;214mTEST\033[0m: Start container environment."
|
|
export BROWSER_OPEN=false
|
|
start db,mail
|
|
|
|
echo -e "\033[38;5;214mTEST\033[0m: Clone Odoo addons."
|
|
generate-ssh-keys
|
|
export GIT_SSH_PRIVATE_KEY
|
|
export GIT_SSH_PUBLIC_KEY
|
|
export ADDONS_GIT_REPOS="https://github.com/OCA/server-env.git#${ODOO_VERSION},https://github.com/OCA/server-tools.git#${ODOO_VERSION},https://github.com/Mint-System/Odoo-Apps-Server-Tools.git#${ODOO_VERSION}"
|
|
run odoo clone-git-addons
|
|
|
|
echo -e "\033[38;5;214mTEST\033[0m: Init Odoo database."
|
|
export ODOO_ADDONS_PATH="/mnt/oca/partner-contact"
|
|
export DB_NAME=test
|
|
export WITHOUT_DEMO=False
|
|
export ODOO_INIT_LOGIN=test
|
|
export ODOO_INIT_PASSWORD=test
|
|
export ODOO_INIT_LANG=de_CH
|
|
export ODOO_INIT_ADDONS=server_environment_ir_config_parameter,module_auto_update,prometheus_exporter,mail_server_filter,contacts,web_enterprise
|
|
export PYTHON_INSTALL=prometheus-client,manifestoo
|
|
run odoo init-db
|
|
|
|
echo -e "\033[38;5;214mTEST\033[0m: Start Odoo container."
|
|
export ODOO_MAIL_SMTP_HOST=mail
|
|
export ODOO_MAIL_SMTP_PORT=1025
|
|
export ODOO_MAIL_SMTP_ENCRYPTION=none
|
|
export SERVER_WIDE_MODULES=module_change_auto_install
|
|
export MODULE_AUTO_INSTALL_DISABLED=odoo_test_xmlrunner
|
|
export AUTO_UPDATE_TRANSLATIONS=True
|
|
export AUTO_UPDATE_MODULES_LIST=True
|
|
export MAIL_DEFAULT_FROM="test"
|
|
export BROWSER_OPEN=false
|
|
export TEST_ADDONS_DIR=/mnt/oca/partner-contact
|
|
export TEST_INCLUDE=partner_firstname
|
|
start odoo
|
|
TIMEOUT=60
|
|
ELAPSED=0
|
|
until curl --silent --fail "http://localhost:8069/web/login" &> /dev/null; do
|
|
if [ "$ELAPSED" -ge "$TIMEOUT" ]; then
|
|
echo "Odoo did not start within $TIMEOUT seconds."
|
|
docker logs odoo
|
|
exit 1
|
|
fi
|
|
echo 'Waiting for Odoo to be ready...'
|
|
sleep 3
|
|
ELAPSED=$((ELAPSED + 3))
|
|
done
|
|
|
|
echo -e "\033[38;5;214mTEST\033[0m: Init repos.yml file."
|
|
cat << EOF > tmp/repos.yml
|
|
github.com/Mint-System/Odoo-Apps-Partner-Contact:
|
|
remotes:
|
|
mint_system: https://github.com/Mint-System/Odoo-Apps-Partner-Contact.git
|
|
merges:
|
|
- mint_system ${ODOO_VERSION}
|
|
target: mint_system ${ODOO_VERSION}
|
|
EOF
|
|
docker cp tmp/repos.yml odoo:/var/lib/odoo/git/repos.yml
|
|
exec odoo aggregate-git-repos
|
|
|
|
echo -e "\033[38;5;214mTEST\033[0m: Init OCA module."
|
|
exec odoo init-module partner_firstname
|
|
|
|
echo -e "\033[38;5;214mTEST\033[0m: Setup and check RPC credentials."
|
|
export ODOO_URL="http://localhost:8069"
|
|
export ODOO_DATABASE=test
|
|
export ODOO_USERNAME=test
|
|
export ODOO_PASSWORD=test
|
|
if ! show-dotenv test; then
|
|
create-dotenv test odoo
|
|
fi
|
|
show-dotenv test
|
|
test-xmlrpc test
|
|
|
|
echo -e "\033[38;5;214mTEST\033[0m: Install, update, disable and remove snippet."
|
|
install-snippet test snippets/mint_system.base.view_partner_form.show_type.xml
|
|
update-snippet test snippets/mint_system.base.view_partner_form.show_type.xml
|
|
disable-snippet test snippets/mint_system.base.view_partner_form.show_type.xml
|
|
remove-snippet test snippets/mint_system.base.view_partner_form.show_type.xml
|
|
|
|
echo -e "\033[38;5;214mTEST\033[0m: Check module auto update."
|
|
sed -i 's/First name/Firstname/g' oca/partner-contact/partner_firstname/models/res_partner.py
|
|
exec odoo update-modules
|
|
#FIXME: Check if module is auto updated
|
|
# docker logs odoo 2>&1 | grep "Run click-odoo-update."
|
|
|
|
echo -e "\033[38;5;214mTEST\033[0m: Check translations auto update."
|
|
sed -i 's/Nachname/Familienname/g' oca/partner-contact/partner_firstname/i18n/de.po
|
|
restart odoo
|
|
#FIXME: Check if translations updated
|
|
# docker logs odoo 2>&1 | grep "Updating translations for all active languages and installed modules."
|
|
|
|
echo -e "\033[38;5;214mTEST\033[0m: Drop the test database."
|
|
drop-db test
|
|
|
|
#FIXME: These tests do not work in GitHub Runner env.
|
|
# echo -e "\033[38;5;214mTEST\033[0m: Prepare container for testing."
|
|
# exec odoo setup-tests
|
|
# exec odoo ls /var/lib/odoo/git/github.com/Mint-System/Odoo-Apps-Partner-Contact
|
|
|
|
# echo -e "\033[38;5;214mTEST\033[0m: Run module tests."
|
|
# CONTAINER_EXEC_PARAM="-w /mnt/oca/partner-contact"
|
|
# exec odoo run-tests
|
|
|
|
echo -e "\033[38;5;214mTEST\033[0m: Run module migration."
|
|
echo "Setup pre migration script for partner_firstname..."
|
|
mkdir -p oca/partner-contact/partner_firstname/migrations/${ODOO_VERSION}.2.0.0
|
|
cat << EOF > oca/partner-contact/partner_firstname/migrations/${ODOO_VERSION}.2.0.0/pre-migrate.py
|
|
import logging
|
|
from odoo.upgrade import util
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
|
|
def migrate(cr, version):
|
|
env = util.env(cr)
|
|
|
|
partners = env["res.partner"].search([])
|
|
for partner in partners:
|
|
partner.name += "!"
|
|
|
|
_logger.info("Updated %s partners", len(partners))
|
|
EOF
|
|
sed -i "/\"version\":/c\ \"version\": \"${ODOO_VERSION}.2.0.0\"," oca/partner-contact/partner_firstname/__manifest__.py
|
|
echo "Execute the module upgrade."
|
|
exec odoo update-module partner_firstname
|
|
#FIXME: Check if module is migrated
|
|
# docker logs odoo 2>&1 | grep "module partner_firstname: Running migration"
|
|
fi
|
|
}
|
|
|
|
# Interactive Shell commands
|
|
|
|
shell() {
|
|
local database="$1"
|
|
if [[ -z "$database" ]]; then
|
|
database="$DB_NAME"
|
|
fi
|
|
local code="$2"
|
|
|
|
set-addons-path
|
|
if [[ -n "$code" ]]; then
|
|
echo "$code" | odoo shell --database "$database" --config "$ODOO_RC" --addons-path="$ADDONS_PATH" --no-http
|
|
else
|
|
odoo shell --database "$database" --config "$ODOO_RC" --addons-path="$ADDONS_PATH" --no-http
|
|
fi
|
|
}
|
|
|
|
psql() {
|
|
local database="$1"
|
|
if [[ -z "$database" ]]; then
|
|
database="$DB_NAME"
|
|
fi
|
|
|
|
$CONTAINER_ENGINE exec -it db psql "postgres://odoo:odoo@localhost:5432/$database"
|
|
}
|
|
|
|
# Database commands
|
|
|
|
init-db() {
|
|
local odoo_init_addons="${ODOO_INIT_ADDONS:-"web"}"
|
|
local database="$1"
|
|
if [[ -z "$database" ]]; then
|
|
database="$DB_NAME"
|
|
fi
|
|
|
|
set-addons-path
|
|
template-odoo-rc
|
|
|
|
echo "Initialize database $database with $odoo_init_addons."
|
|
if [[ -n "$ODOO_PARAM" ]]; then
|
|
odoo -d "$database" -i "$odoo_init_addons" --config "$ODOO_RC" --addons-path="$ADDONS_PATH" --stop-after-init --no-http --load-language "$ODOO_LANGUAGE" "$ODOO_PARAM"
|
|
else
|
|
odoo -d "$database" -i "$odoo_init_addons" --config "$ODOO_RC" --addons-path="$ADDONS_PATH" --stop-after-init --no-http --load-language "$ODOO_LANGUAGE"
|
|
fi
|
|
}
|
|
|
|
import-csv() {
|
|
if [[ -z "$1" ]]; then
|
|
echo '$1 is empty.'
|
|
exit
|
|
fi
|
|
|
|
local database="$1"
|
|
local file_path="$2"
|
|
|
|
# Use default database if second param is not given
|
|
if [[ -z "$2" ]]; then
|
|
database="$DB_NAME"
|
|
file_path="$1"
|
|
fi
|
|
|
|
local file_name=$(basename "$file_path")
|
|
echo "Importing $file_name to ${database}..."
|
|
$CONTAINER_ENGINE cp "$file_path" db:/tmp/
|
|
$CONTAINER_ENGINE exec db psql "postgres://odoo:odoo@localhost:5432/$database" -c "\copy $file_name FROM '/tmp/$file_name' DELIMITER ',' CSV HEADER;"
|
|
}
|
|
|
|
drop-db() {
|
|
local database="$1"
|
|
if [[ -z "$database" ]]; then
|
|
database="$DB_NAME"
|
|
fi
|
|
|
|
$CONTAINER_ENGINE exec -i db psql "postgres://odoo:odoo@localhost:5432/postgres" -c "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = '$database';"
|
|
$CONTAINER_ENGINE exec -i db psql "postgres://odoo:odoo@localhost:5432/postgres" -c "DROP DATABASE \"$database\";"
|
|
}
|
|
|
|
load-language() {
|
|
if [[ -z "$1" ]]; then
|
|
echo '$1 is empty.'
|
|
exit
|
|
fi
|
|
|
|
local database="$1"
|
|
local language="$2"
|
|
|
|
# Use default database if second param is not given
|
|
if [[ -z "$2" ]]; then
|
|
database="$DB_NAME"
|
|
language="$1"
|
|
fi
|
|
set-addons-path
|
|
|
|
echo "Setup language $language for database $database:"
|
|
odoo -d "$database" -u all --config "$ODOO_RC" --addons-path="$ADDONS_PATH" --stop-after-init --no-http --load-language "$language"
|
|
}
|
|
|
|
set-admin() {
|
|
local database="$1"
|
|
if [[ -z "$database" ]]; then
|
|
database="$DB_NAME"
|
|
fi
|
|
|
|
$CONTAINER_ENGINE exec -i db psql "postgres://odoo:odoo@localhost:5432/$database" -c "UPDATE res_users SET active=true,password='\$pbkdf2-sha512\$25000\$JuScEwIg5JzTGqNUivFeqw\$yWTOcix2Afr3XGP2NPY7w4w49e9vpsu14NRndDYXAkbtMF4zkrmx6inVsoLl0zZY30xI/0GzhwonWsK9TUmjWA' WHERE id=2;"
|
|
$CONTAINER_ENGINE exec -i db psql "postgres://odoo:odoo@localhost:5432/$database" -c "SELECT login, password FROM res_users WHERE id=2;"
|
|
}
|
|
|
|
clear-assets() {
|
|
local database="$1"
|
|
if [[ -z "$database" ]]; then
|
|
database="$DB_NAME"
|
|
fi
|
|
|
|
echo "Delete these assets for $database:"
|
|
$CONTAINER_ENGINE exec -i db psql "postgres://odoo:odoo@localhost/$database" -c "select id,name from ir_attachment where res_model='ir.ui.view' and name like '%assets_%';"
|
|
$CONTAINER_ENGINE exec -i db psql "postgres://odoo:odoo@localhost/$database" -c "delete from ir_attachment where res_model='ir.ui.view' and name like '%assets_%';"
|
|
}
|
|
|
|
clear-views() {
|
|
local database="$1"
|
|
if [[ -z "$database" ]]; then
|
|
database="$DB_NAME"
|
|
fi
|
|
|
|
echo "Archive views with prefix '$SNIPPET_PREFIX."
|
|
$CONTAINER_ENGINE exec -i db psql "postgres://odoo:odoo@localhost/$database" -c "SELECT id,name from ir_ui_view WHERE name like '%$SNIPPET_PREFIX%';"
|
|
$CONTAINER_ENGINE exec -i db psql "postgres://odoo:odoo@localhost/$database" -c "UPDATE ir_ui_view SET active=false WHERE name like '%$SNIPPET_PREFIX%';"
|
|
|
|
echo 'Archive views with prefix '\''Odoo Studio:'\''.'
|
|
$CONTAINER_ENGINE exec -i db psql "postgres://odoo:odoo@localhost/$database" -c "SELECT id,name from ir_ui_view WHERE name like '%Odoo Studio:%';"
|
|
$CONTAINER_ENGINE exec -i db psql "postgres://odoo:odoo@localhost/$database" -c "UPDATE ir_ui_view SET active=false WHERE name like '%Odoo Studio:%';"
|
|
|
|
echo "Delete all active views for $database in $CONTAINER :"
|
|
$CONTAINER_ENGINE exec -i db psql "postgres://odoo:odoo@localhost/$database" -c "DELETE FROM report_layout WHERE view_id IN (SELECT id FROM ir_ui_view WHERE active = TRUE);"
|
|
$CONTAINER_ENGINE exec -i db psql "postgres://odoo:odoo@localhost/$database" -c "WITH RECURSIVE views_to_delete AS (SELECT id, inherit_id FROM ir_ui_view WHERE active = TRUE AND inherit_id IS NULL UNION ALL SELECT v.id, v.inherit_id FROM ir_ui_view v JOIN views_to_delete p ON v.inherit_id = p.id) DELETE FROM ir_ui_view WHERE id IN (SELECT id FROM views_to_delete);"
|
|
}
|
|
|
|
clear-filestore() {
|
|
echo "Remove directory $HOME/.local/share/Odoo/filestore/$1"
|
|
rm -rf "$HOME/.local/share/Odoo/filestore/$1"
|
|
}
|
|
|
|
disable-mailserver() {
|
|
if [[ -z "$1" ]]; then
|
|
echo '$1 is empty.'
|
|
exit
|
|
fi
|
|
|
|
load-dotenv "$1"
|
|
images/odoocli/bin/odoocli --method 'write' --model 'ir.mail_server' --field 'active' --value False --domain "[]"
|
|
images/odoocli/bin/odoocli --method 'write' --model 'fetchmail.server' --field 'active' --value False --domain "[]"
|
|
}
|
|
|
|
change-uuid() {
|
|
if [[ -z "$1" ]]; then
|
|
echo '$1 is empty.'
|
|
exit
|
|
fi
|
|
|
|
load-dotenv "$1"
|
|
local uuid=$(uuidgen)
|
|
echo "Set uuid $uuid for $1."
|
|
images/odoocli/bin/odoocli --method 'write' --model 'ir.config_parameter' --domain "[('key', '=', 'database.uuid')]" --field 'value' --value "'$uuid'"
|
|
}
|
|
|
|
update-module-list() {
|
|
if [[ -z "$1" ]]; then
|
|
echo '$1 is empty.'
|
|
exit
|
|
fi
|
|
|
|
set-addons-path
|
|
echo "env['ir.module.module'].update_list()" | odoo shell --database "$1" --config "$ODOO_RC" --addons-path="$ADDONS_PATH" --no-http
|
|
}
|
|
|
|
patch-database() {
|
|
if [[ -z "$1" ]]; then
|
|
echo '$1 is empty.'
|
|
exit
|
|
fi
|
|
|
|
local database="$1"
|
|
local filename="$2"
|
|
|
|
# Use default database if second param is not given
|
|
if [[ -z "$2" ]]; then
|
|
database="$DB_NAME"
|
|
filename="$1"
|
|
fi
|
|
|
|
echo "Apply patch $filename to Database $database."
|
|
cat "$filename" | $CONTAINER_ENGINE exec -i db psql "postgres://$POSTGRES_USER:$POSTGRES_PASSWORD@localhost/$database"
|
|
echo 'Patching database succeeded'
|
|
}
|
|
|
|
# Report commands
|
|
|
|
cloc-odoo() {
|
|
local database="$1"
|
|
if [[ -z "$database" ]]; then
|
|
database="$DB_NAME"
|
|
fi
|
|
|
|
set-addons-path
|
|
odoo cloc -d "$database" --config "$ODOO_RC" --addons-path="$ADDONS_PATH" --stop-after-init --no-http
|
|
}
|
|
|
|
visualize-dependencies() {
|
|
if [[ -z "$1" ]]; then
|
|
echo '$1 is empty.'
|
|
exit
|
|
fi
|
|
|
|
mkdir -p tmp
|
|
./bin/odoo-module-dependencies "$1"
|
|
open-with-os tmp/odoo-module-dependencies.html
|
|
cat tmp/odoo-module-dependencies.mmd
|
|
}
|
|
|
|
# Setup commands
|
|
|
|
install-requirements() {
|
|
if [[ -z "$1" ]]; then
|
|
echo '$1 is empty.'
|
|
exit
|
|
fi
|
|
|
|
local database="$1"
|
|
local file_path="$2"
|
|
|
|
# Use default database if second param is not given
|
|
if [[ -z "$2" ]]; then
|
|
database="$DB_NAME"
|
|
file_path="$1"
|
|
fi
|
|
|
|
local file_name=$(basename "$file_path")
|
|
if ! [[ "$file_name" =~ .*requirements\.txt$ ]]; then
|
|
echo '$1 must point to requirements.txt file.'
|
|
exit
|
|
fi
|
|
|
|
local container_running=$($CONTAINER_ENGINE container inspect -f '{{.State.Running}}' odoo)
|
|
if [[ "$container_running" == "true" ]]; then
|
|
echo 'Installing python packages in container...'
|
|
$CONTAINER_ENGINE exec -u root odoo python -m pip install -r "/mnt/$file_path"
|
|
else
|
|
echo 'Installing python packages in virutal env...'
|
|
uv pip install -r "$file_path"
|
|
fi
|
|
}
|
|
|
|
list-packages() {
|
|
uv pip list
|
|
}
|
|
|
|
generate-module-repo() {
|
|
if [[ -z "$1" ]]; then
|
|
echo '$1 is empty.'
|
|
exit
|
|
fi
|
|
|
|
local repo_path="$1"
|
|
local repo_name=$(basename "$repo_path")
|
|
|
|
# If repo already exists, create new branch
|
|
if [[ -d "$repo_path/.git" ]]; then
|
|
echo "Create new branch $repo_name in $repo_path"
|
|
cd "$repo_path" || exit
|
|
git switch --orphan -c "$ODOO_VERSION"
|
|
|
|
echo "Remove all files from $repo_path"
|
|
rm -rf "$repo_path/*"
|
|
fi
|
|
|
|
echo "Copy template files from templates/$ODOO_VERSION to $repo_path"
|
|
cp "templates/$ODOO_VERSION/*" "$repo_path"
|
|
}
|
|
|
|
# Module commands
|
|
|
|
init-module() {
|
|
if [[ -z "$1" ]]; then
|
|
echo '$1 is empty.'
|
|
exit
|
|
fi
|
|
|
|
local database="$1"
|
|
local module_path="$2"
|
|
|
|
# Use default database if second param is not given
|
|
if [[ -z "$2" ]]; then
|
|
database="$DB_NAME"
|
|
module_path="$1"
|
|
fi
|
|
|
|
# Check if module path is a module repo
|
|
if [[ -f "$module_path/README.md" ]]; then
|
|
|
|
# Look for manifest files in repo folder
|
|
# Get parent folder name of manifest files
|
|
# Create a comma separated string of folder names
|
|
local modules=$(get-modules "$module_path" basename)
|
|
else
|
|
local modules=$(basename "$module_path")
|
|
fi
|
|
|
|
set-addons-path
|
|
echo "Initialize module $modules on ${database}..."
|
|
odoo -d "$database" -i "$modules" --config "$ODOO_RC" \
|
|
--addons-path="$ADDONS_PATH" --stop-after-init --no-http \
|
|
--log-level="$LOG_LEVEL"
|
|
}
|
|
|
|
update-module() {
|
|
if [[ -z "$1" ]]; then
|
|
echo '$1 is empty.'
|
|
exit
|
|
fi
|
|
|
|
local database="$1"
|
|
local module_name=$(basename "$2")
|
|
|
|
# Use default database if second param is not given
|
|
if [[ -z "$2" ]]; then
|
|
database="$DB_NAME"
|
|
module_name=$(basename "$1")
|
|
fi
|
|
|
|
set-addons-path
|
|
echo "Updating module $module_name on ${database}..."
|
|
if [[ -n "$ODOO_PARAM" ]]; then
|
|
odoo --database "$database" --update "$module_name" \
|
|
--config "$ODOO_RC" --addons-path="$ADDONS_PATH" --stop-after-init --no-http "$ODOO_PARAM"
|
|
else
|
|
odoo --database "$database" --update "$module_name" \
|
|
--config "$ODOO_RC" --addons-path="$ADDONS_PATH" --stop-after-init --no-http
|
|
fi
|
|
}
|
|
|
|
translate-module() {
|
|
if [[ -z "$1" ]]; then
|
|
echo '$1 is empty.'
|
|
exit
|
|
fi
|
|
|
|
local module_path="$1"
|
|
local module_name=$(basename "$1")
|
|
local language="$2"
|
|
local database="$3"
|
|
|
|
if [[ -z "$2" ]]; then
|
|
language="$ODOO_LANGUAGE"
|
|
fi
|
|
|
|
if [[ -z "$3" ]]; then
|
|
database="$DB_NAME"
|
|
fi
|
|
|
|
set-addons-path
|
|
echo "Generate $language tranlsation file for $module_name on ${database}..."
|
|
mkdir -p "$module_path/i18n/"
|
|
odoo -d "$database" --config "$ODOO_RC" --addons-path="$ADDONS_PATH" --modules "$module_name" -l "$language" --i18n-export "$module_path/i18n/$language.po"
|
|
|
|
# If lang is de_CH then rename the file to de.po.
|
|
if [[ "$language" == "de_CH" ]]; then
|
|
mv "$module_path/i18n/$language.po" "$module_path/i18n/de.po"
|
|
fi
|
|
}
|
|
|
|
test-module() {
|
|
if [[ -z "$1" ]]; then
|
|
echo '$1 is empty.'
|
|
exit
|
|
fi
|
|
local module=$(basename "$1")
|
|
|
|
set-addons-path
|
|
echo "Testing module $module..."
|
|
odoo -d "$DB_NAME" -i "$module" --config "$ODOO_RC" --addons-path="$ADDONS_PATH" --stop-after-init --no-http --test-tags /"$module_name"
|
|
}
|
|
|
|
test-modules() {
|
|
if [[ -z "$1" ]]; then
|
|
echo '$1 is empty.'
|
|
exit
|
|
fi
|
|
local modules=$(get-modules "$1" basename)
|
|
set-addons-path
|
|
|
|
echo "Collect requirements.txt files in $1."
|
|
find "$1" -type f -name "requirements.txt" -exec uv pip install -r {} \;
|
|
|
|
echo "Collect test-requirements.txt files in $1."
|
|
find "$1" -type f -name "test-requirements.txt" -exec uv pip install -r {} \;
|
|
|
|
depends_modules=$(manifestoo --select-addons-dir "$1" list-depends --separator=,)
|
|
echo "Setup test database with "$ODOO_LANGUAGE" lang and depends modules: $depends_modules"
|
|
odoo -d "$DB_NAME" -i "$depends_modules" --config "$ODOO_RC" --addons-path="$ADDONS_PATH" --stop-after-init --no-http --load-language "$ODOO_LANGUAGE"
|
|
|
|
echo "Testing modules: $modules"
|
|
odoo -d "$DB_NAME" -i "$modules" --config "$ODOO_RC" --addons-path="$ADDONS_PATH" --stop-after-init --no-http --test-enable
|
|
}
|
|
|
|
pytest-module() {
|
|
if [[ -z "$1" ]]; then
|
|
echo '$1 is empty.'
|
|
exit
|
|
fi
|
|
local module_path="$1"
|
|
|
|
echo "Setup custom tmp/odoo.conf for pytest."
|
|
set-addons-path
|
|
local odoo_config="tmp/odoo.conf"
|
|
echo "[options]" > "$odoo_config"
|
|
echo "addons_path=$ADDONS_PATH" >> "$odoo_config"
|
|
echo "db_host = localhost" >> "$odoo_config"
|
|
echo "db_password = odoo" >> "$odoo_config"
|
|
echo "db_user = odoo" >> "$odoo_config"
|
|
|
|
echo "Run pytest with odoo-http option..."
|
|
pytest -s --odoo-http --odoo-config="$odoo_config" --odoo-database="$DB_NAME" "$module_path"
|
|
}
|
|
|
|
remove-module() {
|
|
if [[ -z "$1" ]]; then
|
|
echo '$1 is empty.'
|
|
exit
|
|
fi
|
|
|
|
local database="$1"
|
|
local module_name=$(basename "$2")
|
|
|
|
# Use default database if second param is not given
|
|
if [[ -z "$2" ]]; then
|
|
database="$DB_NAME"
|
|
module_name=$(basename "$1")
|
|
fi
|
|
|
|
set-addons-path
|
|
echo "Remove module $module_name"
|
|
echo "self.env['ir.module.module'].search([('name', '=', '$module_name')]).button_immediate_uninstall()" |
|
|
odoo shell --database "$database" --config "$ODOO_RC" --addons-path="$ADDONS_PATH" --no-http
|
|
}
|
|
|
|
create-module() {
|
|
if [[ -z "$1" ]]; then
|
|
echo '$1 is empty.'
|
|
exit
|
|
fi
|
|
|
|
local module_name=$(basename "$1")
|
|
local parent_dir=$(dirname "$1")
|
|
|
|
echo "Scaffolding module $module_name in ${parent_dir}..."
|
|
odoo scaffold "$module_name" "$parent_dir" -t templates/module
|
|
|
|
echo "Update title, summary, version, license, depends and data in the '__manifest__.py'."
|
|
echo "Remove the 'security' folder if not required."
|
|
}
|
|
|
|
generate-module-docs() {
|
|
if [[ -z "$1" ]]; then
|
|
echo '$1 is empty.'
|
|
exit
|
|
fi
|
|
local module_path="$1"
|
|
local manifest_path="$module_path/__manifest__.py"
|
|
|
|
echo 'Setup readme folder'
|
|
mkdir -p "$module_path/readme"
|
|
cp -n -r "templates/module/readme" "$module_path"
|
|
|
|
echo 'Update DESCRIPTION.rst'
|
|
export MODULE_SUMMARY=$(python3 -c "import ast; d=ast.literal_eval(open('$manifest_path').read()); print(d.get('summary', '').replace('\n', ' ').strip())")
|
|
envsubst < templates/module/readme/DESCRIPTION.md > "$module_path/readme/DESCRIPTION.md"
|
|
|
|
echo 'Update CONTRIBUTORS.rst'
|
|
export GIT_USER_NAME="$(git config user.name)"
|
|
export GIT_USER_EMAIL="$(git config user.email)"
|
|
envsubst < templates/module/readme/CONTRIBUTORS.md > "$module_path/readme/CONTRIBUTORS.md"
|
|
|
|
echo 'Copy icon.png'
|
|
cp templates/module/static/description/icon.png "$module_path/static/description/icon.png"
|
|
|
|
echo 'Copy LICENSE'
|
|
cp templates/module/LICENSE "$module_path/"
|
|
|
|
echo 'Touch __init__.py'
|
|
touch "$module_path/__init__.py"
|
|
|
|
echo "Generate OCA readme for $ORG_NAME org."
|
|
local repo_url=$(git -C "$(dirname $module_path)" remote get-url origin)
|
|
local repo_name=$(basename "${repo_url%.git}")
|
|
oca-gen-addon-readme --repo-name=$repo_name --branch=$ODOO_VERSION --org-name=$ORG_NAME --addon-dir=$module_path
|
|
}
|
|
|
|
generate-module-migration() {
|
|
if [[ -z "$1" ]]; then
|
|
echo '$1 is empty.'
|
|
exit
|
|
fi
|
|
local module_path="$1"
|
|
local module_version=$(get-module-version $module_path)
|
|
|
|
echo "Create migrations folder for version $module_version."
|
|
mkdir -p "$module_path/migrations/$module_version"
|
|
|
|
echo "Copy pre-migrate template."
|
|
cp "templates/migrations/pre-migrate.py" "$module_path/migrations/$module_version"
|
|
}
|
|
|
|
generate-module-security() {
|
|
if [[ -z "$1" ]]; then
|
|
echo '$1 is empty.'
|
|
exit
|
|
fi
|
|
if [[ -z "$2" ]]; then
|
|
echo '$2 is empty.'
|
|
exit
|
|
fi
|
|
|
|
local module_name=$(basename "$1")
|
|
local parent_dir=$(dirname "$1")
|
|
local model_dot_name="$2"
|
|
local model_name=$(echo "$model_dot_name" | tr '.' '_')
|
|
|
|
local access_file="$parent_dir/$module_name/security/ir.model.access.csv"
|
|
mkdir -p "$parent_dir/$module_name/security"
|
|
|
|
if [[ ! -f "$access_file" ]]; then
|
|
echo "Init security file: $access_file"
|
|
echo "id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink" > "$access_file"
|
|
fi
|
|
|
|
# id
|
|
# name
|
|
# model_id:id
|
|
# group_id:id
|
|
# perm_read
|
|
# perm_write
|
|
# perm_create
|
|
# perm_unlink
|
|
|
|
echo "Add model access to $access_file"
|
|
echo "access_${model_name}_user,access.$model_dot_name.user,model_${model_name},base.group_user,1,0,0,0" >> "$access_file"
|
|
echo "access_${model_name}_manager,access.$model_dot_name.manager,model_${model_name},base.group_erp_manager,1,1,1,1" >> "$access_file"
|
|
}
|
|
|
|
export-module-vars() {
|
|
export MODULE_NAME=$(basename "$1") # partner_email_history
|
|
export MODEL_DOT_NAME="$2" # res.partner
|
|
export MODEL_NAME=$(echo "$MODEL_DOT_NAME" | tr '.' '_') # res_partner
|
|
export NAME=$(echo "$MODEL_DOT_NAME" | cut -d"." -f2 | sed 's/[^_]\+/\L\u&/g') # Partners
|
|
export MODEL_DESCRIPTION=$(echo "$MODEL_NAME" | sed 's/[^_]\+/\L\u&/g' | sed 's/_/ /g') # Res Partner
|
|
export MODEL_CAMEL_NAME=$(echo "$MODEL_DESCRIPTION" | sed 's/ //g') # ResPartner
|
|
}
|
|
|
|
export-module-inherit-vars() {
|
|
export MODEL_DOT_NAME="$3" # sale.order
|
|
export VIEW_REF="$2" # sale.view_order_form
|
|
export MODEL_NAME=${MODEL_DOT_NAME//\./_} # sale_order
|
|
export MODULE_NAME=$(basename "$1") # sale_helm
|
|
export SRC_MODULE_NAME=$(echo "$VIEW_REF" | cut -d"." -f1) # sale
|
|
export XML_ID=$(echo "$VIEW_REF" | cut -d"." -f2) # view_order_form
|
|
}
|
|
|
|
generate-module-model() {
|
|
if [[ -z "$1" ]]; then
|
|
echo '$1 is empty.'
|
|
exit
|
|
fi
|
|
if [[ -z "$2" ]]; then
|
|
echo '$2 is empty.'
|
|
exit
|
|
fi
|
|
local module_path="$1"
|
|
local module_name=$(basename "$1")
|
|
|
|
echo 'Substitute model template'
|
|
export-module-vars "$1" "$2"
|
|
envsubst < templates/model.py > "$module_path/models/$MODEL_NAME.py"
|
|
echo "from . import $model_name" >> "$module_path/models/__init__.py"
|
|
echo "File "$module_path/models/$MODEL_NAME.py created.
|
|
}
|
|
|
|
generate-module-inherit() {
|
|
if [[ -z "$1" ]]; then
|
|
echo '$1 is empty.'
|
|
exit
|
|
fi
|
|
if [[ -z "$2" ]]; then
|
|
echo '$2 is empty.'
|
|
exit
|
|
fi
|
|
local module_path="$1"
|
|
local module_name=$(basename "$1")
|
|
|
|
echo 'Substitute model template'
|
|
export-module-vars "$1" "$2"
|
|
envsubst < templates/inherit.py > "$module_path/models/$MODEL_NAME.py"
|
|
echo "from . import $MODEL_NAME" >> "$module_path/models/__init__.py"
|
|
echo "File "$module_path/models/$MODEL_NAME.py created.
|
|
}
|
|
|
|
generate-module-views() {
|
|
if [[ -z "$1" ]]; then
|
|
echo '$1 is empty.'
|
|
exit
|
|
fi
|
|
if [[ -z "$2" ]]; then
|
|
echo '$2 is empty.'
|
|
exit
|
|
fi
|
|
local module_path="$1"
|
|
local module_name=$(basename "$1")
|
|
|
|
echo 'Substitute view template'
|
|
mkdir -p "$module_path/views"
|
|
export-module-vars "$2"
|
|
envsubst < templates/model_views.xml > "$module_path/views/${MODEL_NAME}_views.xml"
|
|
echo "Update file $module_name/__manifest__.py with:"
|
|
echo -e "\t\"views/${MODEL_NAME}_views.xml\","
|
|
}
|
|
|
|
generate-module-snippet() {
|
|
if [[ -z "$1" ]]; then
|
|
echo '$1 is empty.'
|
|
exit
|
|
fi
|
|
if [[ -z "$2" ]]; then
|
|
echo '$2 is empty.'
|
|
exit
|
|
fi
|
|
if [[ -z "$3" ]]; then
|
|
echo '$3 is empty.'
|
|
exit
|
|
fi
|
|
local module_path="$1"
|
|
|
|
echo 'Substitute snippet template'
|
|
mkdir -p "$module_path/views"
|
|
export-module-inherit-vars "$1" "$2" "$3" # addons/sale_workflow/sale_helm sale.view_order_form sale.order
|
|
envsubst < templates/model_snippet.xml > "$module_path/views/${MODEL_NAME}_views.xml"
|
|
echo "Update file $MODULE_NAME/__manifest__.py with:"
|
|
echo -e "\t\"views/${MODEL_NAME}_views.xml\","
|
|
}
|
|
|
|
generate-module-report() {
|
|
if [[ -z "$1" ]]; then
|
|
echo '$1 is empty.'
|
|
exit
|
|
fi
|
|
if [[ -z "$2" ]]; then
|
|
echo '$2 is empty.'
|
|
exit
|
|
fi
|
|
local module_path="$1"
|
|
local module_name=$(basename "$1")
|
|
|
|
echo 'Substitute view template'
|
|
mkdir -p "$module_path/report"
|
|
export-module-vars "$1" "$2"
|
|
envsubst < templates/model_templates.xml > "$module_path/report/${MODEL_NAME}_templates.xml"
|
|
echo "Update file $module_name/__manifest__.py with:"
|
|
echo -e "\t\"report/${MODEL_NAME}_templates.xml\","
|
|
}
|
|
|
|
release-module() {
|
|
if [[ -z "$1" ]]; then
|
|
echo '$1 is empty.'
|
|
exit
|
|
fi
|
|
|
|
local version=$(get-module-version "$1")
|
|
cd "$1" || exit
|
|
local last_tag=$(git describe --tags)
|
|
local release_notes=$(git log "$last_tag"..HEAD --oneline)
|
|
|
|
echo "Tag with v${version}..."
|
|
git tag -a "v$version" -m "v$version"
|
|
|
|
echo 'Release notes:'
|
|
echo "$release_notes"
|
|
}
|
|
|
|
zip-module() {
|
|
if [[ -z "$1" ]]; then
|
|
echo '$1 is empty.'
|
|
exit
|
|
fi
|
|
|
|
# Get version and module name
|
|
local version=$(get-module-version "$1")
|
|
local module=$(basename "$1")
|
|
|
|
# Copy module to tmp folder
|
|
echo "Copy module $module version $version to tmp folder"
|
|
local module_name="$module-$version"
|
|
local module_path="tmp/$module_name"
|
|
rm -rf "$module_path"
|
|
mkdir -p "$module_path"
|
|
cp -r "$1/." "$module_path"
|
|
find "$module_path" -type d -exec chmod u=rwx,go=rx {} \;
|
|
find "$module_path" -type f -exec chmod u=rw,go=r {} \;
|
|
|
|
# Configure and zip module
|
|
echo 'Remove auto install option'
|
|
sed -i'' -e "s/'auto_install': True,/'auto_install': False,/" "$module_path/__manifest__.py"
|
|
echo 'Remove pycache and hidden folders'
|
|
find "$module_path" | grep -E "(\.git|__pycache__|\.pyc|\.pyo$)" | xargs rm -rf
|
|
echo "Zip module $module"
|
|
cd tmp || exit
|
|
rm -f "$module_name".zip
|
|
zip -q -r "$module_name".zip "$module_name"
|
|
cd .. || exit
|
|
}
|
|
|
|
# Upgrade commands
|
|
|
|
migrate-module() {
|
|
if [[ -z "$1" ]]; then
|
|
echo '$1 is empty.'
|
|
exit
|
|
fi
|
|
local module_path="$1"
|
|
local source_odoo_version=$(get-module-version $module_path | cut -d'.' -f1,2)
|
|
|
|
if [[ "$ODOO_VERSION" == "19.0" ]]; then
|
|
local module=$(basename "$module_path")
|
|
local addons_path=$(dirname "$module_path")
|
|
|
|
echo "Run Odoo upgrade_code command for $module_path from ${source_odoo_version}..."
|
|
odoo upgrade_code --addons-path "$addons_path" --from "$source_odoo_version"
|
|
|
|
echo "Replace manifest version $source_odoo_version with $ODOO_VERSION."
|
|
sed -i -E "s/$source_odoo_version/$ODOO_VERSION/g" "$module_path/__manifest__.py"
|
|
else
|
|
echo "Run odoo-module-migrator for $module with source $source_odoo_version and target ${ODOO_VERSION}..."
|
|
odoo-module-migrate --directory $(dirname "$module_path") --modules $(basename "$module_path") \
|
|
--init-version-name $source_odoo_version --target-version-name $ODOO_VERSION --no-commit
|
|
fi
|
|
}
|
|
|
|
upgrade-odoo() {
|
|
if [[ -z "$1" ]]; then
|
|
echo '$1 is empty.'
|
|
exit
|
|
fi
|
|
if [[ "$1" =~ "list" ]]; then
|
|
echo "info"
|
|
echo "all-test"
|
|
echo "all-production"
|
|
echo "dump"
|
|
echo "filestore"
|
|
echo "drop"
|
|
echo "upgrade-test"
|
|
echo "upgrade-production"
|
|
echo "clear-assets"
|
|
echo "init"
|
|
echo "uninstall"
|
|
echo "auto-update"
|
|
echo "update"
|
|
echo "configure-test"
|
|
echo "configure-production"
|
|
echo "restart"
|
|
echo "logs"
|
|
echo "shell"
|
|
echo "psql"
|
|
echo "rename-production"
|
|
echo "stop-container"
|
|
echo "browse"
|
|
exit
|
|
fi
|
|
if [[ -z "$2" ]]; then
|
|
echo '$2 is empty.'
|
|
exit
|
|
fi
|
|
load-dotenv "$1"
|
|
temp_folder="/var/tmp"
|
|
|
|
# Setup ssh aliases
|
|
ssh_server="ssh -p $PORT $SERVER"
|
|
ssh_target_server="ssh -t -p $TARGET_PORT $TARGET_SERVER"
|
|
scp_exec="scp"
|
|
if [[ -n "$JUMP_HOST" ]]; then
|
|
log-warning "Source connection uses jump host: $JUMP_HOST"
|
|
ssh_server="ssh -p $PORT -J $JUMP_HOST $SERVER"
|
|
scp_exec="scp -o ProxyJump=$JUMP_HOST"
|
|
fi
|
|
if [[ -n "$TARGET_JUMP_HOST" ]]; then
|
|
log-warning "Target connection uses jump host: $JUMP_HOST"
|
|
ssh_target_server="ssh -t -p $TARGET_PORT -J $TARGET_JUMP_HOST $TARGET_SERVER"
|
|
fi
|
|
|
|
if [[ "$2" =~ "info" ]]; then
|
|
echo ""
|
|
echo "Source Enviroment"
|
|
echo "-----------------"
|
|
echo "Host: $HOST"
|
|
echo "Server: $SERVER:$PORT"
|
|
echo "Jump Host: $JUMP_HOST"
|
|
echo "Odoo Container: $ODOO_CONTAINER"
|
|
echo "Odoo Version: $ODOO_VERSION"
|
|
echo "Postgres Container: $POSTGRES_CONTAINER"
|
|
echo "Database: $DATABASE"
|
|
echo ""
|
|
echo "Target Enviroment"
|
|
echo "-----------------"
|
|
echo "Host: $TARGET_HOST"
|
|
echo "Server: $TARGET_SERVER:$TARGET_PORT"
|
|
echo "Jump Host: $TARGET_JUMP_HOST"
|
|
echo "Odoo Container: $TARGET_ODOO_CONTAINER"
|
|
echo "Odoo Version: $TARGET_ODOO_VERSION"
|
|
echo "Postgres Container: $TARGET_POSTGRES_CONTAINER"
|
|
echo "Database: $TARGET_DATABASE"
|
|
echo ""
|
|
echo "Upgrade Configuration"
|
|
echo "---------------------"
|
|
echo "Init Addons: $ODOO_ADDONS_INIT"
|
|
echo "Uninstall Addons: $ODOO_ADDONS_UNINSTALL"
|
|
echo ""
|
|
echo "Test Config: $ODOO_CONFIGURE_TEST"
|
|
echo "Production Config: $ODOO_CONFIGURE_PRODUCTION"
|
|
echo ""
|
|
fi
|
|
|
|
if [[ "$2" =~ "all-test" ]]; then
|
|
echo "#FIXME: implement this command."
|
|
fi
|
|
|
|
|
|
if [[ "$2" =~ "all-production" ]]; then
|
|
echo "#FIXME: implement this command."
|
|
fi
|
|
|
|
if [[ "$2" =~ "dump" ]]; then
|
|
log-warning "Run step 'dump' on $SERVER."
|
|
|
|
$ssh_server sudo docker-postgres-backup -c "$POSTGRES_CONTAINER" -d "$DATABASE" -o "$temp_folder"
|
|
|
|
if [[ "$SERVER" != "$TARGET_SERVER" ]]; then
|
|
log-warning "Source and target host are different."
|
|
|
|
echo "Copy dump from $SERVER to $TARGET_SERVER..."
|
|
$ssh_target_server mkdir -p "$temp_folder/$TARGET_POSTGRES_CONTAINER"
|
|
$scp_exec "$SERVER:$temp_folder/$POSTGRES_CONTAINER/$DATABASE.sql" "$TARGET_SERVER:$temp_folder/$TARGET_POSTGRES_CONTAINER/$DATABASE.sql"
|
|
$ssh_target_server sudo docker-postgres-restore -c "$TARGET_POSTGRES_CONTAINER" -d "$DATABASE" -f "$temp_folder/$TARGET_POSTGRES_CONTAINER/$DATABASE.sql" -r
|
|
else
|
|
$ssh_target_server sudo docker-postgres-restore -c "$TARGET_POSTGRES_CONTAINER" -d "$DATABASE" -f "$temp_folder/$POSTGRES_CONTAINER/$DATABASE.sql" -r
|
|
fi
|
|
fi
|
|
|
|
if [[ "$2" =~ "filestore" ]]; then
|
|
log-warning "Run step 'filestore' on $SERVER."
|
|
|
|
$ssh_server docker-export -c "$ODOO_CONTAINER" -p "/var/lib/odoo/filestore/$DATABASE" -o "$temp_folder"
|
|
|
|
if [[ "$SERVER" != "$TARGET_SERVER" ]]; then
|
|
log-warning "Source and target host are different."
|
|
|
|
echo "Copy filestore from $SERVER to $TARGET_SERVER..."
|
|
$scp_exec "$SERVER:$temp_folder/$ODOO_CONTAINER.tar" "$TARGET_SERVER:$temp_folder/$TARGET_ODOO_CONTAINER.tar"
|
|
$ssh_target_server docker-import -c "$TARGET_ODOO_CONTAINER" -p "/var/lib/odoo/filestore/$TARGET_DATABASE" -f "$temp_folder/$TARGET_ODOO_CONTAINER.tar" -r
|
|
else
|
|
$ssh_target_server docker-import -c "$TARGET_ODOO_CONTAINER" -p "/var/lib/odoo/filestore/$TARGET_DATABASE" -f "$temp_folder/$ODOO_CONTAINER.tar" -r
|
|
fi
|
|
fi
|
|
|
|
if [[ "$2" = "drop" ]]; then
|
|
log-warning "Run step 'drop' on $TARGET_SERVER."
|
|
$ssh_target_server docker-postgres-drop -c "$TARGET_POSTGRES_CONTAINER" -d "$TARGET_DATABASE"
|
|
fi
|
|
|
|
if [[ "$2" = "upgrade-test" ]]; then
|
|
log-warning "Run step 'upgrade-test' on $TARGET_SERVER."
|
|
$ssh_target_server docker-odoo-upgrade -c "$TARGET_POSTGRES_CONTAINER" -d "$DATABASE" -s "$ODOO_VERSION" -n "$TARGET_DATABASE" -t "$TARGET_ODOO_VERSION"
|
|
fi
|
|
|
|
if [[ "$2" = "upgrade-production" ]]; then
|
|
log-warning "Run step 'upgrade-production' on $TARGET_SERVER."
|
|
$ssh_target_server docker-odoo-upgrade -m production -c "$TARGET_POSTGRES_CONTAINER" -d "$DATABASE" -s "$ODOO_VERSION" -n "$TARGET_DATABASE" -t "$TARGET_ODOO_VERSION"
|
|
fi
|
|
|
|
if [[ "$2" = "clear-assets" ]]; then
|
|
log-warning "Run step 'clear-assets' on $TARGET_SERVER."
|
|
$ssh_target_server docker-odoo-clear-assets -c "$TARGET_ODOO_CONTAINER" -d "$TARGET_DATABASE"
|
|
fi
|
|
|
|
if [[ "$2" = "init" ]]; then
|
|
log-warning "Run step 'init' on $TARGET_SERVER."
|
|
if [[ -n "$ODOO_ADDONS_INIT" ]]; then
|
|
$ssh_target_server docker-odoo-init -c "$TARGET_ODOO_CONTAINER" -d "$TARGET_DATABASE" -i "$ODOO_ADDONS_INIT"
|
|
else
|
|
echo "Nothing to do."
|
|
fi
|
|
fi
|
|
|
|
if [[ "$2" = "uninstall" ]]; then
|
|
log-warning "Run step 'uninstall' on $TARGET_SERVER."
|
|
$ssh_target_server docker-odoo-uninstall -c "$TARGET_ODOO_CONTAINER" -d "$TARGET_DATABASE" -u "$ODOO_ADDONS_UNINSTALL"
|
|
fi
|
|
|
|
if [[ "$2" = "auto-update" ]]; then
|
|
log-warning "Run step 'auto-update' on $TARGET_SERVER."
|
|
$ssh_target_server docker-odoo-shell -c "$TARGET_ODOO_CONTAINER" -d "$TARGET_DATABASE" -f -p "env['ir.module.module'].upgrade_changed_checksum()"
|
|
fi
|
|
|
|
if [[ "$2" = "update" ]]; then
|
|
log-warning "Run step 'update' on $TARGET_SERVER."
|
|
$ssh_target_server docker-odoo-update -c "$TARGET_ODOO_CONTAINER" -d "$TARGET_DATABASE" -u all
|
|
fi
|
|
|
|
if [[ "$2" = "configure-test" ]]; then
|
|
log-warning "Run step 'configure' on $TARGET_SERVER."
|
|
if [[ -n "$ODOO_CONFIGURE_TEST" ]]; then
|
|
$ssh_target_server docker-odoo-shell -c "$TARGET_ODOO_CONTAINER" -d "$TARGET_DATABASE" -f -p "\"$ODOO_CONFIGURE_TEST\""
|
|
else
|
|
echo "Nothing to do."
|
|
fi
|
|
fi
|
|
|
|
if [[ "$2" = "configure-production" ]]; then
|
|
log-warning "Run step 'configure' on $TARGET_SERVER."
|
|
if [[ -n "$ODOO_CONFIGURE_PRODUCTION" ]]; then
|
|
$ssh_target_server docker-odoo-shell -c "$TARGET_ODOO_CONTAINER" -d "$TARGET_DATABASE" -f -p "\"$ODOO_CONFIGURE_PRODUCTION\""
|
|
else
|
|
echo "Nothing to do."
|
|
fi
|
|
fi
|
|
|
|
if [[ "$2" = "restart" ]]; then
|
|
log-warning "Run step 'restart' on $TARGET_SERVER."
|
|
$ssh_target_server docker restart "$TARGET_ODOO_CONTAINER" "$TARGET_POSTGRES_CONTAINER"
|
|
fi
|
|
|
|
if [[ "$2" = "logs" ]]; then
|
|
log-warning "Run step 'logs' on $TARGET_SERVER."
|
|
containe_id=$($ssh_target_server docker ps -f "name=$TARGET_ODOO_CONTAINER" --format '{{.ID}}' | head -n1)
|
|
$ssh_target_server docker logs -f "$containe_id" --tail 10
|
|
fi
|
|
|
|
if [[ "$2" = "shell" ]]; then
|
|
log-warning "Run step 'shell' on $TARGET_SERVER."
|
|
$ssh_target_server docker-odoo-shell -c "$TARGET_ODOO_CONTAINER" -d "$TARGET_DATABASE"
|
|
fi
|
|
|
|
if [[ "$2" = "psql" ]]; then
|
|
log-warning "Run step 'psql' on $TARGET_SERVER."
|
|
$ssh_target_server docker-postgres-shell -c "$TARGET_POSTGRES_CONTAINER" -d "$TARGET_DATABASE"
|
|
fi
|
|
|
|
if [[ "$2" = "env" ]]; then
|
|
log-warning "Run step 'env' on $TARGET_SERVER."
|
|
containe_id=$($ssh_target_server docker ps -f "name=$TARGET_ODOO_CONTAINER" --format '{{.ID}}' | head -n1)
|
|
$ssh_target_server docker exec "$containe_id" env
|
|
fi
|
|
|
|
if [[ "$2" = "conf" ]]; then
|
|
log-warning "Run step 'conf' on $TARGET_SERVER."
|
|
containe_id=$($ssh_target_server docker ps -f "name=$TARGET_ODOO_CONTAINER" --format '{{.ID}}' | head -n1)
|
|
$ssh_target_server docker exec "$containe_id" cat /etc/odoo/odoo.conf
|
|
fi
|
|
|
|
if [[ "$2" = "stop-container" ]]; then
|
|
log-warning "Run step 'stop-container' on $TARGET_SERVER."
|
|
$ssh_server docker stop "$ODOO_CONTAINER"
|
|
fi
|
|
|
|
if [[ "$2" = "rename-production" ]]; then
|
|
log-warning "Run step 'rename-production' on $TARGET_SERVER."
|
|
if [[ "$POSTGRES_CONTAINER" != "$TARGET_POSTGRES_CONTAINER" ]]; then
|
|
if [[ "$DATABASE" = "odoo" ]]; then
|
|
$ssh_server docker-postgres-rename -c "$TARGET_POSTGRES_CONTAINER" -s "$DATABASE" -t "${DATABASE}-old"
|
|
fi
|
|
$ssh_server docker-postgres-rename -c "$TARGET_POSTGRES_CONTAINER" -s "$TARGET_DATABASE" -t "$DATABASE"
|
|
else
|
|
$ssh_server docker-postgres-rename -c "$TARGET_POSTGRES_CONTAINER" -s "$DATABASE" -t "${DATABASE}-old"
|
|
$ssh_server docker-postgres-rename -c "$TARGET_POSTGRES_CONTAINER" -s "$TARGET_DATABASE" -t "$DATABASE"
|
|
fi
|
|
$ssh_server docker-postgres-list -c "$TARGET_POSTGRES_CONTAINER"
|
|
|
|
if [[ "$DATABASE" = "odoo" ]]; then
|
|
$ssh_server docker exec "$TARGET_ODOO_CONTAINER" mv "/var/lib/odoo/filestore/$DATABASE" "/var/lib/odoo/filestore/$DATABASE-old"
|
|
fi
|
|
$ssh_server docker exec "$TARGET_ODOO_CONTAINER" mv "/var/lib/odoo/filestore/$TARGET_DATABASE" "/var/lib/odoo/filestore/$DATABASE"
|
|
fi
|
|
|
|
if [[ "$2" = "browse" ]]; then
|
|
log-warning "Run step '$2' on $TARGET_SERVER."
|
|
open-url-with-delay "https://$TARGET_HOST" 0
|
|
fi
|
|
|
|
}
|
|
|
|
# Snippet commands
|
|
|
|
reset-views() {
|
|
if [[ -z "$1" ]]; then
|
|
echo '$1 is empty.'
|
|
exit
|
|
fi
|
|
|
|
local database="$1"
|
|
local key=$(basename "$2")
|
|
|
|
# Use default database if second param is not given
|
|
if [[ -z "$2" ]]; then
|
|
database="$DB_NAME"
|
|
key=$(basename "$1")
|
|
fi
|
|
|
|
set-addons-path
|
|
echo "Reset views for key $key"
|
|
echo "self.env['ir.ui.view'].search([('key', 'ilike', '$key')]).reset_arch(mode='hard')" |
|
|
odoo shell --database "$database" --config "$ODOO_RC" --addons-path="$ADDONS_PATH" --no-http
|
|
}
|
|
|
|
create-snippet() {
|
|
if [[ -z "$1" ]]; then
|
|
echo '$1 is empty.'
|
|
exit
|
|
fi
|
|
|
|
export INHERIT_ID=$(echo "$1" | cut -d. -f1-2)
|
|
local file="snippets/$SNIPPET_PREFIX.$1.xml"
|
|
|
|
echo "Create snippet from template: $file"
|
|
envsubst '$INHERIT_ID' < templates/snippet.xml > "$file"
|
|
}
|
|
|
|
install-snippet() {
|
|
if [[ -z "$1" ]]; then
|
|
echo '$1 is empty.'
|
|
exit
|
|
fi
|
|
if [[ -z "$2" ]]; then
|
|
echo '$2 is empty.'
|
|
exit
|
|
fi
|
|
|
|
# Generate view name from filename
|
|
local file=$(basename "$2")
|
|
local name="${file%.xml}"
|
|
local inherit_id=$(echo 'cat //data/@inherit_id' | xmllint --shell "$2" | awk -F'[="]' '!/>/{print $(NF-1)}')
|
|
local type=$(echo 'cat //data/@type' | xmllint --shell "$2" | awk -F'[="]' '!/>/{print $(NF-1)}')
|
|
local model=$(echo 'cat //data/@model' | xmllint --shell "$2" | awk -F'[="]' '!/>/{print $(NF-1)}')
|
|
local priority=$(echo 'cat //data/@priority' | xmllint --shell "$2" | awk -F'[="]' '!/>/{print $(NF-1)}')
|
|
local module_name=$(echo "$inherit_id" | cut -d. -f 1 )
|
|
local xml_id=$(echo "$inherit_id" | cut -d. -f 2 )
|
|
|
|
# Set default values
|
|
: ${type:='qweb'}
|
|
|
|
load-dotenv "$1"
|
|
echo "Search for view with name $module_name.$xml_id"
|
|
inherit_id=$(images/odoocli/bin/odoocli --method "read" --model "ir.model.data" --field "res_id" --domain "[('name', '=', '$xml_id'), ('module', '=', '$module_name')]")
|
|
echo "Found view id $inherit_id"
|
|
|
|
echo "Apply view $name"
|
|
images/odoocli/bin/odoocli --method 'create' --model 'ir.ui.view' --value "{
|
|
'name': '$name',
|
|
'type': '$type',
|
|
'model': '$model',
|
|
'mode': 'extension',
|
|
'priority': $priority,
|
|
'inherit_id': $inherit_id,
|
|
'arch_base': '''$(cat "$2")'''
|
|
}"
|
|
}
|
|
|
|
update-snippet() {
|
|
if [[ -z "$1" ]]; then
|
|
echo '$1 is empty.'
|
|
exit
|
|
fi
|
|
if [[ -z "$2" ]]; then
|
|
echo '$2 is empty.'
|
|
exit
|
|
fi
|
|
|
|
# Generate view name from filename
|
|
local file=$(basename "$2")
|
|
local name="${file%.xml}"
|
|
|
|
load-dotenv "$1"
|
|
echo "Update view with name $name"
|
|
images/odoocli/bin/odoocli --method 'write' --model 'ir.ui.view' --include-archived --domain "[('name', '=', '$name')]" --field 'arch_base' --value "'''$(cat "$2")'''"
|
|
enable-snippet "$1" "$2"
|
|
}
|
|
|
|
disable-snippet() {
|
|
if [[ -z "$1" ]]; then
|
|
echo '$1 is empty.'
|
|
exit
|
|
fi
|
|
if [[ -z "$2" ]]; then
|
|
echo '$2 is empty.'
|
|
exit
|
|
fi
|
|
|
|
# Get name from path
|
|
# path: snippets/mint_system.base.view_partner_tree.show_type.xml
|
|
# name: mint_system.base.view_partner_tree.show_type
|
|
local name="$2"
|
|
if [[ "$2" == *.xml ]]; then
|
|
local file=$(basename "$2")
|
|
name="${file%.xml}"
|
|
fi
|
|
|
|
load-dotenv "$1"
|
|
echo "Disable view with name $name"
|
|
images/odoocli/bin/odoocli --method 'write' --model "ir.ui.view" --domain "[('name', '=', '$name')]" --field "active" --value "False"
|
|
}
|
|
|
|
enable-snippet() {
|
|
if [[ -z "$1" ]]; then
|
|
echo '$1 is empty.'
|
|
exit
|
|
fi
|
|
if [[ -z "$2" ]]; then
|
|
echo '$2 is empty.'
|
|
exit
|
|
fi
|
|
|
|
# Get name from path
|
|
local name="$2"
|
|
if [[ "$2" == *.xml ]]; then
|
|
local file=$(basename "$2")
|
|
name="${file%.xml}"
|
|
fi
|
|
|
|
load-dotenv "$1"
|
|
echo "Enable view with name $name"
|
|
images/odoocli/bin/odoocli --method 'write' --model "ir.ui.view" --include-archived --domain "[('name', '=', '$name')]" --field "active" --value "True"
|
|
}
|
|
|
|
remove-snippet() {
|
|
if [[ -z "$1" ]]; then
|
|
echo '$1 is empty.'
|
|
exit
|
|
fi
|
|
if [[ -z "$2" ]]; then
|
|
echo '$2 is empty.'
|
|
exit
|
|
fi
|
|
|
|
# Get name from path
|
|
local name="$2"
|
|
if [[ "$2" == *.xml ]]; then
|
|
local file=$(basename "$2")
|
|
name="${file%.xml}"
|
|
fi
|
|
|
|
load-dotenv "$1"
|
|
echo "Remove view with name $name"
|
|
images/odoocli/bin/odoocli --method "unlink" --model "ir.ui.view" --domain "[('name', '=', '$name')]"
|
|
}
|
|
|
|
lint-snippets() {
|
|
echo 'Lint snippets files...'
|
|
|
|
local files="snippets/*.xml"
|
|
|
|
for file in $files; do
|
|
|
|
# Access various snippet parameters
|
|
local filename=$(basename "$file")
|
|
local snippet_module=$(echo "$filename" | cut -d. -f2)
|
|
local report=$(echo "$filename" | cut -d. -f3)
|
|
local edit=$(echo "$filename" | cut -d. -f4)
|
|
local inherit_id=$(echo 'cat //data/@inherit_id' | xmllint --shell "$file" | awk -F'[="]' '!/>/{print $(NF-1)}')
|
|
local type=$(echo 'cat //data/@type' | xmllint --shell "$file" | awk -F'[="]' '!/>/{print $(NF-1)}')
|
|
local model=$(echo 'cat //data/@model' | xmllint --shell "$file" | awk -F'[="]' '!/>/{print $(NF-1)}')
|
|
local priority=$(echo 'cat //data/@priority' | xmllint --shell "$file" | awk -F'[="]' '!/>/{print $(NF-1)}')
|
|
local module_name=$(echo "$inherit_id" | cut -d. -f 1 )
|
|
local xml_id=$(echo "$inherit_id" | cut -d. -f 2 )
|
|
|
|
# Check filename
|
|
local count_dots=$(echo "$filename" | grep -o "\." | wc -l)
|
|
if [[ "$count_dots" -ge 5 ]]; then
|
|
echo "Linting failed for $file"
|
|
echo "Dots count: $count_dots"
|
|
echo " Filename: $snippet_module.$report"
|
|
exit 1
|
|
fi
|
|
|
|
# Check if inherit id matches the filename
|
|
if [[ -n "$inherit_id" ]]; then
|
|
if [[ "$inherit_id" != "$snippet_module.$report" ]]; then
|
|
echo "Linting failed for $file"
|
|
echo " Inherit: $inherit_id"
|
|
echo "Filename: $snippet_module.$report"
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
# Check if priority is set
|
|
if [[ -z "$priority" ]] && [[ -n "$inherit_id" ]]; then
|
|
echo "Linting failed for $file"
|
|
echo 'No priority is set.'
|
|
exit 1
|
|
fi
|
|
done
|
|
echo 'No problems with snippets found.'
|
|
}
|
|
|
|
# Project commands
|
|
|
|
load-version() {
|
|
if [[ -n "$1" ]]; then
|
|
ODOO_VERSION="$1"
|
|
fi
|
|
|
|
echo "Copy .gitmodules, .gitmodules.csv and .python-version from 'versions/$ODOO_VERSION'."
|
|
cp "versions/$ODOO_VERSION/.gitmodules" .
|
|
cp "versions/$ODOO_VERSION/.gitmodules.csv" .
|
|
cp "versions/$ODOO_VERSION/.python-version" .
|
|
}
|
|
|
|
checkout() {
|
|
if [[ -z "$1" ]]; then echo "\$1 is empty."; exit; fi
|
|
ODOO_VERSION="$1"
|
|
|
|
# Load .gitmodules and .python-version
|
|
load-version "$ODOO_VERSION"
|
|
# Set .env
|
|
load-revision "$ODOO_VERSION"
|
|
# Clone the addons
|
|
clone-git-folder .
|
|
# Checkout commit ref
|
|
checkout-git-folder "$ODOO_VERSION" .
|
|
# Upate branch
|
|
switch-git-folder "$ODOO_VERSION" .
|
|
}
|
|
|
|
save-version() {
|
|
local git_folders_file="versions/$ODOO_VERSION/"
|
|
echo "Save git refs to .gitmodules.csv."
|
|
|
|
local git_folders=$(ls-git-folder .)
|
|
echo "path,rev" > ".gitmodules.csv"
|
|
for git_folder in $git_folders; do
|
|
local commit_ref=$(git -C "$git_folder" rev-parse HEAD)
|
|
echo "$git_folder,$commit_ref" >> ".gitmodules.csv"
|
|
done
|
|
|
|
echo "Copy .gitmodules and .python-version to 'versions/$ODOO_VERSION'"
|
|
cp ".gitmodules" "versions/$ODOO_VERSION/"
|
|
cp ".gitmodules.csv" "versions/$ODOO_VERSION/"
|
|
cp ".python-version" "versions/$ODOO_VERSION/"
|
|
}
|
|
|
|
|
|
list-versions() {
|
|
ls -1 versions
|
|
}
|
|
|
|
check-version() {
|
|
echo 'Check if Odoo version matches the release file:'
|
|
local odoo_version=$(echo "$ODOO_VERSION" | cut -d'.' -f1)
|
|
grep -E "version_info.+$odoo_version" odoo/odoo/release.py
|
|
}
|
|
|
|
# Revision commands
|
|
|
|
list-revision() {
|
|
ls -1 revisions
|
|
}
|
|
|
|
show-revision() {
|
|
if [[ -n "$1" ]]; then
|
|
ODOO_REVISION="$1"
|
|
fi
|
|
|
|
echo "Odoo Revision: $ODOO_REVISION"
|
|
source "revisions/$ODOO_REVISION"
|
|
|
|
echo "Odoo Ref: $ODOO_REF"
|
|
echo "Odoo Enterprise Ref: $ODOO_ENTERPRISE_REF"
|
|
echo "Odoo Themes Ref: $ODOO_THEME_REF"
|
|
}
|
|
|
|
load-revision() {
|
|
if [[ -n "$1" ]]; then
|
|
ODOO_REVISION="$1"
|
|
fi
|
|
|
|
echo 'Update revision var in .env file.'
|
|
if grep -q "ODOO_REVISION" .env; then
|
|
sed -i -e "s/^ODOO_REVISION=.*/ODOO_REVISION=${ODOO_REVISION}/" .env
|
|
else
|
|
echo "ODOO_REVISION=${ODOO_REVISION}" >> .env
|
|
fi
|
|
|
|
echo "Load refs for revision: $ODOO_REVISION"
|
|
export $(cat "revisions/$ODOO_REVISION" | sed 's/#.*//g' | xargs)
|
|
}
|
|
|
|
checkout-revision() {
|
|
if [[ -n "$1" ]]; then
|
|
ODOO_REVISION="$1"
|
|
fi
|
|
|
|
load-revision "$ODOO_REVISION"
|
|
|
|
echo 'Checkout Odoo git repos...'
|
|
git -C odoo checkout $ODOO_REF
|
|
git -C enterprise checkout $ODOO_ENTERPRISE_REF
|
|
git -C themes checkout $ODOO_THEME_REF
|
|
}
|
|
|
|
create-revision() {
|
|
if [[ -z "$1" ]]; then echo "\$1 is empty."; exit; fi
|
|
|
|
local revision="$1"
|
|
local odoo_date=$(echo "$revision" | cut -d'.' -f3-)
|
|
local before_date=$(date -d "$odoo_date -1 day" +"%Y-%m-%d")
|
|
echo "Checkout Odoo repos on $before_date."
|
|
|
|
# Get git commit by date
|
|
cd odoo
|
|
git fetch
|
|
echo -e "\nShow last 3 commits from $before_date:"
|
|
git --no-pager log -n 3 --pretty=format:"%h %ad %s" --date=short --before="$before_date" origin/$ODOO_VERSION
|
|
local ref=$(git rev-list -n 1 --before="$before_date" origin/$ODOO_VERSION)
|
|
git checkout "$ref"
|
|
cd ..
|
|
|
|
cd enterprise
|
|
git fetch
|
|
echo -e "\nShow last 3 commits from $before_date:"
|
|
git --no-pager log -n 3 --pretty=format:"%h %ad %s" --date=short --before="$before_date" origin/$ODOO_VERSION
|
|
local enterprise_ref=$(git rev-list -n 1 --before="$before_date" origin/$ODOO_VERSION)
|
|
git checkout "$enterprise_ref"
|
|
cd ..
|
|
|
|
cd themes
|
|
git fetch
|
|
echo -e "\nShow last 3 commits from $before_date:"
|
|
git --no-pager log -n 3 --pretty=format:"%h %ad %s" --date=short --before="$before_date" origin/$ODOO_VERSION
|
|
local theme_ref=$(git rev-list -n 1 --before="$before_date" origin/$ODOO_VERSION)
|
|
git checkout "$theme_ref"
|
|
cd ..
|
|
|
|
echo -e "\nCreate revision: $revision"
|
|
export ODOO_REVISION=$revision
|
|
export ODOO_REF=$ref
|
|
export ODOO_ENTERPRISE_REF=$enterprise_ref
|
|
export ODOO_THEME_REF=$theme_ref
|
|
envsubst < templates/odoo_revision > "revisions/$revision"
|
|
chmod +x "revisions/$revision"
|
|
|
|
echo "Checkout revision: $revision"
|
|
checkout-revision "$revision"
|
|
}
|
|
|
|
commit-and-push-revision() {
|
|
if [[ -n "$1" ]]; then
|
|
ODOO_REVISION="$1"
|
|
fi
|
|
|
|
echo 'Remove existing tag...'
|
|
git tag -d "$ODOO_REVISION"
|
|
|
|
echo "Commit with revision $ODOO_REVISION tag..."
|
|
git commit --all --message "feat(revision): release $ODOO_REVISION"
|
|
git tag "$ODOO_REVISION"
|
|
|
|
echo 'Push tags...'
|
|
git push
|
|
git push -f --tags
|
|
}
|
|
|
|
# RPC commands
|
|
|
|
test-xmlrpc() {
|
|
load-dotenv "$1"
|
|
echo "Connect to database and count res.partner records: $(images/odoocli/bin/odoocli --model 'res.partner' --method 'search_count' --domain '[]')"
|
|
}
|
|
|
|
odoocli(){
|
|
images/odoocli/bin/odoocli "$@"
|
|
}
|
|
|
|
manifestoo(){
|
|
set-addons-path
|
|
./.venv$ODOO_VERSION/bin/manifestoo --addons-path="$ADDONS_PATH" "$@"
|
|
}
|
|
|
|
export-website-data() {
|
|
if [[ -n "$1" ]]; then
|
|
load-dotenv $1
|
|
fi
|
|
|
|
export ODOO_URL
|
|
export DB_NAME
|
|
export ODOO_USERNAME
|
|
export ODOO_PASSWORD
|
|
export ODOO_WEBSITE_ID
|
|
|
|
echo -e "\nPython Output:\n\n---"
|
|
python "bin/export-website-data"
|
|
local exit_code=$?
|
|
echo -e "---\n"
|
|
echo -e "Exit code: $exit_code\n"
|
|
}
|
|
|
|
import-website-data() {
|
|
if [[ -n "$1" ]]; then
|
|
load-dotenv $1
|
|
fi
|
|
|
|
export ODOO_URL
|
|
export DB_NAME
|
|
export ODOO_USERNAME
|
|
export ODOO_PASSWORD
|
|
export ODOO_WEBSITE_ID
|
|
|
|
echo -e "\nPython Output:\n\n---"
|
|
python "bin/import-website-data"
|
|
local exit_code=$?
|
|
echo -e "---\n"
|
|
echo -e "Exit code: $exit_code\n"
|
|
}
|
|
|
|
# Vuepress commands
|
|
|
|
install-vuepress() {
|
|
echo 'Install Node.'
|
|
pnpm env use --global lts
|
|
|
|
echo 'Install npm packages.'
|
|
pnpm install
|
|
}
|
|
|
|
dev-vuepress() {
|
|
pnpm run dev
|
|
}
|
|
|
|
build-vuepress() {
|
|
pnpm run build
|
|
|
|
echo "Create vuepress snippets folders."
|
|
mkdir -p .vuepress/dist/snippets
|
|
|
|
echo "Copy snippet files to snippets folder."
|
|
cp snippets/*.xml .vuepress/dist/snippets
|
|
}
|
|
|
|
serve-vuepress() {
|
|
cd .vuepress/dist
|
|
pnpx serve
|
|
}
|
|
|
|
if [[ "$1" == *","* ]]; then
|
|
# Split comma-separated commands
|
|
IFS=',' read -ra COMMANDS <<< "$1"
|
|
for cmd in "${COMMANDS[@]}"; do
|
|
# Trim whitespace
|
|
cmd=$(echo "$cmd" | xargs)
|
|
if declare -f "$cmd" > /dev/null; then
|
|
activate-venv
|
|
"$cmd"
|
|
else
|
|
echo "Unknown command: $cmd"
|
|
help
|
|
exit 1
|
|
fi
|
|
done
|
|
elif declare -f "$1" > /dev/null; then
|
|
activate-venv
|
|
"$1" "${@:2}"
|
|
else
|
|
case "$1" in
|
|
*)
|
|
echo "Unknown command: $1"
|
|
help
|
|
exit 1
|
|
;;
|
|
esac
|
|
fi
|