2018-02-28 16:31:05 +07:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
import contextlib
|
|
|
|
import itertools
|
|
|
|
import logging
|
|
|
|
import psycopg2
|
|
|
|
import re
|
2022-12-02 19:26:01 +07:00
|
|
|
import requests
|
2018-02-28 16:31:05 +07:00
|
|
|
import socket
|
|
|
|
import time
|
[IMP] runbot: runbot 5.0
Runbot initial architechture was working for a single odoo repo, and was
adapted to build enterprise. Addition of upgrade repo and test began
to make result less intuitive revealing more weakness of the system.
Adding to the oddities of duplicate detection and branch matching,
there was some room for improvement in the runbot models.
This (small) commit introduce the runbot v5.0, designed for a closer
match of odoo's development flows, and hopefully improving devs
experience and making runbot configuration more flexible.
**Remotes:** remote intoduction helps to detect duplicate between odoo and
odoo-dev repos: a commit is now on a repo, a repo having multiple remote.
If a hash is in odoo-dev, we consider that it is the same in odoo.
Note: github seems to manage commit kind of the same way. It is possible
to send a status on a commit on odoo when the commit only exists in
odoo-dev.
This change also allows to remove some repo duplicate configuration
between a repo and his dev corresponding repo.
(modules, server files, manifests, ...)
**Trigger:** before v5.0, only one build per repo was created, making it
difficult to tweak what test to execute in what case. The example use
case was for upgrade. We want to test upgrade to master when pushing on
odoo. But we also want to test upgrade the same way when pushing on
upgrade. We introduce a build that should be ran on pushing on either
repo when each repo already have specific tests.
The trigger allows to specify a build to create with a specific config.
The trigger is executed when any repo of the trigger repo is pushed.
The trigger can define depedencies: only build enterprise when pushing
enterprise, but enterprise needs odoo. Test upgrade to master when pushing
either odoo or upgrade.
Trigger will also allows to extract some build like cla that where
executed on both enterprise and odoo, and hidden in a subbuild.
**Bundle:** Cross repo branches/pr branches matching was hidden in build
creation and can be confusing. A build can be detected as a duplicate
of a pr, but not always if naming is wrong or traget is invalid/changes.
This was mainly because of how a community ref will be found. This was
making ci on pr undeterministic if duplicate matching fails. This was
also creating two build, with one pointing to the other when duplicate
detection was working, but the visual result can be confusing.
Associtaions of remotes and bundles fix this by adding all pr and
related branches from all repo in a bundle. First of all this helps to
visualise what the runbot consider has branch matching and that should
be considered as part of the same task, giving a place where to warn
devs of some possible inconsistencies. Associate whith repo/remote, we
can consider branches in the same repo in a bundle as expected to have
the same head. Only one build is created since trigger considers repo,
not remotes.
**Batch:** A batch is a group of build, a batch on a bundle can be
compared to a build on a branch in previous version. When a branch
is pushed, the corresponding bundle creates a new batch, and wait for
new commit. Once no new update are detected in the batch for 60 seconds,
All the trigger are executed if elligible. The created build are added
to the batch in a batch_slot. It is also possible that an corresponding
build exists (duplicate) and is added to the slot instead of creating a
new build.
Co-authored-by d-fence <moc@odoo.com>
2020-06-03 21:17:42 +07:00
|
|
|
import os
|
2018-02-28 16:31:05 +07:00
|
|
|
|
|
|
|
from collections import OrderedDict
|
2019-06-27 22:41:03 +07:00
|
|
|
from datetime import timedelta
|
2024-02-15 22:55:16 +07:00
|
|
|
from babel.dates import LC_TIME, Locale, TIMEDELTA_UNITS
|
2022-06-07 20:56:26 +07:00
|
|
|
from markupsafe import Markup
|
2018-02-28 16:31:05 +07:00
|
|
|
|
2023-09-18 19:37:30 +07:00
|
|
|
from odoo.tools.misc import DEFAULT_SERVER_DATETIME_FORMAT, html_escape, file_open
|
2018-02-28 16:31:05 +07:00
|
|
|
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
|
[IMP] runbot: runbot 5.0
Runbot initial architechture was working for a single odoo repo, and was
adapted to build enterprise. Addition of upgrade repo and test began
to make result less intuitive revealing more weakness of the system.
Adding to the oddities of duplicate detection and branch matching,
there was some room for improvement in the runbot models.
This (small) commit introduce the runbot v5.0, designed for a closer
match of odoo's development flows, and hopefully improving devs
experience and making runbot configuration more flexible.
**Remotes:** remote intoduction helps to detect duplicate between odoo and
odoo-dev repos: a commit is now on a repo, a repo having multiple remote.
If a hash is in odoo-dev, we consider that it is the same in odoo.
Note: github seems to manage commit kind of the same way. It is possible
to send a status on a commit on odoo when the commit only exists in
odoo-dev.
This change also allows to remove some repo duplicate configuration
between a repo and his dev corresponding repo.
(modules, server files, manifests, ...)
**Trigger:** before v5.0, only one build per repo was created, making it
difficult to tweak what test to execute in what case. The example use
case was for upgrade. We want to test upgrade to master when pushing on
odoo. But we also want to test upgrade the same way when pushing on
upgrade. We introduce a build that should be ran on pushing on either
repo when each repo already have specific tests.
The trigger allows to specify a build to create with a specific config.
The trigger is executed when any repo of the trigger repo is pushed.
The trigger can define depedencies: only build enterprise when pushing
enterprise, but enterprise needs odoo. Test upgrade to master when pushing
either odoo or upgrade.
Trigger will also allows to extract some build like cla that where
executed on both enterprise and odoo, and hidden in a subbuild.
**Bundle:** Cross repo branches/pr branches matching was hidden in build
creation and can be confusing. A build can be detected as a duplicate
of a pr, but not always if naming is wrong or traget is invalid/changes.
This was mainly because of how a community ref will be found. This was
making ci on pr undeterministic if duplicate matching fails. This was
also creating two build, with one pointing to the other when duplicate
detection was working, but the visual result can be confusing.
Associtaions of remotes and bundles fix this by adding all pr and
related branches from all repo in a bundle. First of all this helps to
visualise what the runbot consider has branch matching and that should
be considered as part of the same task, giving a place where to warn
devs of some possible inconsistencies. Associate whith repo/remote, we
can consider branches in the same repo in a bundle as expected to have
the same head. Only one build is created since trigger considers repo,
not remotes.
**Batch:** A batch is a group of build, a batch on a bundle can be
compared to a build on a branch in previous version. When a branch
is pushed, the corresponding bundle creates a new batch, and wait for
new commit. Once no new update are detected in the batch for 60 seconds,
All the trigger are executed if elligible. The created build are added
to the batch in a batch_slot. It is also possible that an corresponding
build exists (duplicate) and is added to the slot instead of creating a
new build.
Co-authored-by d-fence <moc@odoo.com>
2020-06-03 21:17:42 +07:00
|
|
|
dest_reg = re.compile(r'^\d{5,}-.+$')
|
[IMP] runbot: share sources between builds
Multibuild can create generate a lots of checkout, especially for small
and fast jobs, which can overload runbot discs since we are trying not
to clean build immediatly. (To ease bug fix and allow wake up)
This commit proposes to store source on a single place, so that
docker can add them as ro volume in the build directory.
The checkout is also moved to the installs jobs, so that
builds containing only create builds steps won't checkout
the sources.
This change implies to use --addons-path correctly, since odoo
and enterprise addons wont be merged in the same repo anymore.
This will allow to test addons a dev will do, with a closer
command line.
This implies to change the code structure a litle, some changes
where made to remove no-so-usefull fields on build, and some
hard-coded logic (manifest_names and server_names) are now
stored on repo instead.
This changes implies that a build CANNOT write in his sources.
It shouldn't be the case, but it means that runbot cannot be
tested on runbot untill datas are written elsewhere than in static.
Other possibilities are possible, like bind mounting the sources
in the build directory instead of adding ro volumes in docker.
Unfortunately, this needs to give access to mount as sudo for
runbot user and changes docjker config to allow mounts
in volumes which is not the case by default. A plus of this
solution would be to be able to make an overlay mount.
2019-07-08 19:44:32 +07:00
|
|
|
|
|
|
|
|
[IMP] runbot: runbot 5.0
Runbot initial architechture was working for a single odoo repo, and was
adapted to build enterprise. Addition of upgrade repo and test began
to make result less intuitive revealing more weakness of the system.
Adding to the oddities of duplicate detection and branch matching,
there was some room for improvement in the runbot models.
This (small) commit introduce the runbot v5.0, designed for a closer
match of odoo's development flows, and hopefully improving devs
experience and making runbot configuration more flexible.
**Remotes:** remote intoduction helps to detect duplicate between odoo and
odoo-dev repos: a commit is now on a repo, a repo having multiple remote.
If a hash is in odoo-dev, we consider that it is the same in odoo.
Note: github seems to manage commit kind of the same way. It is possible
to send a status on a commit on odoo when the commit only exists in
odoo-dev.
This change also allows to remove some repo duplicate configuration
between a repo and his dev corresponding repo.
(modules, server files, manifests, ...)
**Trigger:** before v5.0, only one build per repo was created, making it
difficult to tweak what test to execute in what case. The example use
case was for upgrade. We want to test upgrade to master when pushing on
odoo. But we also want to test upgrade the same way when pushing on
upgrade. We introduce a build that should be ran on pushing on either
repo when each repo already have specific tests.
The trigger allows to specify a build to create with a specific config.
The trigger is executed when any repo of the trigger repo is pushed.
The trigger can define depedencies: only build enterprise when pushing
enterprise, but enterprise needs odoo. Test upgrade to master when pushing
either odoo or upgrade.
Trigger will also allows to extract some build like cla that where
executed on both enterprise and odoo, and hidden in a subbuild.
**Bundle:** Cross repo branches/pr branches matching was hidden in build
creation and can be confusing. A build can be detected as a duplicate
of a pr, but not always if naming is wrong or traget is invalid/changes.
This was mainly because of how a community ref will be found. This was
making ci on pr undeterministic if duplicate matching fails. This was
also creating two build, with one pointing to the other when duplicate
detection was working, but the visual result can be confusing.
Associtaions of remotes and bundles fix this by adding all pr and
related branches from all repo in a bundle. First of all this helps to
visualise what the runbot consider has branch matching and that should
be considered as part of the same task, giving a place where to warn
devs of some possible inconsistencies. Associate whith repo/remote, we
can consider branches in the same repo in a bundle as expected to have
the same head. Only one build is created since trigger considers repo,
not remotes.
**Batch:** A batch is a group of build, a batch on a bundle can be
compared to a build on a branch in previous version. When a branch
is pushed, the corresponding bundle creates a new batch, and wait for
new commit. Once no new update are detected in the batch for 60 seconds,
All the trigger are executed if elligible. The created build are added
to the batch in a batch_slot. It is also possible that an corresponding
build exists (duplicate) and is added to the slot instead of creating a
new build.
Co-authored-by d-fence <moc@odoo.com>
2020-06-03 21:17:42 +07:00
|
|
|
class RunbotException(Exception):
|
|
|
|
pass
|
[IMP] runbot: share sources between builds
Multibuild can create generate a lots of checkout, especially for small
and fast jobs, which can overload runbot discs since we are trying not
to clean build immediatly. (To ease bug fix and allow wake up)
This commit proposes to store source on a single place, so that
docker can add them as ro volume in the build directory.
The checkout is also moved to the installs jobs, so that
builds containing only create builds steps won't checkout
the sources.
This change implies to use --addons-path correctly, since odoo
and enterprise addons wont be merged in the same repo anymore.
This will allow to test addons a dev will do, with a closer
command line.
This implies to change the code structure a litle, some changes
where made to remove no-so-usefull fields on build, and some
hard-coded logic (manifest_names and server_names) are now
stored on repo instead.
This changes implies that a build CANNOT write in his sources.
It shouldn't be the case, but it means that runbot cannot be
tested on runbot untill datas are written elsewhere than in static.
Other possibilities are possible, like bind mounting the sources
in the build directory instead of adding ro volumes in docker.
Unfortunately, this needs to give access to mount as sudo for
runbot user and changes docjker config to allow mounts
in volumes which is not the case by default. A plus of this
solution would be to be able to make an overlay mount.
2019-07-08 19:44:32 +07:00
|
|
|
|
2019-07-12 21:31:17 +07:00
|
|
|
|
2018-02-28 16:31:05 +07:00
|
|
|
def fqdn():
|
2022-11-03 16:06:50 +07:00
|
|
|
return socket.gethostname()
|
2018-02-28 16:31:05 +07:00
|
|
|
|
|
|
|
|
|
|
|
def time2str(t):
|
2018-03-13 22:38:20 +07:00
|
|
|
return time.strftime(DEFAULT_SERVER_DATETIME_FORMAT, t)
|
2018-02-28 16:31:05 +07:00
|
|
|
|
|
|
|
|
|
|
|
def dt2time(datetime):
|
|
|
|
"""Convert datetime to time"""
|
2020-01-06 14:50:57 +07:00
|
|
|
return time.mktime(datetime.timetuple())
|
2018-02-28 16:31:05 +07:00
|
|
|
|
|
|
|
|
|
|
|
def now():
|
|
|
|
return time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
|
|
|
|
|
|
|
|
|
2022-08-23 19:53:27 +07:00
|
|
|
def findall(filename, pattern):
|
2023-09-18 19:37:30 +07:00
|
|
|
return set(re.findall(pattern, file_open(filename).read()))
|
2022-08-23 19:53:27 +07:00
|
|
|
|
|
|
|
|
2018-02-28 16:31:05 +07:00
|
|
|
def grep(filename, string):
|
|
|
|
if os.path.isfile(filename):
|
2019-11-10 23:16:41 +07:00
|
|
|
return find(filename, string) != -1
|
2018-02-28 16:31:05 +07:00
|
|
|
return False
|
|
|
|
|
|
|
|
|
2019-11-10 23:16:41 +07:00
|
|
|
def find(filename, string):
|
2023-09-18 19:37:30 +07:00
|
|
|
return file_open(filename).read().find(string)
|
2019-11-10 23:16:41 +07:00
|
|
|
|
|
|
|
|
2018-02-28 16:31:05 +07:00
|
|
|
def uniq_list(l):
|
|
|
|
return OrderedDict.fromkeys(l).keys()
|
|
|
|
|
|
|
|
|
|
|
|
def flatten(list_of_lists):
|
|
|
|
return list(itertools.chain.from_iterable(list_of_lists))
|
|
|
|
|
|
|
|
|
|
|
|
def rfind(filename, pattern):
|
|
|
|
"""Determine in something in filename matches the pattern"""
|
|
|
|
if os.path.isfile(filename):
|
|
|
|
regexp = re.compile(pattern, re.M)
|
2023-09-18 19:37:30 +07:00
|
|
|
with file_open(filename, 'r') as f:
|
2024-05-14 17:53:25 +07:00
|
|
|
result = regexp.findall(f.read())
|
|
|
|
return result or False
|
2018-02-28 16:31:05 +07:00
|
|
|
return False
|
|
|
|
|
|
|
|
|
[IMP] runbot: runbot 5.0
Runbot initial architechture was working for a single odoo repo, and was
adapted to build enterprise. Addition of upgrade repo and test began
to make result less intuitive revealing more weakness of the system.
Adding to the oddities of duplicate detection and branch matching,
there was some room for improvement in the runbot models.
This (small) commit introduce the runbot v5.0, designed for a closer
match of odoo's development flows, and hopefully improving devs
experience and making runbot configuration more flexible.
**Remotes:** remote intoduction helps to detect duplicate between odoo and
odoo-dev repos: a commit is now on a repo, a repo having multiple remote.
If a hash is in odoo-dev, we consider that it is the same in odoo.
Note: github seems to manage commit kind of the same way. It is possible
to send a status on a commit on odoo when the commit only exists in
odoo-dev.
This change also allows to remove some repo duplicate configuration
between a repo and his dev corresponding repo.
(modules, server files, manifests, ...)
**Trigger:** before v5.0, only one build per repo was created, making it
difficult to tweak what test to execute in what case. The example use
case was for upgrade. We want to test upgrade to master when pushing on
odoo. But we also want to test upgrade the same way when pushing on
upgrade. We introduce a build that should be ran on pushing on either
repo when each repo already have specific tests.
The trigger allows to specify a build to create with a specific config.
The trigger is executed when any repo of the trigger repo is pushed.
The trigger can define depedencies: only build enterprise when pushing
enterprise, but enterprise needs odoo. Test upgrade to master when pushing
either odoo or upgrade.
Trigger will also allows to extract some build like cla that where
executed on both enterprise and odoo, and hidden in a subbuild.
**Bundle:** Cross repo branches/pr branches matching was hidden in build
creation and can be confusing. A build can be detected as a duplicate
of a pr, but not always if naming is wrong or traget is invalid/changes.
This was mainly because of how a community ref will be found. This was
making ci on pr undeterministic if duplicate matching fails. This was
also creating two build, with one pointing to the other when duplicate
detection was working, but the visual result can be confusing.
Associtaions of remotes and bundles fix this by adding all pr and
related branches from all repo in a bundle. First of all this helps to
visualise what the runbot consider has branch matching and that should
be considered as part of the same task, giving a place where to warn
devs of some possible inconsistencies. Associate whith repo/remote, we
can consider branches in the same repo in a bundle as expected to have
the same head. Only one build is created since trigger considers repo,
not remotes.
**Batch:** A batch is a group of build, a batch on a bundle can be
compared to a build on a branch in previous version. When a branch
is pushed, the corresponding bundle creates a new batch, and wait for
new commit. Once no new update are detected in the batch for 60 seconds,
All the trigger are executed if elligible. The created build are added
to the batch in a batch_slot. It is also possible that an corresponding
build exists (duplicate) and is added to the slot instead of creating a
new build.
Co-authored-by d-fence <moc@odoo.com>
2020-06-03 21:17:42 +07:00
|
|
|
def time_delta(time):
|
|
|
|
if isinstance(time, timedelta):
|
|
|
|
return time
|
|
|
|
return timedelta(seconds=-time)
|
|
|
|
|
2024-02-15 22:55:16 +07:00
|
|
|
from babel.dates import format_timedelta as _format_timedelta
|
|
|
|
|
|
|
|
|
|
|
|
def format_timedelta(delta, granularity='second', max_unit=None, threshold=.85,
|
|
|
|
add_direction=False, format='long',
|
|
|
|
locale=LC_TIME):
|
|
|
|
"""
|
|
|
|
Modified version of Dates.format_timedelta
|
|
|
|
"""
|
|
|
|
if format not in ('narrow', 'short', 'long'):
|
|
|
|
raise TypeError('Format must be one of "narrow", "short" or "long"')
|
|
|
|
if isinstance(delta, timedelta):
|
|
|
|
seconds = int((delta.days * 86400) + delta.seconds)
|
|
|
|
else:
|
|
|
|
seconds = delta
|
|
|
|
locale = Locale.parse(locale)
|
|
|
|
|
|
|
|
def _iter_patterns(a_unit):
|
|
|
|
if add_direction:
|
|
|
|
unit_rel_patterns = locale._data['date_fields'][a_unit]
|
|
|
|
if seconds >= 0:
|
|
|
|
yield unit_rel_patterns['future']
|
|
|
|
else:
|
|
|
|
yield unit_rel_patterns['past']
|
|
|
|
a_unit = 'duration-' + a_unit
|
|
|
|
yield locale._data['unit_patterns'].get(a_unit, {}).get(format)
|
|
|
|
|
|
|
|
for unit, secs_per_unit in TIMEDELTA_UNITS:
|
|
|
|
if max_unit and unit != max_unit:
|
|
|
|
continue
|
|
|
|
max_unit = None
|
|
|
|
value = abs(seconds) / secs_per_unit
|
|
|
|
if value >= threshold or unit == granularity:
|
|
|
|
if unit == granularity and value > 0:
|
|
|
|
value = max(1, value)
|
|
|
|
value = int(round(value))
|
|
|
|
plural_form = locale.plural_form(value)
|
|
|
|
pattern = None
|
|
|
|
for patterns in _iter_patterns(unit):
|
|
|
|
if patterns is not None:
|
|
|
|
pattern = patterns[plural_form]
|
|
|
|
break
|
|
|
|
# This really should not happen
|
|
|
|
if pattern is None:
|
|
|
|
return u''
|
|
|
|
return pattern.replace('{0}', str(value))
|
|
|
|
|
|
|
|
return u''
|
|
|
|
|
[IMP] runbot: runbot 5.0
Runbot initial architechture was working for a single odoo repo, and was
adapted to build enterprise. Addition of upgrade repo and test began
to make result less intuitive revealing more weakness of the system.
Adding to the oddities of duplicate detection and branch matching,
there was some room for improvement in the runbot models.
This (small) commit introduce the runbot v5.0, designed for a closer
match of odoo's development flows, and hopefully improving devs
experience and making runbot configuration more flexible.
**Remotes:** remote intoduction helps to detect duplicate between odoo and
odoo-dev repos: a commit is now on a repo, a repo having multiple remote.
If a hash is in odoo-dev, we consider that it is the same in odoo.
Note: github seems to manage commit kind of the same way. It is possible
to send a status on a commit on odoo when the commit only exists in
odoo-dev.
This change also allows to remove some repo duplicate configuration
between a repo and his dev corresponding repo.
(modules, server files, manifests, ...)
**Trigger:** before v5.0, only one build per repo was created, making it
difficult to tweak what test to execute in what case. The example use
case was for upgrade. We want to test upgrade to master when pushing on
odoo. But we also want to test upgrade the same way when pushing on
upgrade. We introduce a build that should be ran on pushing on either
repo when each repo already have specific tests.
The trigger allows to specify a build to create with a specific config.
The trigger is executed when any repo of the trigger repo is pushed.
The trigger can define depedencies: only build enterprise when pushing
enterprise, but enterprise needs odoo. Test upgrade to master when pushing
either odoo or upgrade.
Trigger will also allows to extract some build like cla that where
executed on both enterprise and odoo, and hidden in a subbuild.
**Bundle:** Cross repo branches/pr branches matching was hidden in build
creation and can be confusing. A build can be detected as a duplicate
of a pr, but not always if naming is wrong or traget is invalid/changes.
This was mainly because of how a community ref will be found. This was
making ci on pr undeterministic if duplicate matching fails. This was
also creating two build, with one pointing to the other when duplicate
detection was working, but the visual result can be confusing.
Associtaions of remotes and bundles fix this by adding all pr and
related branches from all repo in a bundle. First of all this helps to
visualise what the runbot consider has branch matching and that should
be considered as part of the same task, giving a place where to warn
devs of some possible inconsistencies. Associate whith repo/remote, we
can consider branches in the same repo in a bundle as expected to have
the same head. Only one build is created since trigger considers repo,
not remotes.
**Batch:** A batch is a group of build, a batch on a bundle can be
compared to a build on a branch in previous version. When a branch
is pushed, the corresponding bundle creates a new batch, and wait for
new commit. Once no new update are detected in the batch for 60 seconds,
All the trigger are executed if elligible. The created build are added
to the batch in a batch_slot. It is also possible that an corresponding
build exists (duplicate) and is added to the slot instead of creating a
new build.
Co-authored-by d-fence <moc@odoo.com>
2020-06-03 21:17:42 +07:00
|
|
|
|
2018-02-28 16:31:05 +07:00
|
|
|
def s2human(time):
|
|
|
|
"""Convert a time in second into an human readable string"""
|
2019-06-27 22:41:03 +07:00
|
|
|
return format_timedelta(
|
[IMP] runbot: runbot 5.0
Runbot initial architechture was working for a single odoo repo, and was
adapted to build enterprise. Addition of upgrade repo and test began
to make result less intuitive revealing more weakness of the system.
Adding to the oddities of duplicate detection and branch matching,
there was some room for improvement in the runbot models.
This (small) commit introduce the runbot v5.0, designed for a closer
match of odoo's development flows, and hopefully improving devs
experience and making runbot configuration more flexible.
**Remotes:** remote intoduction helps to detect duplicate between odoo and
odoo-dev repos: a commit is now on a repo, a repo having multiple remote.
If a hash is in odoo-dev, we consider that it is the same in odoo.
Note: github seems to manage commit kind of the same way. It is possible
to send a status on a commit on odoo when the commit only exists in
odoo-dev.
This change also allows to remove some repo duplicate configuration
between a repo and his dev corresponding repo.
(modules, server files, manifests, ...)
**Trigger:** before v5.0, only one build per repo was created, making it
difficult to tweak what test to execute in what case. The example use
case was for upgrade. We want to test upgrade to master when pushing on
odoo. But we also want to test upgrade the same way when pushing on
upgrade. We introduce a build that should be ran on pushing on either
repo when each repo already have specific tests.
The trigger allows to specify a build to create with a specific config.
The trigger is executed when any repo of the trigger repo is pushed.
The trigger can define depedencies: only build enterprise when pushing
enterprise, but enterprise needs odoo. Test upgrade to master when pushing
either odoo or upgrade.
Trigger will also allows to extract some build like cla that where
executed on both enterprise and odoo, and hidden in a subbuild.
**Bundle:** Cross repo branches/pr branches matching was hidden in build
creation and can be confusing. A build can be detected as a duplicate
of a pr, but not always if naming is wrong or traget is invalid/changes.
This was mainly because of how a community ref will be found. This was
making ci on pr undeterministic if duplicate matching fails. This was
also creating two build, with one pointing to the other when duplicate
detection was working, but the visual result can be confusing.
Associtaions of remotes and bundles fix this by adding all pr and
related branches from all repo in a bundle. First of all this helps to
visualise what the runbot consider has branch matching and that should
be considered as part of the same task, giving a place where to warn
devs of some possible inconsistencies. Associate whith repo/remote, we
can consider branches in the same repo in a bundle as expected to have
the same head. Only one build is created since trigger considers repo,
not remotes.
**Batch:** A batch is a group of build, a batch on a bundle can be
compared to a build on a branch in previous version. When a branch
is pushed, the corresponding bundle creates a new batch, and wait for
new commit. Once no new update are detected in the batch for 60 seconds,
All the trigger are executed if elligible. The created build are added
to the batch in a batch_slot. It is also possible that an corresponding
build exists (duplicate) and is added to the slot instead of creating a
new build.
Co-authored-by d-fence <moc@odoo.com>
2020-06-03 21:17:42 +07:00
|
|
|
time_delta(time),
|
2024-02-15 22:55:16 +07:00
|
|
|
max_unit='hour',
|
2019-06-27 22:41:03 +07:00
|
|
|
format="narrow",
|
|
|
|
threshold=2.1,
|
|
|
|
)
|
2018-02-28 16:31:05 +07:00
|
|
|
|
2020-01-07 23:02:05 +07:00
|
|
|
|
[IMP] runbot: runbot 5.0
Runbot initial architechture was working for a single odoo repo, and was
adapted to build enterprise. Addition of upgrade repo and test began
to make result less intuitive revealing more weakness of the system.
Adding to the oddities of duplicate detection and branch matching,
there was some room for improvement in the runbot models.
This (small) commit introduce the runbot v5.0, designed for a closer
match of odoo's development flows, and hopefully improving devs
experience and making runbot configuration more flexible.
**Remotes:** remote intoduction helps to detect duplicate between odoo and
odoo-dev repos: a commit is now on a repo, a repo having multiple remote.
If a hash is in odoo-dev, we consider that it is the same in odoo.
Note: github seems to manage commit kind of the same way. It is possible
to send a status on a commit on odoo when the commit only exists in
odoo-dev.
This change also allows to remove some repo duplicate configuration
between a repo and his dev corresponding repo.
(modules, server files, manifests, ...)
**Trigger:** before v5.0, only one build per repo was created, making it
difficult to tweak what test to execute in what case. The example use
case was for upgrade. We want to test upgrade to master when pushing on
odoo. But we also want to test upgrade the same way when pushing on
upgrade. We introduce a build that should be ran on pushing on either
repo when each repo already have specific tests.
The trigger allows to specify a build to create with a specific config.
The trigger is executed when any repo of the trigger repo is pushed.
The trigger can define depedencies: only build enterprise when pushing
enterprise, but enterprise needs odoo. Test upgrade to master when pushing
either odoo or upgrade.
Trigger will also allows to extract some build like cla that where
executed on both enterprise and odoo, and hidden in a subbuild.
**Bundle:** Cross repo branches/pr branches matching was hidden in build
creation and can be confusing. A build can be detected as a duplicate
of a pr, but not always if naming is wrong or traget is invalid/changes.
This was mainly because of how a community ref will be found. This was
making ci on pr undeterministic if duplicate matching fails. This was
also creating two build, with one pointing to the other when duplicate
detection was working, but the visual result can be confusing.
Associtaions of remotes and bundles fix this by adding all pr and
related branches from all repo in a bundle. First of all this helps to
visualise what the runbot consider has branch matching and that should
be considered as part of the same task, giving a place where to warn
devs of some possible inconsistencies. Associate whith repo/remote, we
can consider branches in the same repo in a bundle as expected to have
the same head. Only one build is created since trigger considers repo,
not remotes.
**Batch:** A batch is a group of build, a batch on a bundle can be
compared to a build on a branch in previous version. When a branch
is pushed, the corresponding bundle creates a new batch, and wait for
new commit. Once no new update are detected in the batch for 60 seconds,
All the trigger are executed if elligible. The created build are added
to the batch in a batch_slot. It is also possible that an corresponding
build exists (duplicate) and is added to the slot instead of creating a
new build.
Co-authored-by d-fence <moc@odoo.com>
2020-06-03 21:17:42 +07:00
|
|
|
def s2human_long(time):
|
|
|
|
return format_timedelta(
|
|
|
|
time_delta(time),
|
|
|
|
threshold=2.1,
|
|
|
|
add_direction=True, locale='en'
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2018-02-28 16:31:05 +07:00
|
|
|
@contextlib.contextmanager
|
|
|
|
def local_pgadmin_cursor():
|
|
|
|
cnx = None
|
|
|
|
try:
|
|
|
|
cnx = psycopg2.connect("dbname=postgres")
|
|
|
|
cnx.autocommit = True # required for admin commands
|
|
|
|
yield cnx.cursor()
|
|
|
|
finally:
|
|
|
|
if cnx:
|
|
|
|
cnx.close()
|
2020-01-07 23:02:05 +07:00
|
|
|
|
2022-07-07 21:23:01 +07:00
|
|
|
@contextlib.contextmanager
|
|
|
|
def local_pg_cursor(db_name):
|
|
|
|
cnx = None
|
|
|
|
try:
|
|
|
|
cnx = psycopg2.connect(f"dbname={db_name}")
|
|
|
|
yield cnx.cursor()
|
|
|
|
finally:
|
|
|
|
if cnx:
|
|
|
|
cnx.commit()
|
|
|
|
cnx.close()
|
2020-01-07 23:02:05 +07:00
|
|
|
|
|
|
|
def list_local_dbs(additionnal_conditions=None):
|
|
|
|
additionnal_condition_str = ''
|
|
|
|
if additionnal_conditions:
|
|
|
|
additionnal_condition_str = 'AND (%s)' % ' OR '.join(additionnal_conditions)
|
|
|
|
with local_pgadmin_cursor() as local_cr:
|
|
|
|
local_cr.execute("""
|
|
|
|
SELECT datname
|
|
|
|
FROM pg_database
|
|
|
|
WHERE pg_get_userbyid(datdba) = current_user
|
|
|
|
%s
|
|
|
|
""" % additionnal_condition_str)
|
|
|
|
return [d[0] for d in local_cr.fetchall()]
|
2020-02-26 21:13:51 +07:00
|
|
|
|
|
|
|
|
|
|
|
def pseudo_markdown(text):
|
2022-06-07 21:31:29 +07:00
|
|
|
text = html_escape(text)
|
2021-05-10 19:58:26 +07:00
|
|
|
|
|
|
|
# first, extract code blocs:
|
|
|
|
codes = []
|
|
|
|
def code_remove(match):
|
|
|
|
codes.append(match.group(1))
|
[FIX] runbot: fix markdown adding escape
A common error on runbot is to generate link containing a __init__.py
file
[/some/path/to/__init__.py](/some/path/to/__init__.py)
This would be rendered as
<a href="/some/path/to/<ins>init<ins>.py">/some/path/to/<ins>init<ins>.py</a>
Breaking the link, and the display of the name
By default markdown will not render links avoiding this issue, but it
will remain for the content of the a, needing to manage some kind of
escaping.
The way to escape markdown is to add a \ before any special character
This must be done upront before formating, adding the method
markdown_escape
Our implementation of markdown is not meant to meet the exact
specification of markdown but better suit our needs. One of the
requirements is to be able to use it to format message easily but adding
dynamic countent comming from the outside. One of the error than can
occur is also
'Some code `%s`' % code can also cause problem if code contains `
This issue could be solved using indented code block, but this would
need to complexify the generated string, have a dedicated method to
escape the code blocs, ...
Since we have the controll on the input, we can easily sanitize all
ynamic content to avoid such issues. For code block we introduce a way
to escape backtick (\`). It is non standard but will be easier to use.
Combine with that, the build._log method now allows to add args with the
values to format the string (similar to logging) but will escape
params by default. (cr.execute spirit)
name = '__init__.py'
url = 'path/to/__init__.py'
code = '# comment `for` something'
build._log('f', 'Some message [%s](%s) \n `%s`', name, url, code)
name, url and code will be escaped
2024-09-03 20:25:17 +07:00
|
|
|
return f'<code>{len(codes) - 1}</code>'
|
|
|
|
|
|
|
|
escape = r'(?<!\\)(?:(?:\\\\)*)'
|
|
|
|
|
|
|
|
text = re.sub(rf'{escape}`(.+?{escape})`', code_remove, text, flags=re.DOTALL)
|
2021-05-10 19:58:26 +07:00
|
|
|
|
2020-02-26 21:13:51 +07:00
|
|
|
patterns = {
|
2021-05-10 19:58:26 +07:00
|
|
|
r'\*\*(.+?)\*\*': '<strong>\\g<1></strong>',
|
|
|
|
r'~~(.+?)~~': '<del>\\g<1></del>', # it's not official markdown but who cares
|
|
|
|
r'__(.+?)__': '<ins>\\g<1></ins>', # same here, maybe we should change the method name
|
[FIX] runbot: fix markdown adding escape
A common error on runbot is to generate link containing a __init__.py
file
[/some/path/to/__init__.py](/some/path/to/__init__.py)
This would be rendered as
<a href="/some/path/to/<ins>init<ins>.py">/some/path/to/<ins>init<ins>.py</a>
Breaking the link, and the display of the name
By default markdown will not render links avoiding this issue, but it
will remain for the content of the a, needing to manage some kind of
escaping.
The way to escape markdown is to add a \ before any special character
This must be done upront before formating, adding the method
markdown_escape
Our implementation of markdown is not meant to meet the exact
specification of markdown but better suit our needs. One of the
requirements is to be able to use it to format message easily but adding
dynamic countent comming from the outside. One of the error than can
occur is also
'Some code `%s`' % code can also cause problem if code contains `
This issue could be solved using indented code block, but this would
need to complexify the generated string, have a dedicated method to
escape the code blocs, ...
Since we have the controll on the input, we can easily sanitize all
ynamic content to avoid such issues. For code block we introduce a way
to escape backtick (\`). It is non standard but will be easier to use.
Combine with that, the build._log method now allows to add args with the
values to format the string (similar to logging) but will escape
params by default. (cr.execute spirit)
name = '__init__.py'
url = 'path/to/__init__.py'
code = '# comment `for` something'
build._log('f', 'Some message [%s](%s) \n `%s`', name, url, code)
name, url and code will be escaped
2024-09-03 20:25:17 +07:00
|
|
|
r'\r?\n': '<br/>\n',
|
2020-02-26 21:13:51 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
for p, b in patterns.items():
|
|
|
|
text = re.sub(p, b, text, flags=re.DOTALL)
|
|
|
|
|
|
|
|
# icons
|
|
|
|
re_icon = re.compile(r'@icon-([a-z0-9-]+)')
|
2021-05-10 19:58:26 +07:00
|
|
|
text = re_icon.sub('<i class="fa fa-\\g<1>"></i>', text)
|
2020-02-26 21:13:51 +07:00
|
|
|
|
|
|
|
# links
|
[FIX] runbot: fix markdown adding escape
A common error on runbot is to generate link containing a __init__.py
file
[/some/path/to/__init__.py](/some/path/to/__init__.py)
This would be rendered as
<a href="/some/path/to/<ins>init<ins>.py">/some/path/to/<ins>init<ins>.py</a>
Breaking the link, and the display of the name
By default markdown will not render links avoiding this issue, but it
will remain for the content of the a, needing to manage some kind of
escaping.
The way to escape markdown is to add a \ before any special character
This must be done upront before formating, adding the method
markdown_escape
Our implementation of markdown is not meant to meet the exact
specification of markdown but better suit our needs. One of the
requirements is to be able to use it to format message easily but adding
dynamic countent comming from the outside. One of the error than can
occur is also
'Some code `%s`' % code can also cause problem if code contains `
This issue could be solved using indented code block, but this would
need to complexify the generated string, have a dedicated method to
escape the code blocs, ...
Since we have the controll on the input, we can easily sanitize all
ynamic content to avoid such issues. For code block we introduce a way
to escape backtick (\`). It is non standard but will be easier to use.
Combine with that, the build._log method now allows to add args with the
values to format the string (similar to logging) but will escape
params by default. (cr.execute spirit)
name = '__init__.py'
url = 'path/to/__init__.py'
code = '# comment `for` something'
build._log('f', 'Some message [%s](%s) \n `%s`', name, url, code)
name, url and code will be escaped
2024-09-03 20:25:17 +07:00
|
|
|
re_links = re.compile(rf'{escape}\[(.+?){escape}\]{escape}\(((http|/).+?{escape})\)')
|
2021-05-10 19:58:26 +07:00
|
|
|
text = re_links.sub('<a href="\\g<2>">\\g<1></a>', text)
|
|
|
|
|
|
|
|
def code_replace(match):
|
|
|
|
return f'<code>{codes[int(match.group(1))]}</code>'
|
|
|
|
|
2022-06-07 20:56:26 +07:00
|
|
|
text = Markup(re.sub(r'<code>(\d+)</code>', code_replace, text, flags=re.DOTALL))
|
[FIX] runbot: fix markdown adding escape
A common error on runbot is to generate link containing a __init__.py
file
[/some/path/to/__init__.py](/some/path/to/__init__.py)
This would be rendered as
<a href="/some/path/to/<ins>init<ins>.py">/some/path/to/<ins>init<ins>.py</a>
Breaking the link, and the display of the name
By default markdown will not render links avoiding this issue, but it
will remain for the content of the a, needing to manage some kind of
escaping.
The way to escape markdown is to add a \ before any special character
This must be done upront before formating, adding the method
markdown_escape
Our implementation of markdown is not meant to meet the exact
specification of markdown but better suit our needs. One of the
requirements is to be able to use it to format message easily but adding
dynamic countent comming from the outside. One of the error than can
occur is also
'Some code `%s`' % code can also cause problem if code contains `
This issue could be solved using indented code block, but this would
need to complexify the generated string, have a dedicated method to
escape the code blocs, ...
Since we have the controll on the input, we can easily sanitize all
ynamic content to avoid such issues. For code block we introduce a way
to escape backtick (\`). It is non standard but will be easier to use.
Combine with that, the build._log method now allows to add args with the
values to format the string (similar to logging) but will escape
params by default. (cr.execute spirit)
name = '__init__.py'
url = 'path/to/__init__.py'
code = '# comment `for` something'
build._log('f', 'Some message [%s](%s) \n `%s`', name, url, code)
name, url and code will be escaped
2024-09-03 20:25:17 +07:00
|
|
|
text = markdown_unescape(text)
|
|
|
|
return text
|
|
|
|
|
|
|
|
patterns = ['\\', '[', ']', '(', ')', '_', '*', '#', '`']
|
|
|
|
|
|
|
|
def markdown_escape(text):
|
|
|
|
text = str(text)
|
|
|
|
for pat in patterns:
|
|
|
|
text = text.replace(pat, rf'\{pat}')
|
2020-02-26 21:13:51 +07:00
|
|
|
return text
|
2022-12-02 19:26:01 +07:00
|
|
|
|
|
|
|
|
[FIX] runbot: fix markdown adding escape
A common error on runbot is to generate link containing a __init__.py
file
[/some/path/to/__init__.py](/some/path/to/__init__.py)
This would be rendered as
<a href="/some/path/to/<ins>init<ins>.py">/some/path/to/<ins>init<ins>.py</a>
Breaking the link, and the display of the name
By default markdown will not render links avoiding this issue, but it
will remain for the content of the a, needing to manage some kind of
escaping.
The way to escape markdown is to add a \ before any special character
This must be done upront before formating, adding the method
markdown_escape
Our implementation of markdown is not meant to meet the exact
specification of markdown but better suit our needs. One of the
requirements is to be able to use it to format message easily but adding
dynamic countent comming from the outside. One of the error than can
occur is also
'Some code `%s`' % code can also cause problem if code contains `
This issue could be solved using indented code block, but this would
need to complexify the generated string, have a dedicated method to
escape the code blocs, ...
Since we have the controll on the input, we can easily sanitize all
ynamic content to avoid such issues. For code block we introduce a way
to escape backtick (\`). It is non standard but will be easier to use.
Combine with that, the build._log method now allows to add args with the
values to format the string (similar to logging) but will escape
params by default. (cr.execute spirit)
name = '__init__.py'
url = 'path/to/__init__.py'
code = '# comment `for` something'
build._log('f', 'Some message [%s](%s) \n `%s`', name, url, code)
name, url and code will be escaped
2024-09-03 20:25:17 +07:00
|
|
|
def markdown_unescape(text):
|
|
|
|
for pat in patterns:
|
|
|
|
text = text.replace(rf'\{pat}', pat)
|
|
|
|
return text
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-09-18 19:37:30 +07:00
|
|
|
def make_github_session(token):
|
2022-12-02 19:26:01 +07:00
|
|
|
session = requests.Session()
|
|
|
|
if token:
|
|
|
|
session.auth = (token, 'x-oauth-basic')
|
|
|
|
session.headers.update({'Accept': 'application/vnd.github.she-hulk-preview+json'})
|
|
|
|
return session
|
2023-09-18 19:37:30 +07:00
|
|
|
|
|
|
|
def sanitize(name):
|
|
|
|
for i in ['@', ':', '/', '\\', '..']:
|
|
|
|
name = name.replace(i, '_')
|
|
|
|
return name
|
|
|
|
|
|
|
|
|
|
|
|
class ReProxy():
|
|
|
|
@classmethod
|
|
|
|
def match(cls, *args, **kwrags):
|
|
|
|
return re.match(*args, **kwrags)
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def search(cls, *args, **kwrags):
|
|
|
|
return re.search(*args, **kwrags)
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def compile(cls, *args, **kwrags):
|
|
|
|
return re.compile(*args, **kwrags)
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def findall(cls, *args, **kwrags):
|
|
|
|
return re.findall(*args, **kwrags)
|
|
|
|
|
|
|
|
VERBOSE = re.VERBOSE
|
|
|
|
MULTILINE = re.MULTILINE
|