diff --git a/runbot/common.py b/runbot/common.py
index 034ab99c..f9acc67f 100644
--- a/runbot/common.py
+++ b/runbot/common.py
@@ -1,7 +1,6 @@
# -*- coding: utf-8 -*-
import contextlib
-import fcntl
import itertools
import logging
import os
@@ -14,6 +13,7 @@ from collections import OrderedDict
from datetime import timedelta
from babel.dates import format_timedelta
+from werkzeug import utils
from odoo.tools.misc import DEFAULT_SERVER_DATETIME_FORMAT
@@ -122,3 +122,25 @@ def list_local_dbs(additionnal_conditions=None):
%s
""" % additionnal_condition_str)
return [d[0] for d in local_cr.fetchall()]
+
+
+def pseudo_markdown(text):
+ text = utils.escape(text)
+ patterns = {
+ r'\*\*(.+?)\*\*': '\g<1>',
+ r'~~(.+?)~~': '\g<1>', # it's not official markdown but who cares
+ r'__(.+?)__': '\g<1>', # same here, maybe we should change the method name
+ r'`(.+?)`': '\g<1>
',
+ }
+
+ 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-]+)')
+ text = re_icon.sub('', text)
+
+ # links
+ re_links = re.compile(r'\[(.+?)\]\((.+?)\)')
+ text = re_links.sub('\g<1>', text)
+ return text
diff --git a/runbot/models/build.py b/runbot/models/build.py
index 26d73a23..62ee6f1a 100644
--- a/runbot/models/build.py
+++ b/runbot/models/build.py
@@ -8,7 +8,7 @@ import shutil
import subprocess
import time
import datetime
-from ..common import dt2time, fqdn, now, grep, local_pgadmin_cursor, s2human, Commit, dest_reg, os, list_local_dbs
+from ..common import dt2time, fqdn, now, grep, local_pgadmin_cursor, s2human, Commit, dest_reg, os, list_local_dbs, pseudo_markdown
from ..container import docker_build, docker_stop, docker_state, Command
from ..fields import JsonDictField
from odoo.addons.runbot.models.repo import RunbotException
@@ -65,6 +65,7 @@ class runbot_build(models.Model):
repo_id = fields.Many2one(related='branch_id.repo_id', readonly=True, store=True)
name = fields.Char('Revno', required=True)
description = fields.Char('Description', help='Informative description')
+ md_description = fields.Char(compute='_compute_md_description', String='MD Parsed Description', help='Informative description mardown parsed')
host = fields.Char('Host')
port = fields.Integer('Port')
dest = fields.Char(compute='_compute_dest', type='char', string='Dest', readonly=1, store=True)
@@ -178,6 +179,11 @@ class runbot_build(models.Model):
max_days += int(build.gc_delay if build.gc_delay else 0)
build.gc_date = ref_date + datetime.timedelta(days=(max_days))
+ @api.depends('description')
+ def _compute_md_description(self):
+ for build in self:
+ build.md_description = pseudo_markdown(build.description)
+
def _get_top_parent(self):
self.ensure_one()
build = self
@@ -656,10 +662,10 @@ class runbot_build(models.Model):
if build.requested_action == 'wake_up':
if docker_state(build._get_docker_name(), build._path()) == 'RUNNING':
build.write({'requested_action': False, 'local_state': 'running'})
- build._log('wake_up', 'Waking up failed, docker is already running', level='SEPARATOR')
+ build._log('wake_up', 'Waking up failed, **docker is already running**', type='markdown', level='SEPARATOR')
elif not os.path.exists(build._path()):
build.write({'requested_action': False, 'local_state': 'done'})
- build._log('wake_up', 'Impossible to wake-up, build dir does not exists anymore', level='SEPARATOR')
+ build._log('wake_up', 'Impossible to wake-up, **build dir does not exists anymore**', type='markdown', level='SEPARATOR')
else:
try:
log_path = build._path('logs', 'wake_up.txt')
@@ -673,7 +679,7 @@ class runbot_build(models.Model):
'local_state': 'running',
'port': port,
})
- build._log('wake_up', 'Waking up build', level='SEPARATOR')
+ build._log('wake_up', '**Waking up build**', type='markdown', level='SEPARATOR')
self.env['runbot.build.config.step']._run_odoo_run(build, log_path)
# reload_nginx will be triggered by _run_odoo_run
except Exception:
diff --git a/runbot/models/build_config.py b/runbot/models/build_config.py
index 1b937365..40eb6f5b 100644
--- a/runbot/models/build_config.py
+++ b/runbot/models/build_config.py
@@ -205,7 +205,7 @@ class ConfigStep(models.Model):
def _run(self, build):
log_path = build._path('logs', '%s.txt' % self.name)
build.write({'job_start': now(), 'job_end': False}) # state, ...
- build._log('run', 'Starting step %s from config %s' % (self.name, build.config_id.name), level='SEPARATOR')
+ build._log('run', 'Starting step **%s** from config **%s**' % (self.name, build.config_id.name), type='markdown', level='SEPARATOR')
return self._run_step(build, log_path)
def _run_step(self, build, log_path):
diff --git a/runbot/models/event.py b/runbot/models/event.py
index e4f7fff9..95c32be2 100644
--- a/runbot/models/event.py
+++ b/runbot/models/event.py
@@ -2,11 +2,12 @@
import logging
-from odoo import models, fields, api, tools
+from ..common import pseudo_markdown
+from odoo import models, fields, tools
_logger = logging.getLogger(__name__)
-TYPES = [(t, t.capitalize()) for t in 'client server runbot subbuild link'.split()]
+TYPES = [(t, t.capitalize()) for t in 'client server runbot subbuild link markdown'.split()]
class runbot_event(models.Model):
@@ -71,6 +72,12 @@ FOR EACH ROW EXECUTE PROCEDURE runbot_set_logging_build();
""")
+ def _markdown(self):
+ """ Apply pseudo markdown parser for message.
+ """
+ self.ensure_one()
+ return pseudo_markdown(self.message)
+
class RunbotErrorLog(models.Model):
_name = "runbot.error.log"
diff --git a/runbot/static/src/css/runbot.css b/runbot/static/src/css/runbot.css
index 5d3caee8..3340f171 100644
--- a/runbot/static/src/css/runbot.css
+++ b/runbot/static/src/css/runbot.css
@@ -1,6 +1,5 @@
.separator {
border-top: 2px solid #666;
- font-weight: bold;
}
[data-toggle="collapse"] .fa:before {
diff --git a/runbot/templates/build.xml b/runbot/templates/build.xml
index bda3e3d8..13845140 100644
--- a/runbot/templates/build.xml
+++ b/runbot/templates/build.xml
@@ -117,7 +117,7 @@
diff --git a/runbot/tests/test_build.py b/runbot/tests/test_build.py index c2a25ac3..53fa07f6 100644 --- a/runbot/tests/test_build.py +++ b/runbot/tests/test_build.py @@ -92,6 +92,18 @@ class Test_Build(RunbotCase): build.env.cr.execute("SELECT config_data, config_data->'test_write' AS written, config_data->'test_build' AS test_build FROM runbot_build WHERE id = %s", [build.id]) self.assertEqual([({'test_write': 'written', 'test_build': 'foo'}, 'written', 'foo')], self.env.cr.fetchall()) + def test_markdown_description(self): + build = self.create_build({ + 'branch_id': self.branch.id, + 'name': 'd0d0caca0000ffffffffffffffffffffffffffff', + 'port': '1234', + 'description': 'A nice **description**' + }) + self.assertEqual(build.md_description, 'A nice description') + + build.description = "" + self.assertEqual(build.md_description, "<script>console.log('foo')</script>") + def test_config_data_duplicate(self): build = self.create_build({ diff --git a/runbot/tests/test_event.py b/runbot/tests/test_event.py index c53aad2a..83bf44ea 100644 --- a/runbot/tests/test_event.py +++ b/runbot/tests/test_event.py @@ -68,3 +68,60 @@ class TestIrLogging(RunbotCase): build._log('runbot function', 'runbot message') log_lines = self.IrLogging.search([('type', '=', 'runbot'), ('name', '=', 'odoo.runbot'), ('func', '=', 'runbot function'), ('message', '=', 'runbot message'), ('level', '=', 'INFO')]) self.assertEqual(len(log_lines), 1, '_log should be able to add logs from the runbot') + + def test_markdown(self): + log = self.IrLogging.create({ + 'name': 'odoo.runbot', + 'type': 'runbot', + 'path': 'runbot', + 'level': 'INFO', + 'line': 0, + 'func': 'test_markdown', + 'message': 'some **bold text** and also some __underlined text__ and maybe a bit of ~~strikethrough text~~' + }) + + self.assertEqual( + log._markdown(), + 'some bold text and also some underlined text and maybe a bit of
import foo\nfoo.bar
'
+ )
+
+ # test icon
+ log.message = 'Hello @icon-file-text-o'
+ self.assertEqual(
+ log._markdown(),
+ 'Hello '
+ )
+
+ # test links
+ log.message = 'This [link](https://wwww.somewhere.com) goes to somewhere and [this one](http://www.nowhere.com) to nowhere.'
+ self.assertEqual(
+ log._markdown(),
+ 'This link goes to somewhere and this one to nowhere.'
+ )
+
+ # test link with icon
+ log.message = '[@icon-download](https://wwww.somewhere.com) goes to somewhere.'
+ self.assertEqual(
+ log._markdown(),
+ ' goes to somewhere.'
+ )
+
+ # test links with icon and text
+ log.message = 'This [link@icon-download](https://wwww.somewhere.com) goes to somewhere.'
+ self.assertEqual(
+ log._markdown(),
+ 'This link goes to somewhere.'
+ )
+
+ # test sanitization
+ log.message = 'foo '
+ self.assertEqual(
+ log._markdown(),
+ 'foo <script>console.log("hello world")</script>'
+ )