#!/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 function help-table() { CMD_WIDTH=25 OPT_WIDTH=20 DESC_WIDTH=90 COLUMN="| %-${CMD_WIDTH}s | %-${OPT_WIDTH}s | %-${DESC_WIDTH}s |\n" printf "$COLUMN" "Command" "Option" "Description" echo "|$(printf '%*s' $((CMD_WIDTH + 2)) '' | tr ' ' '-')|$(printf '%*s' $((OPT_WIDTH + 2)) '' | tr ' ' '-')|$(printf '%*s' $((DESC_WIDTH + 2)) '' | tr ' ' '-')|" printf "$COLUMN" "activate-venv" "" "Activate virtualenv." printf "$COLUMN" "add-git-submodule" "[url] [path]" "Add git submodule." printf "$COLUMN" "build" "[Dockerfile][platform]" "Build Odoo Container image. Optionally define the target platform." printf "$COLUMN" "backup-env-files" "[path]" "Archive and copy env files to target location." printf "$COLUMN" "build-vuepress" "" "Create Vuepress build." printf "$COLUMN" "change-uuid" "[env]" "Change database uuid via xmlrpc." printf "$COLUMN" "checkout" "[version]" "Checkout Odoo version." printf "$COLUMN" "checkout-git-folder" "" "Checkout git commit." printf "$COLUMN" "checkout-latest-revision" "[version]" "Checkout the latest revision of the Odoo version." printf "$COLUMN" "checkout-revision" "[revision]" "Run Odoo revision script or checkout revision set by env var." printf "$COLUMN" "commit-with-llm" "" "Commit with llm generated commit message." printf "$COLUMN" "clean-git-folder" "" "Clean git folder." printf "$COLUMN" "clear-assets" "[db]" "Clear all assets of Odoo database." printf "$COLUMN" "clear-filestore" "[db]" "Clear local filestore folder. No param will clear all filestores." printf "$COLUMN" "clear-views" "[db]" "Clear all views of Odoo database." printf "$COLUMN" "clock-odoo" "[db]" "Count custom line of codes." printf "$COLUMN" "clone-git-folder" "" "Clone git folder listed in the .gitmodules file." printf "$COLUMN" "commit-git-folder" "[message][path]" "Commit all changes in path." printf "$COLUMN" "container-login" "[user][pass]" "Setup container hub login credentials." printf "$COLUMN" "container-ps" "" "List container processes." printf "$COLUMN" "copy-env" "[env][env]" "Copy env file." printf "$COLUMN" "create-git-feature-branch" "[path]" "Create feature branch for Odoo module." printf "$COLUMN" "create-git-mig-branch" "[path]" "Create migration branch for Odoo module." printf "$COLUMN" "create-module" "[path]" "Create new Odoo module from template." printf "$COLUMN" "create-module-repo" "[path]" "Initialize Odoo module repo from template." printf "$COLUMN" "create-nextcloud-env" "[env]" "Create env file for Nextcloud instance." printf "$COLUMN" "create-odoo-env" "[env]" "Create env file for Odoo instance." printf "$COLUMN" "create-revision" "[revision]" "Create new Odoo revision." printf "$COLUMN" "create-snippet" "[id]" "Create snippet from template." printf "$COLUMN" "debug" "[name]" "Debugg application. Options: native." printf "$COLUMN" "dev-vuepress" "" "Start Vuepress development server." printf "$COLUMN" "disable-mailserver" "[env]" "Disable mail server settings via xmlrpc." printf "$COLUMN" "disable-snippet" "[env][path]" "Disable snippet definition." printf "$COLUMN" "drop-db" "[db]" "Drop target Odoo database." printf "$COLUMN" "edit-env" "[env]" "Open env file in default editor." printf "$COLUMN" "exec" "[name][cmd]" "Run command in container." printf "$COLUMN" "export-website-data" "[env]" "Export website data from Odoo database." printf "$COLUMN" "generate-admin-passwd" "[pass]" "Generate hash for Odoo master password." printf "$COLUMN" "generate-module-docs" "[path]" "Generate readme file for module." printf "$COLUMN" "generate-module-model" "[path][model]" "Generate model in module folder." printf "$COLUMN" "generate-module-inherit" "[path][model]" "Generate inherited model in module folder." printf "$COLUMN" "generate-module-views" "[path][model]" "Generate model views in module folder." printf "$COLUMN" "generate-module-security" "[path][model]" "Generate model access file." printf "$COLUMN" "generate-module-snippet" "[path][model][ref]" "Generate snippet for referefenced view." printf "$COLUMN" "generate-module-wizard" "[path][model]" "Generate wizard for a model in module folder." printf "$COLUMN" "generate-ssh-keys" "" "Generate ssh key pair." printf "$COLUMN" "generate-pg-ssl-keys" "" "Generate PostgreSQL SSL key material." printf "$COLUMN" "get-addons-path" "" "Output addons path." printf "$COLUMN" "status-git-folder" "[path]" "Show status for git folder in path." printf "$COLUMN" "get-modules" "[path][option]" "Get list of modules in path. Option is 'basename' or 'dirname'." printf "$COLUMN" "get-module-version" "[path]" "Get module version from manifest." printf "$COLUMN" "help" "[grep]" "Show help for commands." printf "$COLUMN" "import-csv" "[db][path]" "Import data from csv. Filename must match PostgreSQL table name." printf "$COLUMN" "import-website-data" "[env]" "Import website data to Odoo database." printf "$COLUMN" "info" "" "Show values of project env vars." printf "$COLUMN" "init-db" "[db]" "Initialize the Odoo database." printf "$COLUMN" "init-module" "[db][path,module]" "Initialize Odoo module." printf "$COLUMN" "init-venv" "" "Initialize python virtual env." printf "$COLUMN" "install" "" "Install Odoo requirements in source folder." printf "$COLUMN" "install-odoo-scripts" "" "Install Odoo scripts." printf "$COLUMN" "install-requirements" "[db][path]" "Install python packages from requirements.txt." printf "$COLUMN" "install-snippet" "[env][path]" "Install snippet definition." printf "$COLUMN" "install-vuepress" "" "Install Node build dependencies." printf "$COLUMN" "lint" "" "Run precommit for this project." printf "$COLUMN" "lint-module" "[path]" "Run pylint odoo for module." printf "$COLUMN" "lint-module-repo" "[path]" "Run pylint odoo for modules in repo folder." printf "$COLUMN" "lint-snippets" "" "Run checks for all snippets." printf "$COLUMN" "list-env" "" "List env files." printf "$COLUMN" "list-git-folder" "[grep]" "List path and url of git folders." printf "$COLUMN" "list-modules" "[path]" "Get modules in path as list." printf "$COLUMN" "list-revision" "" "List available Odoo revisions." printf "$COLUMN" "list-versions" "" "List available Odoo versions." printf "$COLUMN" "load-env" "[env]" "Load and export env file." printf "$COLUMN" "load-language" "[db][lang]" "Install language package in Odoo db." printf "$COLUMN" "load-version" "[version]" "Load git refs from version folder." printf "$COLUMN" "load-ssh-key" "" "Load SSH private key from env var." printf "$COLUMN" "logs" "[name]" "Tail container logs. Default is 'odoo'." printf "$COLUMN" "ls-module" "[grep]" "List Odoo addons path space separated." printf "$COLUMN" "ls-git-folder" "[grep]" "List git folders path space separated." printf "$COLUMN" "odoocli" "[param]" "Execute odoocli cli." printf "$COLUMN" "patch-database" "[db][path]" "Apply sql file to database." printf "$COLUMN" "publish" "[Dockerfile]" "Publish Odoo container image." printf "$COLUMN" "pull-git-folder" "" "Pull all git folders listed in the .gitmodules file." printf "$COLUMN" "push-git-folder" "" "Push all git folders in path." printf "$COLUMN" "psql" "[db]" "Start interactive psql shell." printf "$COLUMN" "pytest-module" "[db][name,path]" "Run module tests with pytest." printf "$COLUMN" "release-module" "[path]" "Create GitHub release for a module." printf "$COLUMN" "remove" "[name]" "Remove containers and volumes." printf "$COLUMN" "remove-env" "[env]" "Remove environment config." printf "$COLUMN" "remove-git-folder" "" "Delete all git folders." printf "$COLUMN" "remove-git-submodule" "[path]" "Remove a git submodule." printf "$COLUMN" "remove-module" "[db][name]" "Remove target Odoo module." printf "$COLUMN" "remove-snippet" "[env][path]" "Remove snippet definition." printf "$COLUMN" "remove-venv" "" "Remove virtualenv." printf "$COLUMN" "rename-env" "[env][env]" "Rename env file." printf "$COLUMN" "record-with-memray" "[name]" "Record application memory usage with memray. Options: native." printf "$COLUMN" "record-with-py-spy" "[pid]" "Record and create flamechart for a process." printf "$COLUMN" "reset-git-folder" "" "Abort rebase and reset submodules listed in the .gitmodules file." printf "$COLUMN" "reset-views" "[db][key]" "Execute hard reset on views matching keys." printf "$COLUMN" "restart" "[name]" "Restart container." printf "$COLUMN" "restore-env-files" "[path]" "Extract and copy env files from backup file." printf "$COLUMN" "restore-dotenv" "" "Restore content of .env from pass entry." printf "$COLUMN" "run" "[name][cmd]" "Run container with command." printf "$COLUMN" "save-version" "" "Save git folder refs to version folder." printf "$COLUMN" "serve-vuepress" "" "Serve Vuepress build." printf "$COLUMN" "set-addons-path" "" "Set Odoo addons path env variable." printf "$COLUMN" "set-admin" "[db]" "Sets the password for the first user in database." printf "$COLUMN" "setup-mail" "[db]" "Setup mail config in Odoo db." printf "$COLUMN" "shell" "[db][code]" "Start interactive odoo shell or run code." printf "$COLUMN" "show-env" "[env]" "Output content of the env file." printf "$COLUMN" "show-revision" "[revision]" "Show references of Odoo revision." printf "$COLUMN" "source" "" "Source the Python virtual env." printf "$COLUMN" "stage-git-folder" "[path]" "Stage all files in git folders in path." printf "$COLUMN" "start" "[name][db]" "Start application. Options: none, admin, db, mailgate, mailpit, native, odoo." printf "$COLUMN" "stop" "[name]" "Stop containers." printf "$COLUMN" "store-dotenv" "" "Store content of .env in pass entry." printf "$COLUMN" "switch-git-folder" "[version]" "Switch branch for all git folders listed in the .gitmodules file." printf "$COLUMN" "sync-git-folder" "" "Switch, stash and pull all git folders." printf "$COLUMN" "template-compose" "" "Template the Docker compose file." printf "$COLUMN" "template-odoo-rc" "" "Template the Odoo config file." printf "$COLUMN" "template-repo" "[path]" "Update the repo folder from template." printf "$COLUMN" "test-image" "[Dockerfile]" "Test internals of Docker image." printf "$COLUMN" "test-module" "[db][name,path]" "Test target Odoo module." printf "$COLUMN" "test-module-repo" "[db][path]" "Test target Odoo modules in repo folder." printf "$COLUMN" "test-project" "[clean]" "Run tests for this project." printf "$COLUMN" "test-xmlrpc" "[env]" "Test json rpc connection." printf "$COLUMN" "translate-module" "[path][lang][db]" "Generate translation for Odoo module." printf "$COLUMN" "update-docs" "" "Update all project docs." printf "$COLUMN" "update-help-docs" "" "Write help table to task.md file." printf "$COLUMN" "update-module" "[db][name,path]" "Update target Odoo module." printf "$COLUMN" "update-module-license" "[grep]" "Update LICENSE file for each matching module." printf "$COLUMN" "update-module-list" "[db]" "Update module list of Odoo database." printf "$COLUMN" "update-modules-docs" "" "Update modules docs file." printf "$COLUMN" "update-repo-docs" "[path]" "Update Readme file of the repo." printf "$COLUMN" "update-revision-docs" "" "Update revisions doc file." printf "$COLUMN" "update-snippet" "[env][path]" "Update snippet definition." printf "$COLUMN" "update-snippet-docs" "" "Update snippets doc file." printf "$COLUMN" "update-with-llm" "[glob][prompt]" "Feed module files with prompt to LLM and apply file changes." printf "$COLUMN" "upgrade-module" "[path][version]" "Upgrade code of module from target Odoo version." printf "$COLUMN" "upgrade-odoo" "[db][version][mode]" "Run the Odoo upgrade scripts. Default mode ist 'test'." printf "$COLUMN" "upload-module" "[env][path]" "Zip and upload Odoo module." printf "$COLUMN" "version" "" "Show version of required tools." printf "$COLUMN" "visualize-dependencies" "[path]" "Generate visualizations of module dependencies." printf "$COLUMN" "zip-module" "[path]" "Create zip file for module." } function help() { echo if [ -n "$1" ]; then help-table | grep -i "$1" | column -t -s'|' else echo "task [options]" echo echo "commands:" echo help-table fi echo } # Static env vars PASS_ENTRY="env/${PWD##*/}" PASS_ENTRY="${PASS_ENTRY,,}" ODOO_RC="odoo.conf" TASK_CONFIG_DIR="$HOME/.config/odoo-build" PGSSLROOTCERT="tmp/postgres-odoo/ca.crt" export PGSSLROOTCERT # Dynamic env vars ODOO_VERSION=$(echo "$ODOO_REVISION" | cut -d'.' -f1-2) ODOO_DATABASE=${ODOO_DATABASE:="$ODOO_VERSION"} ODOO_LANGUAGE=${ODOO_LANGUAGE:="de_CH"} ODOO_PORT=${ODOO_PORT:="8069"} POSTGRES_IMAGE=${POSTGRES_IMAGE:="postgres:14-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_TAG=${CONTAINER_TAG:="odoo:${ODOO_REVISION}"} CONTAINER_CONFIG="$HOME/.docker/$CONTAINER_REGISTRY" BROWSER_OPEN=${BROWSER_OPEN:=true} CONTAINER_ENGINE=${CONTAINER_ENGINE:="docker"} COMPOSE_COMMAND=${COMPOSE_COMMAND:="docker compose"} LLM_MODEL=${LLM_MODEL:="llama"} SMTP_SERVER=${SMTP_SERVER:="mailpit"} 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"} # Conditional env vars if [[ "$CONTAINER_ENGINE" == "podman" ]]; then COMPOSE_COMMAND="podman-compose" fi if [[ "$(uname)" == "Darwin" ]]; then OS_RELEASE="Darwin" PYTHON_NOTIFY="" OS_OPEN="open" else OS_RELEASE=$(awk -F= '/^NAME/{print $2}' /etc/os-release | tr -d '"') PYTHON_NOTIFY="inotify" OS_OPEN="xdg-open" fi if [[ "$OS_RELEASE" =~ ^(Darwin)$ ]] && command -v ggrep &> /dev/null; then GREP=ggrep else GREP=grep fi # Import commands [ -f ~/taskfile.build/bin/init-config-dir ] && source ~/taskfile.build/bin/init-config-dir [ -f ~/taskfile.build/bin/commit-with-llm ] && source ~/taskfile.build/bin/commit-with-llm [ -f ~/taskfile.build/bin/list-env ] && source ~/taskfile.build/bin/list-env [ -f ~/taskfile.build/bin/show-env ] && source ~/taskfile.build/bin/show-env [ -f ~/taskfile.build/bin/copy-env ] && source ~/taskfile.build/bin/copy-env [ -f ~/taskfile.build/bin/edit-env ] && source ~/taskfile.build/bin/edit-env [ -f ~/taskfile.build/bin/load-env ] && source ~/taskfile.build/bin/load-env [ -f ~/taskfile.build/bin/rename-env ] && source ~/taskfile.build/bin/rename-env [ -f ~/taskfile.build/bin/update-with-llm ] && source ~/taskfile.build/bin/update-with-llm [ -f ~/taskfile.build/bin/create-odoo-env ] && source ~/taskfile.build/bin/create-odoo-env [ -f ~/taskfile.build/bin/create-nextcloud-env ] && source ~/taskfile.build/bin/create-nextcloud-env [ -f ~/taskfile.build/bin/remove-env ] && source ~/taskfile.build/bin/remove-env [ -f ~/taskfile.build/bin/store-dotenv ] && source ~/taskfile.build/bin/store-dotenv [ -f ~/taskfile.build/bin/restore-dotenv ] && source ~/taskfile.build/bin/restore-dotenv [ -f ~/taskfile.build/bin/backup-env-files ] && source ~/taskfile.build/bin/backup-env-files [ -f ~/taskfile.build/bin/restore-env-files ] && source ~/taskfile.build/bin/restore-env-files # Help Commands function info() { set-addons-path echo "OS Release: $OS_RELEASE" echo "Grep Command: $GREP" 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 "Container Tag: $CONTAINER_TAG" echo "Addons Path: $ADDONS_PATH" } function version() { activate-venv uv --version wkhtmltopdf -V if [ "$CONTAINER_ENGINE" == "docker" ]; then docker -v docker compose version else podman --version podman-compose --version fi } function generate-admin-passwd() { activate-venv if test -z "$1"; then echo "\$1 is empty."; exit; fi ODOO_PASSWORD="$1" bin/hash_password } # Env Commands function 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 } function activate-venv() { echo "Source virtualenv venv$ODOO_VERSION." source "venv$ODOO_VERSION/bin/activate" echo "$(python --version) is active." } function remove-venv() { echo "Remove virtualenv venv$ODOO_VERSION." rm -rf "venv$ODOO_VERSION" } function get-addons-path() { set-addons-path echo "$ADDONS_PATH" } function template-odoo-rc() { echo "Template $ODOO_RC" # Load and export vars defined in odoo conf template for VAR in $($GREP -oP '\$(?:\{([^}]*)\}|([A-Z_][A-Z0-9_]*))' odoo.conf.template); do VAR=${VAR/\$/} VAR=${VAR//\{\//} export $VAR done export BASE_URL_WEBSITE_PARAM=$([ -n "$BASE_URL_WEBSITE" ] && echo "base_url_website = $BASE_URL_WEBSITE") export BASE_URL_APP_PARAM=$([ -n "$BASE_URL_APP" ] && echo "base_url_app = $BASE_URL_APP") export GIT_SSH_PUBLIC_KEY_PARAM=$([ -n "$GIT_SSH_PUBLIC_KEY" ] && echo "git.ssh.public.key = \"$GIT_SSH_PUBLIC_KEY\"") export GIT_SSH_PRIVATE_KEY_PARAM=$([ -n "$GIT_SSH_PRIVATE_KEY" ] && echo "git.ssh.private.key = \"$GIT_SSH_PRIVATE_KEY\"") export UPLOADCARE_PUBLIC_KEY_PARAM=$([ -n "$UPLOADCARE_PUBLIC_KEY" ] && echo "uploadcare.public_key = $UPLOADCARE_PUBLIC_KEY") export UPLOADCARE_SECRET_KEY_PARAM=$([ -n "$UPLOADCARE_SECRET_KEY" ] && echo "uploadcare.secret_key = $UPLOADCARE_SECRET_KEY") export KEYCLOAK_BASE_URL_PARAM=$([ -n "$KEYCLOAK_BASE_URL" ] && echo "keycloak.base_url = $KEYCLOAK_BASE_URL") export KEYCLOAK_CLIENT_ID_PARAM=$([ -n "$KEYCLOAK_CLIENT_ID" ] && echo "keycloak.client_id = $KEYCLOAK_CLIENT_ID") export KEYCLOAK_CLIENT_SECRET_PARAM=$([ -n "$KEYCLOAK_CLIENT_SECRET" ] && echo "keycloak.client_secret = $KEYCLOAK_CLIENT_SECRET") export KEYCLOAK_REALM_PARAM=$([ -n "$KEYCLOAK_REALM" ] && echo "keycloak.realm = $KEYCLOAK_REALM") if [ -n "$GIT_SSH_PRIVATE_KEY" ]; then GIT_SSH_PRIVATE_KEY=$(echo -e "$GIT_SSH_PRIVATE_KEY" | base64 -w0) fi envsubst < "odoo.conf.template" > "$ODOO_RC" } function set-addons-path() { # Read addons path from git module GIT_FOLDERS_PATH="$(git config --file .gitmodules --get-regexp path | awk '{ print $2 }' | sort | tr '\n' ',' | sed 's/,*$//g')" # Remove excluded addons 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") done # Check if addons dir exist for ADDON in $GIT_FOLDERS_PATH; do if [ -d "$ADDON" ]; then echo "Path does not exist: $ADDON" exit 1 fi done # Convert to native paths 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 } function load-ssh-key() { if [ -n "$GIT_SSH_PRIVATE_KEY" ]; then echo "Setup SSH key from env vars." DECODED_GIT_SSH_PRIVATE_KEY=$(echo -e "$GIT_SSH_PRIVATE_KEY" | base64 -d) 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 } function list-versions() { ls -1 versions } # Docs Commands function lint() { activate-venv echo "Run pre-commit in $PWD" pre-commit run } function update-help-docs() { echo "Pipe help table into task.md." help-table > task.md } function update-repo-docs() { if test -z "$1"; then echo "\$1 is empty."; exit; fi README_PATH="$1" FILE_NAME=$(basename "$README_PATH") if [[ "$FILE_NAME" != "README.md" ]]; then README_PATH="$1/README.md" FILE_NAME="README.md" fi REPO_PATH=$(dirname "$README_PATH") MANIFEST_FILES="$REPO_PATH/*/__manifest__.py" # Find marker in readme and clear content after echo "Clear modules table for $README_PATH." MARKER="## Available modules" sed -i "/$MARKER/Q" "$README_PATH" echo "$MARKER" >> "$README_PATH" echo "" >> "$README_PATH" echo "| Module | Summary |" >> "$README_PATH" echo "| --- | --- |" >> "$README_PATH" # Clear table after a marker for MANIFEST_FILE in $MANIFEST_FILES; do MODULE_DIR=$(dirname "$MANIFEST_FILE") MODULE_NAME=$(basename "$MODULE_DIR") echo "Add summary of $MODULE_NAME to readme file." SUMMARY=$($GREP 'summary' "$MANIFEST_FILE" -A 1 | tail -1) echo "| [$MODULE_NAME]($MODULE_NAME) | $SUMMARY |" >> "$README_PATH" done } function update-snippet-docs() { ./bin/update-snippet-docs } function update-revision-docs() { 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 PATH_URL="https://github.com/Mint-System/Odoo-Build/tree/main" for FILE in revisions/*; do if [[ -f "$FILE" ]]; then REVISION=$(basename "$FILE") 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 "$FILE") REVISIONS[$VERSION]+=$'\n```' fi done for REVISION in $(echo "${!REVISIONS[@]}" | tr ' ' '\n' | sort -V); do echo -e "${REVISIONS[$REVISION]}\n" >> "revisions.md" done } function update-modules-docs() { echo "Update module doc file." activate-venv ./bin/generate-module-overview } function update-docs() { update-modules-docs update-revision-docs update-snippet-docs update-help-docs } function ls-module() { if [ -n "$1" ]; then find addons -name "__manifest__.py" | xargs -r dirname | $GREP "$1" |sort -u else find addons -name "__manifest__.py" | xargs -r dirname | sort -u fi } function update-module-license() { if test -z "$1"; then echo "\$1 is empty."; exit; fi GIT_FOLDERS=$(ls-module "$1") for GIT_FOLDER in $GIT_FOLDERS; do echo "Update license file for $GIT_FOLDER." cp templates/module/LICENSE "$GIT_FOLDER/" done } function template-repo(){ if test -z "$1"; then echo "\$1 is empty."; exit; fi REPO_PATH="$1" echo "Copy $ODOO_VERSION template files to $REPO_PATH" cp "templates/task" "$REPO_PATH" cp -r "templates/$ODOO_VERSION/." "$REPO_PATH" rm "$REPO_PATH/.flake8" rm "$REPO_PATH/.isort.cfg" rm "$REPO_PATH/.pylintrc-mandatory" update-repo-docs "$REPO_PATH" } # Module Commands function get-modules() { if test -z "$1"; then echo "\$1 is empty."; exit; fi OPTION="$2" if [ -z "$OPTION" ]; then OPTION="dirname" fi 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]+['\"]|${ODOO_VERSION})" | 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]+['\"]|${ODOO_VERSION})" | xargs -r dirname | xargs -r -I {} basename {} | sort -u | tr "\n" "," | sed 's/,$//') fi echo "$MODULES" } function get-module-version() { if test -z "$1" then echo "\$1 is empty." else # Get version of module 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 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 } function list-modules() { if test -z "$1"; then echo "\$1 is empty."; exit; fi echo $(get-modules "$1" basename | tr "," "\n") } # Container Commands function template-compose() { echo "Template compose.yml" # Load and export vars defined in compose template for VAR in $($GREP -oP '\$\{([^}]+)\}' compose.yml.template); do VAR=${VAR/#\$\{/} VAR=${VAR/%\}/} export $VAR done envsubst < "compose.yml.template" > "compose.yml" } function container-ps() { $CONTAINER_ENGINE ps } function build() { MANIFEST_FILE="images/odoo/Dockerfile" PLATFORM="linux/amd64" # linux/arm64 if [ -n "$1" ]; then MANIFEST_FILE="$1" fi if [ "$MANIFEST_FILE" = "images/odoo-mailgate/Dockerfile" ]; then CONTAINER_TAG="odoo-mailgate" fi if [ "$MANIFEST_FILE" = "images/odoo-upgrade/Dockerfile" ]; then CONTAINER_TAG="odoo-upgrade" fi if [ "$MANIFEST_FILE" = "images/odoocli/Dockerfile" ]; then CONTAINER_TAG="odoocli" fi if [ -n "$2" ]; then PLATFORM="$1" fi ODOO_DATE=$(echo "$ODOO_REVISION" | cut -d'.' -f3-) source "revisions/$ODOO_REVISION" case "$ODOO_VERSION" in "15.0") PYTHON_VERSION="3.9" ;; "16.0") # PYTHON_VERSION="3.9" PYTHON_VERSION="3.11" ;; "17.0") PYTHON_VERSION="3.11" ;; *) PYTHON_VERSION="3.12" ;; esac echo "Python version is: $PYTHON_VERSION" if [ "$CONTAINER_ENGINE" == "podman" ]; then echo "Remove container image with tag ${CONTAINER_REGISTRY}${CONTAINER_TAG}" $CONTAINER_ENGINE image rm -f "${CONTAINER_REGISTRY}${CONTAINER_TAG}" echo "Run Podman build ${CONTAINER_REGISTRY}${CONTAINER_TAG} for ${PLATFORM}" SOURCE_DATE_EPOCH=$(git -C odoo log -1 --pretty=%ct) podman 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}" fi if [ "$CONTAINER_ENGINE" == "docker" ]; then echo "Remove container image with tag ${CONTAINER_REGISTRY}${CONTAINER_TAG}" $CONTAINER_ENGINE image rm -f "${CONTAINER_REGISTRY}${CONTAINER_TAG}" 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}" \ --load fi } function checkout-latest-revision() { if [ -n "$1" ] ; then ODOO_VERSION="$1" fi ODOO_REVISION=$(list-revision | $GREP "$ODOO_VERSION" | tail -n1) checkout-revision "$ODOO_REVISION" } function container-login() { if [ -n "$1" ]; then CONTAINER_REGISTRY_USERNAME="$1" fi if [ -n "$2" ]; then CONTAINER_REGISTRY_PASSWORD="$2" fi if [ "$CONTAINER_ENGINE" == "podman" ]; then echo "podman login with username $CONTAINER_REGISTRY_USERNAME." podman login --username "$CONTAINER_REGISTRY_USERNAME" --password "$CONTAINER_REGISTRY_PASSWORD" docker.io fi if [ "$CONTAINER_ENGINE" == "docker" ]; then echo "Docker login with username $CONTAINER_REGISTRY_USERNAME." echo "$CONTAINER_REGISTRY_PASSWORD" | docker --config "$CONTAINER_CONFIG" login --username "$CONTAINER_REGISTRY_USERNAME" --password-stdin fi } function test-image() { if [ "$1" == "images/odoo-upgrade/Dockerfile" ]; then CONTAINER_TAG="odoo-upgrade" fi if [ "$1" == "images/odoocli/Dockerfile" ]; then CONTAINER_TAG="odoocli" fi if [[ "$CONTAINER_TAG" =~ ^odoo: ]]; then echo -e "\033[38;5;214mTEST\033[0m: Run git clone addons" generate-ssh-keys export GIT_SSH_PRIVATE_KEY export GIT_SSH_PUBLIC_KEY export LOCAL_PATH="./tmp" 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}" mkdir -p "$LOCAL_PATH" export PATH=./images/odoo/bin/:$PATH ./images/odoo/bin/git-clone-addons rm -rf "$LOCAL_PATH/github.com" echo -e "\033[38;5;214mTEST\033[0m: Test set addons path regex with version: $ODOO_VERSION" echo "'version': '1.0'" | $GREP -iE "version.*['\"]([0-9]+\.[0-9]+['\"]|${ODOO_VERSION})" echo "'version': '1.1'" | $GREP -iE "version.*['\"]([0-9]+\.[0-9]+['\"]|${ODOO_VERSION})" echo "'version': '2.5'" | $GREP -iE "version.*['\"]([0-9]+\.[0-9]+['\"]|${ODOO_VERSION})" echo "'version': '13.0.1.0.0'" | $GREP -iE "version.*['\"]([0-9]+\.[0-9]+['\"]|${ODOO_VERSION})" || echo "Must not match" echo "'version': '14.0.2.1.1'" | $GREP -iE "version.*['\"]([0-9]+\.[0-9]+['\"]|${ODOO_VERSION})" || echo "Must not match" echo "'version': '${ODOO_VERSION}.0.0.0'" | $GREP -iE "version.*['\"]([0-9]+\.[0-9]+['\"]|${ODOO_VERSION})" echo '"Version": "1.0"' | $GREP -iE "version.*['\"]([0-9]+\.[0-9]+['\"]|${ODOO_VERSION})" 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 ODOO_DATABASE="$ODOO_DATABASE" -e ODOO_USERNAME="$ODOO_USERNAME" -e ODOO_PASSWORD="$ODOO_PASSWORD" \ mintsystem/odoocli "$@" } docker_odoocli --help fi } function publish() { LATEST_TAG="odoo:$ODOO_VERSION" if [ "$1" == "images/odoo-mailgate/Dockerfile" ]; then CONTAINER_TAG="odoo-mailgate" LATEST_TAG="odoo-mailgate:latest" fi if [ "$1" == "images/odoo-upgrade/Dockerfile" ]; then CONTAINER_TAG="odoo-upgrade" LATEST_TAG="odoo-upgrade:latest" fi if [ "$1" == "images/odoocli/Dockerfile" ]; 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 } function upload-module() { if test -z "$1"; then echo "\$1 is empty."; exit; fi if test -z "$2"; then echo "\$2 is empty."; exit; fi # Check if path is root folder 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 FILE_PATH="tmp/$MODULE_NAME.zip" load-env "$1" 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\n" UPLOADS+=" version: $VERSION\n" fi done echo "odoo_apps:" echo -e "$UPLOADS" } function logs() { CONTAINER="$1" CONTAINER=${CONTAINER:="odoo"} $CONTAINER_ENGINE logs --follow "$CONTAINER" } # Script Commands function install-odoo-scripts() { echo "Installing Odoo scripts with user flag." curl -L https://ansible.build/odoo_scripts/install | bash -s -- --user } function upgrade-odoo() { if test -z "$1"; then echo "\$1/database is empty."; exit; fi if test -z "$2"; then echo "\$2/version is empty."; exit; fi DATABASE="$1" ODOO_TARGET_VERSION="$2" MODE="test" if [ -n "$3" ]; then # Options: test, production MODE="$3" fi NEW_DATABASE="${DATABASE}_${ODOO_TARGET_VERSION}" echo "Execute Odoo upgrade script in $MODE mode for $DATABASE with version $ODOO_TARGET_VERSION." echo "The database will be restored as $NEW_DATABASE." export PGHOST=localhost export PGUSER=odoo export PGPASSWORD=odoo python <(curl -s https://upgrade.odoo.com/upgrade) "$MODE" -d "$DATABASE" -t "$ODOO_TARGET_VERSION" -r "$NEW_DATABASE" echo "Upgrade finished successfully." } # Install Commands function install() { init-venv activate-venv if [[ "$OS_RELEASE" =~ ^(Arch Linux|Darwin|Debian|Fedora Linux|Ubuntu|Pop!_OS)$ ]]; then # Source: https://gist.github.com/faniska/37f896d5e9de5fee925925d7caf3cb9e INSTALLED_VERSION=$(wkhtmltopdf --version 2>&1 | $GREP -Po '^wkhtmltopdf \K[\d.]+') if [ "$INSTALLED_VERSION" != "0.12.4" ]; then echo -e "\033[38;5;214mWget\033[0m: Install wkhtmltopdf" wget https://github.com/wkhtmltopdf/wkhtmltopdf/releases/download/0.12.4/wkhtmltox-0.12.4_linux-generic-amd64.tar.xz tar xvf wkhtmltox*.tar.xz sudo mv wkhtmltox/bin/wkhtmlto* /usr/bin rm -rf wkhtmltox wkhtmltox-0.12.4_linux-generic-amd64.tar.* else echo "wkhtmltopdf version is already 0.12.4" fi fi if [[ "$OS_RELEASE" =~ ^(Debian|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 and gettext" sudo apt install -y libxml2-utils 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;214mPython\033[0m: Install psycopg2-binary" uv pip install psycopg2-binary echo -e "\033[38;5;214mPython\033[0m: Remove psycopg2 from Odoo requirements" sed -i '/^psycopg2/d' odoo/requirements.txt elif [[ "$OS_RELEASE" =~ ^(Darwin)$ ]]; then echo "Welcome daring Mac user. Let's try our best to get this working." echo "Install wkhtmltopdf and some dev libraries" echo "This will give a depreciation warning and you need to enter your sudo password." brew install gettext wkhtmltopdf libsasl2 openldap openssl libxml2 grep echo -e "\033[38;5;214mPython\033[0m: Install the binary version of psycopg2" uv pip install psycopg2-binary --force echo -e "\033[38;5;214mSystem\033[0m: Add LDFLAGS to .env file" if ! $GREP -qxF 'LDFLAGS=-L/opt/homebrew/lib/' .env; then echo '' >> .env echo 'LDFLAGS=-L/opt/homebrew/lib/' >> .env fi elif [[ "$OS_RELEASE" =~ ^(Arch Linux)$ ]]; then echo "Arch is the best" echo -e "\033[38;5;214mPacman\033[0m: Install python-ldap and psycopg2 build dependencies" sudo pacman -S base-devel postgresql else echo "The operating system $OS_RELEASE is not supported." exit 1 fi if [ ! -d "$HOME/taskfile.build" ]; then echo -e "\033[38;5;214mGit\033[0m: Clone taskfile repo" git clone https://github.com/janikvonrotz/taskfile.build.git ~/taskfile.build else echo -e "\033[38;5;214mGit\033[0m: Pull taskfile repo" git -C ~/taskfile.build pull fi echo -e "\033[38;5;214mPython\033[0m: Install Odoo development tools" uv pip install ${PYTHON_NOTIFY} uv pip install -r requirements.txt if [ -f "odoo/requirements.txt" ]; then echo -e "\033[38;5;214mPython\033[0m: Install Odoo dependencies" uv pip install -r odoo/requirements.txt if [ "$ODOO_VERSION" = "17.0" ]; then uv pip install lxml_html_clean Werkzeug==2.2.2 fi echo -e "\033[38;5;214mPython\033[0m: Configure Odoo as python package" uv pip install -e odoo --no-deps fi if [[ "$OS_RELEASE" =~ ^(Fedora Linux)$ ]]; then echo -e "\033[38;5;214mGit\033[0m: Reset Odoo requirements" git -C odoo checkout requirements.txt 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 } # Process Commands function open-url-with-delay(){ if test -z "$1"; then echo "\$1 is empty."; exit; fi if $BROWSER_OPEN; then sleep 2 $OS_OPEN "$1" fi } function start() { activate-venv set-addons-path if [ "$POSTGRES_SSL" = "on" ]; then generate-pg-ssl-keys fi template-compose # Use default database if second param is not given DATABASE="$2" if [ -z "$2" ]; then DATABASE="$ODOO_DATABASE" 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" =~ "admin" ]]; then echo "Open http://localhost:8000 url in your browser." open-url-with-delay "http://localhost:8000" & $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" & $COMPOSE_COMMAND up -d mailpit fi if [[ "$1" =~ "native" ]]; then activate-venv export SMTP_SERVER=localhost template-odoo-rc PORT=8069 while [ $PORT -le 8079 ]; do if ! lsof -i:$PORT >/dev/null 2>&1; then break fi PORT=$((PORT + 1)) done export ODOO_BASE_URL="http://localhost:8069" echo "Open http://localhost:$PORT url in your browser." open-url-with-delay "http://localhost:$PORT/web?debug=1&db=$DATABASE" & odoo --database="$DATABASE" --config "$ODOO_RC" --xmlrpc-port="$PORT" --addons-path="$ADDONS_PATH" --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" & $COMPOSE_COMMAND up -d odoo fi } function debug() { if test -z "$1"; then echo "\$1 is empty."; exit; fi activate-venv set-addons-path PORT=8069 if [[ "$1" =~ "native" ]]; 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" \ --xmlrpc-port="$PORT" --addons-path="$ADDONS_PATH" --dev=all --log-level="$LOG_LEVEL" fi } function run() { if test -z "$1"; then echo "\$1 is empty."; exit; fi if test -z "$2"; then echo "\$2 is empty."; exit; fi activate-venv template-compose echo "Run '$1' container with command '$2'." $COMPOSE_COMMAND run --rm "$1" "$2" "$3" } function exec() { if test -z "$1"; then echo "\$1 is empty."; exit; fi if test -z "$2"; then echo "\$2 is empty."; exit; fi echo "Execute command '$2' with $CONTAINER_EXEC_PARAM in container '$1'." $CONTAINER_ENGINE exec $CONTAINER_EXEC_PARAM "$1" "$2" } function record-with-py-spy() { if test -z "$1"; then echo "\$1 is empty."; exit; fi PID="$1" OUTPUT_FILE="tmp/speedscope-capture.json" 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 -- $OS_OPEN "$SPEEDSCOPE_URL" } function record-with-memray() { if test -z "$1"; then echo "\$1 is empty."; exit; fi activate-venv set-addons-path PORT=8069 OUTPUT_FILE="tmp/memray-capture.bin" if [[ "$1" =~ "native" ]]; 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" \ --xmlrpc-port="$PORT" --addons-path="$ADDONS_PATH" --dev=all --log-level="$LOG_LEVEL" python3 -m memray flamegraph "$OUTPUT_FILE" $OS_OPEN "tmp/memray-flamegraph-output.html" fi } function remove() { activate-venv template-compose if test -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 } function restart() { activate-venv template-compose if test -z "$1"; then $COMPOSE_COMMAND restart fi if [[ "db|mail|odoo" =~ $1 ]]; then $COMPOSE_COMMAND restart "$1" fi } function stop() { activate-venv template-compose if test -z "$1"; then $COMPOSE_COMMAND stop fi $COMPOSE_COMMAND stop "$1" } function 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 } function generate-pg-ssl-keys() { KEYMATERIAL_PATH="tmp/postgres-odoo" 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 chmod 600 "$KEYMATERIAL_PATH/server.key" chmod 644 "$KEYMATERIAL_PATH/server.crt" sudo chown 70:70 "$KEYMATERIAL_PATH/server.key" sudo chown 70:70 "$KEYMATERIAL_PATH/server.crt" fi fi } function test-project() { if [ "$1" = "clean" ]; then echo -e "\033[38;5;214mTEST\033[0m: Cleanup test environment." drop-db test remove odoo remove-env test git -C oca/partner-contact checkout . else activate-venv 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},https://github.com/Mint-System/Odoo-Apps-Connector.git#${ODOO_VERSION}" run odoo git-clone-addons echo -e "\033[38;5;214mTEST\033[0m: Init Odoo database." export ODOO_ADDONS_PATH="/mnt/oca/partner-contact" export ODOO_DATABASE=test 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,git_base,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=session_db,module_change_auto_install export SESSION_DB_URI=postgres://odoo:odoo@db/test export MODULE_AUTO_INSTALL_DISABLED=odoo_test_xmlrunner export AUTO_UPDATE_MODULES=True export AUTO_UPDATE_TRANSLATIONS=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 OCA module." run 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_USERNAME=test export ODOO_PASSWORD=test if ! show-env test; then create-odoo-env test fi show-env test test-xmlrpc test echo -e "\033[38;5;214mTEST\033[0m: Install, update, disable and remove snippet." install-snippet test snippets/base.view_partner_form.show_type.xml update-snippet test snippets/base.view_partner_form.show_type.xml disable-snippet test snippets/base.view_partner_form.show_type.xml remove-snippet test snippets/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 restart odoo until curl --silent --fail "http://localhost:8069/web/login" &> /dev/null; do echo "Waiting for Odoo to be ready ..." sleep 3 done docker logs odoo 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 echo -e "\033[38;5;214mTEST\033[0m: Drop the test database." drop-db test echo -e "\033[38;5;214mTEST\033[0m: Prepare container for testing." exec odoo setup-tests echo -e "\033[38;5;214mTEST\033[0m: Run module tests." CONTAINER_EXEC_PARAM="-w /mnt/oca/partner-contact" exec odoo run-tests fi } # Interactive Shell Commands function shell() { DATABASE="$1" if [ -z "$DATABASE" ]; then DATABASE="$ODOO_DATABASE" fi CODE="$2" activate-venv 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 } function psql() { DATABASE="$1" if [ -z "$DATABASE" ]; then DATABASE="$ODOO_DATABASE" fi $CONTAINER_ENGINE exec -it db psql "postgres://odoo:odoo@localhost:5432/$DATABASE" } # Database Commands function init-db() { ODOO_INIT_ADDONS="${ODOO_INIT_ADDONS:-"web"}" DATABASE="$1" if [ -z "$DATABASE" ]; then DATABASE="$ODOO_DATABASE" fi activate-venv 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 } function import-csv() { if test -z "$1"; then echo "\$1 is empty."; exit; fi DATABASE="$1" FILE_PATH="$2" # Use default database if second param is not given if test -z "$2"; then DATABASE="$ODOO_DATABASE" FILE_PATH="$1" fi 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;" } function drop-db() { DATABASE="$1" if [ -z "$DATABASE" ]; then DATABASE="$ODOO_DATABASE" 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\";" } function setup-mail() { DATABASE="$1" if [ -z "$DATABASE" ]; then DATABASE="$ODOO_DATABASE" fi activate-venv set-addons-path export MAIL_BOUNCE_ALIAS export MAIL_CATCHALL_ALIAS export MAIL_CATCHALL_DOMAIN export MAIL_DEFAULT_FROM export MAIL_ALIAS_DOMAIN echo "Setup mail server configuation for database $DATABASE." odoo shell --database "$DATABASE" --config "$ODOO_RC" --addons-path="$ADDONS_PATH" --no-http < bin/setup-mail } function load-language() { if test -z "$1"; then echo "\$1 is empty."; exit; fi DATABASE="$1" LANGUAGE="$2" # Use default database if second param is not given if test -z "$2"; then DATABASE="$ODOO_DATABASE" LANGUAGE="$1" fi activate-venv 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" } function set-admin() { DATABASE="$1" if [ -z "$DATABASE" ]; then DATABASE="$ODOO_DATABASE" 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;" } function clear-assets() { DATABASE="$1" if [ -z "$DATABASE" ]; then DATABASE="$ODOO_DATABASE" 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_%';" } function clear-views() { DATABASE="$1" if [ -z "$DATABASE" ]; then DATABASE="$ODOO_DATABASE" fi echo "Archive views with prefix 'mint_system'." $CONTAINER_ENGINE exec -i db psql "postgres://odoo:odoo@localhost/$DATABASE" -c "SELECT id,name from ir_ui_view WHERE name like '%mint_system%';" $CONTAINER_ENGINE exec -i db psql "postgres://odoo:odoo@localhost/$DATABASE" -c "UPDATE ir_ui_view SET active=false WHERE name like '%mint_system%';" 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);" } function clear-filestore() { echo "Remove directory $HOME/.local/share/Odoo/filestore/$1" rm -rf "$HOME/.local/share/Odoo/filestore/$1" } function disable-mailserver() { if test -z "$1"; then echo "\$1 is empty."; exit; fi activate-venv load-env "$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 "[]" } function change-uuid() { if test -z "$1"; then echo "\$1 is empty."; exit; fi activate-venv load-env "$1" UUID=$(uuidgen) echo "Set uuid $UUID for $1." --method 'write' --model 'ir.config_parameter' --domain "[('key', '=', 'database.uuid')]" --field 'value' --value "'$UUID'" } function update-module-list() { if test -z "$1"; then echo "\$1 is empty."; exit; fi activate-venv set-addons-path echo "env['ir.module.module'].update_list()" | odoo shell --database "$1" --config "$ODOO_RC" --addons-path="$ADDONS_PATH" --no-http } function patch-database() { if test -z "$1"; then echo "\$1 is empty."; exit; fi DATABASE="$1" FILENAME="$2" # Use default database if second param is not given if test -z "$2"; then DATABASE="$ODOO_DATABASE" 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 function clock-odoo() { DATABASE="$1" if [ -z "$DATABASE" ]; then DATABASE="$ODOO_DATABASE" fi activate-venv set-addons-path odoo cloc -d "$DATABASE" --config "$ODOO_RC" --addons-path="$ADDONS_PATH" --stop-after-init --no-http } function visualize-dependencies() { if test -z "$1"; then echo "\$1 is empty."; exit; fi mkdir -p tmp ./bin/odoo-module-dependencies "$1" $OS_OPEN tmp/odoo-module-dependencies.html } # Repo Commands function install-requirements() { if test -z "$1"; then echo "\$1 is empty."; exit; fi DATABASE="$1" FILE_PATH="$2" # Use default database if second param is not given if test -z "$2"; then DATABASE="$ODOO_DATABASE" FILE_PATH="$1" fi FILE_NAME=$(basename "$FILE_PATH") if [[ "$FILE_NAME" != "requirements.txt" ]]; then echo "\$1 must point to requirements.txt file."; exit; fi 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 activate-venv echo "Installing python packages in virutal env ..." uv pip install -r "$FILE_PATH" fi } function create-module-repo() { if test -z "$1"; then echo "\$1 is empty."; exit; fi REPO_PATH="$1" 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" } function lint-module-repo() { if test -z "$1"; then echo "\$1 is empty."; exit; fi MODULE_PATHS=$(get-modules "$MODULE_PATH") for MODULE_PATH in ${MODULE_PATHS//,/ }; do lint-module "$MODULE_PATH" done } function test-module-repo() { DATABASE="$1" MODULE_PATH="$2" # Use default database if second param is not given if test -z "$2"; then DATABASE="$ODOO_DATABASE" MODULE_PATH="$1" fi MODULES=$(get-modules "$MODULE_PATH" basename) for MODULE in ${MODULES//,/ }; do echo "Testing module $MODULE ..." test-module "$DATABASE" "$MODULE" done } # Module Commands function init-module() { if test -z "$1"; then echo "\$1 is empty."; exit; fi DATABASE="$1" MODULE_PATH="$2" # Use default database if second param is not given if test -z "$2"; then DATABASE="$ODOO_DATABASE" 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 MODULES=$(get-modules "$MODULE_PATH" basename) else MODULES=$(basename "$MODULE_PATH") fi activate-venv 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 } function upgrade-module() { if test -z "$1"; then echo "\$1 is empty."; exit; fi if test -z "$2"; then echo "\$2 is empty."; exit; fi MODULE="$1" SOURCE_ODOO_VERSION="$2" activate-venv set-addons-path if [ "$ODOO_VERSION" = "17.0" ]; then echo "Migrate views of module $MODULE to $ODOO_VERSION ..." odoo --database "$DATABASE" -i "$MODULE" --config "$ODOO_RC" --addons-path="$ADDONS_PATH" --load=base,web,views_migration_17 --stop-after-init fi if [ "$ODOO_VERSION" = "18.0" ]; then echo "Upgrade code of module $MODULE from $SOURCE_ODOO_VERSION ..." odoo upgrade_code --addons-path "$MODULE" --from "$SOURCE_ODOO_VERSION" fi } function update-module() { if test -z "$1"; then echo "\$1 is empty."; exit; fi DATABASE="$1" MODULE_NAME=$(basename "$2") # Use default database if second param is not given if test -z "$2"; then DATABASE="$ODOO_DATABASE" MODULE_NAME=$(basename "$1") fi activate-venv 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 } function translate-module() { if test -z "$1"; then echo "\$1 is empty."; exit; fi MODULE_PATH="$1" MODULE_NAME=$(basename "$1") LANGUAGE="$2" DATABASE="$3" if test -z "$2"; then LANGUAGE="$ODOO_LANGUAGE" fi if test -z "$3"; then DATABASE="$ODOO_DATABASE" fi activate-venv 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 } function lint-module() { if test -z "$1"; then echo "\$1 is empty."; exit; fi activate-venv echo "Set file and folder permissions on $1" find "$1" -type d -exec chmod u=rwx,go=rx {} \; find "$1" -type f -exec chmod u=rw,go=r {} \; echo "Update index.html" rst2html5 "$1/README.rst" "$1/static/description/index.html" pushd . cd "$1" echo "Run pre-commit in $PWD" pre-commit run --all-files # --show-diff-on-failure --color=always echo "Stage changes" git add . popd } function test-module() { DATABASE="$1" MODULE_NAME=$(basename "$2") # Use default database if second param is not given if test -z "$2"; then DATABASE="$ODOO_DATABASE" MODULE_NAME=$(basename "$1") fi activate-venv set-addons-path echo "Testing module $MODULE_NAME on $DATABASE ..." odoo -d "$DATABASE" -i "$MODULE_NAME" --config "$ODOO_RC" --addons-path="$ADDONS_PATH" --stop-after-init --no-http --test-tags /"$MODULE_NAME" } function pytest-module() { DATABASE="$1" MODULE_PATH="$2" # Use default database if second param is not given if test -z "$2"; then DATABASE="$ODOO_DATABASE" MODULE_PATH="$1" fi activate-venv set-addons-path ODOO_CONFIG="tmp/odoo.conf" echo "Write $ODOO_CONFIG" 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" pytest -s --odoo-http --odoo-config="$ODOO_CONFIG" --odoo-database="$DATABASE" "$MODULE_PATH" } function remove-module() { if test -z "$1"; then echo "\$1 is empty."; exit; fi DATABASE="$1" MODULE_NAME=$(basename "$2") # Use default database if second param is not given if test -z "$2"; then DATABASE="$ODOO_DATABASE" MODULE_NAME=$(basename "$1") fi activate-venv 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 } function create-module() { if test -z "$1"; then echo "\$1 is empty."; exit; fi MODULE_NAME=$(basename "$1") PARENT_DIR=$(dirname "$1") activate-venv echo "Scaffolding module $MODULE_NAME in $PARENT_DIR ..." odoo scaffold "$MODULE_NAME" "$PARENT_DIR" -t templates/module } function generate-module-docs() { if test -z "$1"; then echo "\$1 is empty."; exit; fi MODULE_PATH="$1" MANIFEST_PATH="$MODULE_PATH/__manifest__.py" MODULE_TITLE=$(python3 -c "import ast; d=ast.literal_eval(open('$MANIFEST_PATH').read()); print(d.get('name', ''))") MODULE_UNDERLINE=$(python3 -c "import ast; d=ast.literal_eval(open('$MANIFEST_PATH').read()); print('=' * len(d.get('name', '')))") MODULE_SUMMARY=$(python3 -c "import ast; d=ast.literal_eval(open('$MANIFEST_PATH').read()); print(d.get('summary', '').replace('\n', ' ').strip())") MODULE_WIKI_PATH=$(basename "$MODULE_PATH" | sed 's/_/-/g') echo "Update $MODULE_PATH/README.rst" export MODULE_TITLE MODULE_UNDERLINE MODULE_SUMMARY MODULE_WIKI_PATH envsubst '$MODULE_TITLE $MODULE_UNDERLINE $MODULE_SUMMARY $MODULE_WIKI_PATH' < templates/README.rst > "$MODULE_PATH/README.rst" echo "Copy app icon" cp templates/module/static/description/icon.png "$MODULE_PATH/static/description/icon.png" echo "Copy license" cp templates/module/LICENSE "$MODULE_PATH/" } function generate-module-security() { if test -z "$1"; then echo "\$1 is empty."; exit; fi if test -z "$2"; then echo "\$2 is empty."; exit; fi MODULE_NAME=$(basename "$1") PARENT_DIR=$(dirname "$1") MODEL_DOT_NAME="$2" MODEL_NAME=$(echo "$MODEL_DOT_NAME" | tr '.' '_') 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" } function generate-module-model() { if test -z "$1"; then echo "\$1 is empty."; exit; fi if test -z "$2"; then echo "\$2 is empty."; exit; fi MODULE_PATH="$1" MODULE_NAME=$(basename "$1") # Generate template values MODEL_DOT_NAME="$2" # res.partner MODEL_NAME=$(echo "$MODEL_DOT_NAME" | tr '.' '_') # res_partner NAME=$(echo "$MODEL_DOT_NAME" | cut -d"." -f2 | sed 's/[^_]\+/\L\u&/g') # Partners MODEL_DESCRIPTION=$(echo "$MODEL_NAME" | sed 's/[^_]\+/\L\u&/g' | sed 's/_/ /g') # Res Partner MODEL_CAMEL_NAME=$(echo "$MODEL_DESCRIPTION" | sed 's/ //g') # ResPartner export NAME MODULE_NAME MODEL_DOT_NAME MODEL_NAME MODEL_DESCRIPTION MODEL_CAMEL_NAME echo "Substitute model template" envsubst '$NAME $MODULE_NAME $MODEL_DOT_NAME $MODEL_NAME $MODEL_DESCRIPTION $MODEL_CAMEL_NAME' < 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. } function generate-module-inherit() { if test -z "$1"; then echo "\$1 is empty."; exit; fi if test -z "$2"; then echo "\$2 is empty."; exit; fi MODULE_PATH="$1" MODULE_NAME=$(basename "$1") # Generate template values MODEL_DOT_NAME="$2" # res.partner MODEL_NAME=$(echo "$MODEL_DOT_NAME" | tr '.' '_') # res_partner NAME=$(echo "$MODEL_DOT_NAME" | cut -d"." -f2 | sed 's/[^_]\+/\L\u&/g') # Partners MODEL_DESCRIPTION=$(echo "$MODEL_NAME" | sed 's/[^_]\+/\L\u&/g' | sed 's/_/ /g') # Res Partner MODEL_CAMEL_NAME=$(echo "$MODEL_DESCRIPTION" | sed 's/ //g') # ResPartner export NAME MODULE_NAME MODEL_DOT_NAME MODEL_NAME MODEL_DESCRIPTION MODEL_CAMEL_NAME echo "Substitute model template" envsubst '$NAME $MODULE_NAME $MODEL_DOT_NAME $MODEL_NAME $MODEL_DESCRIPTION $MODEL_CAMEL_NAME' < 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. } function generate-module-views() { if test -z "$1"; then echo "\$1 is empty."; exit; fi if test -z "$2"; then echo "\$2 is empty."; exit; fi MODULE_PATH="$1" MODULE_NAME=$(basename "$1") # Generate template values MODEL_DOT_NAME="$2" # res.partner MODEL_NAME=$(echo "$MODEL_DOT_NAME" | tr '.' '_') # res_partner NAME=$(echo "$MODEL_DOT_NAME" | cut -d"." -f2 | sed 's/[^_]\+/\L\u&/g') # Partners MODEL_DESCRIPTION=$(echo "$MODEL_NAME" | sed 's/[^_]\+/\L\u&/g' | sed 's/_/ /g') # Res Partner MODEL_CAMEL_NAME=$(echo "$MODEL_DESCRIPTION" | sed 's/ //g') # ResPartner export NAME MODULE_NAME MODEL_DOT_NAME MODEL_NAME MODEL_DESCRIPTION MODEL_CAMEL_NAME echo "Substitute view template" mkdir -p "$MODULE_PATH/views" envsubst '$NAME $MODULE_NAME $MODEL_DOT_NAME $MODEL_NAME $MODEL_DESCRIPTION $MODEL_CAMEL_NAME' < 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\"," } function generate-module-snippet() { if test -z "$1"; then echo "\$1 is empty."; exit; fi if test -z "$2"; then echo "\$2 is empty."; exit; fi if test -z "$2"; then echo "\$2 is empty."; exit; fi MODULE_PATH="$1" # addons/sale_workflow/sale_helm MODEL_DOT_NAME="$2" # sale.order VIEW_REF="$3" # sale.view_order_form # Generate template values MODEL_NAME=${MODEL_DOT_NAME//\./_} MODULE_NAME=$(basename "$1") SRC_MODULE_NAME=$(echo "$VIEW_REF" | cut -d"." -f1) XML_ID=$(echo "$VIEW_REF" | cut -d"." -f2) export MODULE_NAME MODEL_DOT_NAME SRC_MODULE_NAME VIEW_REF XML_ID echo "Substitute snippet template" envsubst '$MODULE_NAME $MODEL_DOT_NAME $SRC_MODULE_NAME $VIEW_REF $XML_ID' < 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\"" } function release-module() { if test -z "$1"; then echo "\$1 is empty."; exit; fi VERSION=$(get-module-version "$1") cd "$1" || exit LAST_TAG=$(git describe --tags) 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" } function zip-module() { if test -z "$1"; then echo "\$1 is empty."; exit; fi # Get version and module name VERSION=$(get-module-version "$1") MODULE=$(basename "$1") # Copy module to tmp folder echo "Copy module $MODULE version $VERSION to tmp folder" MODULE_NAME="$MODULE-$VERSION" 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 } # Snippet Commands function reset-views() { if test -z "$1"; then echo "\$1 is empty."; exit; fi DATABASE="$1" KEY=$(basename "$2") # Use default database if second param is not given if test -z "$2"; then DATABASE="$ODOO_DATABASE" KEY=$(basename "$1") fi activate-venv 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 } function create-snippet() { if test -z "$1"; then echo "\$1 is empty."; exit; fi export INHERIT_ID=$(echo "$1" | cut -d. -f1-2) FILE="snippets/$1.xml" echo "Create snippet from template: $FILE" envsubst '$INHERIT_ID' < templates/snippet.xml > "$FILE" } function install-snippet() { if test -z "$1"; then echo "\$1 is empty."; exit; fi if test -z "$2"; then echo "\$2 is empty."; exit; fi # Generate view name from filename FILE=$(basename "$2") NAME="mint_system.${FILE%.xml}" INHERIT_ID=$(echo 'cat //data/@inherit_id' | xmllint --shell "$2" | awk -F'[="]' '!/>/{print $(NF-1)}') TYPE=$(echo 'cat //data/@type' | xmllint --shell "$2" | awk -F'[="]' '!/>/{print $(NF-1)}') MODEL=$(echo 'cat //data/@model' | xmllint --shell "$2" | awk -F'[="]' '!/>/{print $(NF-1)}') PRIORITY=$(echo 'cat //data/@priority' | xmllint --shell "$2" | awk -F'[="]' '!/>/{print $(NF-1)}') MODULE_NAME=$(echo "$INHERIT_ID" | cut -d. -f 1 ) XML_ID=$(echo "$INHERIT_ID" | cut -d. -f 2 ) # Set default values : ${TYPE:='qweb'} activate-venv load-env "$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")''' }" } function update-snippet() { if test -z "$1"; then echo "\$1 is empty."; exit; fi if test -z "$2"; then echo "\$2 is empty."; exit; fi # Generate view name from filename FILE=$(basename "$2") NAME="mint_system.${FILE%.xml}" activate-venv load-env "$1" echo "Update view with name $NAME" images/odoocli/bin/odoocli --method 'write' --model 'ir.ui.view' --domain "[('name', '=', '$NAME')]" --field 'arch_base' --value "'''$(cat "$2")'''" } function disable-snippet() { if test -z "$1"; then echo "\$1 is empty."; exit; fi if test -z "$2"; then echo "\$2 is empty."; exit; fi # Generate view name from filename FILE=$(basename "$2") NAME="mint_system.${FILE%.xml}" activate-venv load-env "$1" echo "Disable view with name $NAME" images/odoocli/bin/odoocli --method 'write' --model "ir.ui.view" --domain "[('name', '=', '$NAME')]" --field "active" --value "False" } function remove-snippet() { if test -z "$1"; then echo "\$1 is empty."; exit; fi if test -z "$2"; then echo "\$2 is empty."; exit; fi # Generate view name from filename FILE=$(basename "$2") NAME="mint_system.${FILE%.xml}" activate-venv load-env "$1" echo "Remove view with name $NAME" images/odoocli/bin/odoocli --method "unlink" --model "ir.ui.view" --domain "[('name', '=', '$NAME')]" } function lint-snippets() { echo "Lint snippets files ..." FILES="snippets/*.xml" for FILE in $FILES; do # Access various snippet parameters FILENAME=$(basename "$FILE") SNIPPET_MODULE=$(echo "$FILENAME" | cut -d. -f1) REPORT=$(echo "$FILENAME" | cut -d. -f2) EDIT=$(echo "$FILENAME" | cut -d. -f3) INHERIT_ID=$(echo 'cat //data/@inherit_id' | xmllint --shell "$FILE" | awk -F'[="]' '!/>/{print $(NF-1)}') TYPE=$(echo 'cat //data/@type' | xmllint --shell "$FILE" | awk -F'[="]' '!/>/{print $(NF-1)}') MODEL=$(echo 'cat //data/@model' | xmllint --shell "$FILE" | awk -F'[="]' '!/>/{print $(NF-1)}') PRIORITY=$(echo 'cat //data/@priority' | xmllint --shell "$FILE" | awk -F'[="]' '!/>/{print $(NF-1)}') MODULE_NAME=$(echo "$INHERIT_ID" | cut -d. -f 1 ) XML_ID=$(echo "$INHERIT_ID" | cut -d. -f 2 ) # Check filename COUNT_DOTS=$(echo "$FILENAME" | $GREP -o "\." | wc -l) if [ "$COUNT_DOTS" -ge 4 ]; 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." } # Git Commands function add-git-submodule() { if test -z "$1"; then echo "\$1 is empty."; exit; fi if test -z "$2"; then echo "\$2 is empty."; exit; fi # Add to .gitmodules git submodule add -f -b "$ODOO_VERSION" "$1" "$2" # Save version save-version # Restore staged submodule files git restore --staged .gitmodules "$2" # Remove from working tree # rm -rf .git/modules/"$2" || true # git rm --cached "$2" || true } function ls-git-folder() { if [ -n "$1" ]; then echo $(git config --file .gitmodules --get-regexp url | $GREP "$1" | sed -e 's/submodule.//g' -e 's/.url.*//g' | sort) else echo $(git config --file .gitmodules --get-regexp url | sed -e 's/submodule.//g' -e 's/.url.*//g' | sort) fi } function list-git-folder() { # Define column widths path_width=35 url_width=50 branch_width=8 # Print table header printf "| %-${path_width}s | %-${url_width}s | %-${branch_width}s |\n" "PATH" "URL" "BRANCH" echo "|$(printf '%*s' $((path_width + 2)) '' | tr ' ' '-')|$(printf '%*s' $((url_width + 2)) '' | tr ' ' '-')|$(printf '%*s' $((branch_width + 2)) '' | tr ' ' '-')|" GIT_FOLDERS=$(ls-git-folder "$1") for GIT_FOLDER in $GIT_FOLDERS; do URL=$(git config --file .gitmodules --get "submodule.$GIT_FOLDER.url") URL=${URL#git@github.com:} URL=${URL%.git} BRANCH=$(git -C "$GIT_FOLDER" branch --show-current 2>/dev/null || echo "detached") printf "| %-${path_width}s | %-${url_width}s | %-${branch_width}s |\n" "$GIT_FOLDER" "$URL" "${BRANCH:-detached}" done } function remove-git-folder() { GIT_FOLDERS=$(ls-git-folder) for GIT_FOLDER in $GIT_FOLDERS; do echo "Delete git folder $GIT_FOLDER." rm -rf "$GIT_FOLDER" done } function remove-git-submodule() { if test -z "$1"; then echo "\$1 is empty."; exit; fi echo "Deinit submodule" git submodule deinit -f "$1" echo "Move submodule to tmp folder" mkdir -p tmp/"$1" mv "$1" tmp/"$1" echo "Remove submodule from work tree" git rm -f "$1" echo "Remove submodule from .git folder" rm -rf .git/modules/"$1" } function clone-git-folder() { GIT_FOLDERS=$(ls-git-folder) for GIT_FOLDER in $GIT_FOLDERS; do GIT_FOLDER_PATH=$(git config --file .gitmodules --get submodule."$GIT_FOLDER".path) GIT_FOLDER_URL=$(git config --file .gitmodules --get submodule."$GIT_FOLDER".url) SHALLOW=$(git config --file .gitmodules --get submodule."$GIT_FOLDER".shallow) GIT_FOLDER_BRANCH=$(git config --file .gitmodules --get submodule."$GIT_FOLDER".branch) if [ "$SHALLOW" = "true" ]; then echo "Clone git folder $GIT_FOLDER (shallow) to $ODOO_VERSION" git -C "$GIT_FOLDER" config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*" git clone --depth 1 --no-single-branch "$GIT_FOLDER_URL" "$GIT_FOLDER_PATH" else echo "Clone git folder $GIT_FOLDER to $ODOO_VERSION" git clone --branch="$GIT_FOLDER_BRANCH" "$GIT_FOLDER_URL" "$GIT_FOLDER_PATH" fi done } function pull-git-folder() { GIT_FOLDERS=$(ls-git-folder) for GIT_FOLDER in $GIT_FOLDERS; do echo "Pull git folder $GIT_FOLDER." git -C "$GIT_FOLDER" stash UPDATE=$(git config submodule."$GIT_FOLDER".update) if [ -z "$UPDATE" ]; then git -C "$GIT_FOLDER" pull origin "$ODOO_VERSION" fi; done } function reset-git-folder() { GIT_FOLDERS=$(ls-git-folder) for GIT_FOLDER in $GIT_FOLDERS; do echo "Abort rebase and reset git folder $GIT_FOLDER." git -C "$GIT_FOLDER" rebase --abort git -C "$GIT_FOLDER" reset --hard done } function clean-git-folder() { GIT_FOLDERS=$(ls-git-folder) for GIT_FOLDER in $GIT_FOLDERS; do echo "Clean git folder $GIT_FOLDER." git -C "$GIT_FOLDER" clean -df done } function checkout-git-folder() { echo "Checkout submodule commits." GIT_FOLDERS=$(ls-git-folder) for GIT_FOLDER in $GIT_FOLDERS; do SHALLOW=$(git config --file .gitmodules --get-regexp submodule."$GIT_FOLDER".shallow | awk '{ print $2 }') COMMIT_HASH=$($GREP "$GIT_FOLDER" "versions/$ODOO_VERSION/.gitmodules.csv" | cut -d',' -f2) if [ "$SHALLOW" = "true" ]; then echo "Checkout git folder $GIT_FOLDER (shallow) to $COMMIT_HASH" git -C "$GIT_FOLDER" stash git -C "$GIT_FOLDER" config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*" git -C "$GIT_FOLDER" fetch origin "$COMMIT_HASH" git -C "$GIT_FOLDER" checkout "$COMMIT_HASH" else echo "Checkout git folder $GIT_FOLDER to $COMMIT_HASH" git -C "$GIT_FOLDER" stash git -C "$GIT_FOLDER" checkout "$COMMIT_HASH" fi done } function switch-git-folder() { if [ -n "$1" ]; then ODOO_VERSION="$1" fi GIT_FOLDERS=$(ls-git-folder) for GIT_FOLDER in $GIT_FOLDERS; do SHALLOW=$(git config --file .gitmodules --get-regexp submodule."$GIT_FOLDER".shallow | awk '{ print $2 }') if [ "$SHALLOW" = "true" ]; then echo "Fetch branch $ODOO_VERSION from origin and then switch." git fetch origin "$ODOO_VERSION:$ODOO_VERSION" git -C "$GIT_FOLDER" switch "$ODOO_VERSION" else echo "Switch git folder branch to $ODOO_VERSION: $GIT_FOLDER" git -C "$GIT_FOLDER" stash git -C "$GIT_FOLDER" switch "$ODOO_VERSION" fi done } function status-git-folder() { if test -z "$1"; then echo "\$1 is empty."; exit; fi GIT_FOLDERS=$(ls-git-folder "$1") echo "Show status in git folders." for GIT_FOLDER in $GIT_FOLDERS; do git -C "$GIT_FOLDER" status done } function stage-git-folder() { if test -z "$1"; then echo "\$1 is empty."; exit; fi GIT_FOLDERS=$(ls-git-folder "$1") for GIT_FOLDER in $GIT_FOLDERS; do echo "Stage all files in $GIT_FOLDER." git -C "$GIT_FOLDER" add --all done } function commit-git-folder() { if test -z "$1"; then echo "\$1 is empty."; exit; fi if test -z "$2"; then echo "\$2 is empty."; exit; fi GIT_FOLDERS=$(ls-git-folder "$1") for GIT_FOLDER in $GIT_FOLDERS; do echo "Commit in $GIT_FOLDER." git -C "$GIT_FOLDER" commit --message "$2" done } function push-git-folder() { if test -z "$1"; then echo "\$1 is empty."; exit; fi GIT_FOLDERS=$(ls-git-folder "$1") for GIT_FOLDER in $GIT_FOLDERS; do echo "Push in $GIT_FOLDER." git -C "$GIT_FOLDER" push done } function sync-git-folder() { echo "Switch and pull submodules." switch-git-folder pull-git-folder } function create-git-feature-branch(){ if test -z "$1"; then echo "\$1 is empty."; exit; fi MODULE_NAME=$(basename "$1") MODULE_PATH=$(dirname "$1") echo "Create feature branch for $MODULE_NAME in $MODULE_PATH ..." cd ./"$MODULE_PATH" || exit pwd git switch -c "feature-$MODULE_NAME" } function create-git-mig-branch(){ if test -z "$1"; then echo "\$1 is empty."; exit; fi MODULE_NAME=$(basename "$1") MODULE_PATH=$(dirname "$1") echo "Create mig branch for $MODULE_NAME in $MODULE_PATH ..." cd ./"$MODULE_PATH" || exit pwd git switch -c "$ODOO_VERSION-mig-$MODULE_NAME" } function load-version() { if [ -n "$1" ]; then ODOO_VERSION="$1" fi echo "Copy .gitmodules and .python-version from 'versions/$ODOO_VERSION'." cp "versions/$ODOO_VERSION/.gitmodules" . cp "versions/$ODOO_VERSION/.python-version" . checkout-revision "$ODOO_VERSION" } function checkout() { if test -z "$1"; then echo "\$1 is empty."; exit; fi ODOO_VERSION="$1" load-version clone-git-folder checkout-git-folder switch-git-folder } function save-version() { GIT_FOLDERS_FILE="versions/$ODOO_VERSION/.gitmodules.csv" echo "Save submodule refs to $GIT_FOLDERS_FILE." GIT_FOLDERS=$(ls-git-folder) echo "path,rev" > "$GIT_FOLDERS_FILE" for GIT_FOLDER in $GIT_FOLDERS; do COMMIT_HASH=$(git -C "$GIT_FOLDER" rev-parse HEAD) echo "$GIT_FOLDER,$COMMIT_HASH" >> "$GIT_FOLDERS_FILE" done echo "Copy .gitmodules and .python-version to 'versions/$ODOO_VERSION'" cp ".gitmodules" "versions/$ODOO_VERSION/" cp ".python-version" "versions/$ODOO_VERSION/" } # Revision Commands function list-revision() { ls -1 revisions } function 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" } function checkout-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) echo "Checkout and tag git repos ..." git -C odoo checkout $ODOO_REF git -C odoo tag -a $ODOO_REVISION -m "$ODOO_REVISION" git -C enterprise checkout $ODOO_ENTERPRISE_REF git -C enterprise tag -a $ODOO_REVISION -m "$ODOO_REVISION" git -C themes checkout $ODOO_THEME_REF git -C themes tag -a $ODOO_REVISION -m "$ODOO_REVISION" } function create-revision() { if test -z "$1"; then echo "\$1 is empty."; exit; fi REVISION="$1" ODOO_DATE=$(echo "$REVISION" | cut -d'.' -f3-) 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 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 REF=$(git rev-list -n 1 --before="$BEFORE_DATE" origin/$ODOO_VERSION) git checkout "$REF" cd .. cd enterprise 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 ENTERPRISE_REF=$(git rev-list -n 1 --before="$BEFORE_DATE" origin/$ODOO_VERSION) git checkout "$ENTERPRISE_REF" cd .. cd themes 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 THEME_REF=$(git rev-list -n 1 --before="$BEFORE_DATE" origin/$ODOO_VERSION) git checkout "$THEME_REF" cd .. echo -e "\nCreate revision: $REVISION" export REVISION REF ENTERPRISE_REF THEME_REF envsubst '$REVISION $REF $ENTERPRISE_REF $THEME_REF' < templates/odoo_revision > "revisions/$REVISION" chmod +x "revisions/$REVISION" echo "Checkout revision: $REVISION" checkout-revision "$REVISION" } # RPC Commands function test-xmlrpc() { activate-venv load-env "$1" export ODOO_URL export ODOO_DATABASE export ODOO_USERNAME export ODOO_PASSWORD echo "Connect to database and count res.partner records: $(images/odoocli/bin/odoocli --model "res.partner" --method "search_count" --domain '[]')" } function odoocli(){ activate-venv images/odoocli/bin/odoocli "$@" } function export-website-data() { activate-venv if [ -n "$1" ]; then load-env $1 fi export ODOO_URL export ODOO_DATABASE export ODOO_USERNAME export ODOO_PASSWORD export ODOO_WEBSITE_ID echo -e "\nPython Output:\n\n---" python "bin/export-website-data" EXIT_CODE=$? echo -e "---\n" echo -e "Exit code: $EXIT_CODE\n" } function import-website-data() { activate-venv if [ -n "$1" ]; then load-env $1 fi export ODOO_URL export ODOO_DATABASE export ODOO_USERNAME export ODOO_PASSWORD export ODOO_WEBSITE_ID echo -e "\nPython Output:\n\n---" python "bin/import-website-data" EXIT_CODE=$? echo -e "---\n" echo -e "Exit code: $EXIT_CODE\n" } # Vuepress Commands function install-vuepress() { echo "Install Node." pnpm env use --global lts echo "Install npm packages." pnpm install } function dev-vuepress() { pnpm run dev } function build-vuepress() { pnpm run build } function serve-vuepress() { cd .vuepress/dist pnpx serve } if declare -f "$1" > /dev/null; then "$1" "${@:2}" else case "$1" in source) activate-venv ;; *) echo "Unknown command: $1" help exit 1 ;; esac fi