runbot/runbot/templates/frontend.xml
Xavier-Do 21d6c84b26 [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-05 15:33:41 +02:00

133 lines
7.0 KiB
XML

<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<template id="runbot.bundles">
<t t-call='runbot.layout'>
<t t-set="nav_form">
<form class="form-inline my-2 my-lg-0" role="search" t-att-action="qu(search='')" method="get">
<div class="input-group md-form form-sm form-2 ps-0">
<input class="form-control my-0 py-1" type="text" placeholder="Search" aria-label="Search" name="search" t-att-value="search"/>
<a t-if="has_pr" class="btn btn-primary active input-group-text" title="All" t-att-href="qu(has_pr=None)">
<i class="fa fa-github text-grey"/>
</a>
<a t-else="" class="btn input-group-text" title="Open pull requests" t-att-href="qu(has_pr=1)">
<i class="fa fa-github text-grey"/>
</a>
<button type='submit' class="input-group-text">
<i class="fa fa-search text-grey"/>
</button>
</div>
</form>
</t>
<div class="container-fluid frontend">
<div class="row">
<div class='col-md-12'>
<span class="pull-right" t-call="runbot.slots_infos"/>
</div>
<div class='col-md-12'>
<div t-if="message" class="alert alert-warning" role="alert">
<t t-esc="message" />
</div>
<div t-if="not project" class="mb32">
<h3>No project</h3>
</div>
<div t-else="">
<div t-foreach="bundles" t-as="bundle" class="row bundle_row">
<div class="col-md-3 col-lg-2 cell">
<div class="one_line">
<i t-if="bundle.sticky" class="fa fa-star" style="color: #f0ad4e" />
<a t-attf-href="/runbot/bundle/#{bundle.id}" t-attf-title="View Bundle #{bundle.name}">
<b t-esc="bundle.name"/>
</a>
</div>
<div class="btn-toolbar" role="toolbar" aria-label="Toolbar with button groups">
<div class="btn-group" role="group">
<t t-foreach="categories" t-as="category">
<t t-if="active_category_id != category.id">
<t t-set="last_category_batch" t-value="bundle.with_context(category_id=category.id).last_done_batch"/>
<t t-if="last_category_batch">
<t t-if="category.view_id" t-call="{{category.view_id.sudo().key}}"/>
<a t-else=""
t-attf-title="View last {{category.name}} batch"
t-attf-href="/runbot/batch/{{last_category_batch.id}}"
t-attf-class="fa fa-{{category.icon}}"
/>
</t>
</t>
</t>
</div>
<div class="btn-group" role="group">
<t t-if="not bundle.sticky" t-call="runbot.branch_copy_button"/>
<t t-call="runbot.branch_github_menu"/>
</div>
</div>
<div t-if="bundle.host_id">
<span class="badge text-bg-info" t-esc="bundle.host_id.name"></span>
</div>
</div>
<div class="col-md-9 col-lg-10">
<div class="row gx-0">
<div t-foreach="bundle.last_batchs" t-as="batch" t-attf-class="col-md-6 col-xl-3 {{'d-none d-xl-block' if batch_index > 1 else ''}}">
<t t-call="runbot.batch_tile"/>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</t>
</template>
<template id="runbot.batch_tile">
<t t-set="klass">info</t>
<t t-if="batch.state=='skipped'" t-set="klass">killed</t>
<t t-if="batch.state=='done' and all(slot.build_id.global_result == 'ok' for slot in batch.slot_ids if slot.build_id)" t-set="klass">success</t>
<t t-if="batch.state=='done' and any(slot.build_id.global_result in ('ko', 'warn') for slot in batch.slot_ids)" t-set="klass">danger</t>
<div t-attf-class="batch_tile if more">
<div t-attf-class="card bg-{{klass}}-subtle">
<a t-attf-href="/runbot/batch/#{batch.id}" title="View Batch">
<div class="batch_header">
<span t-attf-class="badge text-bg-{{'warning' if batch.has_warning else 'default'}}">
<t t-esc="batch._get_formated_age()"/>
<i class="fa fa-exclamation-triangle" t-if="batch.has_warning"/>
</span>
<span class="float-end header_hover">View batch...</span>
</div>
</a>
<t t-if="batch.state=='preparing'">
<span><i class="fa fa-cog fa-spin fa-fw"/> preparing</span>
</t>
<div class="batch_slots">
<t t-foreach="batch.slot_ids" t-as="slot">
<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)) or slot.build_id.global_result != 'ok'"
t-call="runbot.slot_button" class="slot_container"/>
</t>
</t>
<div class="slot_filler" t-foreach="range(10)" t-as="x"/>
</div>
<div t-if='more' class="batch_commits">
<div t-foreach="batch.commit_link_ids.sorted(lambda cl: (cl.commit_id.repo_id.sequence, cl.commit_id.repo_id.id))" t-as="commit_link" class="one_line">
<t t-set="match_class" t-value="'info' if commit_link.match_type == 'new' else 'secondary'"/>
<a t-attf-href="/runbot/commit/#{commit_link.commit_id.id}" t-attf-class="badge text-bg-{{match_class}}">
<i class="fa fa-fw fa-hashtag" t-if="commit_link.match_type == 'new'" title="This commit is a new head"/>
<i class="fa fa-fw fa-link" t-if="commit_link.match_type == 'head'" title="This commit is an existing head from bundle branches"/>
<i class="fa fa-fw fa-code-fork" t-if="commit_link.match_type == 'base_match'" title="This commit is matched from a base batch with matching merge_base"/>
<i class="fa fa-fw fa-clock-o" t-if="commit_link.match_type == 'base_head'" title="This commit is the head of a base branch"/>
<t t-esc="commit_link.commit_id.dname"/>
</a>
<a t-att-href="'https://%s/commit/%s' % (commit_link.branch_id.remote_id.base_url, commit_link.commit_id.name)" t-attf-class="badge text-bg-{{match_class}}" title="View Commit on Github"><i class="fa fa-github"/></a>
<span t-esc="commit_link.commit_id.subject"/>
</div>
</div>
</div>
</div>
</template>
</data>
</odoo>