Files
Odoo-Build/task
T

3100 lines
100 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|[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+=("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.")
rows+=("checkout|[version]|Checkout Odoo version.")
rows+=("checkout-git-folder|[branch][path]|Checkout git commit from .gitmodules.")
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+=("run-pre-commit|[path]|Run pre-commit in path.")
rows+=("===DOCKER===| | ")
rows+=("archive-docker-tags||Archive Docker image tags on hub older than 1 year.")
rows+=("build-odoo|[output]|Build Odoo Docker image.")
rows+=("build-odoo-mailgate|[output]|Build Odoo Mailgate Docker image.")
rows+=("build-odoo-upgrade|[output]|Build Odoo Upgrade Docker image.")
rows+=("build-odoo-cli|[output]|Build Odoo CLI Docker image.")
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+=("remove|[name]|Remove containers and volumes.")
rows+=("test-image-script|[folder]|Test scripts of target Docker image 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+=("===DATA===| | ")
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+=("===DEVELOP===| | ")
rows+=("create-module|[path]|Create new Odoo module from template.")
rows+=("create-repo|[path]|Create a new Odoo module repo.")
rows+=("init-repo-branch|[path]|Create branch for current Odoo version in repo.")
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-translation|[path]|Generate module translation and update with llm.")
rows+=("generate-module-wizard|[path][model]|Generate wizard for a model in module folder.")
rows+=("generate-repo-docs|[path]|Generate repo files from template.")
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+=("register-module-repos|[version]|[path]|Register module repos on apps.odoo.com.")
rows+=("uninstall-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+=("odoobin|[param]|Execute odoo binary.")
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 xml 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+=("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+=("===DOC===| | ")
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+=("prepare-migration-brach|[path][module][version]|Create migration branch for a module.")
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.")
rows+=("===JUPYTER===| | ")
rows+=("init-odoo-jupyter|[path]|Init jypyter lab directory.")
rows+=("start-odoo-jupyter|[path][db]|Start juypter lab in directory.")
rows+=("run-jupyter-notebook|[path][db]|Run juypter notebook with papermill.")
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
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
USER_ORG=${USER_ORG:=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}
JPYTER_LAB_PORT=${JPYTER_LAB_PORT:=8888}
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}
CONTAINER_NETWORK=${CONTAINER_NETWORK:=odoo-build_default}
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}
PROXY_MODE=${PROXY_MODE:=False}
# 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"
elif [[ "$ODOO_VERSION" = "19.0" ]]; then
ODOO_PARAM="--with-demo"
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 "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 "Odoo Param: $ODOO_PARAM"
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
# Add included addons
# local includes="addons/thirdparty"
# for include in $includes; do
# git_folders_path="$include,$git_folders_path"
# done
# Filter valid directories
local valid_paths=""
while IFS= read -r addon; do