update
Some checks are pending
Setup Native Action / native (3.12.7) (push) Waiting to run
Setup Native Action / docker (3.12.7) (push) Waiting to run

This commit is contained in:
KaySar12 2025-03-25 11:07:42 +07:00
parent 76590cba19
commit 48758276dc
3 changed files with 304 additions and 152 deletions

View File

@ -1,15 +0,0 @@
version: '3.8'
services:
db-test:
image: postgres:16
user: root
environment:
- POSTGRES_USER=${PG_USER:-changeme}
- POSTGRES_PASSWORD=${PG_PASS:-password}
- POSTGRES_DB=${PG_DB:-postgres}
restart: always
ports:
- ${PG_PORT:-5432}:5432
volumes:
- ${PG_DATA:-./postgresql}:/var/lib/postgresql/data

View File

@ -1,5 +0,0 @@
#Database
PG_DB=nexterp
PG_USER=nexterp
PG_PASS=4ewLWdJ0WQ8I77Slqg0Y8kAj
PG_PORT=

View File

@ -29,11 +29,11 @@ TSEC = time.strftime("%H%M%S", time.gmtime())
version = ... version = ...
version_info = ... version_info = ...
nt_service_name = ... nt_service_name = ...
exec(open(os.path.join(ROOTDIR, 'odoo', 'release.py'), 'rb').read()) exec(open(os.path.join(ROOTDIR, "odoo", "release.py"), "rb").read())
VERSION = version.split('-')[0].replace('saas~', '') VERSION = version.split("-")[0].replace("saas~", "")
GPGPASSPHRASE = os.getenv('GPGPASSPHRASE') GPGPASSPHRASE = os.getenv("GPGPASSPHRASE")
GPGID = os.getenv('GPGID') GPGID = os.getenv("GPGID")
DOCKERVERSION = VERSION.replace('+', '') DOCKERVERSION = VERSION.replace("+", "")
INSTALL_TIMEOUT = 600 INSTALL_TIMEOUT = 600
DOCKERUSER = """ DOCKERUSER = """
@ -43,7 +43,10 @@ RUN mkdir /var/lib/odoo && \
mkdir /data && \ mkdir /data && \
chown odoo:odoo /var/lib/odoo /data chown odoo:odoo /var/lib/odoo /data
USER odoo USER odoo
""" % {'group_id': os.getgid(), 'user_id': os.getuid()} """ % {
"group_id": os.getgid(),
"user_id": os.getuid(),
}
class OdooTestTimeoutError(Exception): class OdooTestTimeoutError(Exception):
@ -55,28 +58,44 @@ class OdooTestError(Exception):
def run_cmd(cmd, chdir=None, timeout=None): def run_cmd(cmd, chdir=None, timeout=None):
logging.info("Running command: '%s'", ' '.join(cmd)) logging.info("Running command: '%s'", " ".join(cmd))
return subprocess.run(cmd, cwd=chdir, timeout=timeout) return subprocess.run(cmd, cwd=chdir, timeout=timeout)
def _rpc_count_modules(addr='http://127.0.0.1', port=8069, dbname='mycompany'): def _rpc_count_modules(addr="http://127.0.0.1", port=8069, dbname="mycompany"):
time.sleep(5) time.sleep(5)
uid = xmlrpclib.ServerProxy('%s:%s/xmlrpc/2/common' % (addr, port)).authenticate( uid = xmlrpclib.ServerProxy("%s:%s/xmlrpc/2/common" % (addr, port)).authenticate(
dbname, 'admin', 'admin', {} dbname, "admin", "admin", {}
) )
modules = xmlrpclib.ServerProxy('%s:%s/xmlrpc/2/object' % (addr, port)).execute( modules = xmlrpclib.ServerProxy("%s:%s/xmlrpc/2/object" % (addr, port)).execute(
dbname, uid, 'admin', 'ir.module.module', 'search', [('state', '=', 'installed')] dbname,
uid,
"admin",
"ir.module.module",
"search",
[("state", "=", "installed")],
) )
if len(modules) > 1: if len(modules) > 1:
time.sleep(1) time.sleep(1)
toinstallmodules = xmlrpclib.ServerProxy('%s:%s/xmlrpc/2/object' % (addr, port)).execute( toinstallmodules = xmlrpclib.ServerProxy(
dbname, uid, 'admin', 'ir.module.module', 'search', [('state', '=', 'to install')] "%s:%s/xmlrpc/2/object" % (addr, port)
).execute(
dbname,
uid,
"admin",
"ir.module.module",
"search",
[("state", "=", "to install")],
) )
if toinstallmodules: if toinstallmodules:
logging.error("Package test: FAILED. Not able to install dependencies of base.") logging.error(
"Package test: FAILED. Not able to install dependencies of base."
)
raise OdooTestError("Installation of package failed") raise OdooTestError("Installation of package failed")
else: else:
logging.info("Package test: successfuly installed %s modules" % len(modules)) logging.info(
"Package test: successfuly installed %s modules" % len(modules)
)
else: else:
logging.error("Package test: FAILED. Not able to install base.") logging.error("Package test: FAILED. Not able to install base.")
raise OdooTestError("Package test: FAILED. Not able to install base.") raise OdooTestError("Package test: FAILED. Not able to install base.")
@ -89,6 +108,7 @@ def publish(args, pub_type, extensions):
:extensions: list of extensions to publish :extensions: list of extensions to publish
:returns: published files :returns: published files
""" """
def _publish(release): def _publish(release):
build_path = os.path.join(args.build_dir, release) build_path = os.path.join(args.build_dir, release)
filename = release.split(os.path.sep)[-1] filename = release.split(os.path.sep)[-1]
@ -98,7 +118,7 @@ def publish(args, pub_type, extensions):
# Latest/symlink handler # Latest/symlink handler
release_abspath = os.path.abspath(release_path) release_abspath = os.path.abspath(release_path)
latest_abspath = release_abspath.replace(TSTAMP, 'latest') latest_abspath = release_abspath.replace(TSTAMP, "latest")
if os.path.islink(latest_abspath): if os.path.islink(latest_abspath):
os.unlink(latest_abspath) os.unlink(latest_abspath)
@ -121,20 +141,23 @@ def gen_deb_package(args, published_files):
# Executes command to produce file_name in path, and moves it to args.pub/deb # Executes command to produce file_name in path, and moves it to args.pub/deb
def _gen_file(args, command, file_name, path): def _gen_file(args, command, file_name, path):
cur_tmp_file_path = os.path.join(path, file_name) cur_tmp_file_path = os.path.join(path, file_name)
with open(cur_tmp_file_path, 'w') as out: with open(cur_tmp_file_path, "w") as out:
subprocess.call(command, stdout=out, cwd=path) subprocess.call(command, stdout=out, cwd=path)
shutil.copy(cur_tmp_file_path, os.path.join(args.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 # Copy files to a temp directory (required because the working directory must contain only the
# files of the last release) # files of the last release)
temp_path = tempfile.mkdtemp(suffix='debPackages') temp_path = tempfile.mkdtemp(suffix="debPackages")
for pub_file_path in published_files: for pub_file_path in published_files:
shutil.copy(pub_file_path, temp_path) shutil.copy(pub_file_path, temp_path)
commands = [ commands = [
(['dpkg-scanpackages', '--multiversion', '.'], "Packages"), # Generate Packages file (
(['dpkg-scansources', '.'], "Sources"), # Generate Sources file ["dpkg-scanpackages", "--multiversion", "."],
(['apt-ftparchive', 'release', '.'], "Release") # Generate Release file "Packages",
), # Generate Packages file
(["dpkg-scansources", "."], "Sources"), # Generate Sources file
(["apt-ftparchive", "release", "."], "Release"), # Generate Release file
] ]
# Generate files # Generate files
for command in commands: for command in commands:
@ -145,7 +168,22 @@ def gen_deb_package(args, published_files):
if args.sign: if args.sign:
# Generate Release.gpg (= signed Release) # Generate Release.gpg (= signed Release)
# Options -abs: -a (Create ASCII armored output), -b (Make a detach signature), -s (Make a signature) # 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(args.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"),
)
# --------------------------------------------------------- # ---------------------------------------------------------
@ -154,26 +192,54 @@ def gen_deb_package(args, published_files):
def rpm_sign(args, file_name): def rpm_sign(args, file_name):
"""Genereate a rpm repo in publish directory""" """Genereate a rpm repo in publish directory"""
# Sign the RPM # Sign the RPM
rpmsign = pexpect.spawn('/bin/bash', ['-c', 'rpm --resign %s' % file_name], cwd=os.path.join(args.pub, 'rpm')) rpmsign = pexpect.spawn(
rpmsign.expect_exact('Enter passphrase: ') "/bin/bash",
rpmsign.send(GPGPASSPHRASE + '\r\n') ["-c", "rpm --resign %s" % file_name],
cwd=os.path.join(args.pub, "rpm"),
)
rpmsign.expect_exact("Enter passphrase: ")
rpmsign.send(GPGPASSPHRASE + "\r\n")
rpmsign.expect(pexpect.EOF) rpmsign.expect(pexpect.EOF)
def _prepare_build_dir(args, win32=False, move_addons=True): def _prepare_build_dir(args, win32=False, move_addons=True):
"""Copy files to the build directory""" """Copy files to the build directory"""
logging.info('Preparing build dir "%s"', args.build_dir) logging.info('Preparing build dir "%s"', args.build_dir)
cmd = ['rsync', '-a', '--delete', '--exclude', '.git', '--exclude', '*.pyc', '--exclude', '*.pyo'] cmd = [
"rsync",
"-a",
"--delete",
"--exclude",
".git",
"--exclude",
"Dockerfile",
"--exclude",
".github",
"--exclude",
".gitea",
"--exclude",
"*.pyc",
"--exclude",
"*.pyo",
"--exclude",
"automation",
"--exclude",
"extra-addons",
"--exclude",
"exercise",
"--exclude",
"deployment",
]
if win32 is False: if win32 is False:
cmd += ['--exclude', 'setup/win32'] cmd += ["--exclude", "setup/win32"]
run_cmd(cmd + ['%s/' % args.odoo_dir, args.build_dir]) run_cmd(cmd + ["%s/" % args.odoo_dir, args.build_dir])
if not move_addons: if not move_addons:
return return
for addon_path in glob(os.path.join(args.build_dir, 'addons/*')): 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: if args.blacklist is None or os.path.basename(addon_path) not in args.blacklist:
try: try:
shutil.move(addon_path, os.path.join(args.build_dir, 'odoo/addons')) shutil.move(addon_path, os.path.join(args.build_dir, "odoo/addons"))
except shutil.Error as e: except shutil.Error as e:
logging.warning("Warning '%s' while moving addon '%s", e, addon_path) logging.warning("Warning '%s' while moving addon '%s", e, addon_path)
if addon_path.startswith(args.build_dir) and os.path.isdir(addon_path): if addon_path.startswith(args.build_dir) and os.path.isdir(addon_path):
@ -181,12 +247,15 @@ def _prepare_build_dir(args, win32=False, move_addons=True):
try: try:
shutil.rmtree(addon_path) shutil.rmtree(addon_path)
except shutil.Error as rm_error: except shutil.Error as rm_error:
logging.warning("Cannot remove '{}': {}".format(addon_path, rm_error)) logging.warning(
"Cannot remove '{}': {}".format(addon_path, rm_error)
)
# Docker stuffs # Docker stuffs
class Docker(): class Docker:
"""Base Docker class. Must be inherited by specific Docker builder class""" """Base Docker class. Must be inherited by specific Docker builder class"""
arch = None arch = None
def __init__(self, args): def __init__(self, args):
@ -194,31 +263,50 @@ class Docker():
:param args: argparse parsed arguments :param args: argparse parsed arguments
""" """
self.args = args self.args = args
self.tag = 'odoo-%s-%s-nightly-tests' % (DOCKERVERSION, self.arch) self.tag = "odoo-%s-%s-nightly-tests" % (DOCKERVERSION, self.arch)
self.container_name = None self.container_name = None
self.exposed_port = None self.exposed_port = None
docker_templates = { docker_templates = {
'tgz': os.path.join(args.build_dir, 'setup/package.dfsrc'), "tgz": os.path.join(args.build_dir, "setup/package.dfsrc"),
'deb': os.path.join(args.build_dir, 'setup/package.dfdebian'), "deb": os.path.join(args.build_dir, "setup/package.dfdebian"),
'rpm': os.path.join(args.build_dir, 'setup/package.dffedora'), "rpm": os.path.join(args.build_dir, "setup/package.dffedora"),
'win': os.path.join(args.build_dir, 'setup/package.dfwine'), "win": os.path.join(args.build_dir, "setup/package.dfwine"),
} }
self.docker_template = Path(docker_templates[self.arch]).read_text(encoding='utf-8').replace('USER odoo', DOCKERUSER) self.docker_template = (
self.test_log_file = '/data/src/test-%s.log' % self.arch Path(docker_templates[self.arch])
self.docker_dir = Path(self.args.build_dir) / 'docker' .read_text(encoding="utf-8")
.replace("USER odoo", DOCKERUSER)
)
self.test_log_file = "/data/src/test-%s.log" % self.arch
self.docker_dir = Path(self.args.build_dir) / "docker"
if not self.docker_dir.exists(): if not self.docker_dir.exists():
self.docker_dir.mkdir() self.docker_dir.mkdir()
self.build_image() self.build_image()
def build_image(self): def build_image(self):
"""Build the dockerimage by copying Dockerfile into build_dir/docker""" """Build the dockerimage by copying Dockerfile into build_dir/docker"""
docker_file = self.docker_dir / 'Dockerfile' docker_file = self.docker_dir / "Dockerfile"
docker_file.write_text(self.docker_template) docker_file.write_text(self.docker_template)
shutil.copy(os.path.join(self.args.build_dir, 'requirements.txt'), self.docker_dir) shutil.copy(
run_cmd(["docker", "build", "--rm=True", "-t", self.tag, "."], chdir=self.docker_dir, timeout=1200).check_returncode() os.path.join(self.args.build_dir, "requirements.txt"), self.docker_dir
)
run_cmd(
["docker", "build", "--rm=True", "-t", self.tag, "."],
chdir=self.docker_dir,
timeout=1200,
).check_returncode()
shutil.rmtree(self.docker_dir) shutil.rmtree(self.docker_dir)
def run(self, cmd, build_dir, container_name, user='odoo', exposed_port=None, detach=False, timeout=None): def run(
self,
cmd,
build_dir,
container_name,
user="odoo",
exposed_port=None,
detach=False,
timeout=None,
):
self.container_name = container_name self.container_name = container_name
docker_cmd = [ docker_cmd = [
"docker", "docker",
@ -226,38 +314,37 @@ class Docker():
"--user=%s" % user, "--user=%s" % user,
"--name=%s" % container_name, "--name=%s" % container_name,
"--rm", "--rm",
"--volume=%s:/data/src" % build_dir "--volume=%s:/data/src" % build_dir,
] ]
if exposed_port: if exposed_port:
docker_cmd.extend(['-p', '127.0.0.1:%s:%s' % (exposed_port, exposed_port)]) docker_cmd.extend(["-p", "127.0.0.1:%s:%s" % (exposed_port, exposed_port)])
self.exposed_port = exposed_port self.exposed_port = exposed_port
if detach: if detach:
docker_cmd.append('-d') docker_cmd.append("-d")
# preserve logs in case of detached docker container # preserve logs in case of detached docker container
cmd = '(%s) > %s 2>&1' % (cmd, self.test_log_file) cmd = "(%s) > %s 2>&1" % (cmd, self.test_log_file)
docker_cmd.extend([ docker_cmd.extend([self.tag, "/bin/bash", "-c", "cd /data/src && %s" % cmd])
self.tag,
"/bin/bash",
"-c",
"cd /data/src && %s" % cmd
])
run_cmd(docker_cmd, timeout=timeout).check_returncode() run_cmd(docker_cmd, timeout=timeout).check_returncode()
def is_running(self): def is_running(self):
dinspect = subprocess.run(['docker', 'container', 'inspect', self.container_name], stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL) dinspect = subprocess.run(
["docker", "container", "inspect", self.container_name],
stderr=subprocess.DEVNULL,
stdout=subprocess.DEVNULL,
)
return True if dinspect.returncode == 0 else False return True if dinspect.returncode == 0 else False
def stop(self): def stop(self):
run_cmd(["docker", "stop", self.container_name]).check_returncode() run_cmd(["docker", "stop", self.container_name]).check_returncode()
def test_odoo(self): def test_odoo(self):
logging.info('Starting to test Odoo install test') logging.info("Starting to test Odoo install test")
start_time = time.time() start_time = time.time()
while self.is_running() and (time.time() - start_time) < INSTALL_TIMEOUT: while self.is_running() and (time.time() - start_time) < INSTALL_TIMEOUT:
time.sleep(5) time.sleep(5)
if os.path.exists(os.path.join(args.build_dir, 'odoo.pid')): if os.path.exists(os.path.join(args.build_dir, "odoo.pid")):
try: try:
_rpc_count_modules(port=self.exposed_port) _rpc_count_modules(port=self.exposed_port)
finally: finally:
@ -265,8 +352,13 @@ class Docker():
return return
if self.is_running(): if self.is_running():
self.stop() self.stop()
raise OdooTestTimeoutError('Odoo pid file never appeared after %s sec' % INSTALL_TIMEOUT) raise OdooTestTimeoutError(
raise OdooTestError('Error while installing/starting Odoo after %s sec.\nSee testlogs.txt in build dir' % int(time.time() - start_time)) "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): def build(self):
"""To be overriden by specific builder""" """To be overriden by specific builder"""
@ -280,21 +372,31 @@ class Docker():
class DockerTgz(Docker): class DockerTgz(Docker):
"""Docker class to build python src package""" """Docker class to build python src package"""
arch = 'tgz' arch = "tgz"
def build(self): def build(self):
logging.info('Start building python tgz package') 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) self.run(
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)) "python3 setup.py sdist --quiet --formats=gztar,zip",
os.rename(glob('%s/dist/odoo-*.zip' % self.args.build_dir)[0], '%s/odoo_%s.%s.zip' % (self.args.build_dir, VERSION, TSTAMP)) self.args.build_dir,
logging.info('Finished building python tgz package') "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): def start_test(self):
if not self.args.test: if not self.args.test:
return return
logging.info('Start testing python tgz package') logging.info("Start testing python tgz package")
cmds = [ cmds = [
'service postgresql start', "service postgresql start",
'su postgres -s /bin/bash -c "createuser -s odoo"', 'su postgres -s /bin/bash -c "createuser -s odoo"',
'su odoo -s /bin/bash -c "python3 -m venv /var/lib/odoo/odoovenv"', 'su odoo -s /bin/bash -c "python3 -m venv /var/lib/odoo/odoovenv"',
'su odoo -s /bin/bash -c "/var/lib/odoo/odoovenv/bin/python3 -m pip install --upgrade pip"', 'su odoo -s /bin/bash -c "/var/lib/odoo/odoovenv/bin/python3 -m pip install --upgrade pip"',
@ -304,92 +406,122 @@ class DockerTgz(Docker):
'su odoo -s /bin/bash -c "/var/lib/odoo/odoovenv/bin/odoo -d mycompany -i base --stop-after-init"', 'su odoo -s /bin/bash -c "/var/lib/odoo/odoovenv/bin/odoo -d mycompany -i base --stop-after-init"',
'su odoo -s /bin/bash -c "/var/lib/odoo/odoovenv/bin/odoo -d mycompany --pidfile=/data/src/odoo.pid"', 'su odoo -s /bin/bash -c "/var/lib/odoo/odoovenv/bin/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.run(
" && ".join(cmds),
self.args.build_dir,
"odoo-src-test-%s" % TSTAMP,
user="root",
detach=True,
exposed_port=8069,
timeout=300,
)
self.test_odoo() self.test_odoo()
logging.info('Finished testing tgz package') logging.info("Finished testing tgz package")
class DockerDeb(Docker): class DockerDeb(Docker):
"""Docker class to build debian package""" """Docker class to build debian package"""
arch = 'deb' arch = "deb"
def build(self): def build(self):
logging.info('Start building debian package') logging.info("Start building debian package")
# Append timestamp to version for the .dsc to refer the right .tar.gz # 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 = [
cmds.append('dpkg-buildpackage -rfakeroot -uc -us -tc') "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 # As the packages are built in the parent of the buildir, we move them back to build_dir
cmds.append('mv ../odoo_* ./') cmds.append("mv ../odoo_* ./")
self.run(' && '.join(cmds), self.args.build_dir, 'odoo-deb-build-%s' % TSTAMP) self.run(" && ".join(cmds), self.args.build_dir, "odoo-deb-build-%s" % TSTAMP)
logging.info('Finished building debian package') logging.info("Finished building debian package")
def start_test(self): def start_test(self):
if not self.args.test: if not self.args.test:
return return
logging.info('Start testing debian package') logging.info("Start testing debian package")
cmds = [ cmds = [
'service postgresql start', "service postgresql start",
'/usr/bin/apt-get update -y', "/usr/bin/apt-get update -y",
f'/usr/bin/apt-get install -y /data/src/odoo_{VERSION}.{TSTAMP}_all.deb', f"/usr/bin/apt-get install -y /data/src/odoo_{VERSION}.{TSTAMP}_all.deb",
'su odoo -s /bin/bash -c "odoo -d mycompany -i base --pidfile=/data/src/odoo.pid"', 'su odoo -s /bin/bash -c "odoo -d mycompany -i base --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.run(
" && ".join(cmds),
self.args.build_dir,
"odoo-deb-test-%s" % TSTAMP,
user="root",
detach=True,
exposed_port=8069,
timeout=300,
)
self.test_odoo() self.test_odoo()
logging.info('Finished testing debian package') logging.info("Finished testing debian package")
class DockerRpm(Docker): class DockerRpm(Docker):
"""Docker class to build rpm package""" """Docker class to build rpm package"""
arch = 'rpm' arch = "rpm"
def build(self): def build(self):
logging.info('Start building fedora rpm package') logging.info("Start building fedora rpm package")
rpmbuild_dir = '/var/lib/odoo/rpmbuild' rpmbuild_dir = "/var/lib/odoo/rpmbuild"
cmds = [ cmds = [
'cd /data/src', "cd /data/src",
'mkdir -p dist', "mkdir -p dist",
'rpmdev-setuptree -d', "rpmdev-setuptree -d",
f'cp -a /data/src/setup/rpm/odoo.spec {rpmbuild_dir}/SPECS/', f"cp -a /data/src/setup/rpm/odoo.spec {rpmbuild_dir}/SPECS/",
f'tar --transform "s/^\\./odoo-{VERSION}/" -c -z -f {rpmbuild_dir}/SOURCES/odoo-{VERSION}.tar.gz .', f'tar --transform "s/^\\./odoo-{VERSION}/" -c -z -f {rpmbuild_dir}/SOURCES/odoo-{VERSION}.tar.gz .',
f'rpmbuild -bb --define="%version {VERSION}" /data/src/setup/rpm/odoo.spec', f'rpmbuild -bb --define="%version {VERSION}" /data/src/setup/rpm/odoo.spec',
f'mv {rpmbuild_dir}/RPMS/noarch/odoo*.rpm /data/src/dist/' f"mv {rpmbuild_dir}/RPMS/noarch/odoo*.rpm /data/src/dist/",
] ]
self.run(' && '.join(cmds), self.args.build_dir, f'odoo-rpm-build-{TSTAMP}') self.run(" && ".join(cmds), self.args.build_dir, f"odoo-rpm-build-{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)) os.rename(
logging.info('Finished building fedora rpm package') 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): def start_test(self):
if not self.args.test: if not self.args.test:
return return
logging.info('Start testing rpm package') logging.info("Start testing rpm package")
cmds = [ cmds = [
'su postgres -c "/usr/bin/pg_ctl -D /var/lib/postgres/data start"', 'su postgres -c "/usr/bin/pg_ctl -D /var/lib/postgres/data start"',
'sleep 5', "sleep 5",
'su postgres -c "createuser -s odoo"', 'su postgres -c "createuser -s odoo"',
'su odoo -c "createdb mycompany"', 'su odoo -c "createdb mycompany"',
'dnf install -d 0 -e 0 /data/src/odoo_%s.%s.rpm -y' % (VERSION, TSTAMP), "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 -i base --stop-after-init"',
'su odoo -s /bin/bash -c "odoo -c /etc/odoo/odoo.conf -d mycompany --pidfile=/data/src/odoo.pid"', '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.run(
" && ".join(cmds),
args.build_dir,
"odoo-rpm-test-%s" % TSTAMP,
user="root",
detach=True,
exposed_port=8069,
timeout=300,
)
self.test_odoo() self.test_odoo()
logging.info('Finished testing rpm package') logging.info("Finished testing rpm package")
def gen_rpm_repo(self, args, rpm_filepath): def gen_rpm_repo(self, args, rpm_filepath):
pub_repodata_path = os.path.join(args.pub, 'rpm', 'repodata') pub_repodata_path = os.path.join(args.pub, "rpm", "repodata")
# Removes the old repodata # Removes the old repodata
if os.path.isdir(pub_repodata_path): if os.path.isdir(pub_repodata_path):
shutil.rmtree(pub_repodata_path) shutil.rmtree(pub_repodata_path)
# Copy files to a temp directory (required because the working directory must contain only the # Copy files to a temp directory (required because the working directory must contain only the
# files of the last release) # files of the last release)
temp_path = tempfile.mkdtemp(suffix='rpmPackages') temp_path = tempfile.mkdtemp(suffix="rpmPackages")
shutil.copy(rpm_filepath, temp_path) shutil.copy(rpm_filepath, temp_path)
logging.info('Start creating rpm repo') logging.info("Start creating rpm repo")
self.run('createrepo /data/src/', temp_path, 'odoo-rpm-createrepo-%s' % TSTAMP) self.run("createrepo /data/src/", temp_path, "odoo-rpm-createrepo-%s" % TSTAMP)
shutil.copytree(os.path.join(temp_path, "repodata"), pub_repodata_path) shutil.copytree(os.path.join(temp_path, "repodata"), pub_repodata_path)
# Remove temp directory # Remove temp directory
@ -399,46 +531,84 @@ class DockerRpm(Docker):
class DockerWine(Docker): class DockerWine(Docker):
"""Docker class to build Windows package""" """Docker class to build Windows package"""
arch = 'win' arch = "win"
def build_image(self): def build_image(self):
shutil.copy(os.path.join(self.args.build_dir, 'setup/win32/requirements-local-proxy.txt'), self.docker_dir) shutil.copy(
os.path.join(
self.args.build_dir, "setup/win32/requirements-local-proxy.txt"
),
self.docker_dir,
)
super().build_image() super().build_image()
def build(self): def build(self):
logging.info('Start building windows package') logging.info("Start building windows package")
winver = "%s.%s" % (VERSION.replace('~', '_').replace('+', ''), TSTAMP) winver = "%s.%s" % (VERSION.replace("~", "_").replace("+", ""), TSTAMP)
container_python = '/var/lib/odoo/.wine/drive_c/odoobuild/WinPy64/python-3.12.3.amd64/python.exe' container_python = "/var/lib/odoo/.wine/drive_c/odoobuild/WinPy64/python-3.12.3.amd64/python.exe"
nsis_args = f'/DVERSION={winver} /DMAJOR_VERSION={version_info[0]} /DMINOR_VERSION={version_info[1]} /DSERVICENAME={nt_service_name} /DPYTHONVERSION=3.12.3' nsis_args = f"/DVERSION={winver} /DMAJOR_VERSION={version_info[0]} /DMINOR_VERSION={version_info[1]} /DSERVICENAME={nt_service_name} /DPYTHONVERSION=3.12.3"
cmds = [ cmds = [
rf'wine {container_python} -m pip install --upgrade pip', rf"wine {container_python} -m pip install --upgrade pip",
rf'cat /data/src/requirements*.txt | while read PACKAGE; do wine {container_python} -m pip install "${{PACKAGE%%#*}}" ; done', rf'cat /data/src/requirements*.txt | while read PACKAGE; do wine {container_python} -m pip install "${{PACKAGE%%#*}}" ; done',
rf'wine "c:\nsis-3.10\makensis.exe" {nsis_args} "c:\odoobuild\server\setup\win32\setup.nsi"', rf'wine "c:\nsis-3.10\makensis.exe" {nsis_args} "c:\odoobuild\server\setup\win32\setup.nsi"',
rf'wine {container_python} -m pip list' rf"wine {container_python} -m pip list",
] ]
self.run(' && '.join(cmds), self.args.build_dir, 'odoo-win-build-%s' % TSTAMP) self.run(" && ".join(cmds), self.args.build_dir, "odoo-win-build-%s" % TSTAMP)
logging.info('Finished building Windows package') logging.info("Finished building Windows package")
def parse_args(): def parse_args():
ap = argparse.ArgumentParser() ap = argparse.ArgumentParser()
build_dir = "%s-%s-%s" % (ROOTDIR, TSEC, TSTAMP) build_dir = "%s-%s-%s" % (ROOTDIR, TSEC, TSTAMP)
log_levels = {"debug": logging.DEBUG, "info": logging.INFO, "warning": logging.WARN, "error": logging.ERROR, "critical": logging.CRITICAL} 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(
ap.add_argument("-p", "--pub", default=None, help="pub directory %(default)s", metavar="DIR") "-b",
ap.add_argument("--logging", action="store", choices=list(log_levels.keys()), default="info", help="Logging level") "--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-deb", action="store_true")
ap.add_argument("--build-rpm", action="store_true") ap.add_argument("--build-rpm", action="store_true")
ap.add_argument("--build-tgz", action="store_true") ap.add_argument("--build-tgz", action="store_true")
ap.add_argument("--build-win", action="store_true") ap.add_argument("--build-win", action="store_true")
ap.add_argument("-t", "--test", action="store_true", default=False, help="Test built packages") ap.add_argument(
ap.add_argument("-s", "--sign", action="store_true", default=False, help="Sign Debian package / generate Rpm repo") "-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("--no-remove", action="store_true", help="don't remove build dir")
ap.add_argument("--blacklist", nargs="*", help="Modules to blacklist in package") ap.add_argument("--blacklist", nargs="*", help="Modules to blacklist in package")
parsed_args = ap.parse_args() 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]) 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 parsed_args.odoo_dir = ROOTDIR
return parsed_args return parsed_args
@ -451,7 +621,7 @@ def main(args):
docker_tgz.build() docker_tgz.build()
try: try:
docker_tgz.start_test() docker_tgz.start_test()
published_files = publish(args, 'tgz', ['tar.gz', 'zip']) published_files = publish(args, "tgz", ["tar.gz", "zip"])
except Exception as e: except Exception as e:
logging.error("Won't publish the tgz release.\n Exception: %s" % str(e)) logging.error("Won't publish the tgz release.\n Exception: %s" % str(e))
if args.build_rpm: if args.build_rpm:
@ -460,11 +630,11 @@ def main(args):
docker_rpm.build() docker_rpm.build()
try: try:
docker_rpm.start_test() docker_rpm.start_test()
published_files = publish(args, 'rpm', ['rpm']) published_files = publish(args, "rpm", ["rpm"])
if args.sign: if args.sign:
logging.info('Signing rpm package') logging.info("Signing rpm package")
rpm_sign(args, published_files[0]) rpm_sign(args, published_files[0])
logging.info('Generate rpm repo') logging.info("Generate rpm repo")
docker_rpm.gen_rpm_repo(args, published_files[0]) docker_rpm.gen_rpm_repo(args, published_files[0])
except Exception as e: except Exception as e:
logging.error("Won't publish the rpm release.\n Exception: %s" % str(e)) logging.error("Won't publish the rpm release.\n Exception: %s" % str(e))
@ -474,7 +644,9 @@ def main(args):
docker_deb.build() docker_deb.build()
try: try:
docker_deb.start_test() docker_deb.start_test()
published_files = publish(args, 'deb', ['deb', 'dsc', 'changes', 'tar.xz']) published_files = publish(
args, "deb", ["deb", "dsc", "changes", "tar.xz"]
)
gen_deb_package(args, published_files) gen_deb_package(args, published_files)
except Exception as e: except Exception as e:
logging.error("Won't publish the deb release.\n Exception: %s" % str(e)) logging.error("Won't publish the deb release.\n Exception: %s" % str(e))
@ -483,11 +655,11 @@ def main(args):
docker_wine = DockerWine(args) docker_wine = DockerWine(args)
docker_wine.build() docker_wine.build()
try: try:
published_files = publish(args, 'windows', ['exe']) published_files = publish(args, "windows", ["exe"])
except Exception as e: except Exception as e:
logging.error("Won't publish the exe release.\n Exception: %s" % str(e)) logging.error("Won't publish the exe release.\n Exception: %s" % str(e))
except Exception as e: except Exception as e:
logging.error('Something bad happened ! : {}'.format(e)) logging.error("Something bad happened ! : {}".format(e))
traceback.print_exc() traceback.print_exc()
finally: finally:
if args.no_remove: if args.no_remove:
@ -495,10 +667,10 @@ def main(args):
else: else:
if os.path.exists(args.build_dir): if os.path.exists(args.build_dir):
shutil.rmtree(args.build_dir) shutil.rmtree(args.build_dir)
logging.info('Build dir %s removed' % args.build_dir) logging.info("Build dir %s removed" % args.build_dir)
if __name__ == '__main__': if __name__ == "__main__":
args = parse_args() args = parse_args()
if os.path.exists(args.build_dir): if os.path.exists(args.build_dir):
logging.error('Build dir "%s" already exists.', args.build_dir) logging.error('Build dir "%s" already exists.', args.build_dir)