diff --git a/setup.cfg b/setup.cfg index 5b0fa922022..ffccdc094ac 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,3 +1,6 @@ +[install] +optimize=1 + [bdist_rpm] no-autoreq = yes install-script = setup/redhat/install.sh @@ -48,3 +51,4 @@ requires = python3-werkzeug python3-xlwt python3-xlrd + diff --git a/setup/package.dfdebian b/setup/package.dfdebian index a57981c11ab..433ea576714 100644 --- a/setup/package.dfdebian +++ b/setup/package.dfdebian @@ -1,6 +1,6 @@ # Please note that this Dockerfile is used for testing nightly builds and should # not be used to deploy Odoo -FROM debian:stretch +FROM debian:buster MAINTAINER Odoo S.A. RUN apt-get update && \ @@ -12,11 +12,14 @@ RUN dpkg-reconfigure locales && \ locale-gen C.UTF-8 && \ /usr/sbin/update-locale LANG=C.UTF-8 ENV LC_ALL C.UTF-8 +ENV DEBIAN_FRONTEND noninteractive RUN apt-get update -qq && \ apt-get upgrade -qq -y && \ apt-get install -qq -y\ adduser \ + dh-python \ + packaging-dev \ postgresql \ postgresql-client \ python3 \ @@ -51,7 +54,8 @@ RUN apt-get update -qq && \ python3-vatnumber \ python3-vobject \ python3-werkzeug \ - python3-xlsxwriter && \ + python3-xlsxwriter \ + rsync && \ rm -rf /var/lib/apt/lists/* RUN echo "PS1=\"[\u@nightly-tests] # \"" > ~/.bashrc diff --git a/setup/package.dffedora b/setup/package.dffedora index b455b49db3c..6f745e1b533 100644 --- a/setup/package.dffedora +++ b/setup/package.dffedora @@ -1,6 +1,6 @@ # Please note that this Dockerfile is used for testing nightly builds and should # not be used to deploy Odoo -FROM fedora:26 +FROM fedora:30 MAINTAINER Odoo S.A. # Dependencies and postgres @@ -45,17 +45,17 @@ RUN dnf update -d 0 -e 0 -y && \ python3-werkzeug \ python3-xlwt \ python3-xlrd \ + python3-xlsxwriter \ libsass \ pytz \ postgresql \ postgresql-server \ postgresql-libs \ postgresql-contrib \ - postgresql-devel -y && \ + postgresql-devel \ + rpmdevtools -y && \ dnf clean all -RUN pip3 install XlsxWriter - # Postgres configuration RUN mkdir -p /var/lib/postgres/data RUN chown -R postgres:postgres /var/lib/postgres/data diff --git a/setup/package.dfsrc b/setup/package.dfsrc index 5b106923047..e37ffcc9e29 100644 --- a/setup/package.dfsrc +++ b/setup/package.dfsrc @@ -1,6 +1,6 @@ # Please note that this Dockerfile is used for testing nightly builds and should # not be used to deploy Odoo -FROM debian:stretch +FROM debian:buster MAINTAINER Odoo S.A. RUN apt-get update && \ @@ -20,7 +20,7 @@ RUN apt-get update -qq && \ postgresql-server-dev-all \ postgresql-client \ adduser \ - libsass0 \ + libsass1 \ libxml2-dev \ libxslt1-dev \ libldap2-dev \ diff --git a/setup/package.py b/setup/package.py index 36029881bdd..9bdaf299126 100755 --- a/setup/package.py +++ b/setup/package.py @@ -1,89 +1,61 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from __future__ import print_function + +import argparse import logging -import optparse import os import pexpect import shutil import signal import subprocess +import sys import tempfile import time import traceback -try: - from xmlrpc import client as xmlrpclib -except ImportError: - import xmlrpclib -from contextlib import contextmanager -from glob import glob -from os.path import abspath, dirname, join -from sys import stdout, stderr -from tempfile import NamedTemporaryFile +from xmlrpc import client as xmlrpclib -# apt-get install rsync python-pexpect debhelper python-setuptools +from glob import glob #---------------------------------------------------------- # Utils #---------------------------------------------------------- -exec(open(join(dirname(__file__), '..', 'odoo', 'release.py'), 'rb').read()) -version = version.split('-')[0].replace('saas~','') -docker_version = version.replace('+', '') -timestamp = time.strftime("%Y%m%d", time.gmtime()) + +ROOTDIR = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) +TSTAMP = time.strftime("%Y%m%d", time.gmtime()) +exec(open(os.path.join(ROOTDIR, 'odoo', 'release.py'), 'rb').read()) +VERSION = version.split('-')[0].replace('saas~', '') GPGPASSPHRASE = os.getenv('GPGPASSPHRASE') GPGID = os.getenv('GPGID') -PUBLISH_DIRS = { - 'debian': 'deb', - 'redhat': 'rpm', - 'tarball': 'src', - 'windows': 'exe', -} -ADDONS_NOT_TO_PUBLISH = [ -] +DOCKERVERSION = VERSION.replace('+', '') +INSTALL_TIMEOUT = 600 + +DOCKERUSER = """ +RUN mkdir /var/lib/odoo && \ + groupadd -g %(group_id)s odoo && \ + useradd -u %(user_id)s -g odoo odoo -d /var/lib/odoo && \ + mkdir /data && \ + chown odoo:odoo /var/lib/odoo /data +USER odoo +""" % {'group_id': os.getgid(), 'user_id': os.getuid()} -def move_glob(source, wildcards, destination): - """Move files matched by wildcards from source to destination - wildcards can be a single string wildcard like '*.deb' or a list of wildcards - """ - if not os.path.isdir(destination): - raise BaseException('Destination "{}" is not a directory'.format(destination)) - if isinstance(wildcards, str): - wildcards = [wildcards] - for wc in wildcards: - for file_path in glob(os.path.join(source, wc)): - shutil.move(file_path, destination) +def run_cmd(cmd, chdir=None, timeout=None): + logging.info("Running command %s", cmd) + return subprocess.run(cmd, cwd=chdir, timeout=timeout) -def mkdir(d): - if not os.path.isdir(d): - os.makedirs(d) - -def system(l, chdir=None): - logging.info("System call: {}".format(l)) - if chdir: - cwd = os.getcwd() - os.chdir(chdir) - if isinstance(l, list): - rc = os.spawnvp(os.P_WAIT, l[0], l) - elif isinstance(l, str): - tmp = ['sh', '-c', l] - rc = os.spawnvp(os.P_WAIT, tmp[0], tmp) - if chdir: - os.chdir(cwd) - return rc def _rpc_count_modules(addr='http://127.0.0.1', port=8069, dbname='mycompany'): time.sleep(5) - uid = xmlrpclib.ServerProxy('%s:%s/xmlrpc/common' % (addr, port)).authenticate( + uid = xmlrpclib.ServerProxy('%s:%s/xmlrpc/2/common' % (addr, port)).authenticate( dbname, 'admin', 'admin', {} ) - modules = xmlrpclib.ServerProxy('%s:%s/xmlrpc/object' % (addr, port)).execute( + modules = xmlrpclib.ServerProxy('%s:%s/xmlrpc/2/object' % (addr, port)).execute( dbname, uid, 'admin', 'ir.module.module', 'search', [('state', '=', 'installed')] ) - if modules and len(modules) > 1: + if len(modules) > 1: time.sleep(1) - toinstallmodules = xmlrpclib.ServerProxy('%s:%s/xmlrpc/object' % (addr, port)).execute( + toinstallmodules = xmlrpclib.ServerProxy('%s:%s/xmlrpc/2/object' % (addr, port)).execute( dbname, uid, 'admin', 'ir.module.module', 'search', [('state', '=', 'to install')] ) if toinstallmodules: @@ -93,22 +65,26 @@ def _rpc_count_modules(addr='http://127.0.0.1', port=8069, dbname='mycompany'): logging.info("Package test: successfuly installed %s modules" % len(modules)) else: logging.error("Package test: FAILED. Not able to install base.") - raise Exception("Installation of package failed") + raise Exception("Package test: FAILED. Not able to install base.") -def publish(o, type, extensions): - def _publish(o, release): - arch = '' + +def publish(args, pub_type, extensions): + """Publish builded package (move builded files and generate a symlink to the latests) + :args: parsed program args + :pub_type: one of [deb, rpm, src, exe] + :extensions: list of extensions to publish + :returns: published files + """ + def _publish(release): + build_path = os.path.join(args.build_dir, release) filename = release.split(os.path.sep)[-1] - - release_dir = PUBLISH_DIRS[type] - release_path = join(o.pub, release_dir, filename) - - system('mkdir -p %s' % join(o.pub, release_dir)) - shutil.move(join(o.build_dir, release), release_path) + release_dir = os.path.join(args.pub, pub_type) + release_path = os.path.join(release_dir, filename) + os.renames(build_path, release_path) # Latest/symlink handler - release_abspath = abspath(release_path) - latest_abspath = release_abspath.replace(timestamp, 'latest') + release_abspath = os.path.abspath(release_path) + latest_abspath = release_abspath.replace(TSTAMP, 'latest') if os.path.islink(latest_abspath): os.unlink(latest_abspath) @@ -119,282 +95,28 @@ def publish(o, type, extensions): published = [] for extension in extensions: - release = glob("%s/odoo_*.%s" % (o.build_dir, extension)) + release = glob("%s/odoo_*.%s" % (args.build_dir, extension)) if release: - published.append(_publish(o, release[0])) + published.append(_publish(release[0])) return published -class OdooDocker(object): - def __init__(self): - self.log_file = NamedTemporaryFile(mode='w+b', prefix="bash", suffix=".txt", delete=False) - self.port = 8069 # TODO sle: reliable way to get a free port? - self.prompt_re = '[root@nightly-tests] # ' - self.timeout = 600 - def system(self, command): - self.docker.sendline(command) - self.docker.expect_exact(self.prompt_re) - - def start(self, docker_image, build_dir, pub_dir): - self.build_dir = build_dir - self.pub_dir = pub_dir - - self.docker = pexpect.spawn( - 'docker run -v %s:/opt/release -p 127.0.0.1:%s:8069' - ' -t -i %s /bin/bash --noediting' % (self.build_dir, self.port, docker_image), - timeout=self.timeout, - searchwindowsize=len(self.prompt_re) + 1, - ) - time.sleep(2) # let the bash start - self.docker.logfile_read = self.log_file - self.id = subprocess.check_output('docker ps -l -q', shell=True).strip().decode('ascii') - - def end(self): - try: - _rpc_count_modules(port=str(self.port)) - except Exception as e: - logging.error('Exception during docker execution: %s:' % str(e)) - logging.error('Error during docker execution: printing the bash output:') - with open(self.log_file.name) as f: - print('\n'.join(f.readlines()), file=stderr) - raise - finally: - self.docker.close() - system('docker rm -f %s' % self.id) - self.log_file.close() - os.remove(self.log_file.name) - -@contextmanager -def docker(docker_image, build_dir, pub_dir): - _docker = OdooDocker() - try: - _docker.start(docker_image, build_dir, pub_dir) - try: - yield _docker - except Exception: - raise - finally: - _docker.end() - -class KVM(object): - def __init__(self, o, image, ssh_key='', login='openerp'): - self.o = o - self.image = image - self.ssh_key = ssh_key - self.login = login - - def timeout(self,signum,frame): - logging.warning("vm timeout kill (pid: {})".format(self.pid)) - os.kill(self.pid,15) - - def start(self): - l="kvm -cpu core2duo -smp 2,sockets=2,cores=1,threads=1 -net nic,model=rtl8139 -net user,hostfwd=tcp:127.0.0.1:10022-:22,hostfwd=tcp:127.0.0.1:18069-:8069,hostfwd=tcp:127.0.0.1:15432-:5432 -m 1024 -drive".split(" ") - #l.append('file=%s,if=virtio,index=0,boot=on,snapshot=on'%self.image) - l.append('file=%s,snapshot=on'%self.image) - #l.extend(['-vnc','127.0.0.1:1']) - l.append('-nographic') - logging.info("Starting kvm: {}".format( " ".join(l))) - self.pid=os.spawnvp(os.P_NOWAIT, l[0], l) - time.sleep(50) - signal.alarm(2400) - signal.signal(signal.SIGALRM, self.timeout) - try: - self.run() - finally: - signal.signal(signal.SIGALRM, signal.SIG_DFL) - os.kill(self.pid,15) - time.sleep(10) - - def ssh(self,cmd): - l=['ssh','-o','UserKnownHostsFile=/dev/null','-o','StrictHostKeyChecking=no','-p','10022','-i',self.ssh_key,'%s@127.0.0.1'%self.login,cmd] - system(l) - - def rsync(self,args,options='--delete --exclude .git --exclude .tx --exclude __pycache__'): - cmd ='rsync -rt -e "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -p 10022 -i %s" %s %s' % (self.ssh_key, options, args) - system(cmd) - - def run(self): - pass - -class KVMWinBuildExe(KVM): - def run(self): - with open(join(self.o.build_dir, 'setup/win32/Makefile.version'), 'w') as f: - f.write("VERSION=%s\n" % version.replace('~', '_').replace('+', '')) - with open(join(self.o.build_dir, 'setup/win32/Makefile.python'), 'w') as f: - f.write("PYTHON_VERSION=%s\n" % self.o.vm_winxp_python_version.replace('.', '')) - with open(join(self.o.build_dir, 'setup/win32/Makefile.servicename'), 'w') as f: - f.write("SERVICENAME=%s\n" % nt_service_name) - - remote_build_dir = '/cygdrive/c/odoobuild/server/' - - self.ssh("mkdir -p build") - logging.info("Syncing Odoo files to virtual machine...") - self.rsync('%s/ %s@127.0.0.1:%s' % (self.o.build_dir, self.login, remote_build_dir)) - self.ssh("cd {}setup/win32;time make allinone;".format(remote_build_dir)) - self.rsync('%s@127.0.0.1:%ssetup/win32/release/ %s/' % (self.login, remote_build_dir, self.o.build_dir), '') - logging.info("KVMWinBuildExe.run(): done") - -class KVMWinTestExe(KVM): - def run(self): - setuppath = glob("%s/openerp-server-setup-*.exe" % self.o.build_dir)[0] - setupfile = setuppath.split('/')[-1] - setupversion = setupfile.split('openerp-server-setup-')[1].split('.exe')[0] - - self.rsync('"%s" %s@127.0.0.1:' % (setuppath, self.login)) - self.ssh("TEMP=/tmp ./%s /S" % setupfile) - self.ssh('PGPASSWORD=openpgpwd /cygdrive/c/"Program Files"/"Odoo %s"/PostgreSQL/bin/createdb.exe -e -U openpg mycompany' % setupversion) - self.ssh('netsh advfirewall set publicprofile state off') - self.ssh('/cygdrive/c/"Program Files"/"Odoo {sv}"/python/python.exe \'c:\\Program Files\\Odoo {sv}\\server\\odoo-bin\' -d mycompany -i base --stop-after-init'.format(sv=setupversion)) - _rpc_count_modules(port=18069) - -#---------------------------------------------------------- -# Stage: building -#---------------------------------------------------------- -def _prepare_build_dir(o, win32=False): - - cmd = ['rsync', '-a', '--exclude', '.git', '--exclude', '*.pyc', '--exclude', '*.pyo'] - if not win32: - cmd += ['--exclude', 'setup/win32'] - system(cmd + ['%s/' % o.odoo_dir, o.build_dir]) - for addon_path in glob(join(o.build_dir, 'addons/*')): - if addon_path.split(os.path.sep)[-1] not in ADDONS_NOT_TO_PUBLISH: - try: - shutil.move(addon_path, join(o.build_dir, 'odoo/addons')) - except shutil.Error as e: - # Thrown when the add-on is already in odoo/addons (if _prepare_build_dir - # has already been called once) - logging.warning("Warning '{}' while moving addon '{}'".format(e,addon_path)) - if addon_path.startswith(o.build_dir) and os.path.isdir(addon_path): - logging.info("Removing '{}'".format(addon_path)) - try: - shutil.rmtree(addon_path) - except shutil.Error as rm_error: - logging.warning("Cannot remove '{}': {}".format(addon_path, rm_error)) - -def build_tgz(o): - system(['python3', 'setup.py', 'sdist', '--quiet', '--formats=gztar,zip'], o.build_dir) - system(['mv', glob('%s/dist/odoo-*.tar.gz' % o.build_dir)[0], '%s/odoo_%s.%s.tar.gz' % (o.build_dir, version, timestamp)]) - system(['mv', glob('%s/dist/odoo-*.zip' % o.build_dir)[0], '%s/odoo_%s.%s.zip' % (o.build_dir, version, timestamp)]) - -def build_deb(o): - # Append timestamp to version for the .dsc to refer the right .tar.gz - cmd=['sed', '-i', '1s/^.*$/odoo (%s.%s) stable; urgency=low/'%(version,timestamp), 'debian/changelog'] - subprocess.call(cmd, cwd=o.build_dir) - if not o.no_debsign: - deb = pexpect.spawn('dpkg-buildpackage -rfakeroot -k%s' % GPGID, cwd=o.build_dir) - deb.logfile = stdout.buffer - if GPGPASSPHRASE: - deb.expect_exact('Enter passphrase: ', timeout=1200) - deb.send(GPGPASSPHRASE + '\r\n') - deb.expect_exact('Enter passphrase: ') - deb.send(GPGPASSPHRASE + '\r\n') - deb.expect(pexpect.EOF, timeout=1200) - else: - subprocess.call(['dpkg-buildpackage', '-rfakeroot', '-uc', '-us'], cwd=o.build_dir) - # As the packages are built in the parent of the buildir, we move them back to build_dir - build_dir_parent = '{}/../'.format(o.build_dir) - wildcards = ['odoo_{}'.format(wc) for wc in ('*.deb', '*.dsc', '*_amd64.changes', '*.tar.gz', '*.tar.xz')] - move_glob(build_dir_parent, wildcards, o.build_dir) - -def build_rpm(o): - system(['python3', 'setup.py', '--quiet', 'bdist_rpm'], o.build_dir) - system(['mv', glob('%s/dist/odoo-*.noarch.rpm' % o.build_dir)[0], '%s/odoo_%s.%s.noarch.rpm' % (o.build_dir, version, timestamp)]) - -def build_exe(o): - KVMWinBuildExe(o, o.vm_winxp_image, o.vm_winxp_ssh_key, o.vm_winxp_login).start() - system(['cp', glob('%s/openerp*.exe' % o.build_dir)[0], '%s/odoo_%s.%s.exe' % (o.build_dir, version, timestamp)]) - -#---------------------------------------------------------- -# Stage: testing -#---------------------------------------------------------- -def _prepare_testing(o): - logging.info('Preparing testing') - if not o.no_tarball: - logging.info('Preparing docker container instance for tarball') - subprocess.call(["mkdir", "docker_src"], cwd=o.build_dir) - subprocess.call(["cp", "package.dfsrc", os.path.join(o.build_dir, "docker_src", "Dockerfile")], - cwd=os.path.join(o.odoo_dir, "setup")) - # Use rsync to copy requirements.txt in order to keep original permissions - subprocess.call(["rsync", "-a", "requirements.txt", os.path.join(o.build_dir, "docker_src")], - cwd=os.path.join(o.odoo_dir)) - subprocess.call(["docker", "build", "-t", "odoo-%s-src-nightly-tests" % docker_version, "."], - cwd=os.path.join(o.build_dir, "docker_src")) - if not o.no_debian: - logging.info('Preparing docker container instance for debian') - subprocess.call(["mkdir", "docker_debian"], cwd=o.build_dir) - subprocess.call(["cp", "package.dfdebian", os.path.join(o.build_dir, "docker_debian", "Dockerfile")], - cwd=os.path.join(o.odoo_dir, "setup")) - # Use rsync to copy requirements.txt in order to keep original permissions - subprocess.call(["rsync", "-a", "requirements.txt", os.path.join(o.build_dir, "docker_debian")], - cwd=os.path.join(o.odoo_dir)) - subprocess.call(["docker", "build", "-t", "odoo-%s-debian-nightly-tests" % docker_version, "."], - cwd=os.path.join(o.build_dir, "docker_debian")) - if not o.no_rpm: - logging.info('Preparing docker container instance for RPM') - subprocess.call(["mkdir", "docker_fedora"], cwd=o.build_dir) - subprocess.call(["cp", "package.dffedora", os.path.join(o.build_dir, "docker_fedora", "Dockerfile")], - cwd=os.path.join(o.odoo_dir, "setup")) - subprocess.call(["docker", "build", "-t", "odoo-%s-fedora-nightly-tests" % docker_version, "."], - cwd=os.path.join(o.build_dir, "docker_fedora")) - -def test_tgz(o): - logging.info('Testing tarball in docker') - with docker('odoo-%s-src-nightly-tests' % docker_version, o.build_dir, o.pub) as wheezy: - wheezy.release = '*.tar.gz' - wheezy.system("service postgresql start") - wheezy.system('pip3 install /opt/release/%s' % wheezy.release) - wheezy.system("useradd --system --no-create-home odoo") - wheezy.system('su postgres -s /bin/bash -c "createuser -s odoo"') - wheezy.system('su postgres -s /bin/bash -c "createdb mycompany"') - wheezy.system('mkdir /var/lib/odoo') - wheezy.system('chown odoo:odoo /var/lib/odoo') - wheezy.system('su odoo -s /bin/bash -c "odoo -d mycompany -i base --stop-after-init"') - wheezy.system('su odoo -s /bin/bash -c "odoo -d mycompany &"') - -def test_deb(o): - logging.info('Testing deb package in docker') - with docker('odoo-%s-debian-nightly-tests' % docker_version, o.build_dir, o.pub) as wheezy: - wheezy.release = '*.deb' - wheezy.system("service postgresql start") - wheezy.system('su postgres -s /bin/bash -c "createdb mycompany"') - wheezy.system('/usr/bin/dpkg -i /opt/release/%s' % wheezy.release) - wheezy.system('/usr/bin/apt-get install -f -y') - wheezy.system('su odoo -s /bin/bash -c "odoo -c /etc/odoo/odoo.conf -d mycompany -i base --stop-after-init"') - wheezy.system('su odoo -s /bin/bash -c "odoo -c /etc/odoo/odoo.conf -d mycompany &"') - -def test_rpm(o): - logging.info('Testing rpm in docker') - with docker('odoo-%s-fedora-nightly-tests' % docker_version, o.build_dir, o.pub) as fedora24: - fedora24.release = '*.noarch.rpm' - # Start postgresql - fedora24.system('su postgres -c "/usr/bin/pg_ctl -D /var/lib/postgres/data start"') - fedora24.system('sleep 5') - fedora24.system('su postgres -c "createdb mycompany"') - # Odoo install - fedora24.system('dnf install -d 0 -e 0 /opt/release/%s -y' % fedora24.release) - fedora24.system('su odoo -s /bin/bash -c "odoo -c /etc/odoo/odoo.conf -d mycompany -i base --stop-after-init"') - fedora24.system('su odoo -s /bin/bash -c "odoo -c /etc/odoo/odoo.conf -d mycompany &"') - -def test_exe(o): - logging.info('Testng windows installer in KVM') - KVMWinTestExe(o, o.vm_winxp_image, o.vm_winxp_ssh_key, o.vm_winxp_login).start() - -#--------------------------------------------------------- +# --------------------------------------------------------- # Generates Packages, Sources and Release files of debian package -#--------------------------------------------------------- -def gen_deb_package(o, published_files): - # Executes command to produce file_name in path, and moves it to o.pub/deb - def _gen_file(o, command, file_name, path): +# --------------------------------------------------------- +def gen_deb_package(args, published_files): + # Executes command to produce file_name in path, and moves it to args.pub/deb + def _gen_file(args, command, file_name, path): cur_tmp_file_path = os.path.join(path, file_name) with open(cur_tmp_file_path, 'w') as out: subprocess.call(command, stdout=out, cwd=path) - system(['cp', cur_tmp_file_path, os.path.join(o.pub, 'deb', file_name)]) + shutil.copy(cur_tmp_file_path, os.path.join(args.pub, 'deb', file_name)) # Copy files to a temp directory (required because the working directory must contain only the # files of the last release) temp_path = tempfile.mkdtemp(suffix='debPackages') for pub_file_path in published_files: - system(['cp', pub_file_path, temp_path]) + shutil.copy(pub_file_path, temp_path) commands = [ (['dpkg-scanpackages', '.'], "Packages"), # Generate Packages file @@ -403,134 +125,443 @@ def gen_deb_package(o, published_files): ] # Generate files for command in commands: - _gen_file(o, command[0], command[-1], temp_path) + _gen_file(args, command[0], command[-1], temp_path) # Remove temp directory shutil.rmtree(temp_path) - if not o.no_debsign: + if args.sign: # Generate Release.gpg (= signed Release) # Options -abs: -a (Create ASCII armored output), -b (Make a detach signature), -s (Make a signature) - subprocess.call(['gpg', '--default-key', GPGID, '--passphrase', GPGPASSPHRASE, '--yes', '-abs', '--no-tty', '-o', 'Release.gpg', 'Release'], cwd=os.path.join(o.pub, 'deb')) + subprocess.call(['gpg', '--default-key', GPGID, '--passphrase', GPGPASSPHRASE, '--yes', '-abs', '--no-tty', '-o', 'Release.gpg', 'Release'], cwd=os.path.join(args.pub, 'deb')) -#--------------------------------------------------------- + +# --------------------------------------------------------- # Generates an RPM repo -#--------------------------------------------------------- -def gen_rpm_repo(o, file_name): +# --------------------------------------------------------- +def gen_rpm_repo(args, file_name): + """Genereate a rpm repo in publish directory""" # Sign the RPM - rpmsign = pexpect.spawn('/bin/bash', ['-c', 'rpm --resign %s' % file_name], cwd=os.path.join(o.pub, 'rpm')) + rpmsign = pexpect.spawn('/bin/bash', ['-c', 'rpm --resign %s' % file_name], cwd=os.path.join(args.pub, 'rpm')) rpmsign.expect_exact('Enter pass phrase: ') rpmsign.send(GPGPASSPHRASE + '\r\n') rpmsign.expect(pexpect.EOF) # Removes the old repodata - subprocess.call(['rm', '-rf', os.path.join(o.pub, 'rpm', 'repodata')]) + shutil.rmtree(os.path.join(args.pub, 'rpm', 'repodata')) # Copy files to a temp directory (required because the working directory must contain only the # files of the last release) temp_path = tempfile.mkdtemp(suffix='rpmPackages') - subprocess.call(['cp', file_name, temp_path]) + shutil.copy(file_name, temp_path) - subprocess.call(['createrepo', temp_path]) # creates a repodata folder in temp_path - subprocess.call(['cp', '-r', os.path.join(temp_path, "repodata"), os.path.join(o.pub, 'rpm')]) + run_cmd(['createrepo', temp_path]).check_returncode() # creates a repodata folder in temp_path + shutil.copytree(os.path.join(temp_path, "repodata"), os.path.join(args.pub, 'rpm')) # Remove temp directory shutil.rmtree(temp_path) -#---------------------------------------------------------- -# Options and Main -#---------------------------------------------------------- -def options(): - op = optparse.OptionParser() - root = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) - build_dir = "%s-%s" % (root, timestamp) - log_levels = { "debug" : logging.DEBUG, "info": logging.INFO, "warning": logging.WARN, "error": logging.ERROR, "critical": logging.CRITICAL } +def _prepare_build_dir(args, win32=False): + """Copy files to the build directory""" + logging.info('Preparing build dir "%s"', args.build_dir) + cmd = ['rsync', '-a', '--delete', '--exclude', '.git', '--exclude', '*.pyc', '--exclude', '*.pyo'] + if win32 is False: + cmd += ['--exclude', 'setup/win32'] - op.add_option("-b", "--build-dir", default=build_dir, help="build directory (%default)", metavar="DIR") - op.add_option("-p", "--pub", default=None, help="pub directory (%default)", metavar="DIR") - op.add_option("", "--no-testing", action="store_true", help="don't test the built packages") + run_cmd(cmd + ['%s/' % args.odoo_dir, args.build_dir]) + for addon_path in glob(os.path.join(args.build_dir, 'addons/*')): + if args.blacklist is None or os.path.basename(addon_path) not in args.blacklist: + try: + shutil.move(addon_path, os.path.join(args.build_dir, 'odoo/addons')) + except shutil.Error as e: + logging.warning("Warning '%s' while moving addon '%s", e, addon_path) + if addon_path.startswith(args.build_dir) and os.path.isdir(addon_path): + logging.info("Removing ''".format(addon_path)) + try: + shutil.rmtree(addon_path) + except shutil.Error as rm_error: + logging.warning("Cannot remove '{}': {}".format(addon_path, rm_error)) - op.add_option("", "--no-debian", action="store_true", help="don't build the debian package") - op.add_option("", "--no-debsign", action="store_true", help="don't sign the debian package") - op.add_option("", "--no-rpm", action="store_true", help="don't build the rpm package") - op.add_option("", "--no-tarball", action="store_true", help="don't build the tarball") - op.add_option("", "--no-windows", action="store_true", help="don't build the windows package") + +# Docker stuffs +class OdooTestTimeoutError(Exception): + pass + + +class OdooTestError(Exception): + pass + + +class Docker(): + """Base Docker class. Must be inherited by specific Docker builder class""" + arch = None + + def __init__(self, args): + """ + :param args: argparse parsed arguments + """ + self.args = args + self.tag = 'odoo-%s-%s-nightly-tests' % (DOCKERVERSION, self.arch) + self.container_name = None + self.exposed_port = None + dockerfiles = { + 'tgz': os.path.join(args.build_dir, 'setup/package.dfsrc'), + 'deb': os.path.join(args.build_dir, 'setup/package.dfdebian'), + 'rpm': os.path.join(args.build_dir, 'setup/package.dffedora'), + } + self.dockerfile = dockerfiles[self.arch] + self.test_log_file = '/data/src/test-%s.log' % self.arch + self.build_image() + + def build_image(self): + """Build the dockerimage by copying Dockerfile into build_dir/docker""" + docker_dir = os.path.join(self.args.build_dir, 'docker') + docker_file_path = os.path.join(docker_dir, 'Dockerfile') + os.mkdir(docker_dir) + shutil.copy(self.dockerfile, docker_file_path) + with open(docker_file_path, 'a') as dockerfile: + dockerfile.write(DOCKERUSER) + shutil.copy(os.path.join(self.args.build_dir, 'requirements.txt'), docker_dir) + run_cmd(["docker", "build", "--rm=True", "-t", self.tag, "."], chdir=docker_dir, timeout=1200).check_returncode() + shutil.rmtree(docker_dir) + + def run(self, cmd, build_dir, container_name, user='odoo', exposed_port=None, detach=False, timeout=None): + self.container_name = container_name + docker_cmd = [ + "docker", + "run", + "--user=%s" % user, + "--name=%s" % container_name, + "--rm", + "--volume=%s:/data/src" % build_dir + ] + + if exposed_port: + docker_cmd.extend(['-p', '127.0.0.1:%s:%s' % (exposed_port, exposed_port)]) + self.exposed_port = exposed_port + if detach: + docker_cmd.append('-d') + # preserve logs in case of detached docker container + cmd = '(%s) > %s 2>&1' % (cmd, self.test_log_file) + + docker_cmd.extend([ + self.tag, + "/bin/bash", + "-c", + "cd /data/src && %s" % cmd + ]) + run_cmd(docker_cmd, timeout=timeout).check_returncode() + + def is_running(self): + dinspect = subprocess.run(['docker', 'container', 'inspect', self.container_name], stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL) + return True if dinspect.returncode == 0 else False + + def stop(self): + run_cmd(["docker", "stop", self.container_name]).check_returncode() + + def test_odoo(self): + logging.info('Starting to test Odoo install test') + start_time = time.time() + while self.is_running() and (time.time() - start_time) < INSTALL_TIMEOUT: + time.sleep(5) + if os.path.exists(os.path.join(args.build_dir, 'odoo.pid')): + _rpc_count_modules(port=self.exposed_port) + self.stop() + return + if self.is_running(): + self.stop() + raise OdooTestTimeoutError('Odoo pid file never appeared after %s sec' % INSTALL_TIMEOUT) + raise OdooTestError('Error while installing/starting Odoo after %s sec.\nSee testlogs.txt in build dir' % int(time.time() - start_time)) + + def build(self): + """To be overriden by specific builder""" + pass + + def start_test(self): + """To be overriden by specific builder""" + pass + + +class DockerTgz(Docker): + """Docker class to build python src package""" + + arch = 'tgz' + + def build(self): + logging.info('Start building python tgz package') + self.run('python3 setup.py sdist --quiet --formats=gztar,zip', self.args.build_dir, 'odoo-src-build-%s' % TSTAMP) + os.rename(glob('%s/dist/odoo-*.tar.gz' % self.args.build_dir)[0], '%s/odoo_%s.%s.tar.gz' % (self.args.build_dir, VERSION, TSTAMP)) + os.rename(glob('%s/dist/odoo-*.zip' % self.args.build_dir)[0], '%s/odoo_%s.%s.zip' % (self.args.build_dir, VERSION, TSTAMP)) + logging.info('Finished building python tgz package') + + def start_test(self): + if not self.args.test: + return + logging.info('Start testing python tgz package') + cmds = [ + 'service postgresql start', + 'pip3 install /data/src/odoo_%s.%s.tar.gz' % (VERSION, TSTAMP), + 'su postgres -s /bin/bash -c "createuser -s odoo"', + 'su postgres -s /bin/bash -c "createdb mycompany"', + 'su odoo -s /bin/bash -c "odoo -d mycompany -i base --stop-after-init"', + 'su odoo -s /bin/bash -c "odoo -d mycompany --pidfile=/data/src/odoo.pid"', + ] + self.run(' && '.join(cmds), self.args.build_dir, 'odoo-src-test-%s' % TSTAMP, user='root', detach=True, exposed_port=8069, timeout=300) + self.test_odoo() + logging.info('Finished testing tgz package') + + +class DockerDeb(Docker): + """Docker class to build debian package""" + + arch = 'deb' + + def build(self): + logging.info('Start building debian package') + # Append timestamp to version for the .dsc to refer the right .tar.gz + cmds = ["sed -i '1s/^.*$/odoo (%s.%s) stable; urgency=low/' debian/changelog" % (VERSION, TSTAMP)] + cmds.append('dpkg-buildpackage -rfakeroot -uc -us -tc') + # As the packages are built in the parent of the buildir, we move them back to build_dir + cmds.append('mv ../odoo_* ./') + self.run(' && '.join(cmds), self.args.build_dir, 'odoo-deb-build-%s' % TSTAMP) + logging.info('Finished building debian package') + + def start_test(self): + if not self.args.test: + return + logging.info('Start testing debian package') + cmds = [ + 'service postgresql start', + 'su postgres -s /bin/bash -c "createdb mycompany"', + '/usr/bin/apt-get update -y', + '/usr/bin/dpkg -i /data/src/odoo_%s.%s_all.deb ; /usr/bin/apt-get install -f -y' % (VERSION, TSTAMP), + 'su odoo -s /bin/bash -c "odoo -d mycompany -i base --stop-after-init"', + 'su odoo -s /bin/bash -c "odoo -d mycompany --pidfile=/data/src/odoo.pid"', + ] + self.run(' && '.join(cmds), self.args.build_dir, 'odoo-deb-test-%s' % TSTAMP, user='root', detach=True, exposed_port=8069, timeout=300) + self.test_odoo() + logging.info('Finished testing debian package') + + +class DockerRpm(Docker): + """Docker class to build rpm package""" + + arch = 'rpm' + + def build(self): + logging.info('Start building fedora rpm package') + self.run('python3 setup.py --quiet bdist_rpm', self.args.build_dir, 'odoo-rpm-build-%s' % TSTAMP) + os.rename(glob('%s/dist/odoo-*.noarch.rpm' % self.args.build_dir)[0], '%s/odoo_%s.%s.rpm' % (self.args.build_dir, VERSION, TSTAMP)) + logging.info('Finished building fedora rpm package') + + def start_test(self): + if not self.args.test: + return + logging.info('Start testing rpm package') + cmds = [ + 'su postgres -c "/usr/bin/pg_ctl -D /var/lib/postgres/data start"', + 'sleep 5', + 'su postgres -c "createdb mycompany"', + 'dnf install -d 0 -e 0 /data/src/odoo_%s.%s.rpm -y' % (VERSION, TSTAMP), + 'su odoo -s /bin/bash -c "odoo -c /etc/odoo/odoo.conf -d mycompany -i base --stop-after-init"', + 'su odoo -s /bin/bash -c "odoo -c /etc/odoo/odoo.conf -d mycompany --pidfile=/data/src/odoo.pid"', + ] + self.run(' && '.join(cmds), args.build_dir, 'odoo-rpm-test-%s' % TSTAMP, user='root', detach=True, exposed_port=8069, timeout=300) + self.test_odoo() + logging.info('Finished testing rpm package') + + +# KVM stuffs +class KVM(object): + def __init__(self, args): + self.args = args + self.image = args.vm_winxp_image + self.ssh_key = args.vm_winxp_ssh_key + self.login = args.vm_winxp_login + + def timeout(self, signum, frame): + logging.warning("vm timeout kill (pid: {})".format(self.kvm_proc.pid)) + self.kvm_proc.terminate() + + def start(self): + kvm_cmd = [ + "kvm", + "-cpu", "core2duo", + "-smp", "2,sockets=2,cores=1,threads=1", + "-net", "nic,model=rtl8139", + "-net", "user,hostfwd=tcp:127.0.0.1:10022-:22,hostfwd=tcp:127.0.0.1:18069-:8069,hostfwd=tcp:127.0.0.1:15432-:5432", + "-m", "1024", + "-drive", "file=%s,snapshot=on" % self.image, + "-nographic" + ] + logging.info("Starting kvm: {}".format(" ".join(kvm_cmd))) + self.kvm_proc = subprocess.Popen(kvm_cmd) + time.sleep(50) + signal.alarm(2400) + signal.signal(signal.SIGALRM, self.timeout) + try: + self.run() + finally: + signal.signal(signal.SIGALRM, signal.SIG_DFL) + self.kvm_proc.terminate() + time.sleep(10) + + def ssh(self, cmd): + run_cmd([ + 'ssh', + '-o', 'UserKnownHostsFile=/dev/null', + '-o', 'StrictHostKeyChecking=no', + '-p', '10022', + '-i', self.ssh_key, + '%s@127.0.0.1' % self.login, + cmd + ]).check_returncode() + + def rsync(self, rsync_args, options=['--delete', '--exclude', '.git', '--exclude', '.tx', '--exclude', '__pycache__']): + cmd = [ + 'rsync', + '-rt', + '-e', 'ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -p 10022 -i %s' % self.ssh_key + ] + cmd.extend(options) + cmd.extend(rsync_args) + run_cmd(cmd).check_returncode() + + def run(self): + pass + + +class KVMWinBuildExe(KVM): + def run(self): + logging.info('Start building Windows package') + with open(os.path.join(self.args.build_dir, 'setup/win32/Makefile.version'), 'w') as f: + f.write("VERSION=%s\n" % VERSION.replace('~', '_').replace('+', '')) + with open(os.path.join(self.args.build_dir, 'setup/win32/Makefile.python'), 'w') as f: + f.write("PYTHON_VERSION=%s\n" % self.args.vm_winxp_python_version.replace('.', '')) + with open(os.path.join(self.args.build_dir, 'setup/win32/Makefile.servicename'), 'w') as f: + f.write("SERVICENAME=%s\n" % nt_service_name) + + remote_build_dir = '/cygdrive/c/odoobuild/server/' + + self.ssh("mkdir -p build") + logging.info("Syncing Odoo files to virtual machine...") + self.rsync(['%s/' % self.args.build_dir, '%s@127.0.0.1:%s' % (self.login, remote_build_dir)]) + self.ssh("cd {}setup/win32;time make allinone;".format(remote_build_dir)) + self.rsync(['%s@127.0.0.1:%ssetup/win32/release/' % (self.login, remote_build_dir), '%s/' % self.args.build_dir]) + logging.info('Finished building Windows package') + + +class KVMWinTestExe(KVM): + def run(self): + logging.info('Start testing Windows package') + setup_path = glob("%s/openerp-server-setup-*.exe" % self.args.build_dir)[0] + setupfile = setup_path.split('/')[-1] + setupversion = setupfile.split('openerp-server-setup-')[1].split('.exe')[0] + + self.rsync(['"%s"' % setup_path, '%s@127.0.0.1:' % self.login]) + self.ssh("TEMP=/tmp ./%s /S" % setupfile) + self.ssh('PGPASSWORD=openpgpwd /cygdrive/c/"Program Files"/"Odoo %s"/PostgreSQL/bin/createdb.exe -e -U openpg mycompany' % setupversion) + self.ssh('netsh advfirewall set publicprofile state off') + self.ssh('/cygdrive/c/"Program Files"/"Odoo {sv}"/python/python.exe \'c:\\Program Files\\Odoo {sv}\\server\\odoo-bin\' -d mycompany -i base --stop-after-init'.format(sv=setupversion)) + _rpc_count_modules(port=18069) + logging.info('Finished testing Windows package') + + +def build_exe(args): + KVMWinBuildExe(args).start() + shutil.copy(glob('%s/openerp*.exe' % args.build_dir)[0], '%s/odoo_%s.%s.exe' % (args.build_dir, VERSION, TSTAMP)) + + +def test_exe(args): + if args.test: + KVMWinTestExe(args).start() + + +def parse_args(): + ap = argparse.ArgumentParser() + build_dir = "%s-%s" % (ROOTDIR, TSTAMP) + log_levels = {"debug": logging.DEBUG, "info": logging.INFO, "warning": logging.WARN, "error": logging.ERROR, "critical": logging.CRITICAL} + + ap.add_argument("-b", "--build-dir", default=build_dir, help="build directory (%(default)s)", metavar="DIR") + ap.add_argument("-p", "--pub", default=None, help="pub directory %(default)s", metavar="DIR") + ap.add_argument("--logging", action="store", choices=list(log_levels.keys()), default="info", help="Logging level") + ap.add_argument("--build-deb", action="store_true") + ap.add_argument("--build-rpm", action="store_true") + ap.add_argument("--build-tgz", action="store_true") + ap.add_argument("--build-win", action="store_true") # Windows VM - op.add_option("", "--vm-winxp-image", default='/home/odoo/vm/win1036/win10_winpy36.qcow2', help="%default") - op.add_option("", "--vm-winxp-ssh-key", default='/home/odoo/vm/win1036/id_rsa', help="%default") - op.add_option("", "--vm-winxp-login", default='Naresh', help="Windows login (%default)") - op.add_option("", "--vm-winxp-python-version", default='3.6', help="Windows Python version installed in the VM (default: %default)") - - op.add_option("", "--no-remove", action="store_true", help="don't remove build dir") - op.add_option("", "--logging", action="store", type="choice", choices=list(log_levels.keys()), default="info", help="Logging level") + ap.add_argument("--vm-winxp-image", default='/home/odoo/vm/win1036/win10_winpy36.qcow2', help="%(default)s") + ap.add_argument("--vm-winxp-ssh-key", default='/home/odoo/vm/win1036/id_rsa', help="%(default)s") + ap.add_argument("--vm-winxp-login", default='Naresh', help="Windows login %(default)s") + ap.add_argument("--vm-winxp-python-version", default='3.6', help="Windows Python version installed in the VM (default: %(default)s)") - (o, args) = op.parse_args() - logging.basicConfig(format='%(asctime)s %(levelname)s: %(message)s', datefmt='%Y-%m-%d %I:%M:%S', level=log_levels[o.logging]) - # derive other options - o.odoo_dir = root - o.pkg = join(o.build_dir, 'pkg') - o.work = join(o.build_dir, 'openerp-%s' % version) - o.work_addons = join(o.work, 'odoo', 'addons') + ap.add_argument("-t", "--test", action="store_true", default=False, help="Test built packages") + ap.add_argument("-s", "--sign", action="store_true", default=False, help="Sign Debian package / generate Rpm repo") + ap.add_argument("--no-remove", action="store_true", help="don't remove build dir") + ap.add_argument("--blacklist", nargs="*", help="Modules to blacklist in package") - return o + parsed_args = ap.parse_args() + logging.basicConfig(format='%(asctime)s %(levelname)s: %(message)s', datefmt='%Y-%m-%d %I:%M:%S', level=log_levels[parsed_args.logging]) + parsed_args.odoo_dir = ROOTDIR + return parsed_args -def main(): - o = options() - _prepare_build_dir(o) - if not o.no_testing: - _prepare_testing(o) + +def main(args): try: - if not o.no_tarball: - build_tgz(o) + if args.build_tgz: + _prepare_build_dir(args) + docker_tgz = DockerTgz(args) + docker_tgz.build() try: - if not o.no_testing: - test_tgz(o) - published_files = publish(o, 'tarball', ['tar.gz', 'zip']) + docker_tgz.start_test() + published_files = publish(args, 'tgz', ['tar.gz', 'zip']) except Exception as e: logging.error("Won't publish the tgz release.\n Exception: %s" % str(e)) - if not o.no_debian: - build_deb(o) + if args.build_rpm: + _prepare_build_dir(args) + docker_rpm = DockerRpm(args) + docker_rpm.build() try: - if not o.no_testing: - test_deb(o) - published_files = publish(o, 'debian', ['deb', 'dsc', 'changes', 'tar.xz']) - gen_deb_package(o, published_files) - except Exception as e: - logging.error("Won't publish the deb release.\n Exception: %s" % str(e)) - traceback.print_exc() - if not o.no_rpm: - build_rpm(o) - try: - if not o.no_testing: - test_rpm(o) - published_files = publish(o, 'redhat', ['noarch.rpm']) - gen_rpm_repo(o, published_files[0]) + docker_rpm.start_test() + published_files = publish(args, 'rpm', ['rpm']) + if args.sign: + gen_rpm_repo(args, published_files[0]) except Exception as e: logging.error("Won't publish the rpm release.\n Exception: %s" % str(e)) - if not o.no_windows: - _prepare_build_dir(o, win32=True) - build_exe(o) + if args.build_deb: + _prepare_build_dir(args) + docker_deb = DockerDeb(args) + docker_deb.build() try: - if not o.no_testing: - test_exe(o) - published_files = publish(o, 'windows', ['exe']) + docker_deb.start_test() + published_files = publish(args, 'deb', ['deb', 'dsc', 'changes', 'tar.xz']) + gen_deb_package(args, published_files) + except Exception as e: + logging.error("Won't publish the deb release.\n Exception: %s" % str(e)) + if args.build_win: + _prepare_build_dir(args, win32=True) + build_exe(args) + try: + test_exe(args) + published_files = publish(args, 'windows', ['exe']) except Exception as e: logging.error("Won't publish the exe release.\n Exception: %s" % str(e)) except Exception as e: logging.error('Something bad happened ! : {}'.format(e)) traceback.print_exc() finally: - if o.no_remove: - logging.info('Build dir "{}" not removed'.format(o.build_dir)) + if args.no_remove: + logging.info('Build dir "{}" not removed'.format(args.build_dir)) else: - shutil.rmtree(o.build_dir) - logging.info('Build dir %s removed' % o.build_dir) - - if not o.no_testing and not (o.no_debian and o.no_rpm and o.no_tarball): - system("docker rm -f `docker ps -a | awk '{print $1 }'` 2>>/dev/null") - logging.info('Remaining dockers removed') + if os.path.exists(args.build_dir): + shutil.rmtree(args.build_dir) + logging.info('Build dir %s removed' % args.build_dir) if __name__ == '__main__': - main() + args = parse_args() + if os.path.exists(args.build_dir): + logging.error('Build dir "%s" already exists.', args.build_dir) + sys.exit(1) + main(args) diff --git a/setup/redhat/install.sh b/setup/redhat/install.sh index 982cc8759e9..b7a7f3eb40d 100644 --- a/setup/redhat/install.sh +++ b/setup/redhat/install.sh @@ -1,3 +1,3 @@ #!/bin/sh set -e -python3 setup.py install --prefix=/usr --root=$RPM_BUILD_ROOT --record=INSTALLED_FILES --install-lib usr/lib/python3.6/site-packages/ +python3 setup.py install --prefix=/usr --root=$RPM_BUILD_ROOT --record=INSTALLED_FILES --install-lib usr/lib/python3.7/site-packages/ diff --git a/setup/redhat/postinstall.sh b/setup/redhat/postinstall.sh index e6ee9106f7d..c14dd9a55a4 100644 --- a/setup/redhat/postinstall.sh +++ b/setup/redhat/postinstall.sh @@ -10,12 +10,6 @@ ODOO_LOG_DIR=/var/log/odoo ODOO_LOG_FILE=$ODOO_LOG_DIR/odoo-server.log ODOO_USER="odoo" -if [ -d /usr/lib/python3.7 ]; then - SITE_PACK_DIR37=/usr/lib/python3.7/site-packages - [[ ! -d ${SITE_PACK_DIR37} ]] && mkdir -p ${SITE_PACK_DIR37} - ln -s /usr/lib/python3.6/site-packages/odoo ${SITE_PACK_DIR37}/odoo -fi - if ! getent passwd | grep -q "^odoo:"; then groupadd $ODOO_GROUP adduser --system --no-create-home $ODOO_USER -g $ODOO_GROUP @@ -34,7 +28,7 @@ db_host = False db_port = False db_user = $ODOO_USER db_password = False -addons_path = /usr/lib/python3.6/site-packages/odoo/addons +addons_path = /usr/lib/python3.7/site-packages/odoo/addons " > $ODOO_CONFIGURATION_FILE chown $ODOO_USER:$ODOO_GROUP $ODOO_CONFIGURATION_FILE chmod 0640 $ODOO_CONFIGURATION_FILE