diff --git a/Dockerfile b/Dockerfile index d9d26aa..e839f81 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ FROM cloudron/base:3.2.0@sha256:ba1d566164a67c266782545ea9809dc611c4152e27686fd14060332dd88263ea # Reference: https://github.com/odoo/docker/blob/master/15.0/Dockerfile -RUN mkdir -p /app/code /app/pkg /app/data +RUN mkdir -p /app/code /app/pkg /app/data /app/code/auto/addons WORKDIR /app/code RUN apt-get update && \ @@ -18,26 +18,52 @@ RUN curl -o wkhtmltox.deb -sSL https://github.com/wkhtmltopdf/wkhtmltopdf/releas RUN npm install -g rtlcss +COPY bin/* /usr/local/bin/ + +COPY lib/doodbalib /usr/local/lib/python3.8/dist-packages/doodbalib +COPY custom /app/code/custom +RUN chmod -R a+rx /usr/local/bin \ + && chmod -R a+rX /usr/local/lib/python3.8/dist-packages/doodbalib \ + && sync + # Install Odoo -ARG ODOO_VERSION=15.0 -ARG ODOO_COMMIT_HASH=f07c63d3e4135d658bb952f1b4880e97d0b66992 +# sync extra addons + +ENV ODOO_VERSION=14.0 +ENV ODOO_SOURCE=OCA/OCB +ENV DEPTH_DEFAULT=100 +ENV DEPTH_MERGE=500 +RUN git config --global user.email "$CLOUDRON_MAIL_SMTP_USERNAME" +RUN git config --global user.name "Cloudron service" -RUN curl -L https://github.com/odoo/odoo/archive/${ODOO_COMMIT_HASH}.tar.gz | tar zx --strip-components 1 -C /app/code && \ - pip3 install wheel && \ - pip3 install -r requirements.txt +# RUN curl -L https://github.com/odoo/odoo/archive/${ODOO_COMMIT_HASH}.tar.gz | tar zx --strip-components 1 -C /app/code && \ +RUN git clone https://github.com/odoo/odoo.git --depth 1 -b $ODOO_VERSION /app/code/odoo +RUN pip3 install wheel && \ + pip3 install -r https://raw.githubusercontent.com/$ODOO_SOURCE/$ODOO_VERSION/requirements.txt && \ + pip3 install psycopg2==2.8.6 \ + && pip3 install git-aggregator \ + && (python3 -m compileall -q /usr/local/lib/python3.8/ || true) # Patch Odoo to prevent connecting to the default database named 'postgres' every now and then. -RUN sed -i.bak "748i\ to = tools.config['db_name']" /app/code/odoo/sql_db.py +RUN sed -i.bak "718i\ to = tools.config['db_name']" /app/code/odoo/odoo/sql_db.py # Properly map the LDAP attribute 'displayname' instead of 'cn' to the display name of the logged in user. -RUN sed -i.bak "194s/'cn'/'displayname'/" /app/code/addons/auth_ldap/models/res_company_ldap.py +RUN sed -i.bak "181s/'cn'/'displayname'/" /app/code/odoo/addons/auth_ldap/models/res_company_ldap.py RUN rm -rf /var/log/nginx && mkdir /run/nginx && ln -s /run/nginx /var/log/nginx # Copy entrypoint script and Odoo configuration file ADD start.sh odoo.conf.sample nginx.conf /app/pkg/ + +WORKDIR /app/code/custom/src +RUN gitaggregate -c /app/code/custom/src/repos.yaml --expand-env +RUN /app/code/custom/build.d/110-addons-link +RUN /app/code/custom/build.d/200-dependencies +RUN /app/code/custom/build.d/400-clean +RUN /app/code/custom/build.d/900-dependencies-cleanup + RUN mkdir -p /app/data/odoo/filestore /app/data/odoo/addons && \ chown -R cloudron:cloudron /app/data -CMD [ "/app/pkg/start.sh" ] \ No newline at end of file +CMD [ "/app/pkg/start.sh" ] diff --git a/bin/addons b/bin/addons new file mode 100755 index 0000000..5be5f1d --- /dev/null +++ b/bin/addons @@ -0,0 +1,144 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +from __future__ import print_function + +import ast +import os +import sys +from argparse import ArgumentParser +from subprocess import check_call + +from doodbalib import ( + CORE, + ENTERPRISE, + MANIFESTS, + PRIVATE, + SRC_DIR, + AddonsConfigError, + addons_config, + logger, +) + +# Exit codes +EXIT_NO_ADDONS = 0x4 + +# Define CLI options +parser = ArgumentParser(description="Install addons in current environment") +parser.add_argument( + "action", + choices=("init", "update", "list"), + help="What to do with the matched addons.", +) +parser.add_argument( + "-c", "--core", action="store_true", help="Use all Odoo core addons" +) +parser.add_argument( + "-d", + "--dependencies", + action="store_true", + help="Use only dependencies of selected addons", +) +parser.add_argument("-e", "--extra", action="store_true", help="Use all extra addons") +parser.add_argument( + "-f", + "--fullpath", + action="store_true", + help="Print addon's full path, only useful with list mode", +) +parser.add_argument( + "-i", "--installable", action="store_true", help="Include only installable addons" +) +parser.add_argument( + "-n", "--enterprise", action="store_true", help="Use all enterprise addons" +) +parser.add_argument( + "-p", "--private", action="store_true", help="Use all private addons" +) +parser.add_argument( + "-s", + "--separator", + type=str, + default=",", + help="String that separates addons only useful with list mode", +) +parser.add_argument( + "-t", + "--test", + action="store_true", + help="Run unit tests for these addons, usually combined with update", +) +parser.add_argument( + "-x", + "--explicit", + action="store_true", + help="Fail if any addon is explicitly declared but not found", +) +parser.add_argument( + "-w", + "--with", + action="append", + dest="with_", + default=[], + help="Addons to include always.", +) +parser.add_argument( + "-W", "--without", action="append", default=[], help="Addons to exclude always." +) + +# Generate the matching addons set +args = parser.parse_args() +dependencies = {"base"} +addons = set(args.with_) +without = set(args.without) +if addons & without: + sys.exit("Cannot include and exclude the same addon!") +if args.dependencies and args.fullpath: + sys.exit("Unsupported combination of --dependencies and --fullpath") +try: + for addon, repo in addons_config(strict=args.explicit): + if addon in without: + continue + core_ok = args.core and repo == CORE + enterprise_ok = args.enterprise and repo == ENTERPRISE + extra_ok = args.extra and repo not in {CORE, ENTERPRISE, PRIVATE} + private_ok = args.private and repo == PRIVATE + manual_ok = addon in addons + if private_ok or core_ok or extra_ok or enterprise_ok or manual_ok: + addon_path = os.path.join(SRC_DIR, repo, addon) + manifest = {} + for manifest_name in MANIFESTS: + try: + manifest_path = os.path.join(addon_path, manifest_name) + with open(manifest_path, "r") as code: + manifest = ast.literal_eval(code.read()) + break + except IOError: + continue + if args.installable and not manifest.get("installable", True): + continue + dependencies.update(manifest.get("depends", [])) + if args.fullpath and args.action == "list": + addon = addon_path + addons.add(addon) +except AddonsConfigError as error: + sys.exit(error.message) +# Use dependencies instead, if requested +if args.dependencies: + addons = dependencies - addons +addons -= without + +# Do the required action +if not addons: + print("No addons found", file=sys.stderr) + sys.exit(EXIT_NO_ADDONS) +addons = args.separator.join(sorted(addons)) +if args.action == "list": + print(addons) +else: + command = ["odoo", "--stop-after-init", "--{}".format(args.action), addons] + if args.test: + command += ["--test-enable", "--workers", "0"] + if os.environ.get("PGDATABASE"): + command += ["--db-filter", u"^{}$".format(os.environ.get("PGDATABASE"))] + logger.info("Executing %s", " ".join(command)) + check_call(command) diff --git a/bin/autoaggregate b/bin/autoaggregate new file mode 100755 index 0000000..c36f466 --- /dev/null +++ b/bin/autoaggregate @@ -0,0 +1,165 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +import os +import sys +from multiprocessing import cpu_count +from subprocess import check_call + +import yaml +from doodbalib import ( + ADDONS_YAML, + AUTO_REPOS_YAML, + CORE, + ODOO_DIR, + PRIVATE, + REPOS_YAML, + SRC_DIR, + logger, +) + +# if the umask matches the `chmod -R u+rwX,g+rX-w,o= /opt/odoo` command the build is faster as we don't need to fix as +# many permissions after auto aggregation +UMASK = os.environ.get("UMASK") or "0027" +UID = int(os.environ.get("UID") or -1) +GID = int(os.environ.get("GID") or -1) +DEFAULT_REPO_PATTERN = os.environ.get("DEFAULT_REPO_PATTERN") +DEFAULT_REPO_PATTERN_ODOO = os.environ.get("DEFAULT_REPO_PATTERN_ODOO") +log_level = os.environ.get("LOG_LEVEL", "INFO") + + +def aggregate(config): + """Execute git aggregator to pull git code. + + :param str config: + Path where to find the ``repos.yaml`` file. + """ + logger.info("Running gitaggregate with %s", config) + + def pre_exec_umask(): + # Download git code with the specified umask, if set, otherwise use "0027" + os.umask(int(UMASK, 8)) + + pre_execs = [pre_exec_umask] + + def pre_exec(): + for _exec in pre_execs: + try: + _exec() + except Exception as e: + logger.error("Error in %s: %s" % (_exec, e)) + logger.exception(e) + raise + + if ~GID: + + def pre_exec_gid(): + # execute git with GID + os.setgid(GID) + + pre_execs.append(pre_exec_gid) + + if ~UID: + + def pre_exec_uid(): + # execute git with UID + os.setuid(UID) + # set odoo home directory + # (git checks if user has a config in $HOME, and we cannot read /root as odoo user) + os.environ["HOME"] = "/home/odoo" + + pre_execs.append(pre_exec_uid) + + check_call( + [ + "gitaggregate", + "--expand-env", + "--config", + config, + "--log-level", + log_level, + "--jobs", + str(cpu_count() or 1), + "aggregate", + ], + cwd=SRC_DIR, + stderr=sys.stderr, + stdout=sys.stdout, + preexec_fn=pre_exec, + ) + + +def origin_for( + folder, + default_repo_pattern=DEFAULT_REPO_PATTERN, + odoo_repo_pattern=DEFAULT_REPO_PATTERN_ODOO, +): + """Guess the default git origin for that folder. + + :param str folder: + Normally an absolute path to an expected git repo, whose name should + match the git repository where it comes from, using the env-supplied + pattern. + """ + base = os.path.basename(folder) + pattern = default_repo_pattern + if base == "odoo": + pattern = odoo_repo_pattern + return pattern.format(base) + + +def missing_repos_config(): + """Find the undefined repositories and return their default configuration. + + :return dict: + git-aggregator-ready configuration dict for undefined repositories. + """ + defined, expected = set(), {ODOO_DIR} + # Find the repositories defined by hand + try: + with open(REPOS_YAML) as yaml_file: + for doc in yaml.safe_load_all(yaml_file): + for repo in doc: + defined.add(os.path.abspath(os.path.join(SRC_DIR, repo))) + except (IOError, AttributeError): + logger.debug("No repositories defined by hand") + addons_env = {} + # Find the repositories that should be present + try: + with open(ADDONS_YAML) as yaml_file: + for doc in yaml.safe_load_all(yaml_file): + env = dict(os.environ, **doc.get("ENV", {})) + for repo in doc: + if repo in {PRIVATE, "ONLY", "ENV"}: + continue + if repo == CORE: + repo_path = ODOO_DIR + else: + repo_path = os.path.abspath(os.path.join(SRC_DIR, repo)) + if not os.path.exists(repo_path) or os.path.isdir( + os.path.join(repo_path, ".git") + ): + expected.add(repo_path) + addons_env[repo_path] = env + except (IOError, AttributeError): + logger.debug("No addons are expected to be present") + # Find the undefined repositories and generate a config for them + missing = expected - defined + config = {} + for repo_path in missing: + env = addons_env.get(repo_path, os.environ) + depth = env["DEPTH_DEFAULT"] + origin_version = "origin %s" % env["ODOO_VERSION"] + config[repo_path] = { + "defaults": {"depth": depth}, + "merges": [origin_version], + "remotes": { + "origin": origin_for( + repo_path, + env["DEFAULT_REPO_PATTERN"], + env["DEFAULT_REPO_PATTERN_ODOO"], + ) + }, + "target": origin_version, + } + logger.debug("Generated missing repos config %r", config) + return config diff --git a/bin/config-generate b/bin/config-generate new file mode 100755 index 0000000..7865890 --- /dev/null +++ b/bin/config-generate @@ -0,0 +1,52 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +"""Generate Odoo server configuration from templates""" + +import os +from contextlib import closing +from string import Template + +from doodbalib import logger + +try: + # Python 2, where io.StringIO fails because it is unicode-only + from StringIO import StringIO +except ImportError: + from io import StringIO + +try: + from configparser import RawConfigParser + + parser = RawConfigParser(strict=False) +except ImportError: + # Python 2, where strict=True doesn't exist + from ConfigParser import RawConfigParser + + parser = RawConfigParser() + +ODOO_VERSION = os.environ.get("ODOO_VERSION") +TARGET_FILE = os.environ.get("ODOO_RC", "/opt/odoo/auto/odoo.conf") +CONFIG_DIRS = ("/opt/odoo/common/conf.d", "/opt/odoo/custom/conf.d") +CONFIG_FILES = [] + +# Read all configuraiton files found in those folders +logger.info("Merging found configuration files in %s", TARGET_FILE) +for dir_ in CONFIG_DIRS: + try: + for file_ in sorted(os.listdir(dir_)): + parser.read(os.path.join(dir_, file_)) + except OSError: # TODO Use FileNotFoundError when we drop python 2 + continue + +# Write it to a memory string object +with closing(StringIO()) as resultfp: + parser.write(resultfp) + resultfp.seek(0) + # Obtain the config string + result = resultfp.read() + # Expand environment variables found within + result = Template(result).substitute(os.environ) + logger.debug("Resulting configuration:\n%s", result) + # Write it to destination + with open(TARGET_FILE, "w") as targetfp: + targetfp.write(result) diff --git a/bin/direxec b/bin/direxec new file mode 100755 index 0000000..adecba7 --- /dev/null +++ b/bin/direxec @@ -0,0 +1,75 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import subprocess +import sys +from logging import DEBUG, INFO, WARNING + +from doodbalib import logger, which + +# Call this file linked from another file called `build` or `entrypoint` +mode = os.path.basename(__file__) + +dir_odoo = "/opt/odoo" +dir_common = os.path.join(dir_odoo, "common", "%s.d" % mode) +dir_custom = os.path.join(dir_odoo, "custom", "%s.d" % mode) + +# Find scripts +files = [(d, dir_common) for d in os.listdir(dir_common)] +try: + files += [(d, dir_custom) for d in os.listdir(dir_custom)] +except OSError: + pass + +# Run scripts +for executable, folder in sorted(files): + command = os.path.join(folder, executable) + if os.access(command, os.X_OK): + logger.debug("Executing %s", command) + subprocess.check_call(command) + +# Allow to omit 1st command and default to `odoo` +extra_command = sys.argv[1:] +if extra_command: + if extra_command[0] == "shell" or extra_command[0].startswith("-"): + extra_command.insert(0, "odoo") + # Set the DB creation language, if needed + if extra_command[0] in {"odoo", "/usr/local/bin/odoo"}: + if os.environ.get("INITIAL_LANG"): + from psycopg2 import OperationalError, connect + + try: + connection = connect(dbname=os.environ.get("PGDATABASE")) + connection.close() + except OperationalError: + # No DB exists, set initial language + extra_command += ["--load-language", os.environ["INITIAL_LANG"]] + if os.environ.get("PTVSD_ENABLE") == "1": + # Warn deprecation + logger.log( + WARNING, + "ptvsd has beed deprecated for python debugging. " + "Please use debugpy (see https://github.com/Tecnativa/doodba#debugpy)", + ) + # See `python -m ptvsd -h` to understand this + extra_command[0] = os.path.realpath(which(extra_command[0])) + extra_command = ( + ["python", "-m", "ptvsd"] + + os.environ.get("PTVSD_ARGS", "").split() + + extra_command + ) + elif os.environ["DEBUGPY_ENABLE"] == "1": + # See `python -m debugpy -h` to understand this + extra_command[0] = os.path.realpath(which(extra_command[0])) + extra_command = ( + ["python", "-m", "debugpy"] + + os.environ["DEBUGPY_ARGS"].split() + + extra_command + ) + logger.log( + DEBUG if extra_command[0] == "/qa/insider" else INFO, + "Executing %s", + " ".join(extra_command), + ) + os.execvp(extra_command[0], extra_command) diff --git a/bin/log b/bin/log new file mode 100755 index 0000000..ebc8d39 --- /dev/null +++ b/bin/log @@ -0,0 +1,15 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +import argparse +import logging + +from doodbalib import LOG_LEVELS, logger + +parser = argparse.ArgumentParser(description="Easy logging for scripts") +parser.add_argument("level", choices=LOG_LEVELS) +parser.add_argument("message", nargs="+") +arguments = parser.parse_args() + +logger.log(getattr(logging, arguments.level), " ".join(arguments.message)) diff --git a/bin/pot b/bin/pot new file mode 100755 index 0000000..b901ab3 --- /dev/null +++ b/bin/pot @@ -0,0 +1,11 @@ +#!/bin/bash +# Shortcut to run Odoo in unit testing mode +set -e +addons=$1 +shift +log INFO Executing Odoo in i18n export mode for addons $addons +# HACK Odoo needs a *.po file to guess the output format +ln -sf /dev/stdout /tmp/stdout.po +set -x +exec odoo --stop-after-init -d "$PGDATABASE" --i18n-export /tmp/stdout.po \ + --modules "$addons" --update "$addons" --workers 0 "$@" diff --git a/bin/preparedb b/bin/preparedb new file mode 100755 index 0000000..b8a0663 --- /dev/null +++ b/bin/preparedb @@ -0,0 +1,18 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Script to prepare the database with initial data + +import click +import click_odoo + + +@click.command() +@click_odoo.env_options(default_log_level="info", database_must_exist=True) +def main(env): + """Set report.url in the database to be pointing at localhost.""" + env["ir.config_parameter"].set_param("report.url", "http://localhost:8069") + env.cr.commit() + + +if __name__ == "__main__": + main() diff --git a/custom/build.d/100-repos-aggregate b/custom/build.d/100-repos-aggregate new file mode 100755 index 0000000..7c57c6d --- /dev/null +++ b/custom/build.d/100-repos-aggregate @@ -0,0 +1,18 @@ +#!/bin/bash +set -e + +# make sure odoo has a user.name configured, as merges would not succeed otherwise +# (even if GIT_AUTHOR_NAME and EMAIL are set and should be used, it seems gitaggregate is not passing them to git) +su --shell="$SHELL" odoo -c 'git config user.name 1>/dev/null || git config --global user.name "'"$GIT_AUTHOR_NAME"'"' + +# copy ssh directory to odoo user as well (gitaggregate may also be run as odoo user) +if [[ ! -e ~odoo/.ssh ]] ; then + cp -a /opt/odoo/custom/ssh ~odoo/.ssh +fi + +if [ "$AGGREGATE" != true ]; then + log WARNING Not aggregating code repositories + exit 0 +fi + +exec autoaggregate diff --git a/custom/build.d/110-addons-link b/custom/build.d/110-addons-link new file mode 100755 index 0000000..021875e --- /dev/null +++ b/custom/build.d/110-addons-link @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +import os +from glob import iglob + +from doodbalib import ADDONS_DIR, ADDONS_YAML, SRC_DIR, addons_config, logger + +logger.info("Linking all addons from %s in %s", ADDONS_YAML, ADDONS_DIR) + +# Remove all links in addons dir +for link in iglob(os.path.join(ADDONS_DIR, "*")): + os.remove(link) +# Add new links +for addon, repo in addons_config(): + src = os.path.relpath(os.path.join(SRC_DIR, repo, addon), ADDONS_DIR) + dst = os.path.join(ADDONS_DIR, addon) + os.symlink(src, dst) + logger.debug("Linked %s in %s", src, dst) diff --git a/custom/build.d/200-dependencies b/custom/build.d/200-dependencies new file mode 100755 index 0000000..d6f0326 --- /dev/null +++ b/custom/build.d/200-dependencies @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Copyright 2017 LasLabs Inc. + +from glob import glob +from os.path import basename, join, splitext + +from doodbalib import CUSTOM_DIR, FILE_APT_BUILD, SRC_DIR +from doodbalib.installer import INSTALLERS, install, logger + +# Build dependencies installed before any others +install("apt", FILE_APT_BUILD) + +for name in INSTALLERS: + req_files = [] + # Normal dependency installation + req_files.append(join(CUSTOM_DIR, "dependencies", "%s.txt" % name)) + for req_file in req_files: + install(name, req_file) + +# Sorted dependencies installation +dep_files = sorted(glob(join(CUSTOM_DIR, "dependencies", "[0-9]*-*"))) +for dep_file in dep_files: + root, ext = splitext(basename(dep_file)) + # Get the installer (xxx-installer[-description][.ext]) + installer = root.split("-", 2)[1] + if installer not in INSTALLERS: + logger.error("Unknown installer: %s", installer) + raise Exception + install(installer, dep_file) diff --git a/custom/build.d/300-fontconfig b/custom/build.d/300-fontconfig new file mode 100755 index 0000000..1067c7b --- /dev/null +++ b/custom/build.d/300-fontconfig @@ -0,0 +1,31 @@ +#!/bin/bash +set -e + +mkdir -p ~cloudron/.config/fontconfig/conf.d +cat < ~odoo/.config/fontconfig/conf.d/100-doodba.conf + + + + + + monospace + + $FONT_MONO + + + + sans-serif + + $FONT_SANS + + + + serif + + $FONT_SERIF + + + +END + +chown cloudron:cloudron ~cloudron/.config diff --git a/custom/build.d/400-clean b/custom/build.d/400-clean new file mode 100755 index 0000000..d737007 --- /dev/null +++ b/custom/build.d/400-clean @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +import os +import shutil +import sys + +from doodbalib import CLEAN, ODOO_DIR, PRIVATE_DIR, SRC_DIR, addons_config, logger + +if not CLEAN: + logger.warning("Not cleaning garbage") + sys.exit() + +# Get the enabled paths +repos_addons = {} +for addon, repo in addons_config(filtered=False): + repo_path = os.path.realpath(os.path.join(SRC_DIR, repo)) + repos_addons.setdefault(repo_path, set()) + repos_addons[repo_path].add(addon) +logger.debug("Addon paths enabled: %s", repos_addons) + +# Traverse src dir and remove anything not explicitly enabled +for directory, subdirectories, subfiles in os.walk(SRC_DIR): + logger.debug("Checking for cleanup directory %s", directory) + # Skip main src directory + if directory == SRC_DIR: + continue + # Always skip private/* + if directory == PRIVATE_DIR: + subdirectories[:] = [] + continue + # Inside the odoo dir, skip all but addons dir + if directory == ODOO_DIR: + subdirectories[:] = ["addons"] + continue + try: + # Get addons enalbed in current directory + enabled_addons = repos_addons[directory] + except KeyError: + # This isn't a repo; is there anything inside to preserve? + directory += os.path.sep + if any(repo.startswith(directory) for repo in repos_addons): + # Then, let's walk in; we'll remove later if needed + continue + else: + # This is an addons repo; do not walk into the enabled ones + for addon in enabled_addons: + subdirectories.remove(addon) + continue + # Remove every other directory + logger.info("Removing directory %s", directory) + shutil.rmtree(directory) diff --git a/custom/build.d/500-compile b/custom/build.d/500-compile new file mode 100755 index 0000000..3a20f5d --- /dev/null +++ b/custom/build.d/500-compile @@ -0,0 +1,10 @@ +#!/bin/bash +set -e + +if [ "$COMPILE" != true ]; then + log WARNING Not compiling Python code + exit 0 +fi + +log INFO Compiling all Python code in /opt/odoo +python -m compileall -q /opt/odoo diff --git a/custom/build.d/900-dependencies-cleanup b/custom/build.d/900-dependencies-cleanup new file mode 100755 index 0000000..7b6bdec --- /dev/null +++ b/custom/build.d/900-dependencies-cleanup @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Copyright 2017 LasLabs Inc. + +import os + +from doodbalib import CUSTOM_DIR, FILE_APT_BUILD +from doodbalib.installer import INSTALLERS + +# Build-time dependencies must be removed when finishing build +if os.path.isfile(FILE_APT_BUILD): + installer = INSTALLERS["apt"](FILE_APT_BUILD) + installer.remove() + installer.cleanup() + + +# Clean up garbage generated by respective package managers +for name, class_ in INSTALLERS.items(): + req_file = os.path.join(CUSTOM_DIR, "dependencies", "%s.txt" % name) + if os.path.isfile(req_file): + class_(req_file).cleanup() diff --git a/custom/dependencies/apt.txt b/custom/dependencies/apt.txt new file mode 100644 index 0000000..1149537 --- /dev/null +++ b/custom/dependencies/apt.txt @@ -0,0 +1,8 @@ +python3-pandas +cups +libcups2-dev +python3-dev +build-essential +libcairo2-dev +pkg-config +xmlsec1 diff --git a/custom/dependencies/apt_build.txt b/custom/dependencies/apt_build.txt new file mode 100644 index 0000000..8df773d --- /dev/null +++ b/custom/dependencies/apt_build.txt @@ -0,0 +1,3 @@ +libldap2-dev +libssl-dev +libsasl2-dev \ No newline at end of file diff --git a/custom/dependencies/gem.txt b/custom/dependencies/gem.txt new file mode 100644 index 0000000..e69de29 diff --git a/custom/dependencies/npm.txt b/custom/dependencies/npm.txt new file mode 100644 index 0000000..e69de29 diff --git a/custom/dependencies/pip.txt b/custom/dependencies/pip.txt new file mode 100644 index 0000000..99570da --- /dev/null +++ b/custom/dependencies/pip.txt @@ -0,0 +1,43 @@ +git+https://github.com/OCA/openupgradelib.git@master +unicodecsv +unidecode +py3o.template +PyPDF2 +py3o.formats +zeep +parse-accept-language +pyquerystring +cerberus==1.3.2 +apispec>=4.0.0 +marshmallow +marshmallow-objects>=2.0.0 +cachetools +boto +pycups +bravado_core +facebook_business +python-telegram-bot +swagger_spec_validator +viberbot +PyMuPDF==1.16.14 +factur-x +regex +dateparser +pycairo +rocketchat_API +ovh +weboob +payplug +qrcode +markdownify +requests_oauthlib +pyocclient +cryptography>=2.3 +jwcrypto==0.5.0 +freezegun +pysaml2 +formio-data +pysaml2 +odoo_test_helper +python-jose +click-odoo-contrib \ No newline at end of file diff --git a/custom/src/addons.yaml b/custom/src/addons.yaml new file mode 100644 index 0000000..821acab --- /dev/null +++ b/custom/src/addons.yaml @@ -0,0 +1,164 @@ +sale-workflow: + - "*" +openupgrade: + - "openupgrade_framework" + - "openupgrade_scripts" +partner-contact: + - "*" +server-ux: + - "*" +bank-payment: + - "*" +account-financial-tools: + - "account_*" + - "base_*" +community-data-files: + - "*" +hr: + - "*" +l10n-france: + - "*" +l10n-switzerland: + - "*" +product-attribute: + - "*" +project: + - "*" +project-reporting: + - "*" +web: + - "*" +website: + - "*" +mis-builder: + - "*" +operating-unit: + - "*" +connector: + - "*" +purchase-workflow: + - "*" +server-env: + - "*" +odoo-theme: + - "*" +report-engine: + - "*" +formio: + - "*" +# learning_addons: +# - "*" +muk-web: + - "*" +rest-framework: + - "*" +server-auth: + - "*" +server-tools: + - "*" +wms: + - "*" +stock-logistics-warehouse: + - "*" +contract: + - "*" +field-service: + - "*" +queue: + - "*" +commission: + - "*" +pms: + - "*" +dms: + - "*" +social: + - "*" +pos: + - "*" +product-configurator: + - "*" +stock-logistics-workflow: + - "*" +account-financial-reporting: + - "*" +bank-statement-import: + - "*" +account-reconcile: + - "*" +manufacture: + - "*" +multi-company: + - "*" +account-analytic: + - "*" +stock-logistics-reporting: + - "*" +account-invoice-reporting: + - "*" +sale-reporting: + - "*" +account-closing: + - "*" +account-payment: + - "*" +edi: + - "*" +timesheet: + - "*" +odoo-pim: + - "*" +delivery-carrier: + - "*" +storage: + - "*" +product-variant: + - "*" +e-commerce: + - "*" +hr-expense: + - "*" +crm: + - "*" +maintenance: + - "*" +connector-telephony: + - "*" +server-backend: + - "*" +intrastat-extrastat: + - "*" +brand: + - "*" +hr-holidays: + - "*" +server-brand: + - "*" +report-print-send: + - "*" +calendar: + - "*" +credit-control: + - "*" +# myc-extra-addons: +# - "*" +account-invoicing: + - "*" +sync-addons: + - "*" +vertical-cooperative: + - "*" +# nj-addons: +# - "*" +vertical-association: + - "*" +account-move-import: + - "*" +galicea-addons: + - "*" +straga-main: + - "*" +odoo-usability: + - "*" +odoo-py3o-report-templates: + - "*" diff --git a/custom/src/private/.editorconfig b/custom/src/private/.editorconfig new file mode 100644 index 0000000..bfd7ac5 --- /dev/null +++ b/custom/src/private/.editorconfig @@ -0,0 +1,20 @@ +# Configuration for known file extensions +[*.{css,js,json,less,md,py,rst,sass,scss,xml,yaml,yml}] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[*.{json,yml,yaml,rst,md}] +indent_size = 2 + +# Do not configure editor for libs and autogenerated content +[{*/static/{lib,src/lib}/**,*/static/description/index.html,*/readme/../README.rst}] +charset = unset +end_of_line = unset +indent_size = unset +indent_style = unset +insert_final_newline = false +trim_trailing_whitespace = false diff --git a/custom/src/repos.yaml b/custom/src/repos.yaml new file mode 100644 index 0000000..5451def --- /dev/null +++ b/custom/src/repos.yaml @@ -0,0 +1,884 @@ +./sale-workflow: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: $DEPTH_DEFAULT + remotes: + origin: https://github.com/OCA/sale-workflow.git + target: origin 14.0 + merges: + - origin 14.0 +./partner-contact: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: $DEPTH_DEFAULT + remotes: + oca: https://github.com/OCA/partner-contact.git + # myc: ssh://git@git.myceliandre.fr:5022/OCA/partner-contact.git + target: oca 14.0 + merges: + #- myc 14.0_partner_favorite + - oca 14.0 +./server-ux: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: $DEPTH_DEFAULT + remotes: + oca: https://github.com/OCA/server-ux.git + target: oca 14.0 + merges: + - oca 14.0 +./bank-payment: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: 200 + remotes: + oca: https://github.com/OCA/bank-payment.git + target: oca 14.0 + merges: + - oca 14.0 + - oca refs/pull/820/head + - oca refs/pull/822/head + #- oca refs/pull/831/head + #- oca refs/pull/858/head +./account-financial-tools: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: 200 + remotes: + oca: https://github.com/OCA/account-financial-tools.git + target: oca 14.0 + merges: + - oca 14.0 + - oca refs/pull/1087/head + - oca refs/pull/1236/head +./community-data-files: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: $DEPTH_DEFAULT + remotes: + oca: https://github.com/OCA/community-data-files.git + target: oca 14.0 + merges: + - oca 14.0 +./hr: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: $DEPTH_DEFAULT + remotes: + oca: https://github.com/OCA/hr.git + target: oca 14.0 + merges: + - oca 14.0 +./l10n-switzerland: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: $DEPTH_DEFAULT + remotes: + oca: https://github.com/OCA/l10n-switzerland.git + target: oca 12.0 + merges: + - oca 12.0 +./l10n-france: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: 200 + remotes: + oca: https://github.com/OCA/l10n-france.git + target: oca 14.0 + merges: + - oca 14.0 + - oca refs/pull/257/head + - oca refs/pull/321/head +./product-attribute: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: $DEPTH_DEFAULT + remotes: + oca: https://github.com/OCA/product-attribute.git + target: oca 14.0 + merges: + - oca 14.0 +./project: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: $DEPTH_DEFAULT + remotes: + oca: https://github.com/OCA/project.git + target: oca 14.0 + merges: + - oca 14.0 +./project-reporting: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: 200 + remotes: + oca: https://github.com/OCA/project-reporting.git + target: oca 14.0 + merges: + - oca 14.0 + - oca refs/pull/44/head +./website: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: $DEPTH_DEFAULT + remotes: + oca: https://github.com/OCA/website.git + target: oca 14.0 + merges: + - oca 14.0 +./web: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: $DEPTH_DEFAULT + remotes: + oca: https://github.com/OCA/web.git + target: oca 14.0 + merges: + - oca 14.0 +./mis-builder: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: $DEPTH_DEFAULT + remotes: + oca: https://github.com/OCA/mis-builder.git + target: oca 14.0 + merges: + - oca 14.0 +./operating-unit: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: $DEPTH_DEFAULT + remotes: + oca: https://github.com/OCA/operating-unit.git + target: oca 14.0 + merges: + - oca 14.0 +./connector: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: $DEPTH_DEFAULT + remotes: + oca: https://github.com/OCA/connector.git + target: oca 14.0 + merges: + - oca 14.0 +./delivery-carrier: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: $DEPTH_DEFAULT + remotes: + origin: https://github.com/OCA/delivery-carrier.git + target: origin 14.0 + merges: + - origin 14.0 +./purchase-workflow: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: $DEPTH_DEFAULT + remotes: + origin: https://github.com/OCA/purchase-workflow.git + target: origin 14.0 + merges: + - origin 14.0 +./server-env: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: $DEPTH_DEFAULT + remotes: + origin: https://github.com/OCA/server-env.git + target: origin 14.0 + merges: + - origin 14.0 +./odoo-theme: + defaults: + depth: $DEPTH_DEFAULT + remotes: + myc: https://git.myceliandre.fr/Myceliandre/odoo_theme.git + target: myc 14.0 + merges: + - myc 14.0 +./report-engine: + defaults: + depth: 200 + remotes: + oca: https://github.com/OCA/reporting-engine.git + target: oca 14.0 + merges: + #- oca refs/pull/445/head + #- oca refs/pull/506/head + #- oca refs/pull/526/head + #- oca refs/pull/502/head + # report_py3o PR + #- oca refs/pull/445/head + - oca 14.0 +./formio: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: $DEPTH_DEFAULT + remotes: + origin: https://github.com/novacode-nl/odoo-formio.git + target: origin 14.0 + merges: + - origin 14.0 +# ./learning_addons: +# defaults: +# # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner +# # You may need a bigger depth when merging PRs (use $DEPTH_MERGE +# # for a sane value of 100 commits) +# depth: $DEPTH_DEFAULT +# remotes: +# origin: ssh://git@git.myceliandre.fr:5022/Myceliandre/odoo_learning_addons.git +# target: origin 14.0 +# merges: +# - origin 14.0 +./muk-web: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: $DEPTH_DEFAULT + remotes: + origin: https://github.com/muk-it/muk_web.git + target: origin 14.0 + merges: + - origin 14.0 +./rest-framework: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: $DEPTH_DEFAULT + remotes: + oca: https://github.com/OCA/rest-framework.git + target: oca 14.0 + merges: + - oca 14.0 +./server-auth: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: $DEPTH_DEFAULT + remotes: + oca: https://github.com/OCA/server-auth.git + target: oca 14.0 + merges: + - oca 14.0 +./server-tools: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: $DEPTH_DEFAULT + remotes: + oca: https://github.com/OCA/server-tools.git + target: oca 14.0 + merges: + - oca 14.0 +./wms: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: $DEPTH_DEFAULT + remotes: + oca: https://github.com/OCA/wms.git + target: oca 14.0 + merges: + - oca 14.0 +./stock-logistics-warehouse: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: $DEPTH_DEFAULT + remotes: + oca: https://github.com/OCA/stock-logistics-warehouse.git + target: oca 14.0 + merges: + - oca 14.0 +./contract: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: $DEPTH_DEFAULT + remotes: + oca: https://github.com/OCA/contract.git + target: oca 14.0 + merges: + - oca 14.0 +./field-service: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: $DEPTH_DEFAULT + remotes: + oca: https://github.com/OCA/field-service.git + target: oca 14.0 + merges: + - oca 14.0 +./queue: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: $DEPTH_DEFAULT + remotes: + oca: https://github.com/OCA/queue.git + target: oca 14.0 + merges: + - oca 14.0 +./commission: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: $DEPTH_DEFAULT + remotes: + oca: https://github.com/OCA/commission.git + target: oca 14.0 + merges: + - oca 14.0 +./pms: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: $DEPTH_DEFAULT + remotes: + oca: https://github.com/OCA/pms.git + target: oca 14.0 + merges: + - oca 14.0 +./dms: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: $DEPTH_DEFAULT + remotes: + oca: https://github.com/OCA/dms.git + target: oca 14.0 + merges: + - oca 14.0 +./social: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: $DEPTH_DEFAULT + remotes: + oca: https://github.com/OCA/social.git + target: oca 14.0 + merges: + - oca 14.0 +./pos: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: $DEPTH_DEFAULT + remotes: + oca: https://github.com/OCA/pos.git + target: oca 14.0 + merges: + - oca 14.0 +./product-configurator: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: $DEPTH_DEFAULT + remotes: + oca: https://github.com/OCA/product-configurator.git + target: oca 14.0 + merges: + - oca 14.0 +./stock-logistics-workflow: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: $DEPTH_DEFAULT + remotes: + oca: https://github.com/OCA/stock-logistics-workflow.git + target: oca 14.0 + merges: + - oca 14.0 +./account-financial-reporting: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: $DEPTH_DEFAULT + remotes: + oca: https://github.com/OCA/account-financial-reporting.git + target: oca 14.0 + merges: + - oca 14.0 +./bank-statement-import: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: 200 + remotes: + oca: https://github.com/OCA/bank-statement-import.git + target: oca 14.0 + merges: + - oca 14.0 + - oca refs/pull/396/head + - oca refs/pull/417/head + - oca refs/pull/423/head +./account-reconcile: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: 200 + remotes: + oca: https://github.com/OCA/account-reconcile.git + target: oca 14.0 + merges: + - oca 14.0 + - oca refs/pull/361/head + - oca refs/pull/382/head + - oca refs/pull/402/head + - oca refs/pull/412/head +./manufacture: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: $DEPTH_DEFAULT + remotes: + oca: https://github.com/OCA/manufacture.git + target: oca 14.0 + merges: + - oca 14.0 +./multi-company: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: $DEPTH_DEFAULT + remotes: + oca: https://github.com/OCA/multi-company.git + target: oca 14.0 + merges: + - oca 14.0 +./account-analytic: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: $DEPTH_DEFAULT + remotes: + oca: https://github.com/OCA/account-analytic.git + target: oca 14.0 + merges: + - oca 14.0 +./stock-logistics-reporting: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: $DEPTH_DEFAULT + remotes: + oca: https://github.com/OCA/stock-logistics-reporting.git + target: oca 14.0 + merges: + - oca 14.0 +./account-invoice-reporting: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: $DEPTH_DEFAULT + remotes: + oca: https://github.com/OCA/account-invoice-reporting.git + target: oca 14.0 + merges: + - oca 14.0 +./account-invoicing: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: 200 + remotes: + oca: https://github.com/OCA/account-invoicing.git + target: oca 14.0 + merges: + - oca 14.0 +./sale-reporting: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: $DEPTH_DEFAULT + remotes: + oca: https://github.com/OCA/sale-reporting.git + target: oca 14.0 + merges: + - oca 14.0 +./account-closing: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: 200 + remotes: + oca: https://github.com/OCA/account-closing.git + target: oca 14.0 + merges: + - oca 14.0 + - oca refs/pull/155/head + - oca refs/pull/174/head +./account-payment: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: $DEPTH_DEFAULT + remotes: + oca: https://github.com/OCA/account-payment.git + target: oca 14.0 + merges: + - oca 14.0 +./edi: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: 200 + remotes: + oca: https://github.com/OCA/edi.git + target: oca 14.0 + merges: + - oca 14.0 + - oca refs/pull/326/head + #- oca refs/pull/334/head + - oca refs/pull/454/head +./timesheet: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: $DEPTH_DEFAULT + remotes: + oca: https://github.com/OCA/timesheet.git + target: oca 14.0 + merges: + - oca 14.0 +./odoo-pim: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: $DEPTH_DEFAULT + remotes: + oca: https://github.com/OCA/odoo-pim.git + target: oca 14.0 + merges: + - oca 14.0 +./storage: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: $DEPTH_DEFAULT + remotes: + oca: https://github.com/OCA/storage.git + target: oca 14.0 + merges: + - oca 14.0 +./product-variant: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: $DEPTH_DEFAULT + remotes: + oca: https://github.com/OCA/product-variant.git + target: oca 14.0 + merges: + - oca 14.0 +./e-commerce: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: $DEPTH_DEFAULT + remotes: + oca: https://github.com/OCA/e-commerce.git + target: oca 14.0 + merges: + - oca 14.0 +./hr-expense: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: $DEPTH_DEFAULT + remotes: + oca: https://github.com/OCA/hr-expense.git + target: oca 14.0 + merges: + - oca 14.0 +./crm: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: $DEPTH_DEFAULT + remotes: + oca: https://github.com/OCA/crm.git + target: oca 14.0 + merges: + - oca 14.0 +./maintenance: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: $DEPTH_DEFAULT + remotes: + oca: https://github.com/OCA/maintenance.git + target: oca 14.0 + merges: + - oca 14.0 +./connector-telephony: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: $DEPTH_DEFAULT + remotes: + oca: https://github.com/OCA/connector-telephony.git + target: oca 14.0 + merges: + - oca 14.0 +./server-backend: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: $DEPTH_DEFAULT + remotes: + oca: https://github.com/OCA/server-backend.git + target: oca 14.0 + merges: + - oca 14.0 +./intrastat-extrastat: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: $DEPTH_DEFAULT + remotes: + oca: https://github.com/OCA/intrastat-extrastat.git + target: oca 14.0 + merges: + - oca 14.0 +./brand: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: $DEPTH_DEFAULT + remotes: + oca: https://github.com/OCA/brand.git + target: oca 14.0 + merges: + - oca 14.0 +./hr-holidays: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: $DEPTH_DEFAULT + remotes: + oca: https://github.com/OCA/hr-holidays.git + target: oca 14.0 + merges: + - oca 14.0 +./server-brand: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: $DEPTH_DEFAULT + remotes: + oca: https://github.com/OCA/server-brand.git + target: oca 14.0 + merges: + - oca 14.0 +./report-print-send: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: $DEPTH_DEFAULT + remotes: + oca: https://github.com/OCA/report-print-send.git + target: oca 14.0 + merges: + - oca 14.0 +./calendar: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: $DEPTH_DEFAULT + remotes: + oca: https://github.com/OCA/calendar.git + target: oca 14.0 + merges: + - oca 14.0 +./credit-control: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: $DEPTH_DEFAULT + remotes: + oca: https://github.com/OCA/credit-control.git + target: oca 14.0 + merges: + - oca 14.0 +# ./myc-extra-addons: +# defaults: +# depth: $DEPTH_DEFAULT +# remotes: +# myc: ssh://git@git.myceliandre.fr:5022/njeudy/myc-extra-addons.git +# target: myc 14.0 +# merges: +# - myc 14.0 +./sync-addons: + defaults: + depth: $DEPTH_DEFAULT + remotes: + origin: https://github.com/itpp-labs/sync-addons.git + target: origin 14.0 + merges: + - origin 14.0 +./vertical-cooperative: + defaults: + depth: $DEPTH_DEFAULT + remotes: + origin: https://git.myceliandre.fr/njeudy/vertical-cooperative.git + target: origin 14.0-MIG-INITIAL + merges: + - origin 14.0-MIG-INITIAL +# ./nj-addons: +# defaults: +# depth: $DEPTH_DEFAULT +# remotes: +# origin: ssh://git@git.myceliandre.fr:5022/nj.0k.io/nj-addons.git +# target: origin 14.0 +# merges: +# - origin 14.0 +./vertical-association: + defaults: + depth: $DEPTH_DEFAULT + remotes: + origin: https://github.com/OCA/vertical-association.git + target: origin 14.0 + merges: + - origin 14.0 +./account-move-import: + defaults: + depth: $DEPTH_DEFAULT + remotes: + origin: https://github.com/akretion/account-move-import.git + target: origin 14.0 + merges: + - origin 14.0 +./galicea-addons: + defaults: + # Shallow repositories ($DEPTH_DEFAULT=1) are faster & thinner + # You may need a bigger depth when merging PRs (use $DEPTH_MERGE + # for a sane value of 100 commits) + depth: $DEPTH_DEFAULT + remotes: + origin: https://git.myceliandre.fr/Myceliandre/galicea-odoo-addons-ecosystem.git + target: origin 14.0 + merges: + - origin 14.0 +./openupgrade: + defaults: + depth: $DEPTH_DEFAULT + remotes: + origin: https://github.com/OCA/OpenUpgrade.git + target: origin 14.0 + merges: + - origin 14.0 +./straga-main: + defaults: + depth: $DEPTH_DEFAULT + remotes: + origin: https://github.com/straga/odoo_vik_main.git + target: origin 14.0 + merges: + - origin 14.0 +./odoo-usability: + defaults: + depth: $DEPTH_DEFAULT + remotes: + origin: https://github.com/akretion/odoo-usability.git + target: origin 14.0 + merges: + - origin 14.0 +./odoo-py3o-report-templates: + defaults: + depth: $DEPTH_DEFAULT + remotes: + origin: https://github.com/akretion/odoo-py3o-report-templates.git + target: origin 14.0 + merges: + - origin 14.0 diff --git a/custom/ssh/cloudron_git.rsa b/custom/ssh/cloudron_git.rsa new file mode 100644 index 0000000..bef2d2c --- /dev/null +++ b/custom/ssh/cloudron_git.rsa @@ -0,0 +1,38 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn +NhAAAAAwEAAQAAAYEAxTkiUEpg2rhVAbvImkk2BAKIB13kCg2IyNuiUtPwKWBt4gyDzQwS +r9+lhDcTb41UmQFn0++dBCveRkTTafY+d23aWSHf+vfM470CSzdwothHIukNnPyRvWAwhO +Z7X51evA2hYuUvdeZex0Rqrwdxu1vrpCsPwHxiEAMm5Adc8ybqEiKBkoRv20PKas1WEl/m +RpSTadGUZVh0fJFp9gRFKmZMqXWm81hB5MpKL4OBd/APhRZfqNT0WOTIBHFKySjAqNi4H8 +eUW3voi/ivSMTCv1MyybzSEHRLS8fzDa9zJ6uXp6/SOVSNyIT8oNqBBOG0Bk0w2y9E32lR +tnqmugVU40CSoIwf9LyCy3pSdqM1mM+sXTsd/tqY4Vo/H1m6U+zjEX1/9pLYbS0uDbdCAv +ChoxQg+HtCZ74wX2c+yFrVcDqqHNOqCbbPdjSNrdCFVdZtx2A2AuqALFnXskc1lJ4VTOsJ +Fr5QoAmLGRYN0lvqzNVUwI/BklmIYOgi8cvXf4PxAAAFkItx43GLceNxAAAAB3NzaC1yc2 +EAAAGBAMU5IlBKYNq4VQG7yJpJNgQCiAdd5AoNiMjbolLT8ClgbeIMg80MEq/fpYQ3E2+N +VJkBZ9PvnQQr3kZE02n2Pndt2lkh3/r3zOO9Aks3cKLYRyLpDZz8kb1gMITme1+dXrwNoW +LlL3XmXsdEaq8Hcbtb66QrD8B8YhADJuQHXPMm6hIigZKEb9tDymrNVhJf5kaUk2nRlGVY +dHyRafYERSpmTKl1pvNYQeTKSi+DgXfwD4UWX6jU9FjkyARxSskowKjYuB/HlFt76Iv4r0 +jEwr9TMsm80hB0S0vH8w2vcyerl6ev0jlUjciE/KDagQThtAZNMNsvRN9pUbZ6proFVONA +kqCMH/S8gst6UnajNZjPrF07Hf7amOFaPx9ZulPs4xF9f/aS2G0tLg23QgLwoaMUIPh7Qm +e+MF9nPsha1XA6qhzTqgm2z3Y0ja3QhVXWbcdgNgLqgCxZ17JHNZSeFUzrCRa+UKAJixkW +DdJb6szVVMCPwZJZiGDoIvHL13+D8QAAAAMBAAEAAAGAAxi7jjEsxiJgy08sfieqHnP/uM +Xjn7jIrgaszMohGjU2ZHc31o9a98H2MlY/CuBYNLLN84jumTMrIUVRYHeKUYu7Au1CPAmK +AQVltNKhBR2KOGUaXp2kmCmbeWq5Ay5QX3mDUC8zCJHeaRiM6ESgp4Vw9LnsXGRXkdLK2I +e5EORKhpBeInPL4dB1rCmfMViqH++TRPUSdGjoI1CRLliw0VKb34lGXsnC9xmqAob5EG4H +gFpylA8L6x1kepVgzDnEjYf9DEHwapmBrqFzamItaVX0/tCbz9Z+pJKPwbQUNiI685vpto +y+1N3ebPlQWIYVMe8nNJk5sHU1fHSvwaUy7LHC7rQS5M8+rPk9uJ6Gn4IzrMT+krUXhLQ7 +ty8MeA2MJfkZPh2hewp+oBInocNrQY/YoEIG+GymWl5bWd14Oq1aD3c6kiQx4wFbS+UQ8v +K24PwXQMp44Js8tu8VzW8PkJDS0tWIBbj6vb3VLnLzPSi96RXcUwGNtB5nIkgKbdt1AAAA +wQDarcaxa7NIz5MkRgLhWNUPSZRh9upUnck6Ul7JnMYeVkNTm6u17V5sl1qj6Tk+EhZvHL +5pwMra8XtCDq5rtQuG6d7SQp0aB1uQEXioIkSzazaMO2KRxSJsLjXpSwPQn6dcrwEpDafX +1bgTocYA+fg54qv0F1luBOojv5XaUYY/6Qh0avdVohP00uS5vnNCSrFR4K/VB2Xgi3F0Io +dm7gdGwN/VCXpkydW3o8x/ka1vWQhAWdhfcd4ZNu10tya3m1wAAADBAPveoaaPPK0k4v5B +5TMXlnz939H7i+iwBx3cDgZNRX8c1nnhb+rjy/JQV0N01v6CUmAR4NgRe4SrCb7HgRcvAd +Ac2uYzT3f5/F3Gj/zETGM1cCrMox64BPqIPkMrQVtq58AdclJqqiqvbYYl5oycbkKd2CcF +dhMh5GAI2RTDMcxQzcM5EBGh9vWxUtCosNBsGMRm8jvUXg8fpNIduf6B+qU3pNen6otPPt +ydGZStR+iAkf4p8ny0OJQ+lTPMfimzvwAAAMEAyHUXp/60l7g6A1s5lOgvnUwJYlVk5MRl +QEfdAHVbIhqM+Vig4po1nk2zVf+VKtZe6JIalcrelHydohMgFIsMsfFOn/lhjuL+yUaeb9 +ud0aJmP7MPOcf2uFv5iqN87Q893OHdkoZSak2SHWQm/Sho3tHKaM7OdQwOiwJqnzyPc8Dg +YD/JJWsqzNRCQ9BL7zuaf1+0gb5lBJGw95kBDg7rOuKQXdk7uWxQCZPXj3/xO2kk0t/cTa +cgHT4D/mOucfRPAAAAFm5qZXVkeUBERVNLVE9QLU1MUk5HOEQBAgME +-----END OPENSSH PRIVATE KEY----- diff --git a/custom/ssh/cloudron_git.rsa.pub b/custom/ssh/cloudron_git.rsa.pub new file mode 100644 index 0000000..b0a9792 --- /dev/null +++ b/custom/ssh/cloudron_git.rsa.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDFOSJQSmDauFUBu8iaSTYEAogHXeQKDYjI26JS0/ApYG3iDIPNDBKv36WENxNvjVSZAWfT750EK95GRNNp9j53bdpZId/698zjvQJLN3Ci2Eci6Q2c/JG9YDCE5ntfnV68DaFi5S915l7HRGqvB3G7W+ukKw/AfGIQAybkB1zzJuoSIoGShG/bQ8pqzVYSX+ZGlJNp0ZRlWHR8kWn2BEUqZkypdabzWEHkykovg4F38A+FFl+o1PRY5MgEcUrJKMCo2Lgfx5Rbe+iL+K9IxMK/UzLJvNIQdEtLx/MNr3Mnq5enr9I5VI3IhPyg2oEE4bQGTTDbL0TfaVG2eqa6BVTjQJKgjB/0vILLelJ2ozWYz6xdOx3+2pjhWj8fWbpT7OMRfX/2kthtLS4Nt0IC8KGjFCD4e0JnvjBfZz7IWtVwOqoc06oJts92NI2t0IVV1m3HYDYC6oAsWdeyRzWUnhVM6wkWvlCgCYsZFg3SW+rM1VTAj8GSWYhg6CLxy9d/g/E= njeudy@DESKTOP-MLRNG8D diff --git a/custom/ssh/config b/custom/ssh/config new file mode 100644 index 0000000..5dadff1 --- /dev/null +++ b/custom/ssh/config @@ -0,0 +1,7 @@ +# See syntax in https://www.ssh.com/ssh/config/ and `man ssh_config` +Host * + IgnoreUnknown UseKeychain + UseKeychain yes + IdentityFile /root/.ssh/cloudron_git.rsa + PubkeyAcceptedKeyTypes=+ssh-dss + AddKeysToAgent yes \ No newline at end of file diff --git a/custom/ssh/known_hosts b/custom/ssh/known_hosts new file mode 100644 index 0000000..0a49a7d --- /dev/null +++ b/custom/ssh/known_hosts @@ -0,0 +1,14 @@ +# Use `ssh-keyscan` to fill this file and ensure remote git hosts ssh keys + +# bitbucket.org:22 SSH-2.0-conker_1.0.298-8c5a6f7 app-126 +bitbucket.org ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAubiN81eDcafrgMeLzaFPsw2kNvEcqTKl/VqLat/MaB33pZy0y3rJZtnqwR2qOOvbwKZYKiEO1O6VqNEBxKvJJelCq0dTXWT5pbO2gDXC6h6QDXCaHo6pOHGPUy+YBaGQRGuSusMEASYiWunYN0vCAI8QaXnWMXNMdFP3jHAJH0eDsoiGnLPBlBp4TNm6rYI74nMzgz3B9IikW4WVK+dc8KZJZWYjAuORU3jc1c/NPskD2ASinf8v3xnfXeukU0sJ5N6m5E8VLjObPEO+mN2t/FZTMZLiFqPWc/ALSqnMnnhwrNi2rbfg/rd/IpL8Le3pSBne8+seeFVBoGqzHM9yXw== + +# github.com:22 SSH-2.0-libssh-0.7.0 +github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ== + +# gitlab.com:22 SSH-2.0-OpenSSH_7.2p2 Ubuntu-4ubuntu2.2 +gitlab.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCsj2bNKTBSpIYDEGk9KxsGh3mySTRgMtXL583qmBpzeQ+jqCMRgBqB98u3z++J1sKlXHWfM9dyhSevkMwSbhoR8XIq/U0tCNyokEi/ueaBMCvbcTHhO7FcwzY92WK4Yt0aGROY5qX2UKSeOvuP4D6TPqKF1onrSzH9bx9XUf2lEdWT/ia1NEKjunUqu1xOB/StKDHMoX4/OKyIzuS0q/T1zOATthvasJFoPrAjkohTyaDUz2LN5JoH839hViyEG82yB+MjcFV5MU3N1l1QL3cVUCh93xSaua1N85qivl+siMkPGbO5xR/En4iEY6K2XPASUEMaieWVNTRCtJ4S8H+9 +# gitlab.com:22 SSH-2.0-OpenSSH_7.2p2 Ubuntu-4ubuntu2.2 +gitlab.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFSMqzJeV9rUzU4kWitGjeR4PWSa29SPqJ1fVkhtj3Hw9xjLVXVYrU9QlYWrOLXBpQ6KWjbjTDTdDkoohFzgbEY= +# gitlab.com:22 SSH-2.0-OpenSSH_7.2p2 Ubuntu-4ubuntu2.2 +gitlab.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAfuCHKVTjquxvt6CM6tdG4SLp1Btn/nOeHHE5UOzRdf diff --git a/lib/doodbalib/__init__.py b/lib/doodbalib/__init__.py new file mode 100644 index 0000000..b0e035b --- /dev/null +++ b/lib/doodbalib/__init__.py @@ -0,0 +1,173 @@ +# -*- coding: utf-8 -*- +import logging +import os +from glob import glob +from pprint import pformat +from subprocess import check_output + +import yaml + +# Constants needed in scripts +CUSTOM_DIR = "/app/code/custom" +AUTO_DIR = "/app/code/auto" +ADDONS_DIR = os.path.join(AUTO_DIR, "addons") +SRC_DIR = os.path.join(CUSTOM_DIR, "src") + +ADDONS_YAML = os.path.join(SRC_DIR, "addons") +if os.path.isfile("%s.yaml" % ADDONS_YAML): + ADDONS_YAML = "%s.yaml" % ADDONS_YAML +else: + ADDONS_YAML = "%s.yml" % ADDONS_YAML + +REPOS_YAML = os.path.join(SRC_DIR, "repos") +if os.path.isfile("%s.yaml" % REPOS_YAML): + REPOS_YAML = "%s.yaml" % REPOS_YAML +else: + REPOS_YAML = "%s.yml" % REPOS_YAML + +AUTO_REPOS_YAML = os.path.join(AUTO_DIR, "repos") +if os.path.isfile("%s.yml" % AUTO_REPOS_YAML): + AUTO_REPOS_YAML = "%s.yml" % AUTO_REPOS_YAML +else: + AUTO_REPOS_YAML = "%s.yaml" % AUTO_REPOS_YAML + +CLEAN = os.environ.get("CLEAN") == "true" +LOG_LEVELS = frozenset({"DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"}) +FILE_APT_BUILD = os.path.join(CUSTOM_DIR, "dependencies", "apt_build.txt") +PRIVATE = "private" +CORE = "odoo/addons" +ENTERPRISE = "enterprise" +PRIVATE_DIR = os.path.join(SRC_DIR, PRIVATE) +CORE_DIR = os.path.join(SRC_DIR, CORE) +ODOO_DIR = os.path.join(SRC_DIR, "odoo") +ODOO_VERSION = os.environ["ODOO_VERSION"] +MANIFESTS = ("__manifest__.py", "__openerp__.py") + +# Customize logging for build +logger = logging.getLogger("doodba") +log_handler = logging.StreamHandler() +log_formatter = logging.Formatter("%(name)s %(levelname)s: %(message)s") +log_handler.setFormatter(log_formatter) +logger.addHandler(log_handler) +_log_level = os.environ.get("LOG_LEVEL", "") +if _log_level.isdigit(): + _log_level = int(_log_level) +elif _log_level in LOG_LEVELS: + _log_level = getattr(logging, _log_level) +else: + if _log_level: + logger.warning("Wrong value in $LOG_LEVEL, falling back to INFO") + _log_level = logging.INFO +logger.setLevel(_log_level) + + +class AddonsConfigError(Exception): + def __init__(self, message, *args): + super(AddonsConfigError, self).__init__(message, *args) + self.message = message + + +def addons_config(filtered=True, strict=False): + """Yield addon name and path from ``ADDONS_YAML``. + + :param bool filtered: + Use ``False`` to include all addon definitions. Use ``True`` (default) + to include only those matched by ``ONLY`` clauses, if any. + + :param bool strict: + Use ``True`` to raise an exception if any declared addon is not found. + + :return Iterator[str, str]: + A generator that yields ``(addon, repo)`` pairs. + """ + config = dict() + missing_glob = set() + missing_manifest = set() + all_globs = {} + try: + with open(ADDONS_YAML) as addons_file: + for doc in yaml.safe_load_all(addons_file): + # Skip sections with ONLY and that don't match + only = doc.pop("ONLY", {}) + if not filtered: + doc.setdefault(CORE, ["*"]) + doc.setdefault(PRIVATE, ["*"]) + elif any( + os.environ.get(key) not in values for key, values in only.items() + ): + logger.debug("Skipping section with ONLY %s", only) + continue + # Flatten all sections in a single dict + for repo, partial_globs in doc.items(): + if repo == "ENV": + continue + logger.debug("Processing %s repo", repo) + all_globs.setdefault(repo, set()) + all_globs[repo].update(partial_globs) + except IOError: + logger.debug("Could not find addons configuration yaml.") + # Add default values for special sections + for repo in (CORE, PRIVATE): + all_globs.setdefault(repo, {"*"}) + logger.debug("Merged addons definition before expanding: %r", all_globs) + # Expand all globs and store config + for repo, partial_globs in all_globs.items(): + for partial_glob in partial_globs: + logger.debug("Expanding in repo %s glob %s", repo, partial_glob) + full_glob = os.path.join(SRC_DIR, repo, partial_glob) + found = glob(full_glob) + if not found: + # Projects without private addons should never fail + if (repo, partial_glob) != (PRIVATE, "*"): + missing_glob.add(full_glob) + logger.debug("Skipping unexpandable glob '%s'", full_glob) + continue + for addon in found: + if not os.path.isdir(addon): + continue + manifests = (os.path.join(addon, m) for m in MANIFESTS) + if not any(os.path.isfile(m) for m in manifests): + missing_manifest.add(addon) + logger.debug( + "Skipping '%s' as it is not a valid Odoo " "module", addon + ) + continue + logger.debug("Registering addon %s", addon) + addon = os.path.basename(addon) + config.setdefault(addon, set()) + config[addon].add(repo) + # Fail now if running in strict mode + if strict: + error = [] + if missing_glob: + error += ["Addons not found:", pformat(missing_glob)] + if missing_manifest: + error += ["Addons without manifest:", pformat(missing_manifest)] + if error: + raise AddonsConfigError("\n".join(error), missing_glob, missing_manifest) + logger.debug("Resulting configuration after expanding: %r", config) + for addon, repos in config.items(): + # Private addons are most important + if PRIVATE in repos: + yield addon, PRIVATE + continue + # Odoo core addons are least important + if repos == {CORE}: + yield addon, CORE + continue + repos.discard(CORE) + # Other addons fall in between + if filtered and len(repos) != 1: + raise AddonsConfigError( + u"Addon {} defined in several repos {}".format(addon, repos) + ) + for repo in repos: + yield addon, repo + + +try: + from shutil import which +except ImportError: + # Custom which implementation for Python 2 + def which(binary): + return check_output(["which", binary]).strip() diff --git a/lib/doodbalib/installer.py b/lib/doodbalib/installer.py new file mode 100644 index 0000000..2f926cc --- /dev/null +++ b/lib/doodbalib/installer.py @@ -0,0 +1,119 @@ +# -*- coding: utf-8 -*- +from collections import OrderedDict +from os.path import exists +from subprocess import check_call + +from doodbalib import logger + + +class Installer(object): + """Base class to install packages with some package system.""" + + _cleanup_commands = [] + _install_command = None + _remove_command = None + + def __init__(self, file_path): + self.file_path = file_path + self._requirements = self.requirements() + + def _run_command(self, command): + logger.info("Executing: %s", command) + return check_call(command, shell=isinstance(command, str)) + + def cleanup(self): + """Remove cache and other garbage produced by the installer engine.""" + for command in self._cleanup_commands: + self._run_command(command) + + def install(self): + """Install the requirements from the given file.""" + if self._requirements: + return not self._run_command(self._install_command + self._requirements) + else: + logger.info("No installable requirements found in %s", self.file_path) + return False + + def remove(self): + """Uninstall the requirements from the given file.""" + if not self._remove_command: + return + if self._requirements: + self._run_command(self._remove_command + self._requirements) + else: + logger.info("No removable requirements found in %s", self.file_path) + + def requirements(self): + """Get a list of requirements from the given file.""" + requirements = [] + try: + with open(self.file_path, "r") as fh: + for line in fh: + line = line.strip() + if not line or line.startswith("#"): + continue + requirements += line.split() + except IOError: + # No requirements file + pass + return requirements + + +class AptInstaller(Installer): + _cleanup_commands = [["apt-get", "-y", "autoremove"], "rm -Rf /var/lib/apt/lists/*"] + _install_command = [ + "apt-get", + "-o", + "Dpkg::Options::=--force-confdef", + "-o", + "Dpkg::Options::=--force-confold", + "-y", + "--no-install-recommends", + "install", + ] + _remove_command = ["apt-get", "purge", "-y"] + + def _dirty(self): + return exists("/var/lib/apt/lists/lock") + + def cleanup(self): + if self._dirty(): + super(AptInstaller, self).cleanup() + + def install(self): + if not self._dirty() and self._requirements: + self._run_command(["apt-get", "update"]) + return super(AptInstaller, self).install() + + +class GemInstaller(Installer): + _cleanup_commands = ["rm -Rf ~/.gem /var/lib/gems/*/cache/"] + _install_command = ["gem", "install", "--no-document", "--no-update-sources"] + + +class NpmInstaller(Installer): + _cleanup_commands = ["rm -Rf ~/.npm /tmp/*"] + _install_command = ["npm", "install", "-g"] + + +class PipInstaller(Installer): + _install_command = ["pip", "install", "--no-cache-dir", "-r"] + + def requirements(self): + """Pip will use its ``--requirements`` feature.""" + return [self.file_path] if exists(self.file_path) else [] + + +INSTALLERS = OrderedDict( + [ + ("apt", AptInstaller), + ("gem", GemInstaller), + ("npm", NpmInstaller), + ("pip", PipInstaller), + ] +) + + +def install(installer, file_path): + """Perform a given type of installation from a given file.""" + return INSTALLERS[installer](file_path).install() diff --git a/start.sh b/start.sh index 363a41b..10b06aa 100755 --- a/start.sh +++ b/start.sh @@ -18,15 +18,16 @@ chown -R cloudron:cloudron /run # Check for First Run if [[ ! -f /app/data/odoo.conf ]]; then + echo "First run. Initializing DB..." # Initialize the database, and exit. - /usr/local/bin/gosu cloudron:cloudron /app/code/odoo-bin -i base,auth_ldap,fetchmail --without-demo all --data-dir /app/data/odoo --logfile /run/odoo/runtime.log -d $CLOUDRON_POSTGRESQL_DATABASE --db_host $CLOUDRON_POSTGRESQL_HOST --db_port $CLOUDRON_POSTGRESQL_PORT --db_user $CLOUDRON_POSTGRESQL_USERNAME --db_pass $CLOUDRON_POSTGRESQL_PASSWORD --stop-after-init + /usr/local/bin/gosu cloudron:cloudron /app/code/odoo/odoo-bin -i base,auth_ldap,fetchmail --without-demo all --data-dir /app/data/odoo --logfile /run/odoo/runtime.log -d $CLOUDRON_POSTGRESQL_DATABASE --db_host $CLOUDRON_POSTGRESQL_HOST --db_port $CLOUDRON_POSTGRESQL_PORT --db_user $CLOUDRON_POSTGRESQL_USERNAME --db_pass $CLOUDRON_POSTGRESQL_PASSWORD --stop-after-init echo "Initialized successfully." - echo "Adding required tables/relations for mail settings." - pg_cli "INSERT INTO public.res_config_settings (create_uid, create_date, write_uid, write_date, company_id, user_default_rights, external_email_server_default, module_base_import, module_google_calendar, module_microsoft_calendar, module_mail_plugin, module_google_drive, module_google_spreadsheet, module_auth_oauth, module_auth_ldap, module_base_gengo, module_account_inter_company_rules, module_pad, module_voip, module_web_unsplash, module_partner_autocomplete, module_base_geolocalize, module_google_recaptcha, group_multi_currency, show_effect, profiling_enabled_until, module_product_images, unsplash_access_key, fail_counter, alias_domain, restrict_template_rendering, use_twilio_rtc_servers, twilio_account_sid, twilio_account_token, auth_signup_reset_password, auth_signup_uninvited, auth_signup_template_user_id) VALUES (2, 'NOW()', 2, 'NOW()', 1, false, true, true, false, false, false, false, false, false, true, false, false, false, false, true, true, false, false, false, true, NULL, false, NULL, 0, '$CLOUDRON_APP_DOMAIN', false, false, NULL, NULL, false, 'b2b', 5) ON CONFLICT (id) DO NOTHING;" + # echo "Adding required tables/relations for mail settings." + # pg_cli "INSERT INTO public.res_config_settings (create_uid, create_date, write_uid, write_date, company_id, user_default_rights, external_email_server_default, module_base_import, module_google_calendar, module_microsoft_calendar, module_google_drive, module_google_spreadsheet, module_auth_oauth, module_auth_ldap, module_base_gengo, module_account_inter_company_rules, module_pad, module_voip, module_web_unsplash, module_partner_autocomplete, module_base_geolocalize, module_google_recaptcha, group_multi_currency, show_effect, module_product_images, unsplash_access_key, fail_counter, alias_domain, restrict_template_rendering, use_twilio_rtc_servers, twilio_account_sid, twilio_account_token, auth_signup_reset_password, auth_signup_uninvited, auth_signup_template_user_id) VALUES (2, 'NOW()', 2, 'NOW()', 1, false, true, true, false, false, false, false, false, true, false, false, false, false, true, true, false, false, false, true, false, NULL, 0, '$CLOUDRON_APP_DOMAIN', false, false, NULL, NULL, false, 'b2b', 5) ON CONFLICT (id) DO NOTHING;" pg_cli "INSERT INTO public.ir_config_parameter (key, value, create_uid, create_date, write_uid, write_date) VALUES ('base_setup.default_external_email_server', 'True', 2, 'NOW()', 2, 'NOW()');" pg_cli "INSERT INTO public.ir_config_parameter (key, value, create_uid, create_date, write_uid, write_date) VALUES ('mail.catchall.domain', '$CLOUDRON_APP_DOMAIN', 2, 'NOW()', 2, 'NOW()');" @@ -36,7 +37,9 @@ if [[ ! -f /app/data/odoo.conf ]]; then echo "Copying default configuration file to /app/data/odoo.conf..." cp /app/pkg/odoo.conf.sample /app/data/odoo.conf - + + crudini --set /app/data/odoo.conf 'options' list_db "False" + crudini --set /app/data/odoo.conf 'options' admin_password "$CLOUDRON_MAIL_SMTP_PASSWORD" echo "First run complete." fi @@ -44,7 +47,7 @@ fi echo "Ensuring proper [options] in /app/data/odoo.conf ..." # Custom paths -crudini --set /app/data/odoo.conf 'options' addons_path "/app/code/addons,/app/data/extra-addons" +crudini --set /app/data/odoo.conf 'options' addons_path "/app/code/auto/addons,/app/data/extra-addons,/app/code/odoo/addons" crudini --set /app/data/odoo.conf 'options' data_dir "/app/data/odoo" # Logging @@ -61,7 +64,6 @@ crudini --set /app/data/odoo.conf 'options' port '8069' crudini --set /app/data/odoo.conf 'options' longpolling_port '8072' # Securing Odoo -crudini --set /app/data/odoo.conf 'options' list_db "False" crudini --set /app/data/odoo.conf 'options' test_enable "False" crudini --set /app/data/odoo.conf 'options' test_file "False" crudini --set /app/data/odoo.conf 'options' test_report_directory "False" @@ -93,7 +95,7 @@ if [[ -z "${CLOUDRON_MAIL_SMTP_SERVER+x}" ]]; then pg_cli "UPDATE public.ir_mail_server SET active='f' WHERE name LIKE 'Cloudron%';" else echo "SMTP is enabled. Adding values to config." - pg_cli "INSERT INTO public.ir_mail_server (id, name, from_filter, smtp_host, smtp_port, smtp_authentication, smtp_user, smtp_pass, smtp_encryption, smtp_ssl_certificate, smtp_ssl_private_key, smtp_debug, sequence, active, create_uid, create_date, write_uid, write_date) VALUES (1, 'Cloudron SMTP Service', NULL, '$CLOUDRON_MAIL_SMTP_SERVER', $CLOUDRON_MAIL_SMTP_PORT, 'login', '$CLOUDRON_MAIL_SMTP_USERNAME', '$CLOUDRON_MAIL_SMTP_PASSWORD', 'none', NULL, NULL, false, 10, true, 2, 'NOW()', 2, 'NOW()') ON CONFLICT (id) DO NOTHING;" + pg_cli "INSERT INTO public.ir_mail_server (id, name, smtp_host, smtp_port, smtp_user, smtp_pass, smtp_encryption, smtp_debug, sequence, active, create_uid, create_date, write_uid, write_date) VALUES (1, 'Cloudron SMTP Service', '$CLOUDRON_MAIL_SMTP_SERVER', $CLOUDRON_MAIL_SMTP_PORT, '$CLOUDRON_MAIL_SMTP_USERNAME', '$CLOUDRON_MAIL_SMTP_PASSWORD', 'none', false, 10, true, 2, 'NOW()', 2, 'NOW()') ON CONFLICT (id) DO NOTHING;" fi # LDAP Configuration @@ -147,4 +149,4 @@ echo "Done. Starting server with $worker_count workers.." chown -R cloudron:cloudron /app/data/ -/usr/local/bin/gosu cloudron:cloudron /app/code/odoo-bin -c /app/data/odoo.conf +/usr/local/bin/gosu cloudron:cloudron /app/code/odoo/odoo-bin -c /app/data/odoo.conf