diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index d0a9893d5e45d5c05ff63f967924cdb091334810_LmdpdGxhYi1jaS55bWw=..0d4e37c13858ac34f5fbe5a0271cafb2ad90ab45_LmdpdGxhYi1jaS55bWw= 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -5,10 +5,8 @@
   project: xcg/ci-templates
 - file: deploy-doc.gitlab-ci.yaml
   project: xcg/ci-templates
-- file: shell-lint.gitlab-ci.yaml
-  project: xcg/ci-templates
 
 variables:
   TAG_LATEST: branch/default
   DOCKER_IMAGE: xcgd/odoo_scripts
   HTML_DOC_SOURCES: doc/_build/html
@@ -10,12 +8,8 @@
 
 variables:
   TAG_LATEST: branch/default
   DOCKER_IMAGE: xcgd/odoo_scripts
   HTML_DOC_SOURCES: doc/_build/html
-  SH_SCRIPTS: create_archive start
-
-checkbashisms:
-  allow_failure: true
 
 import_jsonrpc_odoo11_test:
   stage: test
@@ -33,6 +27,7 @@
     ODOO_TYPE: odoo11
   before_script:
   - pip3 install $CI_PROJECT_DIR --disable-pip-version-check --no-cache-dir
+  - apt update && apt install zsh -yq
   script:
   - sudo --preserve-env -u odoo $CI_PROJECT_DIR/start --db_host=db -d test_setup --max-cron-threads=0 --without-demo=all --data-dir /var/lib/odoo --init base &
   # wait for odoo to install the modules
diff --git a/NEWS.rst b/NEWS.rst
index d0a9893d5e45d5c05ff63f967924cdb091334810_TkVXUy5yc3Q=..0d4e37c13858ac34f5fbe5a0271cafb2ad90ab45_TkVXUy5yc3Q= 100644
--- a/NEWS.rst
+++ b/NEWS.rst
@@ -5,4 +5,8 @@
 6.0.0
 -----
 
+Change to how output is colorized in shell scripts.
+
+Add .expand to several options so that a super project can reuse other super projects.
+
 Fix --odoo-help in docker_dev_start to avoid requiring a database.
@@ -8,5 +12,4 @@
 Fix --odoo-help in docker_dev_start to avoid requiring a database.
-
 5.0.0
 -----
 
diff --git a/README.rst b/README.rst
index d0a9893d5e45d5c05ff63f967924cdb091334810_UkVBRE1FLnJzdA==..0d4e37c13858ac34f5fbe5a0271cafb2ad90ab45_UkVBRE1FLnJzdA== 100644
--- a/README.rst
+++ b/README.rst
@@ -7,7 +7,7 @@
 Shell Scripts
 =============
 
-Those scripts are not installed when installing this python module.
+Those scripts are not installed when installing this python module. They are meant to be used on a system that has Odoo installed, inside an image for example.
 
 start
 -----
@@ -20,5 +20,5 @@
 
 It expect the configuration file (`setup.cfg`_) to have the following keys:
 
-- ``odoo_type``: type of odoo (odoo7, odoo8, odoo10, odoo11 or odoo13) [default to odoo8]. The environment variable ODOO_TYPE can be used instead.
+- ``odoo_type``. The environment variable ODOO_TYPE can be also be used instead to override the configuration file.
 - ``modules``: list of directories and files to include (also used by another script)
@@ -24,2 +24,3 @@
 - ``modules``: list of directories and files to include (also used by another script)
+- ``load-language``.
 
@@ -25,5 +26,10 @@
 
-The environment variable ODOO_ADDONS_PATH can be used to set the path for the addons, it can be empty if there is no path to include.
+These environment variables can be set:
+
+ODOO_ADDONS_PATH
+    can be used to set the path for the addons, it can be empty if there is no path to include.
+PRE_ODOO_BIN
+    Will be used in the command line calling odoo. Useful to run coverage tests for example.
 
 do_tests
 --------
@@ -46,7 +52,7 @@
 
 The script call `run_tests`_.
 
-It expect the configuration file (`setup.cfg`_) to have the following keys:
+It expects the configuration file (`setup.cfg`_) to have the following keys:
 
 - ``module_list``: list (separated by white space) of odoo modules to install on the database (default to empty list)
 - ``unaccent``: boolean to indicate if unaccent is to be installed on the database (default to True)
@@ -81,7 +87,8 @@
 The recommend way to install this module is to run ``pip3 install .``, eventually with the ``editable`` option.
 Some scripts are in other sections, because they need some specialized library, in that case you need to indicate the name of section as in ``pip3 install ".[docker]"``.
 The prerequisites for this module or one of its section can be installed by using pip3 or the package manager; the requirements are defined in ``setup.py``.
-As there is no way of indicating sections (it is even handling them incorrectly), do not use ``python3 setup.py install``.
+
+.. important:: As there is no way of indicating sections (it is even handling them incorrectly), do not use ``python3 setup.py install``.
 
 docker_isort
 ------------
@@ -101,7 +108,7 @@
 update_duplicate_sources.py
 ---------------------------
 
-This script will update a metaproject that only contain the sources of the one it is launched from.
+This script will update a project that only contain the sources of the one it is launched from.
 
 It expect the configuration file (`setup.cfg`_) to have the following keys:
 
@@ -203,7 +210,7 @@
 setup.cfg
 =========
 
-The scripts expects configuration in the odoo_scripts section of ``setup.cfg``. This file is in the super project, not the one in ``odoo_scripts``.
+The scripts expects configuration in the odoo_scripts section of ``setup.cfg``. This is the file in the super project, not the one in `Odoo Scripts`_.
 
 Configuration keys:
 
@@ -236,7 +243,7 @@
   cd src
   git clone git@github.com:RobSis/zsh-completion-generator.git
 
-Generate completion file for python scripts (from the superproject for the scripts that need to be run from there, and with the required requirements too)::
+Generate completion file for python scripts (from the super project for the scripts that need to be run from there, and with the required requirements too)::
 
   for command_name in docker_dev_start docker_build docker_build_copy docker_build_clean do_tests conf2reST import_base_import import_jsonrpc import_sql docker_flake8 docker_isort docker_pg; do
     $command_name --help | ~/src/zsh-completion-generator/help2comp.py $command_name > ~/.local/share/zsh/completion/_$command_name
diff --git a/common b/common
new file mode 100644
index 0000000000000000000000000000000000000000..0d4e37c13858ac34f5fbe5a0271cafb2ad90ab45_Y29tbW9u
--- /dev/null
+++ b/common
@@ -0,0 +1,84 @@
+#!/bin/zsh
+# vim: set shiftwidth=4 softtabstop=4:
+# Common functions and env var
+
+# assume we are called from the project home
+project_home=$PWD
+project_name=$(basename "$project_home")
+
+if ! type python3 > /dev/null; then
+    python="python2"
+else
+    python="python3"
+fi
+
+# handy colorized variables
+esc=$(printf '\033')
+RESET="${esc}[0m"
+DEBUG="[${esc}[36mDEBUG${RESET}]"
+INFO="[${esc}[34mINFO ${RESET}]"
+WARN="[${esc}[33mWARN ${RESET}]"
+FATAL="[${esc}[41m${esc}[37mFATAL${RESET}]"
+GREEN="${esc}[32m"
+OK="[${GREEN} OK${RESET}  ]"
+RED="${esc}[31m"
+KO="[${esc}[41m${esc}[37m KO  ${RESET}]"
+
+function read_configuration_key () {
+    # Read a configuration key from any ini file
+    # first argument is file path
+    # second argument is section
+    # third argument is the key to use
+    # fourth argument is default value, if any
+    # fifth argument is separator, default to a space
+    file=$1
+    section=$2
+    configuration_key=$3
+    default_value=$4
+    separator=${5- }
+    echo $($python -B -c "from six.moves import configparser ; c = configparser.ConfigParser() ; c.read('${file}') ; print('$separator'.join(c.get('$section', '$configuration_key').split()) if c.has_option('$section', '$configuration_key') else '$default_value')")
+}
+
+function read_odoo_scripts_configuration_key () {
+    # Return configuration value from odoo_scripts configuration file
+    # first argument is the key to use
+    # second argument is default value, if any
+    configuration_key=${1}
+    default_value=$2
+    read_configuration_key "${project_home}/setup.cfg" odoo_scripts $configuration_key $default_value
+}
+
+function read_odoo_scripts_expandable_configuration_key () {
+  # return configuration value by appending any expanded values from defined super projects
+  # first argument is the key to use
+  # second argument is the directory to use, defaults to project home
+  # third optional argument is the separator to use (default to comma)
+  # fourth optional argument is to indicate if the expanded project should be used as prefix for the values (default to not use, use True to use), adding a /
+  # fifth optional argument is a prefix to use (default to none)
+  configuration_key=${1}
+  base_dir="${2-${project_home}}"
+  separator=${3-,}
+  prefix_with_expand=${4-False}
+  prefix=$5
+  # read expanded configurations
+  expanded_project_pathes=($($python -B -c "from six.moves import configparser ; c = configparser.ConfigParser() ; c.read('$base_dir/setup.cfg') ; print(c.get('odoo_scripts', '$configuration_key.expand') if c.has_option('odoo_scripts', '$configuration_key.expand') else '')"))
+  for expanded_project_path in $expanded_project_pathes; do
+    that_project_prefix=$prefix
+    if [[ "$prefix_with_expand" == True ]];
+    then
+      that_project_prefix=${that_project_prefix}${expanded_project_path}/
+    fi
+    expanded_configuration_value=$(read_odoo_scripts_expandable_configuration_key $configuration_key "$base_dir/$expanded_project_path" "$separator" $prefix_with_expand "$that_project_prefix")
+    if [[ -n $configuration_value ]]; then
+      configuration_value+=$separator
+    fi
+    configuration_value+=$expanded_configuration_value
+  done
+  # then add local values
+  this_project_configuration_value+=$($python -B -c "from six.moves import configparser ; c = configparser.ConfigParser() ; c.read('$base_dir/setup.cfg') ; print('$separator'.join(['$prefix'+a for a in c.get('odoo_scripts', '$configuration_key').split()] if c.has_option('odoo_scripts', '$configuration_key') else []))")
+  if [[ -n $this_project_configuration_value && -n $configuration_value ]]; then
+      configuration_value+=$separator
+  fi
+  configuration_value+=$this_project_configuration_value
+  echo $configuration_value
+}
diff --git a/create_archive b/create_archive
index d0a9893d5e45d5c05ff63f967924cdb091334810_Y3JlYXRlX2FyY2hpdmU=..0d4e37c13858ac34f5fbe5a0271cafb2ad90ab45_Y3JlYXRlX2FyY2hpdmU= 100755
--- a/create_archive
+++ b/create_archive
@@ -1,5 +1,5 @@
-#!/bin/sh
+#!/bin/zsh
 # vim: set shiftwidth=4 softtabstop=4:
 
 # Create archive of project sources
 
@@ -2,7 +2,10 @@
 # vim: set shiftwidth=4 softtabstop=4:
 
 # Create archive of project sources
 
+here=$(dirname "$0")
+source $here/common
+
 # Arguments:
 # - directory of the odoo sources
 # - name of the tar file (the target file will have this name + .xz) if no name specified, name is <project name>.tar
@@ -6,5 +9,4 @@
 # Arguments:
 # - directory of the odoo sources
 # - name of the tar file (the target file will have this name + .xz) if no name specified, name is <project name>.tar
-
 odoo_dir=$1
@@ -10,3 +12,3 @@
 odoo_dir=$1
-tar_file=$2
+tar_file=${2-${project_name}.tar}
 
@@ -12,15 +14,6 @@
 
-here=$(dirname "$0")
-project_home=$(cd "$here" && cd .. && echo "$PWD")
-project_name=$(basename "$project_home")
-python=python
-
-odoo_modules="$($python -B -c "import ConfigParser ; c = ConfigParser.ConfigParser() ; c.read('${project_home}/setup.cfg') ; print(' '.join(c.get('odoo_scripts', 'modules', '').split()))")"
-dependencies="$($python -B -c "import ConfigParser ; c = ConfigParser.ConfigParser() ; c.read('${project_home}/setup.cfg') ; print(c.has_section('odoo_scripts') and c.has_option('odoo_scripts', 'dependencies') and' '.join(c.get('odoo_scripts', 'dependencies', 'dependencies').split()) or 'dependencies')")"
-if [ -z "$tar_file" ];
-then
-    tar_file=${project_name}.tar
-fi
+odoo_modules="$(read_odoo_scripts_expandable_configuration_key modules "$project_home" " ")"
+dependencies="$(read_odoo_scripts_expandable_configuration_key dependencies "$project_home" " ")"
 
 cd "$project_home" || exit 1
 # Create empty tar
@@ -29,9 +22,9 @@
 # Add modules, in odoo_modules directory (whatever the original directory name)
 for module in $(eval echo "$odoo_modules") ;
 do
-    tar uf "$tar_file" --transform="s|^$(dirname "$module")|odoo_modules|" "$module" --show-transform --exclude-vcs --exclude-backups --exclude='*.pyc' --exclude='.drone.yml' --exclude='.gitlab-ci.yml'
+    tar uf "$tar_file" --transform="s|^$(dirname "$module")|odoo_modules|" --show-transform --exclude-vcs --exclude-backups --exclude='*.pyc' --exclude='.drone.yml' --exclude='.gitlab-ci.yml' --atime-preserve "$module"
 done
 
 # Add dependencies, in dependencies directory (whatever the original directory name)
 for dep in $(eval echo "$dependencies") ;
 do
@@ -33,11 +26,11 @@
 done
 
 # Add dependencies, in dependencies directory (whatever the original directory name)
 for dep in $(eval echo "$dependencies") ;
 do
-    tar uf "$tar_file" --transform="s|^$(dirname "$dep")|dependencies|" "$dep" --exclude-vcs --exclude-backups --exclude='*.pyc' --exclude='.drone.yml' --exclude='.gitlab-ci.yml'
+    tar uf "$tar_file" --transform="s|^$(dirname "$dep")|dependencies|" --exclude-vcs --exclude-backups --exclude='*.pyc' --exclude='.drone.yml' --exclude='.gitlab-ci.yml' --atime-preserve "$dep"
 done
 
 # Add version number file if present
 if [ -f VERSION ];
 then
@@ -39,10 +32,10 @@
 done
 
 # Add version number file if present
 if [ -f VERSION ];
 then
-    tar uf "tar_file" VERSION
+    tar uf "$tar_file" --atime-preserve VERSION
 fi
 
 if [ -d "$odoo_dir" ];
 then
@@ -45,9 +38,9 @@
 fi
 
 if [ -d "$odoo_dir" ];
 then
-    tar uf "$tar_file" --transform="s|^$(echo "$odoo_dir" | sed -e 's|/\(.*\)|\1|')|odoo|" "$odoo_dir" --exclude-vcs --exclude-backups --exclude='*.pyc'
+    tar uf "$tar_file" --transform="s|^$(echo "$odoo_dir" | sed -e 's|/\(.*\)|\1|')|odoo|" --exclude-vcs --exclude-backups --exclude='*.pyc' --atime-preserve "$odoo_dir"
 fi
 
 # Compress the file
 xz "$tar_file"
@@ -50,4 +43,5 @@
 fi
 
 # Compress the file
 xz "$tar_file"
+echo $INFO Tar file $tar_file.xz created
diff --git a/do_tests b/do_tests
index d0a9893d5e45d5c05ff63f967924cdb091334810_ZG9fdGVzdHM=..0d4e37c13858ac34f5fbe5a0271cafb2ad90ab45_ZG9fdGVzdHM= 100755
--- a/do_tests
+++ b/do_tests
@@ -16,13 +16,8 @@
 # Version 5.0.0
 
 here=$(dirname $0)
-# assume we are called from the project home
-project_home=$PWD
-if ! type python3 > /dev/null; then
-    python="python2"
-else
-    python="python3"
-fi
+source $here/common
+
 # this is the same value as conf/drone/odoo.conf
 if [[ -e "${project_home}/conf/drone/odoo.conf" ]];
 then
@@ -26,8 +21,8 @@
 # this is the same value as conf/drone/odoo.conf
 if [[ -e "${project_home}/conf/drone/odoo.conf" ]];
 then
-    dbowner=$($python -B -c "from six.moves import configparser ; c = configparser.ConfigParser() ; c.read('${project_home}/conf/drone/odoo.conf') ; print(c.get('options', 'db_user'))")
-    unaccent=$($python -B -c "from six.moves import configparser ; c = configparser.ConfigParser() ; c.read('${project_home}/conf/drone/odoo.conf') ; print(c.get('options', 'unaccent') if c.has_option('options', 'unaccent') else False)")
+    dbowner=$(read_configuration_key ${project_home}/conf/drone/odoo.conf options db_user)
+    unaccent=$(read_configuration_key ${project_home}/conf/drone/odoo.conf options unaccent False)
 # this is the same value as conf/dev/odoo.conf
 elif [[ -e "${project_home}/conf/dev/odoo.conf" ]];
 then
@@ -31,9 +26,9 @@
 # this is the same value as conf/dev/odoo.conf
 elif [[ -e "${project_home}/conf/dev/odoo.conf" ]];
 then
-    dbowner=$($python -B -c "from six.moves import configparser ; c = configparser.ConfigParser() ; c.read('${project_home}/conf/dev/odoo.conf') ; print(c.get('options', 'db_user'))")
-    unaccent=$($python -B -c "from six.moves import configparser ; c = configparser.ConfigParser() ; c.read('${project_home}/conf/dev/odoo.conf') ; print(c.get('options', 'unaccent') if c.has_option('options', 'unaccent') else False)")
+    dbowner=$(read_configuration_key ${project_home}/conf/dev/odoo.conf options db_user)
+    unaccent=$(read_configuration_key ${project_home}/conf/dev/odoo.conf options unaccent False)
 else
     dbowner=odoo
     unaccent=False
 fi
@@ -36,28 +31,10 @@
 else
     dbowner=odoo
     unaccent=False
 fi
-modules_to_install=${ODOO_SCRIPTS_MODULE_LIST-$($python -B -c "from six.moves import configparser ; c = configparser.ConfigParser() ; c.read('${project_home}/setup.cfg') ; print(','.join(c.get('odoo_scripts', 'module_list').split()))")}
-if [[ -e "${project_home}/setup.cfg" ]];
-then
-    fail_on_errors=${ODOO_SCRIPTS_FAIL_ON_ERRORS-$($python -B -c "from six.moves import configparser ; c = configparser.ConfigParser() ; c.read('${project_home}/setup.cfg') ; print(c.get('odoo_scripts', 'fail_on_errors') if c.has_option('odoo_scripts', 'fail_on_errors') else True)")}
-else
-    fail_on_errors=${ODOO_SCRIPTS_FAIL_ON_ERRORS-True}
-fi
-
-# color stuff
-autoload colors
-if [[ "$terminfo[colors]" -gt 7 ]];
-then
-    echo "[DEBUG] colors"
-    colors
-fi
-for COLOR in RED GREEN YELLOW BLUE MAGENTA CYAN BLACK WHITE; do
-    eval $COLOR='$fg_no_bold[${(L)COLOR}]'
-    eval BOLD_$COLOR='$fg_bold[${(L)COLOR}]'
-done
-eval RESET='$reset_color'
+modules_to_install=${ODOO_SCRIPTS_MODULE_LIST-$(read_odoo_scripts_expandable_configuration_key module_list)}
+fail_on_errors=${ODOO_SCRIPTS_FAIL_ON_ERRORS-$(read_odoo_scripts_configuration_key fail_on_errors True)}
 
 # argument handling
 if [[ $# -gt 0 ]];
 then
@@ -60,10 +37,10 @@
 
 # argument handling
 if [[ $# -gt 0 ]];
 then
-    echo "[${YELLOW}INFO${RESET}] Using database host $1"
+    echo "$INFO Using database host $1"
     PG_HOST=-h$1
     ODOO_HOST=--db_host=$1
     shift
     if [[ $# -gt 0 ]];
     then
@@ -65,9 +42,9 @@
     PG_HOST=-h$1
     ODOO_HOST=--db_host=$1
     shift
     if [[ $# -gt 0 ]];
     then
-        echo "[${YELLOW}INFO${RESET}] Using database user $1"
+        echo "$INFO Using database user $1"
         PG_USER=-U$1
         ODOO_USER=--db_user=$1
         dbowner=$1
@@ -98,5 +75,5 @@
 
 if [[ -z "$ODOO_NO_DROP" ]];
 then
-    echo "[${YELLOW}INFO${RESET}] Drop any existing database '$dbname'"
+    echo "$INFO Drop any existing database '$dbname'"
     dropdb $PG_HOST $PG_USER --if-exists $dbname --no-password
@@ -102,4 +79,4 @@
     dropdb $PG_HOST $PG_USER --if-exists $dbname --no-password
-    echo "[${YELLOW}INFO${RESET}] Create database '$dbname' with owner $dbowner"
-    createdb $PG_HOST $PG_USER -O $dbowner $dbname --no-password || { echo ${RED}FATAL${RESET} - Cannot create test database ; exit 1; }
+    echo "$INFO Create database '$dbname' with owner $dbowner"
+    createdb $PG_HOST $PG_USER -O $dbowner $dbname --no-password || { echo "$FATAL Cannot create test database" ; exit 1; }
 fi
@@ -105,4 +82,6 @@
 fi
-# TODO should also add all extensions from setup.cfg listed in pg.extensions
+
+# Add extensions
+typeset -a extensions
 if [[ "$unaccent" == "True" ]];
 then
@@ -107,5 +86,4 @@
 if [[ "$unaccent" == "True" ]];
 then
-    echo "[${YELLOW}INFO${RESET}] Add unaccent to '$dbname'"
-    psql $PG_HOST $PG_USER $dbname -c "CREATE EXTENSION IF NOT EXISTS unaccent;"
+    extensions+=unaccent
 fi
@@ -111,6 +89,12 @@
 fi
-echo "[${YELLOW}INFO${RESET}] Modules to install: $modules_to_install"
-echo "[${YELLOW}INFO${RESET}] Install those modules"
+extensions+=($(read_odoo_scripts_configuration_key pg.extensions))
+for extension in $extensions; do
+    echo "$INFO Create extension $extension in '$dbname'"
+    psql $PG_HOST $PG_USER $dbname -c "CREATE EXTENSION IF NOT EXISTS $extension;"
+done
+
+echo "$INFO Modules to install: $modules_to_install"
+echo "$INFO Install those modules"
 logfile=$(tempfile --suffix=.log)
 function clean_logfile() {
     rm $logfile
@@ -120,7 +104,7 @@
 start_status=$pipestatus[1]
 if [[ $start_status -ne 0 ]];
 then
-    echo "[${RED} KO ${RESET}] Cannot install modules (exit ${start_status})"
+    echo "$KO Cannot install modules (exit ${start_status})"
     exit $start_status
 fi
 criticals=$(grep ' CRITICAL' -F $logfile -c)
@@ -129,7 +113,7 @@
 warnings=$(grep ' WARNING' -F $logfile -c)
 if [[ $criticals -gt 0 ]];
 then
-    echo "[${RED} KO ${RESET}] Cannot install modules ($criticals CRITICAL messages found)"
+    echo "$KO Cannot install modules ($criticals CRITICAL messages found)"
     exit 2
 fi
 
@@ -137,5 +121,5 @@
 then
     if [[ "$fail_on_errors" == "False" ]];
     then
-        echo "[${MAGENTA}WARN${RESET}] Install modules with $errors ERROR messages"
+        echo "$WARN Install modules with $errors ERROR messages"
     else
@@ -141,5 +125,5 @@
     else
-        echo "[${RED} KO ${RESET}] Cannot install modules ($errors ERROR messages found)"
+        echo "$KO Cannot install modules ($errors ERROR messages found)"
         exit 1
     fi
 else
@@ -147,5 +131,5 @@
     then
         if [[ "${FAIL_ON_WARNING:-False}" == "False" ]];
         then
-            echo "[${MAGENTA}WARN${RESET}] Modules installed ($warnings WARNING messages)"
+            echo "$WARN Modules installed ($warnings WARNING messages)"
         else
@@ -151,5 +135,5 @@
         else
-            echo "[${RED} KO ${RESET}] Cannot install modules ($warnings WARNING messages found)"
+            echo "$KO Cannot install modules ($warnings WARNING messages found)"
             exit 1
         fi
     else
@@ -153,6 +137,6 @@
             exit 1
         fi
     else
-        echo "[${GREEN} OK ${RESET}] Modules installed"
+        echo "$OK Modules installed"
     fi
 fi
@@ -157,4 +141,4 @@
     fi
 fi
-echo "[${YELLOW}INFO${RESET}] Run tests on $dbname"
+echo "$INFO Run tests on $dbname"
 $here/run_tests $dbname $ODOO_HOST $ODOO_USER $*
diff --git a/odoo_scripts/config.py b/odoo_scripts/config.py
index d0a9893d5e45d5c05ff63f967924cdb091334810_b2Rvb19zY3JpcHRzL2NvbmZpZy5weQ==..0d4e37c13858ac34f5fbe5a0271cafb2ad90ab45_b2Rvb19zY3JpcHRzL2NvbmZpZy5weQ== 100644
--- a/odoo_scripts/config.py
+++ b/odoo_scripts/config.py
@@ -3,4 +3,5 @@
 import configparser
 import logging
 import os
+import sys
 from glob import glob
@@ -6,7 +7,8 @@
 from glob import glob
+from typing import List, Optional
 
 _logger = logging.getLogger(__name__)
 
 
 class Config(object):
     """Singleton for the configuration.
@@ -7,13 +9,12 @@
 
 _logger = logging.getLogger(__name__)
 
 
 class Config(object):
     """Singleton for the configuration.
-    At the moment, only read from `setup.cfg`.
     """
 
     __instance = None
 
     def __new__(cls):
         if Config.__instance is None:
@@ -14,9 +15,9 @@
     """
 
     __instance = None
 
     def __new__(cls):
         if Config.__instance is None:
-            Config.__instance = object.__new__(cls)
+            Config.__instance = Configuration()
         return Config.__instance
 
@@ -21,4 +22,11 @@
         return Config.__instance
 
-    def __init__(self):
+
+class Configuration(object):
+    """
+    At the moment, only read from `setup.cfg`.
+    """
+
+    def __init__(self, base_path: str = None):
+        self._expanded_configuration = dict()
         setup_path = "setup.cfg"
@@ -24,6 +32,8 @@
         setup_path = "setup.cfg"
+        if base_path:
+            setup_path = os.path.join(base_path, setup_path)
         section = "odoo_scripts"
         config_parser = configparser.ConfigParser()
         if not os.path.exists(setup_path):
             _logger.warning("Missing %s", setup_path)
         else:
@@ -25,7 +35,27 @@
         section = "odoo_scripts"
         config_parser = configparser.ConfigParser()
         if not os.path.exists(setup_path):
             _logger.warning("Missing %s", setup_path)
         else:
-            config_parser.read(os.path.join(os.getcwd(), "setup.cfg"))
+            config_parser.read(setup_path)
+
+        def set_from_glob_values(
+            base: Optional[str], glob_values: List[str]
+        ) -> set:
+            result_set = set()
+            for glob_value in glob_values:
+                if base:
+                    glob_value = os.path.join(base, glob_value)
+                for file in glob(glob_value):
+                    if os.path.isdir(file):
+                        result_set.add(file)
+            return result_set
+
+        def get_expanded_configuration(path_name: str):
+            if path_name not in self._expanded_configuration:
+                self._expanded_configuration[path_name] = Configuration(
+                    path_name
+                )
+            return self._expanded_configuration[path_name]
+
         for key in ("modules", "dependencies", "other_sources"):
@@ -31,10 +61,15 @@
         for key in ("modules", "dependencies", "other_sources"):
-            key_set = set()
-            for key_glob in config_parser.get(
-                section, key, fallback=""
-            ).split():
-                for file in glob(key_glob):
-                    if os.path.isdir(file):
-                        key_set.add(file)
-            setattr(self, key, list(key_set))
+            # get the values from this configuration
+            values = config_parser.get(section, key, fallback="").split()
+            set_values = set_from_glob_values(base_path, values)
+            # handle expand, getting value from another configuration
+            expand_value = config_parser.get(
+                section, key + ".expand", fallback=None
+            )
+            if expand_value:
+                for element in expand_value.split():
+                    set_values.update(
+                        getattr(get_expanded_configuration(element), key)
+                    )
+            setattr(self, key, list(set_values))
 
@@ -40,3 +75,3 @@
 
-        def key_format(key: str) -> str:
+        def key_format(a_key: str) -> str:
             """Replace invalid characters in an attribute"""
@@ -42,4 +77,4 @@
             """Replace invalid characters in an attribute"""
-            return key.replace("-", "_").replace(".", "_")
+            return a_key.replace("-", "_").replace(".", "_")
 
         for key in ("module_list", "module_list_tests", "pg.extensions"):
@@ -44,7 +79,8 @@
 
         for key in ("module_list", "module_list_tests", "pg.extensions"):
-            setattr(
-                self,
-                key_format(key),
-                config_parser.get(section, key, fallback="").split(),
+            value = list()
+            formatted_key = key_format(key)
+            # add any expanded value
+            expand_value = config_parser.get(
+                section, key + ".expand", fallback=None
             )
@@ -50,7 +86,18 @@
             )
+            if expand_value:
+                for element in expand_value.split():
+                    value.extend(
+                        getattr(
+                            get_expanded_configuration(element), formatted_key
+                        )
+                    )
+            # add local values
+            value += config_parser.get(section, key, fallback="").split()
+            # then set it
+            setattr(self, formatted_key, value)
         for key in ("db_user", "db_password", "load-language"):
             setattr(
                 self,
                 key_format(key),
                 config_parser.get(section, key, fallback=None),
             )
@@ -51,9 +98,10 @@
         for key in ("db_user", "db_password", "load-language"):
             setattr(
                 self,
                 key_format(key),
                 config_parser.get(section, key, fallback=None),
             )
+
         self.registry = config_parser.get(
             section, "registry", fallback="registry.xcg.io"
         )
@@ -78,3 +126,18 @@
         self.start_py3o = config_parser.get(
             section, "start_py3o", fallback="no"
         ) in ("yes", "true")
+
+
+def main(argv=None):
+    """Display configuration. Useful for development of odoo_scripts.
+    """
+    c = Config()
+    for key, value in c.__dict__.items():
+        print(key, "=", value)
+    return 0
+
+
+if __name__ == "__main__":
+    return_code = main(sys.argv[1:])
+    if return_code:
+        exit(return_code)
diff --git a/odoo_scripts/docker_build_copy.py b/odoo_scripts/docker_build_copy.py
index d0a9893d5e45d5c05ff63f967924cdb091334810_b2Rvb19zY3JpcHRzL2RvY2tlcl9idWlsZF9jb3B5LnB5..0d4e37c13858ac34f5fbe5a0271cafb2ad90ab45_b2Rvb19zY3JpcHRzL2RvY2tlcl9idWlsZF9jb3B5LnB5 100644
--- a/odoo_scripts/docker_build_copy.py
+++ b/odoo_scripts/docker_build_copy.py
@@ -61,7 +61,10 @@
     c = Config()
     modules = [os.path.realpath(module) for module in c.modules]
     # copy files
-    _logger.info("Copying Odoo modules files to odoo_modules")
+    target = "odoo_modules"
+    _logger.info("Copying Odoo modules files to %s", target)
+    if not os.path.exists(target):
+        os.mkdir(target)
     cmd = (
         [
             "rsync",
@@ -75,8 +78,8 @@
             "--times",
         ]
         + modules
-        + ["odoo_modules"]
+        + [target]
     )
     _logger.debug(" ".join(cmd))
     call(cmd)
     # this only sync static from our modules
@@ -79,8 +82,11 @@
     )
     _logger.debug(" ".join(cmd))
     call(cmd)
     # this only sync static from our modules
-    _logger.info("Copying static files to static")
+    target = "static"
+    _logger.info("Copying static files to %s", target)
+    if not os.path.exists(target):
+        os.mkdir(target)
     cmd = (
         [
             "rsync",
@@ -96,7 +102,7 @@
             "--prune-empty-dirs",
         ]
         + modules
-        + ["static"]
+        + [target]
     )
     _logger.debug(" ".join(cmd))
     call(cmd)
diff --git a/run_tests b/run_tests
index d0a9893d5e45d5c05ff63f967924cdb091334810_cnVuX3Rlc3Rz..0d4e37c13858ac34f5fbe5a0271cafb2ad90ab45_cnVuX3Rlc3Rz 100755
--- a/run_tests
+++ b/run_tests
@@ -4,6 +4,6 @@
 # This script is used to run tests on a database with demo data installed.
 #
 # Either provide the name of the database (first argument) or it will default to <project>_test
-# The DB user can be overriden by setting ODOO_SCRIPTS_DB_USER.
+# The DB user can be overridden by setting ODOO_SCRIPTS_DB_USER.
 # Version 5.0.0
 here=$(dirname $0)
@@ -8,13 +8,9 @@
 # Version 5.0.0
 here=$(dirname $0)
-# assume we are called from the project home
-project_home=$PWD
-if ! type python3 > /dev/null; then
-    python="python2"
-else
-    python="python3"
-fi
-tested_modules=${ODOO_SCRIPTS_MODULE_LIST_TESTS-$($python -B -c "from six.moves import configparser ; c = configparser.ConfigParser() ; c.read('${project_home}/setup.cfg') ; print(','.join(c.get('odoo_scripts', 'module_list_tests').split() if c.has_option('odoo_scripts', 'module_list_tests') else []))")}
+source $here/common
+
+tested_modules=${ODOO_SCRIPTS_MODULE_LIST_TESTS-$(read_odoo_scripts_expandable_configuration_key module_list_tests)}
+
 if [[ "$tested_modules" != "" ]];
 then 
     update="-u $tested_modules"
@@ -26,7 +22,7 @@
 else
     shift
 fi
-echo "Using database $dbname for tests"
+echo "$INFO Using database $dbname for tests"
 
 if [[ -n "$ODOO_SCRIPTS_DB_USER" ]]
 then
@@ -30,7 +26,7 @@
 
 if [[ -n "$ODOO_SCRIPTS_DB_USER" ]]
 then
-    echo "Using db user $ODOO_SCRIPTS_DB_USER"
+    echo "$INFO Using db user $ODOO_SCRIPTS_DB_USER"
     db_user_param="--db_user $ODOO_SCRIPTS_DB_USER"
 fi
 
@@ -34,7 +30,8 @@
     db_user_param="--db_user $ODOO_SCRIPTS_DB_USER"
 fi
 
-ODOO_TYPE=${ODOO_TYPE:-$($python -B -c "from six.moves import configparser ; c = configparser.ConfigParser() ; c.read('${project_home}/setup.cfg') ; print(c.get('odoo_scripts', 'odoo_type') if c.has_option('odoo_scripts', 'odoo_type') else 'odoo8')")}
+ODOO_TYPE=${ODOO_TYPE:-$(read_odoo_scripts_configuration_key odoo_type odoo8)}
+
 if [ "$ODOO_TYPE" = "odoo8" ];
 then
     loglevel="--log-level=info"
@@ -81,7 +78,7 @@
     failed=$(grep 'openerp.modules.loading: At least one test failed when loading the modules.' $logfile -c)
 fi
 echo ''
-echo '\e[2m*****\e[22m \e[1mTest results\e[21m \e[2m*****\e[22m'
+echo "${esc}[2m*****${esc}[22m ${esc}[1mTest results${esc}[21m ${esc}[2m*****${esc}[22m"
 echo "Ran $tests_ran tests, $tests_failures failures, $tests_errors errors"
 if [ $ODOO_TYPE != 'odoo13' ];
 then
@@ -85,9 +82,9 @@
 echo "Ran $tests_ran tests, $tests_failures failures, $tests_errors errors"
 if [ $ODOO_TYPE != 'odoo13' ];
 then
-    echo "$ok modules passing"
+    echo "$OK $ok modules passing"
 fi
 echo ''
 
 if [[ $ok -gt 0 ]];
 then
@@ -89,9 +86,9 @@
 fi
 echo ''
 
 if [[ $ok -gt 0 ]];
 then
-    echo -e '\033[32mOK:\033[0m'
+    echo -e '${GREEN}Passes${RESET}:'
 
     # Highlight the "OK"s in green (32).
     GREP_COLOR='0;32' grep --color=always ' OK' $logfile || true
@@ -99,7 +96,7 @@
 
 if [[ $failed -gt 0 ]];
 then
-    echo -e '\033[31mFailures\033[0m:'
+    echo -e '${RED}Failures${RESET}:'
 
     # Highlight the failure matches in red (31).
     GREP_COLOR='0;31' grep --color=always "CRITICAL\|FAIL\(ED\|:\)" $logfile
@@ -115,8 +112,8 @@
 rm $logfile
 if [[ $start_status -ne 0 ]];
 then
-    echo "[${RED}FATAL${RESET}] Tests failed (exit ${start_status})"
+    echo "$FATAL Tests failed (exit ${start_status})"
     exit $start_status
 fi
 if [[ $tests_failures -gt 0 ]] || [[ $tests_errors -gt 0 ]];
 then
@@ -119,9 +116,9 @@
     exit $start_status
 fi
 if [[ $tests_failures -gt 0 ]] || [[ $tests_errors -gt 0 ]];
 then
-    echo "[${RED}FATAL${RESET}] Tests failed (found failure and errors in Ran N failures, M errors)"
+    echo "$FATAL Tests failed (found failure and errors in Ran N failures, M errors)"
     exit 42
 fi
 if [[ $failed -gt 0 ]];
 then
@@ -124,11 +121,11 @@
     exit 42
 fi
 if [[ $failed -gt 0 ]];
 then
-    echo "[${RED}FATAL${RESET}] Tests failed (found failure with grep)"
+    echo "$FATAL Tests failed (found failure with grep)"
     exit 1
 fi
 if [[ "${FAIL_ON_WARNING:-False}" != "False" ]];
 then
     if [[ $warnings -gt 0 ]];
     then
@@ -129,10 +126,10 @@
     exit 1
 fi
 if [[ "${FAIL_ON_WARNING:-False}" != "False" ]];
 then
     if [[ $warnings -gt 0 ]];
     then
-        echo "[${RED}FATAL${RESET}] Tests failed ($warnings WARNING found)"
+        echo "$FATAL Tests failed ($warnings WARNING found)"
         exit 1
     fi
 fi
diff --git a/start b/start
index d0a9893d5e45d5c05ff63f967924cdb091334810_c3RhcnQ=..0d4e37c13858ac34f5fbe5a0271cafb2ad90ab45_c3RhcnQ= 100755
--- a/start
+++ b/start
@@ -1,4 +1,4 @@
-#!/bin/sh
+#!/bin/zsh
 # vim: set shiftwidth=4 softtabstop=4:
 # 
 # This script start odoo for development
@@ -8,12 +8,7 @@
 #
 # Version 5.0.0
 
-# assume we are called from the project home
-project_home=$PWD
-if [ -x "$(command -v python3)" ];
-then
-    python="python3"
-else
-    python="python2"
-fi
+here=$(dirname $0)
+source $here/common
+
 # odoo7, odoo8, odoo10, odoo11, odoo13
@@ -19,3 +14,3 @@
 # odoo7, odoo8, odoo10, odoo11, odoo13
-ODOO_TYPE=${ODOO_TYPE:-$($python -B -c "from six.moves import configparser ; c = configparser.ConfigParser() ; c.read('${project_home}/setup.cfg') ; print(c.get('odoo_scripts', 'odoo_type') if c.has_option('odoo_scripts', 'odoo_type') else 'odoo8')")}
+ODOO_TYPE=${ODOO_TYPE:-$(read_odoo_scripts_configuration_key odoo_type odoo8)}
 
@@ -21,5 +16,5 @@
 
-echo "INFO - Starting odoo ($ODOO_TYPE) for development"
+echo "$INFO Starting odoo ($ODOO_TYPE) for development"
 
 #
 # Do some magic to find the odoo command to run
@@ -35,7 +30,7 @@
         odoo_command=odoo
         ;;
 esac
-echo "DEBUG - Trying odoo command $odoo_command"
+echo "$DEBUG Trying odoo command $odoo_command"
 if [ -x "$(command -v $odoo_command)" ];
 then
     odoo_bin=$odoo_command
@@ -39,5 +34,5 @@
 if [ -x "$(command -v $odoo_command)" ];
 then
     odoo_bin=$odoo_command
-    echo "DEBUG - Command found"
+    echo "$DEBUG Command found"
 else
@@ -43,6 +38,6 @@
 else
-    echo "DEBUG - No odoo command found"
-    echo "DEBUG - Trying import in python"
+    echo "$DEBUG No odoo command found"
+    echo "$DEBUG Trying import in python"
     if [ "$ODOO_TYPE" = "odoo7" ] || [ "$ODOO_TYPE" = "odoo8" ];
     then
         print_statement="import os;import openerp;print os.path.abspath(openerp.__file__)"
@@ -54,5 +49,5 @@
     odoo_bin=$odoo_command
     if test -z "$module_path";
     then
-        echo "DEBUG - No odoo found by using module path"
+        echo "$DEBUG No odoo found by using module path"
         virtualenv_name=$(basename "$(readlink -f "$(dirname start)")")
@@ -58,5 +53,5 @@
         virtualenv_name=$(basename "$(readlink -f "$(dirname start)")")
-        echo "DEBUG - Trying $virtualenv_name virtualenvwrapper"
+        echo "$DEBUG Trying $virtualenv_name virtualenvwrapper"
         # assume you use virtualenvwrapper, and try the same env name as the project
         venv_python=$WORKON_HOME/$virtualenv_name/bin/python
         if [ -e "$venv_python" ];
@@ -65,7 +60,7 @@
         fi
         if test -z "$module_path";
         then
-            echo "DEBUG - No odoo found by using virtuenvwrapper"
-            echo "DEBUG - Trying in /opt/odoo/sources/odoo"
+            echo "$DEBUG No odoo found by using virtuenvwrapper"
+            echo "$DEBUG Trying in /opt/odoo/sources/odoo"
             if test -e "/opt/odoo/sources/odoo/$odoo_bin";
             then
@@ -70,6 +65,6 @@
             if test -e "/opt/odoo/sources/odoo/$odoo_bin";
             then
-                echo "DEBUG - Found in /opt/odoo/sources/odoo"
+                echo "$DEBUG Found in /opt/odoo/sources/odoo"
                 odoo_bin=/opt/odoo/sources/odoo/$odoo_bin
                 module_path="/opt/odoo/sources/odoo/odoo/__init__.py"
             else
@@ -73,8 +68,8 @@
                 odoo_bin=/opt/odoo/sources/odoo/$odoo_bin
                 module_path="/opt/odoo/sources/odoo/odoo/__init__.py"
             else
-                echo "FATAL - No odoo found"
-                echo "INFO - Maybe you forgot to activate your environment?"
-                exit 1    
+                echo "$FATAL No odoo found"
+                echo "$INFO Maybe you forgot to activate your environment?"
+                exit 1
             fi
         else
@@ -79,5 +74,5 @@
             fi
         else
-            echo "DEBUG - Odoo found by using virtuenvwrapper"
+            echo "$DEBUG Odoo found by using virtuenvwrapper"
         fi
     else
@@ -82,6 +77,6 @@
         fi
     else
-        echo "DEBUG - Odoo found by using module path ($module_path)"
+        echo "$DEBUG Odoo found by using module path ($module_path)"
     fi
 
     odoo=$(dirname "$(dirname $module_path)")
@@ -112,6 +107,6 @@
 else
     if [ -e "${project_home}/conf/drone/odoo.conf" ];
     then
-        config="-c ${project_home}/conf/drone/odoo.conf"
+        config="--config=${project_home}/conf/drone/odoo.conf"
     elif [ -e "${project_home}/conf/dev/odoo.conf" ];
     then
@@ -116,6 +111,6 @@
     elif [ -e "${project_home}/conf/dev/odoo.conf" ];
     then
-        config="-c ${project_home}/conf/dev/odoo.conf"
+        config="--config=${project_home}/conf/dev/odoo.conf"
     fi
     if echo "$args" | grep -q -e '\( \|^\)--addons-path';
     then
@@ -128,5 +123,6 @@
                 pathes=$ODOO_ADDONS_PATH
             fi
         else
+            modules_to_use=$(read_odoo_scripts_expandable_configuration_key modules ${project_home} , True)
             if [ "$ODOO_TYPE" = "odoo7" ] || [ "$ODOO_TYPE" = "odoo8" ];
             then
@@ -131,4 +127,4 @@
             if [ "$ODOO_TYPE" = "odoo7" ] || [ "$ODOO_TYPE" = "odoo8" ];
             then
-                pathes=$($python -B -c "from six.moves import configparser ; import os ; c = configparser.ConfigParser() ; c.read('${project_home}/setup.cfg') ; print(','.join(['$odoo_addons_path/addons']+['${project_home}/'+line for line in set(os.path.dirname(path) for path in c.get('odoo_scripts', 'modules').split())]))")
+                pathes=$($python -B -c "import os;print(','.join(['$odoo_addons_path/addons']+['${project_home}/'+line for line in set(os.path.dirname(path) for path in '${modules_to_use}'.split(','))]))")
             else
@@ -134,5 +130,5 @@
             else
-                pathes=$($python -B -c "from six.moves import configparser ; import os ; c = configparser.ConfigParser() ; c.read('${project_home}/setup.cfg') ; print(','.join(['${project_home}/'+line for line in set(os.path.dirname(path) for path in c.get('odoo_scripts', 'modules').split())]))")
+                pathes=$($python -B -c "import os;print(','.join(['${project_home}/'+line for line in set(os.path.dirname(path) for path in '${modules_to_use}'.split(','))]))")
             fi
         fi
         if [ -n "$pathes" ];
@@ -144,5 +140,5 @@
 # use load-language from setup.cfg if none already provided
 if echo "$args" | grep -q -e '\( \|^\)-c' -e '\( \|^\)--load-language';
 then
-    echo "INFO - load-language flag detected, no reading from setup.cfg"
+    echo "$INFO load-language flag detected, no reading from setup.cfg"
 else
@@ -148,4 +144,4 @@
 else
-    load_language=$($python -B -c "from six.moves import configparser ; c = configparser.ConfigParser() ; c.read('${project_home}/setup.cfg') ; print(c.get('odoo_scripts', 'load-language') if c.has_option('odoo_scripts', 'load-language') else '')")
+    load_language=$(read_odoo_scripts_configuration_key load-language)
     if [ -n "$load_language" ];
     then
@@ -150,6 +146,6 @@
     if [ -n "$load_language" ];
     then
-        load_language="--load-language $load_language"
+        load_language="--load-language=$load_language"
     fi
 fi
 
@@ -157,7 +153,7 @@
 # Start Odoo
 #
 
-echo "INFO - Odoo Version: $($odoo_bin --version)"
-echo "DEBUG - command line is: ${PRE_ODOO_BIN}$(command -v $odoo_bin) $config $db_host $addons_path $args $load_language"
-# shellcheck disable=SC2046,SC2086
-${PRE_ODOO_BIN}$(command -v $odoo_bin) $config $db_host $addons_path $args $load_language
+echo "$INFO Odoo Version: $($odoo_bin --version)"
+echo "$DEBUG command line is: ${PRE_ODOO_BIN}$(which $odoo_bin) $config $db_host $addons_path $args $load_language"
+# need to use eval due to spaces in the variables, otherwise zsh would treat one as an argument
+exec eval ${PRE_ODOO_BIN}$(which $odoo_bin) $config $db_host $addons_path $args $load_language