[IMP]: add linitng tools (+bunch of small fixes)

This commit is contained in:
Xavier-Do 2020-10-14 11:32:08 +02:00
parent c3cc9013df
commit f9272cb207
12 changed files with 55 additions and 25 deletions

View File

@ -1 +1,2 @@
matplotlib==3.0.2 matplotlib==3.0.2
unidiff

View File

@ -127,6 +127,7 @@ def pseudo_markdown(text):
r'~~(.+?)~~': '<del>\g<1></del>', # it's not official markdown but who cares 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 r'__(.+?)__': '<ins>\g<1></ins>', # same here, maybe we should change the method name
r'`(.+?)`': '<code>\g<1></code>', r'`(.+?)`': '<code>\g<1></code>',
r'\r?\n': '<br/>',
} }
for p, b in patterns.items(): for p, b in patterns.items():

View File

@ -145,6 +145,7 @@ def _docker_run(run_cmd, log_path, build_dir, container_name, exposed_ports=None
logs = open(log_path, 'w') logs = open(log_path, 'w')
run_cmd = 'cd /data/build;touch start-%s;%s;cd /data/build;touch end-%s' % (container_name, run_cmd, container_name) run_cmd = 'cd /data/build;touch start-%s;%s;cd /data/build;touch end-%s' % (container_name, run_cmd, container_name)
docker_clear_state(container_name, build_dir) # ensure that no state are remaining docker_clear_state(container_name, build_dir) # ensure that no state are remaining
open(os.path.join(build_dir, 'exist-%s' % container_name), 'w+').close()
logs.write("Docker command:\n%s\n=================================================\n" % cmd_object) logs.write("Docker command:\n%s\n=================================================\n" % cmd_object)
# create start script # create start script
docker_command = [ docker_command = [
@ -208,8 +209,13 @@ def docker_is_running(container_name):
def docker_state(container_name, build_dir): def docker_state(container_name, build_dir):
container_name = sanitize_container_name(container_name) container_name = sanitize_container_name(container_name)
exist = os.path.exists(os.path.join(build_dir, 'exist-%s' % container_name))
started = os.path.exists(os.path.join(build_dir, 'start-%s' % container_name)) started = os.path.exists(os.path.join(build_dir, 'start-%s' % container_name))
ended = os.path.exists(os.path.join(build_dir, 'end-%s' % container_name)) ended = os.path.exists(os.path.join(build_dir, 'end-%s' % container_name))
if not exist:
return 'VOID'
if ended: if ended:
return 'END' return 'END'
@ -229,6 +235,8 @@ def docker_clear_state(container_name, build_dir):
os.remove(os.path.join(build_dir, 'start-%s' % container_name)) os.remove(os.path.join(build_dir, 'start-%s' % container_name))
if os.path.exists(os.path.join(build_dir, 'end-%s' % container_name)): if os.path.exists(os.path.join(build_dir, 'end-%s' % container_name)):
os.remove(os.path.join(build_dir, 'end-%s' % container_name)) os.remove(os.path.join(build_dir, 'end-%s' % container_name))
if os.path.exists(os.path.join(build_dir, 'exist-%s' % container_name)):
os.remove(os.path.join(build_dir, 'exist-%s' % container_name))
def docker_get_gateway_ip(): def docker_get_gateway_ip():
@ -386,6 +394,7 @@ if os.environ.get('RUNBOT_MODE') == 'test':
def fake_docker_run(run_cmd, log_path, build_dir, container_name, exposed_ports=None, cpu_limit=None, preexec_fn=None, ro_volumes=None, env_variables=None, *args, **kwargs): def fake_docker_run(run_cmd, log_path, build_dir, container_name, exposed_ports=None, cpu_limit=None, preexec_fn=None, ro_volumes=None, env_variables=None, *args, **kwargs):
_logger.info('Docker Fake Run: %s', run_cmd) _logger.info('Docker Fake Run: %s', run_cmd)
open(os.path.join(build_dir, 'exist-%s' % container_name), 'w').write('fake end')
open(os.path.join(build_dir, 'start-%s' % container_name), 'w').write('fake start\n') open(os.path.join(build_dir, 'start-%s' % container_name), 'w').write('fake start\n')
open(os.path.join(build_dir, 'end-%s' % container_name), 'w').write('fake end') open(os.path.join(build_dir, 'end-%s' % container_name), 'w').write('fake end')
with open(log_path, 'w') as log_file: with open(log_path, 'w') as log_file:

View File

@ -213,7 +213,7 @@ class BuildResult(models.Model):
def _compute_log_list(self): # storing this field because it will be access trhoug repo viewn and keep track of the list at create def _compute_log_list(self): # storing this field because it will be access trhoug repo viewn and keep track of the list at create
for build in self: for build in self:
build.log_list = ','.join({step.name for step in build.params_id.config_id.step_ids() if step._has_log()}) build.log_list = ','.join({step.name for step in build.params_id.config_id.step_ids() if step._has_log()})
# should be moved # TODO replace logic, add log file to list when executed (avoid 404, link log on docker start, avoid fake is_docker_step)
@api.depends('children_ids.global_state', 'local_state') @api.depends('children_ids.global_state', 'local_state')
def _compute_global_state(self): def _compute_global_state(self):

View File

@ -1,10 +1,12 @@
import base64 import base64
import glob import glob
import json
import logging import logging
import fnmatch import fnmatch
import re import re
import shlex import shlex
import time import time
from unidiff import PatchSet
from ..common import now, grep, time2str, rfind, s2human, os, RunbotException from ..common import now, grep, time2str, rfind, s2human, os, RunbotException
from ..container import docker_run, docker_get_gateway_ip, Command from ..container import docker_run, docker_get_gateway_ip, Command
from odoo import models, fields, api from odoo import models, fields, api
@ -265,11 +267,12 @@ class ConfigStep(models.Model):
'log_path': build._path('logs', '%s.txt' % self.name), 'log_path': build._path('logs', '%s.txt' % self.name),
'glob': glob.glob, 'glob': glob.glob,
'Command': Command, 'Command': Command,
'base64': base64,
're': re, 're': re,
'time': time, 'time': time,
'grep': grep, 'grep': grep,
'rfind': rfind, 'rfind': rfind,
'json_loads': json.loads,
'PatchSet': PatchSet,
} }
def _run_python(self, build, log_path): def _run_python(self, build, log_path):
@ -290,7 +293,7 @@ class ConfigStep(models.Model):
if not self: if not self:
return False return False
self.ensure_one() self.ensure_one()
return self.job_type in ('install_odoo', 'run_odoo', 'restore', 'test_upgrade') or (self.job_type == 'python' and ('docker_run(' in self.python_code or '_run_install_odoo(' in self.python_code)) return self.job_type in ('install_odoo', 'run_odoo', 'restore', 'test_upgrade') or (self.job_type == 'python' and ('docker_run(' in self.python_code or '_run_' in self.python_code))
def _run_run_odoo(self, build, log_path, force=False): def _run_run_odoo(self, build, log_path, force=False):
if not force: if not force:
@ -963,9 +966,11 @@ class ConfigStep(models.Model):
self._check_log, self._check_log,
self._check_module_loaded, self._check_module_loaded,
self._check_error, self._check_error,
self._check_warning,
self._check_build_ended self._check_build_ended
] ]
if build.local_result != 'warn':
checkers.append(self._check_warning)
local_result = self._get_checkers_result(build, checkers) local_result = self._get_checkers_result(build, checkers)
build_values['local_result'] = build._get_worst_result([build.local_result, local_result]) build_values['local_result'] = build._get_worst_result([build.local_result, local_result])
return build_values return build_values

View File

@ -2,8 +2,9 @@
<odoo> <odoo>
<data> <data>
<template id="runbot.batch"> <template id="runbot.batch">
<t t-call='website.layout'> <t t-call="website.layout">
<div class="table-responsive"> <div class="row">
<div class="col-lg-6">
<table class="table table-stripped"> <table class="table table-stripped">
<tr> <tr>
<td>Bundle</td> <td>Bundle</td>
@ -110,10 +111,22 @@
</div> </div>
</td> </td>
</tr> </tr>
</table>
</div>
<div class="col-lg-6">
<table class="table table-stripped">
<tr> <tr>
<td>Builds</td> <td>Builds</td>
<td> <td>
<t t-foreach="batch.slot_ids" t-as="slot"> <t t-foreach="batch.slot_ids.filtered(lambda s: not s.manual)" t-as="slot">
<t t-call="runbot.slot_button"/>
</t>
</td>
</tr>
<tr>
<td>Manual</td>
<td>
<t t-foreach="batch.slot_ids.filtered(lambda s: s.manual)" t-as="slot">
<t t-call="runbot.slot_button"/> <t t-call="runbot.slot_button"/>
</t> </t>
</td> </td>
@ -129,16 +142,14 @@
</td> </td>
</tr> </tr>
</table> </table>
</div>
</div> </div>
<t t-foreach="batch.log_ids" t-as="log"> <t t-foreach="batch.log_ids" t-as="log">
<t t-set="logclass" t-value="dict(ERROR='danger', WARNING='warning', INFO='info').get(log.level, 'warning')"/> <t t-set="logclass" t-value="dict(ERROR='danger', WARNING='warning', INFO='info').get(log.level, 'warning')"/>
<div t-attf-class="alert alert-{{logclass}}"> <div t-attf-class="alert alert-{{logclass}}">
<b t-esc="log.level"/> <b t-esc="log.level"/>
-- --
<t t-foreach="log._markdown().split('\n')" t-as="line"> <t t-raw="log._markdown()"/>
<span t-esc="line"/>
<br t-if="not line_last"/>
</t>
</div> </div>
</t> </t>
</t> </t>

View File

@ -125,7 +125,7 @@
<t t-foreach="build.params_id.build_ids" t-as="simbuild"> <t t-foreach="build.params_id.build_ids" t-as="simbuild">
<a t-if="simbuild.id != build.id" t-attf-href="/runbot/build/#{simbuild.id}"> <a t-if="simbuild.id != build.id" t-attf-href="/runbot/build/#{simbuild.id}">
<span <span
t-attf-class="label label-{{simbuild.get_color_class()}}" t-attf-class="badge badge-{{simbuild.get_color_class()}}"
t-esc="simbuild.id"/> t-esc="simbuild.id"/>
</a> </a>
</t> </t>
@ -179,7 +179,7 @@
</t> </t>
</td> </td>
<td> <td>
<span t-attf-class="label label-info" t-esc="child.get_formated_build_time()"/> <span t-attf-class="badge badge-info" t-esc="child.get_formated_build_time()"/>
</td> </td>
<td> <td>
<t t-call="runbot.build_button"> <t t-call="runbot.build_button">
@ -217,7 +217,7 @@
<t t-foreach="build.sudo().log_ids" t-as="l"> <t t-foreach="build.sudo().log_ids" t-as="l">
<t t-set="subbuild" t-value="(([child for child in build.children_ids if child.id == int(l.path)] if l.type == 'subbuild' else False) or [build.browse()])[0]"/> <t t-set="subbuild" t-value="(([child for child in build.children_ids if child.id == int(l.path)] if l.type == 'subbuild' else False) or [build.browse()])[0]"/>
<t t-set="logclass" t-value="dict(CRITICAL='danger', ERROR='danger', WARNING='warning', OK='success', SEPARATOR='separator').get(l.level)"/> <t t-set="logclass" t-value="dict(CRITICAL='danger', ERROR='danger', WARNING='warning', OK='success', SEPARATOR='separator').get(l.level)"/>
<tr t-attf-class="'bg-%s-light' % {{logclass}} if {{logclass}} != 'separator' else {{logclass}}"> <tr t-att-class="'separator' if logclass != 'separator' else ''">
<td style="white-space: nowrap; width:1%;"> <td style="white-space: nowrap; width:1%;">
<t t-esc="l.create_date.strftime('%Y-%m-%d %H:%M:%S')"/> <t t-esc="l.create_date.strftime('%Y-%m-%d %H:%M:%S')"/>
</td> </td>
@ -273,9 +273,7 @@
<t t-elif="l.type == 'markdown'" t-raw="l._markdown()"/> <t t-elif="l.type == 'markdown'" t-raw="l._markdown()"/>
<t t-else=""> <t t-else="">
<t t-if="'\n' not in l.message" t-esc="l.message"/> <t t-if="'\n' not in l.message" t-esc="l.message"/>
<pre t-if="'\n' in l.message" style="margin:0;padding:0; border: none;"> <pre t-if="'\n' in l.message" style="margin:0;padding:0; border: none;"><t t-esc="l.message"/></pre>
<t t-esc="l.message"/>
</pre>
<t t-if="l.type == 'subbuild' and subbuild.sudo().error_log_ids"> <t t-if="l.type == 'subbuild' and subbuild.sudo().error_log_ids">
<a class="show" data-toggle="collapse" t-attf-data-target="#subbuild-{{subbuild.id}}"> <a class="show" data-toggle="collapse" t-attf-data-target="#subbuild-{{subbuild.id}}">
<i class="fa"/> <i class="fa"/>

View File

@ -88,7 +88,7 @@
<div class="batch_slots"> <div class="batch_slots">
<t t-foreach="batch.slot_ids" t-as="slot"> <t t-foreach="batch.slot_ids" t-as="slot">
<t t-if="slot.build_id"> <t t-if="slot.build_id">
<div t-if="(not slot.trigger_id.hide and trigger_display is None) or (trigger_display and slot.trigger_id.id in trigger_display)" <div t-if="not slot.trigger_id.manual and ((not slot.trigger_id.hide and trigger_display is None) or (trigger_display and slot.trigger_id.id in trigger_display))"
t-call="runbot.slot_button" class="slot_container"/> t-call="runbot.slot_button" class="slot_container"/>
</t> </t>
</t> </t>

View File

@ -67,7 +67,7 @@
<t t-if="triggers"> <t t-if="triggers">
<input type="hidden" name="update_triggers" t-att-value="project.id"/> <input type="hidden" name="update_triggers" t-att-value="project.id"/>
<t t-foreach="triggers" t-as="trigger"> <t t-foreach="triggers" t-as="trigger">
<div class="text-nowrap"> <div t-if="not trigger.manual" class="text-nowrap">
<input type="checkbox" t-attf-name="trigger_{{trigger.id}}" t-attf-id="trigger_{{trigger.id}}" t-att-checked="trigger_display is None or trigger.id in trigger_display"/> <input type="checkbox" t-attf-name="trigger_{{trigger.id}}" t-attf-id="trigger_{{trigger.id}}" t-att-checked="trigger_display is None or trigger.id in trigger_display"/>
<label t-attf-for="trigger_{{trigger.id}}" t-esc="trigger.name"/> <label t-attf-for="trigger_{{trigger.id}}" t-esc="trigger.name"/>
</div> </div>

View File

@ -71,12 +71,19 @@ class TestIrLogging(RunbotCase):
'some <strong>bold text</strong> and also some <ins>underlined text</ins> and maybe a bit of <del>strikethrough text</del>' 'some <strong>bold text</strong> and also some <ins>underlined text</ins> and maybe a bit of <del>strikethrough text</del>'
) )
log.message = 'a bit of code `import foo\nfoo.bar`' #log.message = 'a bit of code `import foo\nfoo.bar`'
#self.assertEqual(
# log._markdown(),
# 'a bit of code <code>import foo\nfoo.bar</code>'
#)
log.message = 'a bit of code :\n`import foo`'
self.assertEqual( self.assertEqual(
log._markdown(), log._markdown(),
'a bit of code <code>import foo\nfoo.bar</code>' 'a bit of code :<br/><code>import foo</code>'
) )
# test icon # test icon
log.message = 'Hello @icon-file-text-o' log.message = 'Hello @icon-file-text-o'
self.assertEqual( self.assertEqual(

View File

@ -161,8 +161,6 @@ class TestUpgradeFlow(RunbotCase):
'upgrade_dumps_trigger_id': self.trigger_addons_nightly.id, 'upgrade_dumps_trigger_id': self.trigger_addons_nightly.id,
}) })
self.branch_upgrade.bundle_id # force recompute TODO remove this once fixed
with mute_logger('odoo.addons.runbot.models.commit'): with mute_logger('odoo.addons.runbot.models.commit'):
self.build_niglty_master, self.build_weekly_master = self.create_version('master') self.build_niglty_master, self.build_weekly_master = self.create_version('master')
self.build_niglty_11, self.build_weekly_11 = self.create_version('11.0') self.build_niglty_11, self.build_weekly_11 = self.create_version('11.0')

View File

@ -35,7 +35,7 @@ class Step(models.Model):
cla = ''.join(io.open(f, encoding='utf-8').read() for f in cla_glob) cla = ''.join(io.open(f, encoding='utf-8').read() for f in cla_glob)
if cla.lower().find(email) == -1: if cla.lower().find(email) == -1:
error = True error = True
build._log('check_cla', 'Invalid email format %s' % email, level="ERROR") build._log('check_cla', 'Email not found in cla file %s' % email, level="ERROR")
except UnicodeDecodeError: except UnicodeDecodeError:
error = True error = True
build._log('check_cla', 'Invalid CLA encoding (must be utf-8)', level="ERROR") build._log('check_cla', 'Invalid CLA encoding (must be utf-8)', level="ERROR")