mirror of
https://github.com/arcan1s/ahriman.git
synced 2025-04-24 15:27:17 +00:00
migration of jinja tempaltes to bootstrap (#30)
This commit is contained in:
parent
ecf45bc3bb
commit
9b8c9b2b2d
@ -29,8 +29,9 @@ target =
|
|||||||
target =
|
target =
|
||||||
|
|
||||||
[email]
|
[email]
|
||||||
|
full_template_path = /usr/share/ahriman/repo-index.jinja2
|
||||||
no_empty_report = yes
|
no_empty_report = yes
|
||||||
template_path = /usr/share/ahriman/repo-index.jinja2
|
template_path = /usr/share/ahriman/email-index.jinja2
|
||||||
ssl = disabled
|
ssl = disabled
|
||||||
|
|
||||||
[html]
|
[html]
|
||||||
|
@ -3,14 +3,18 @@
|
|||||||
<head>
|
<head>
|
||||||
<title>{{ repository }}</title>
|
<title>{{ repository }}</title>
|
||||||
|
|
||||||
{% include "style.jinja2" %}
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
|
||||||
{% include "sorttable.jinja2" %}
|
<script src="https://kit.fontawesome.com/0d6d6d5226.js" crossorigin="anonymous"></script>
|
||||||
{% include "search.jinja2" %}
|
<link href="https://unpkg.com/bootstrap-table@1.18.3/dist/bootstrap-table.min.css" rel="stylesheet">
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-KyZXEAg3QhqLMpG8r+8fhAXLRk2vvoC2f3B09zVXn8CA5QIVfZOJ3BCsw2P0p/We" crossorigin="anonymous">
|
||||||
|
|
||||||
|
{% include "style.jinja2" %}
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div class="root">
|
|
||||||
|
<div class="container">
|
||||||
<h1>ahriman
|
<h1>ahriman
|
||||||
{% if authorized %}
|
{% if authorized %}
|
||||||
<img src="https://img.shields.io/badge/version-{{ version }}-informational" alt="{{ version }}">
|
<img src="https://img.shields.io/badge/version-{{ version }}-informational" alt="{{ version }}">
|
||||||
@ -18,58 +22,100 @@
|
|||||||
<img src="https://img.shields.io/badge/service%20status-{{ service.status }}-{{ service.status_color }}" alt="{{ service.status }}" title="{{ service.timestamp }}">
|
<img src="https://img.shields.io/badge/service%20status-{{ service.status }}-{{ service.status_color }}" alt="{{ service.status }}" title="{{ service.timestamp }}">
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</h1>
|
</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
{% include "login-form.jinja2" %}
|
<div class="container">
|
||||||
{% include "login-form-hide.jinja2" %}
|
<table id="packages" class="table table-striped table-hover" cellspacing="0"
|
||||||
{% include "search-line.jinja2" %}
|
data-toggle="table"
|
||||||
|
data-pagination="true"
|
||||||
<section class="element">
|
data-page-siz="10"
|
||||||
<table class="sortable search-table">
|
data-page-list="[10, 25, 50, 100, all]"
|
||||||
<tr class="header">
|
data-search="true"
|
||||||
<th>package base</th>
|
data-show-columns="true"
|
||||||
<th>packages</th>
|
data-show-export="true"
|
||||||
<th>version</th>
|
data-sortable="true">
|
||||||
<th>last update</th>
|
<thead class="table-primary">
|
||||||
<th>status</th>
|
<tr>
|
||||||
|
<th data-sortable="true">package base</th>
|
||||||
|
<th data-sortable="true">packages</th>
|
||||||
|
<th data-sortable="true">version</th>
|
||||||
|
<th data-sortable="true">last update</th>
|
||||||
|
<th data-sortable="true">status</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
{% if authorized %}
|
<tbody>
|
||||||
{% for package in packages %}
|
{% if authorized %}
|
||||||
<tr class="package">
|
{% for package in packages %}
|
||||||
<td class="include-search"><a href="{{ package.web_url }}" title="{{ package.base }}">{{ package.base }}</a></td>
|
<tr>
|
||||||
<td class="include-search">{{ package.packages|join("<br>"|safe) }}</td>
|
<td><a href="{{ package.web_url }}" title="{{ package.base }}">{{ package.base }}</a></td>
|
||||||
<td>{{ package.version }}</td>
|
<td>{{ package.packages|join("<br>"|safe) }}</td>
|
||||||
<td>{{ package.timestamp }}</td>
|
<td>{{ package.version }}</td>
|
||||||
<td class="status package-{{ package.status }}">{{ package.status }}</td>
|
<td>{{ package.timestamp }}</td>
|
||||||
</tr>
|
<td class="table-{{ package.status_color }}">{{ package.status }}</td>
|
||||||
{% endfor %}
|
|
||||||
{% else %}
|
|
||||||
<tr class="package">
|
|
||||||
<td colspan="100%">In order to see statuses you must login first</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endfor %}
|
||||||
</table>
|
{% else %}
|
||||||
</section>
|
<tr>
|
||||||
|
<td colspan="100%">In order to see statuses you must login first</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
<footer>
|
<div class="container">
|
||||||
<ul class="navigation">
|
<footer class="d-flex flex-wrap justify-content-between align-items-center border-top">
|
||||||
<li><a href="https://github.com/arcan1s/ahriman" title="sources">ahriman</a></li>
|
<ul class="nav">
|
||||||
<li><a href="https://github.com/arcan1s/ahriman/releases" title="releases list">releases</a></li>
|
<li><a class="nav-link" href="https://github.com/arcan1s/ahriman" title="sources">ahriman</a></li>
|
||||||
<li><a href="https://github.com/arcan1s/ahriman/issues" title="issues tracker">report a bug</a></li>
|
<li><a class="nav-link" href="https://github.com/arcan1s/ahriman/releases" title="releases list">releases</a></li>
|
||||||
{% if auth_enabled %}
|
<li><a class="nav-link" href="https://github.com/arcan1s/ahriman/issues" title="issues tracker">report a bug</a></li>
|
||||||
<li class="right">
|
|
||||||
{% if auth_username is not none %}
|
|
||||||
<form action="/logout" method="post">
|
|
||||||
<button class="login" type="submit">logout ({{ auth_username }})</button>
|
|
||||||
</form>
|
|
||||||
{% else %}
|
|
||||||
<button class="login" onclick="document.getElementById('login-form').style.display='block'">login</button>
|
|
||||||
{% endif %}
|
|
||||||
</li>
|
|
||||||
{% endif %}
|
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
{% if auth_enabled %}
|
||||||
|
{% if auth_username is none %}
|
||||||
|
<button type="button" class="btn btn-link" data-bs-toggle="modal" data-bs-target="#loginForm" style="text-decoration: none">login</button>
|
||||||
|
{% else %}
|
||||||
|
<form action="/logout" method="post">
|
||||||
|
<button type="submit" class="btn btn-link" style="text-decoration: none">logout ({{ auth_username }})</button>
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="loginForm" tabindex="-1" role="dialog" class="modal fade">
|
||||||
|
<div class="modal-dialog modal-login">
|
||||||
|
<div class="modal-content">
|
||||||
|
<form action="/login" method="post">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h4 class="modal-title">login</h4>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="username" class="col-form-label">username</label>
|
||||||
|
<input id="username" type="text" class="form-control" placeholder="enter username" name="username" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="password" class="col-form-label">password</label>
|
||||||
|
<input id="password" type="password" class="form-control" placeholder="enter username" name="password" required>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<input type="submit" class="btn btn-primary" value="Login">
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/jquery/dist/jquery.min.js"></script>
|
||||||
|
<script src="https://unpkg.com/tableexport.jquery.plugin/tableExport.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-U1DAWAznBHeqEIlVSCgzq+c9gqGAJn5c/t99JyeKa9xxaYpSvHU5awsuZVVFIhvj" crossorigin="anonymous"></script>
|
||||||
|
<script src="https://unpkg.com/bootstrap-table@1.18.3/dist/bootstrap-table.min.js"></script>
|
||||||
|
<script src="https://unpkg.com/bootstrap-table@1.18.3/dist/extensions/export/bootstrap-table-export.min.js"></script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
44
package/share/ahriman/email-index.jinja2
Normal file
44
package/share/ahriman/email-index.jinja2
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
{#simplified version of full report#}
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<title>{{ repository }}</title>
|
||||||
|
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-KyZXEAg3QhqLMpG8r+8fhAXLRk2vvoC2f3B09zVXn8CA5QIVfZOJ3BCsw2P0p/We" crossorigin="anonymous">
|
||||||
|
|
||||||
|
{% include "style.jinja2" %}
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<table id="packages" class="table table-striped" cellspacing="0">
|
||||||
|
<thead class="table-primary">
|
||||||
|
<tr>
|
||||||
|
<th>package</th>
|
||||||
|
<th>version</th>
|
||||||
|
<th>archive size</th>
|
||||||
|
<th>installed size</th>
|
||||||
|
<th>build date</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
{% for package in packages %}
|
||||||
|
<tr>
|
||||||
|
<td><a href="{{ link_path }}/{{ package.filename }}" title="{{ package.name }}">{{ package.name }}</a></td>
|
||||||
|
<td>{{ package.version }}</td>
|
||||||
|
<td>{{ package.archive_size }}</td>
|
||||||
|
<td>{{ package.installed_size }}</td>
|
||||||
|
<td>{{ package.build_date }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
@ -1,9 +0,0 @@
|
|||||||
<script>
|
|
||||||
const modal = document.getElementById('login-form');
|
|
||||||
|
|
||||||
window.onclick = function(event) {
|
|
||||||
if (event.target === modal) {
|
|
||||||
modal.style.display = "none";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
@ -1,18 +0,0 @@
|
|||||||
{#idea is from here https://www.w3schools.com/howto/howto_css_login_form.asp#}
|
|
||||||
<div id="login-form" class="modal-login-form">
|
|
||||||
<form class="modal-login-form-content animate" action="/login" method="post">
|
|
||||||
<div class="login-container">
|
|
||||||
<label for="username"><b>username</b></label>
|
|
||||||
<input type="text" placeholder="enter username" name="username" required>
|
|
||||||
|
|
||||||
<label for="password"><b>password</b></label>
|
|
||||||
<input type="password" placeholder="enter password" name="password" required>
|
|
||||||
|
|
||||||
<button class="login" type="submit">login</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="login-container">
|
|
||||||
<button class="cancel" onclick="document.getElementById('login-form').style.display='none'">cancel</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
@ -3,66 +3,82 @@
|
|||||||
<head>
|
<head>
|
||||||
<title>{{ repository }}</title>
|
<title>{{ repository }}</title>
|
||||||
|
|
||||||
{% include "style.jinja2" %}
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
|
||||||
{% if extended_report %}
|
<script src="https://kit.fontawesome.com/0d6d6d5226.js" crossorigin="anonymous"></script>
|
||||||
{% include "sorttable.jinja2" %}
|
<link href="https://unpkg.com/bootstrap-table@1.18.3/dist/bootstrap-table.min.css" rel="stylesheet">
|
||||||
{% include "search.jinja2" %}
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-KyZXEAg3QhqLMpG8r+8fhAXLRk2vvoC2f3B09zVXn8CA5QIVfZOJ3BCsw2P0p/We" crossorigin="anonymous">
|
||||||
{% endif %}
|
|
||||||
|
{% include "style.jinja2" %}
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div class="root">
|
|
||||||
{% if extended_report %}
|
|
||||||
<h1>Archlinux user repository</h1>
|
|
||||||
|
|
||||||
<section class="element">
|
<div class="container">
|
||||||
{% if pgp_key is not none %}
|
<h1>Archlinux user repository</h1>
|
||||||
<p>This repository is signed with <a href="http://keys.gnupg.net/pks/lookup?search=0x{{ pgp_key }}&fingerprint=on&op=index" title="key search">{{ pgp_key }}</a> by default.</p>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<code>
|
|
||||||
$ cat /etc/pacman.conf<br>
|
|
||||||
[{{ repository }}]<br>
|
|
||||||
Server = {{ link_path }}<br>
|
|
||||||
SigLevel = Database{% if has_repo_signed %}Required{% else %}Never{% endif %} Package{% if has_package_signed %}Required{% else %}Never{% endif %} TrustedOnly
|
|
||||||
</code>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
{% include "search-line.jinja2" %}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<section class="element">
|
|
||||||
<table class="sortable search-table">
|
|
||||||
<tr class="header">
|
|
||||||
<th>package</th>
|
|
||||||
<th>version</th>
|
|
||||||
<th>archive size</th>
|
|
||||||
<th>installed size</th>
|
|
||||||
<th>build date</th>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
{% for package in packages %}
|
|
||||||
<tr class="package">
|
|
||||||
<td class="include-search"><a href="{{ link_path }}/{{ package.filename }}" title="{{ package.name }}">{{ package.name }}</a></td>
|
|
||||||
<td>{{ package.version }}</td>
|
|
||||||
<td>{{ package.archive_size }}</td>
|
|
||||||
<td>{{ package.installed_size }}</td>
|
|
||||||
<td>{{ package.build_date }}</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</table>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
{% if extended_report %}
|
|
||||||
<footer>
|
|
||||||
<ul class="navigation">
|
|
||||||
{% if homepage is not none %}
|
|
||||||
<li><a href="{{ homepage }}" title="homepage">Homepage</a></li>
|
|
||||||
{% endif %}
|
|
||||||
</ul>
|
|
||||||
</footer>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
{% if pgp_key is not none %}
|
||||||
|
<p>This repository is signed with <a href="https://pgp.mit.edu/pks/lookup?search=0x{{ pgp_key }}&fingerprint=on&op=index" title="key search">{{ pgp_key }}</a> by default.</p>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<pre>$ cat /etc/pacman.conf
|
||||||
|
[{{ repository }}]
|
||||||
|
Server = {{ link_path }}
|
||||||
|
SigLevel = Database{% if has_repo_signed %}Required{% else %}Never{% endif %} Package{% if has_package_signed %}Required{% else %}Never{% endif %} TrustedOnly</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<table id="packages" class="table table-striped table-hover" cellspacing="0"
|
||||||
|
data-toggle="table"
|
||||||
|
data-pagination="true"
|
||||||
|
data-page-siz="10"
|
||||||
|
data-page-list="[10, 25, 50, 100, all]"
|
||||||
|
data-search="true"
|
||||||
|
data-show-columns="true"
|
||||||
|
data-show-export="true"
|
||||||
|
data-sortable="true">
|
||||||
|
<thead class="table-primary">
|
||||||
|
<tr>
|
||||||
|
<th data-sortable="true">package</th>
|
||||||
|
<th data-sortable="true">version</th>
|
||||||
|
<th data-sortable="true">archive size</th>
|
||||||
|
<th data-sortable="true">installed size</th>
|
||||||
|
<th data-sortable="true">build date</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
{% for package in packages %}
|
||||||
|
<tr>
|
||||||
|
<td><a href="{{ link_path }}/{{ package.filename }}" title="{{ package.name }}">{{ package.name }}</a></td>
|
||||||
|
<td>{{ package.version }}</td>
|
||||||
|
<td>{{ package.archive_size }}</td>
|
||||||
|
<td>{{ package.installed_size }}</td>
|
||||||
|
<td>{{ package.build_date }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<footer class="d-flex flex-wrap justify-content-between align-items-center border-top">
|
||||||
|
<ul class="nav">
|
||||||
|
{% if homepage is not none %}
|
||||||
|
<li><a class="nav-link" href="{{ homepage }}" title="homepage">Homepage</a></li>
|
||||||
|
{% endif %}
|
||||||
|
</ul>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/jquery/dist/jquery.min.js"></script>
|
||||||
|
<script src="https://unpkg.com/tableexport.jquery.plugin/tableExport.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-U1DAWAznBHeqEIlVSCgzq+c9gqGAJn5c/t99JyeKa9xxaYpSvHU5awsuZVVFIhvj" crossorigin="anonymous"></script>
|
||||||
|
<script src="https://unpkg.com/bootstrap-table@1.18.3/dist/bootstrap-table.min.js"></script>
|
||||||
|
<script src="https://unpkg.com/bootstrap-table@1.18.3/dist/extensions/export/bootstrap-table-export.min.js"></script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
<section class="element">
|
|
||||||
<input type="search" id="search" onkeyup="searchInTable()" placeholder="search for package" title="search for package"/>
|
|
||||||
</section>
|
|
@ -1,26 +0,0 @@
|
|||||||
<script type="text/javascript">
|
|
||||||
function searchInTable() {
|
|
||||||
const input = document.getElementById("search");
|
|
||||||
const filter = input.value.toLowerCase();
|
|
||||||
const tables = document.getElementsByClassName("search-table");
|
|
||||||
|
|
||||||
for (let i = 0; i < tables.length; i++) {
|
|
||||||
const trs = tables[i].getElementsByTagName("tr");
|
|
||||||
// from 1 coz of header
|
|
||||||
for (let i = 1; i < trs.length; i++) {
|
|
||||||
let tr = trs[i].getElementsByClassName("include-search");
|
|
||||||
let display = "none";
|
|
||||||
for (let j = 0; j < tr.length; j++) {
|
|
||||||
if (tr[j].tagName.toLowerCase() === "td") {
|
|
||||||
let contains = (element) => tr[j].innerHTML.toLowerCase().indexOf(element) > -1
|
|
||||||
if (filter.some(contains)) {
|
|
||||||
display = "";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
trs[i].style.display = display;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
@ -1 +0,0 @@
|
|||||||
<script src="https://www.kryogenix.org/code/browser/sorttable/sorttable.js"></script>
|
|
@ -1,215 +1 @@
|
|||||||
<style>
|
<style></style>
|
||||||
:root {
|
|
||||||
--color-building: 255, 255, 146;
|
|
||||||
--color-failed: 255, 94, 94;
|
|
||||||
--color-pending: 255, 255, 146;
|
|
||||||
--color-success: 94, 255, 94;
|
|
||||||
--color-unknown: 225, 225, 225;
|
|
||||||
|
|
||||||
--color-header: 200, 200, 255;
|
|
||||||
--color-hover: 255, 255, 225;
|
|
||||||
--color-line-blue: 235, 235, 255;
|
|
||||||
--color-line-white: 255, 255, 255;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes blink-building {
|
|
||||||
0% { background-color: rgba(var(--color-building), 1.0); }
|
|
||||||
10% { background-color: rgba(var(--color-building), 0.9); }
|
|
||||||
20% { background-color: rgba(var(--color-building), 0.8); }
|
|
||||||
30% { background-color: rgba(var(--color-building), 0.7); }
|
|
||||||
40% { background-color: rgba(var(--color-building), 0.6); }
|
|
||||||
50% { background-color: rgba(var(--color-building), 0.5); }
|
|
||||||
60% { background-color: rgba(var(--color-building), 0.4); }
|
|
||||||
70% { background-color: rgba(var(--color-building), 0.3); }
|
|
||||||
80% { background-color: rgba(var(--color-building), 0.2); }
|
|
||||||
90% { background-color: rgba(var(--color-building), 0.1); }
|
|
||||||
100% { background-color: rgba(var(--color-building), 0.0); }
|
|
||||||
}
|
|
||||||
|
|
||||||
div.root {
|
|
||||||
width: 70%;
|
|
||||||
padding: 15px 15% 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
section.element, footer {
|
|
||||||
width: 100%;
|
|
||||||
padding: 10px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
code, input, table {
|
|
||||||
width: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* table description */
|
|
||||||
th, td {
|
|
||||||
padding: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
tr.package:nth-child(odd) {
|
|
||||||
background-color: rgba(var(--color-line-white), 1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
tr.package:nth-child(even) {
|
|
||||||
background-color: rgba(var(--color-line-blue), 1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
tr.package:hover {
|
|
||||||
background-color: rgba(var(--color-hover), 1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
tr.header{
|
|
||||||
background-color: rgba(var(--color-header), 1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
td.status {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
td.package-unknown {
|
|
||||||
background-color: rgba(var(--color-unknown), 1.0);
|
|
||||||
}
|
|
||||||
td.package-pending {
|
|
||||||
background-color: rgba(var(--color-pending), 1.0);
|
|
||||||
}
|
|
||||||
td.package-building {
|
|
||||||
background-color: rgba(var(--color-building), 1.0);
|
|
||||||
animation-name: blink-building;
|
|
||||||
animation-duration: 1s;
|
|
||||||
animation-timing-function: linear;
|
|
||||||
animation-iteration-count: infinite;
|
|
||||||
animation-direction: alternate;
|
|
||||||
}
|
|
||||||
td.package-failed {
|
|
||||||
background-color: rgba(var(--color-failed), 1.0);
|
|
||||||
}
|
|
||||||
td.package-success {
|
|
||||||
background-color: rgba(var(--color-success), 1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
li.service-unknown {
|
|
||||||
background-color: rgba(var(--color-unknown), 1.0);
|
|
||||||
}
|
|
||||||
li.service-building {
|
|
||||||
background-color: rgba(var(--color-building), 1.0);
|
|
||||||
animation-name: blink-building;
|
|
||||||
animation-duration: 1s;
|
|
||||||
animation-timing-function: linear;
|
|
||||||
animation-iteration-count: infinite;
|
|
||||||
animation-direction: alternate;
|
|
||||||
}
|
|
||||||
li.service-failed {
|
|
||||||
background-color: rgba(var(--color-failed), 1.0);
|
|
||||||
}
|
|
||||||
li.service-success {
|
|
||||||
background-color: rgba(var(--color-success), 1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* navigation footer description */
|
|
||||||
ul.navigation {
|
|
||||||
list-style-type: none;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
background-color: rgba(var(--color-header), 1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.navigation li {
|
|
||||||
float: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.navigation li.right {
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.navigation li a {
|
|
||||||
display: block;
|
|
||||||
color: black;
|
|
||||||
text-align: center;
|
|
||||||
text-decoration: none;
|
|
||||||
padding: 14px 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.navigation li a:hover {
|
|
||||||
opacity: 0.6;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* login button in footer and modal page */
|
|
||||||
button.login {
|
|
||||||
background-color: rgba(var(--color-header), 1.0);
|
|
||||||
padding: 14px 16px;
|
|
||||||
border: none;
|
|
||||||
cursor: pointer;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
button.login:hover {
|
|
||||||
opacity: 0.6;
|
|
||||||
}
|
|
||||||
|
|
||||||
button.cancel {
|
|
||||||
background-color: rgba(var(--color-failed), 1.0);
|
|
||||||
padding: 14px 16px;
|
|
||||||
border: none;
|
|
||||||
cursor: pointer;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
button.cancel:hover {
|
|
||||||
opacity: 0.6;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* modal page inputs and containers */
|
|
||||||
input[type=text], input[type=password] {
|
|
||||||
width: 100%;
|
|
||||||
padding: 12px 20px;
|
|
||||||
margin: 8px 0;
|
|
||||||
display: inline-block;
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-container {
|
|
||||||
padding: 14px 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
span.password {
|
|
||||||
float: right;
|
|
||||||
padding-top: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-login-form {
|
|
||||||
display: none;
|
|
||||||
position: fixed;
|
|
||||||
z-index: 1;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background-color: rgb(0, 0, 0);
|
|
||||||
background-color: rgba(0, 0, 0, 0.4);
|
|
||||||
padding-top: 60px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-login-form-content {
|
|
||||||
background-color: #fefefe;
|
|
||||||
margin: 5% auto 15% auto;
|
|
||||||
border: 1px solid #888;
|
|
||||||
width: 25%;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* modal page animation */
|
|
||||||
.animate {
|
|
||||||
-webkit-animation: animatezoom 0.6s;
|
|
||||||
animation: animatezoom 0.6s
|
|
||||||
}
|
|
||||||
|
|
||||||
@-webkit-keyframes animatezoom {
|
|
||||||
from {-webkit-transform: scale(0)}
|
|
||||||
to {-webkit-transform: scale(1)}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes animatezoom {
|
|
||||||
from {transform: scale(0)}
|
|
||||||
to {transform: scale(1)}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
4
setup.py
4
setup.py
@ -66,10 +66,8 @@ setup(
|
|||||||
]),
|
]),
|
||||||
("share/ahriman", [
|
("share/ahriman", [
|
||||||
"package/share/ahriman/build-status.jinja2",
|
"package/share/ahriman/build-status.jinja2",
|
||||||
|
"package/share/ahriman/email-index.jinja2",
|
||||||
"package/share/ahriman/repo-index.jinja2",
|
"package/share/ahriman/repo-index.jinja2",
|
||||||
"package/share/ahriman/search.jinja2",
|
|
||||||
"package/share/ahriman/search-line.jinja2",
|
|
||||||
"package/share/ahriman/sorttable.jinja2",
|
|
||||||
"package/share/ahriman/style.jinja2",
|
"package/share/ahriman/style.jinja2",
|
||||||
]),
|
]),
|
||||||
],
|
],
|
||||||
|
@ -188,7 +188,7 @@ def _set_key_import_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
|||||||
parser = root.add_parser("key-import", help="import PGP key",
|
parser = root.add_parser("key-import", help="import PGP key",
|
||||||
description="import PGP key from public sources to repository user",
|
description="import PGP key from public sources to repository user",
|
||||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
||||||
parser.add_argument("--key-server", help="key server for key import", default="keys.gnupg.net")
|
parser.add_argument("--key-server", help="key server for key import", default="pgp.mit.edu")
|
||||||
parser.add_argument("key", help="PGP key to import from public server")
|
parser.add_argument("key", help="PGP key to import from public server")
|
||||||
parser.set_defaults(handler=handlers.KeyImport, architecture=[""], lock=None, no_report=True)
|
parser.set_defaults(handler=handlers.KeyImport, architecture=[""], lock=None, no_report=True)
|
||||||
return parser
|
return parser
|
||||||
|
@ -24,7 +24,7 @@ import logging
|
|||||||
|
|
||||||
from logging.config import fileConfig
|
from logging.config import fileConfig
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Dict, List, Optional, Type
|
from typing import Any, Dict, List, Optional, Type
|
||||||
|
|
||||||
|
|
||||||
class Configuration(configparser.RawConfigParser):
|
class Configuration(configparser.RawConfigParser):
|
||||||
@ -41,6 +41,8 @@ class Configuration(configparser.RawConfigParser):
|
|||||||
|
|
||||||
ARCHITECTURE_SPECIFIC_SECTIONS = ["build", "html", "rsync", "s3", "sign", "web"]
|
ARCHITECTURE_SPECIFIC_SECTIONS = ["build", "html", "rsync", "s3", "sign", "web"]
|
||||||
|
|
||||||
|
_UNSET = object()
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
"""
|
"""
|
||||||
default constructor. In the most cases must not be called directly
|
default constructor. In the most cases must not be called directly
|
||||||
@ -109,15 +111,21 @@ class Configuration(configparser.RawConfigParser):
|
|||||||
return []
|
return []
|
||||||
return raw.split()
|
return raw.split()
|
||||||
|
|
||||||
def getpath(self, section: str, key: str) -> Path:
|
def getpath(self, section: str, key: str, fallback: Any = _UNSET) -> Path:
|
||||||
"""
|
"""
|
||||||
helper to generate absolute configuration path for relative settings value
|
helper to generate absolute configuration path for relative settings value
|
||||||
:param section: section name
|
:param section: section name
|
||||||
:param key: key name
|
:param key: key name
|
||||||
|
:param fallback: optional fallback value
|
||||||
:return: absolute path according to current path configuration
|
:return: absolute path according to current path configuration
|
||||||
"""
|
"""
|
||||||
value = Path(self.get(section, key))
|
try:
|
||||||
if self.path is None or value.is_absolute():
|
value = Path(self.get(section, key))
|
||||||
|
except (configparser.NoOptionError, configparser.NoSectionError):
|
||||||
|
if fallback is self._UNSET:
|
||||||
|
raise
|
||||||
|
value = fallback
|
||||||
|
if self.path is None or not isinstance(value, Path) or value.is_absolute():
|
||||||
return value
|
return value
|
||||||
return self.path.parent / value
|
return self.path.parent / value
|
||||||
|
|
||||||
|
@ -54,6 +54,9 @@ class Email(Report, JinjaTemplate):
|
|||||||
Report.__init__(self, architecture, configuration)
|
Report.__init__(self, architecture, configuration)
|
||||||
JinjaTemplate.__init__(self, "email", configuration)
|
JinjaTemplate.__init__(self, "email", configuration)
|
||||||
|
|
||||||
|
self.full_template_path = configuration.getpath("email", "full_template_path", fallback=None)
|
||||||
|
self.template_path = configuration.getpath("email", "template_path")
|
||||||
|
|
||||||
# base smtp settings
|
# base smtp settings
|
||||||
self.host = configuration.get("email", "host")
|
self.host = configuration.get("email", "host")
|
||||||
self.no_empty_report = configuration.getboolean("email", "no_empty_report", fallback=True)
|
self.no_empty_report = configuration.getboolean("email", "no_empty_report", fallback=True)
|
||||||
@ -100,6 +103,9 @@ class Email(Report, JinjaTemplate):
|
|||||||
"""
|
"""
|
||||||
if self.no_empty_report and not built_packages:
|
if self.no_empty_report and not built_packages:
|
||||||
return
|
return
|
||||||
text = self.make_html(built_packages, False)
|
text = self.make_html(built_packages, self.template_path)
|
||||||
attachments = {"index.html": self.make_html(packages, True)}
|
if self.full_template_path is not None:
|
||||||
|
attachments = {"index.html": self.make_html(packages, self.full_template_path)}
|
||||||
|
else:
|
||||||
|
attachments = {}
|
||||||
self._send(text, attachments)
|
self._send(text, attachments)
|
||||||
|
@ -41,6 +41,7 @@ class HTML(Report, JinjaTemplate):
|
|||||||
JinjaTemplate.__init__(self, "html", configuration)
|
JinjaTemplate.__init__(self, "html", configuration)
|
||||||
|
|
||||||
self.report_path = configuration.getpath("html", "path")
|
self.report_path = configuration.getpath("html", "path")
|
||||||
|
self.template_path = configuration.getpath("html", "template_path")
|
||||||
|
|
||||||
def generate(self, packages: Iterable[Package], built_packages: Iterable[Package]) -> None:
|
def generate(self, packages: Iterable[Package], built_packages: Iterable[Package]) -> None:
|
||||||
"""
|
"""
|
||||||
@ -48,5 +49,5 @@ class HTML(Report, JinjaTemplate):
|
|||||||
:param packages: list of packages to generate report
|
:param packages: list of packages to generate report
|
||||||
:param built_packages: list of packages which has just been built
|
:param built_packages: list of packages which has just been built
|
||||||
"""
|
"""
|
||||||
html = self.make_html(packages, True)
|
html = self.make_html(packages, self.template_path)
|
||||||
self.report_path.write_text(html)
|
self.report_path.write_text(html)
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#
|
#
|
||||||
import jinja2
|
import jinja2
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
from typing import Callable, Dict, Iterable
|
from typing import Callable, Dict, Iterable
|
||||||
|
|
||||||
from ahriman.core.configuration import Configuration
|
from ahriman.core.configuration import Configuration
|
||||||
@ -59,7 +60,6 @@ class JinjaTemplate:
|
|||||||
:ivar name: repository name
|
:ivar name: repository name
|
||||||
:ivar default_pgp_key: default PGP key
|
:ivar default_pgp_key: default PGP key
|
||||||
:ivar sign_targets: targets to sign enabled in configuration
|
:ivar sign_targets: targets to sign enabled in configuration
|
||||||
:ivar template_path: path to directory with jinja templates
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, section: str, configuration: Configuration) -> None:
|
def __init__(self, section: str, configuration: Configuration) -> None:
|
||||||
@ -69,7 +69,6 @@ class JinjaTemplate:
|
|||||||
:param configuration: configuration instance
|
:param configuration: configuration instance
|
||||||
"""
|
"""
|
||||||
self.link_path = configuration.get(section, "link_path")
|
self.link_path = configuration.get(section, "link_path")
|
||||||
self.template_path = configuration.getpath(section, "template_path")
|
|
||||||
|
|
||||||
# base template vars
|
# base template vars
|
||||||
self.homepage = configuration.get(section, "homepage", fallback=None)
|
self.homepage = configuration.get(section, "homepage", fallback=None)
|
||||||
@ -77,16 +76,16 @@ class JinjaTemplate:
|
|||||||
|
|
||||||
self.sign_targets, self.default_pgp_key = GPG.sign_options(configuration)
|
self.sign_targets, self.default_pgp_key = GPG.sign_options(configuration)
|
||||||
|
|
||||||
def make_html(self, packages: Iterable[Package], extended_report: bool) -> str:
|
def make_html(self, packages: Iterable[Package], template_path: Path) -> str:
|
||||||
"""
|
"""
|
||||||
generate report for the specified packages
|
generate report for the specified packages
|
||||||
:param packages: list of packages to generate report
|
:param packages: list of packages to generate report
|
||||||
:param extended_report: include additional blocks to the report
|
:param template_path: path to jinja template
|
||||||
"""
|
"""
|
||||||
# idea comes from https://stackoverflow.com/a/38642558
|
# idea comes from https://stackoverflow.com/a/38642558
|
||||||
loader = jinja2.FileSystemLoader(searchpath=self.template_path.parent)
|
loader = jinja2.FileSystemLoader(searchpath=template_path.parent)
|
||||||
environment = jinja2.Environment(loader=loader, autoescape=True)
|
environment = jinja2.Environment(loader=loader, autoescape=True)
|
||||||
template = environment.get_template(self.template_path.name)
|
template = environment.get_template(template_path.name)
|
||||||
|
|
||||||
content = [
|
content = [
|
||||||
{
|
{
|
||||||
@ -107,7 +106,6 @@ class JinjaTemplate:
|
|||||||
comparator: Callable[[Dict[str, str]], str] = lambda item: item["filename"]
|
comparator: Callable[[Dict[str, str]], str] = lambda item: item["filename"]
|
||||||
|
|
||||||
return template.render(
|
return template.render(
|
||||||
extended_report=extended_report,
|
|
||||||
homepage=self.homepage,
|
homepage=self.homepage,
|
||||||
link_path=self.link_path,
|
link_path=self.link_path,
|
||||||
has_package_signed=SignSettings.Packages in self.sign_targets,
|
has_package_signed=SignSettings.Packages in self.sign_targets,
|
||||||
|
@ -58,6 +58,21 @@ class BuildStatusEnum(Enum):
|
|||||||
return "success"
|
return "success"
|
||||||
return "inactive"
|
return "inactive"
|
||||||
|
|
||||||
|
def bootstrap_color(self) -> str:
|
||||||
|
"""
|
||||||
|
converts itself to bootstrap color
|
||||||
|
:return: bootstrap color
|
||||||
|
"""
|
||||||
|
if self == BuildStatusEnum.Pending:
|
||||||
|
return "warning"
|
||||||
|
if self == BuildStatusEnum.Building:
|
||||||
|
return "warning"
|
||||||
|
if self == BuildStatusEnum.Failed:
|
||||||
|
return "danger"
|
||||||
|
if self == BuildStatusEnum.Success:
|
||||||
|
return "success"
|
||||||
|
return "secondary"
|
||||||
|
|
||||||
|
|
||||||
class BuildStatus:
|
class BuildStatus:
|
||||||
"""
|
"""
|
||||||
|
@ -44,6 +44,7 @@ class IndexView(BaseView):
|
|||||||
* licenses, sorted list of strings
|
* licenses, sorted list of strings
|
||||||
* packages, sorted list of strings
|
* packages, sorted list of strings
|
||||||
* status, string based on enum value
|
* status, string based on enum value
|
||||||
|
* status_color, string based on enum value
|
||||||
* timestamp, pretty printed datetime, string
|
* timestamp, pretty printed datetime, string
|
||||||
* version, string
|
* version, string
|
||||||
* web_url, string
|
* web_url, string
|
||||||
@ -70,6 +71,7 @@ class IndexView(BaseView):
|
|||||||
"licenses": package.licenses,
|
"licenses": package.licenses,
|
||||||
"packages": list(sorted(package.packages)),
|
"packages": list(sorted(package.packages)),
|
||||||
"status": status.status.value,
|
"status": status.status.value,
|
||||||
|
"status_color": status.status.bootstrap_color(),
|
||||||
"timestamp": pretty_datetime(status.timestamp),
|
"timestamp": pretty_datetime(status.timestamp),
|
||||||
"version": package.version,
|
"version": package.version,
|
||||||
"web_url": package.web_url
|
"web_url": package.web_url
|
||||||
|
@ -13,7 +13,7 @@ def _default_args(args: argparse.Namespace) -> argparse.Namespace:
|
|||||||
:return: generated arguments for these test cases
|
:return: generated arguments for these test cases
|
||||||
"""
|
"""
|
||||||
args.key = "0xE989490C"
|
args.key = "0xE989490C"
|
||||||
args.key_server = "keys.gnupg.net"
|
args.key_server = "pgp.mit.edu"
|
||||||
return args
|
return args
|
||||||
|
|
||||||
|
|
||||||
|
@ -105,6 +105,21 @@ def test_generate_with_built(configuration: Configuration, package_ahriman: Pack
|
|||||||
send_mock.assert_called_once()
|
send_mock.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
|
def test_generate_with_built_and_full_path(
|
||||||
|
configuration: Configuration,
|
||||||
|
package_ahriman: Package,
|
||||||
|
mocker: MockerFixture) -> None:
|
||||||
|
"""
|
||||||
|
must generate report with built packages
|
||||||
|
"""
|
||||||
|
send_mock = mocker.patch("ahriman.core.report.email.Email._send")
|
||||||
|
|
||||||
|
report = Email("x86_64", configuration)
|
||||||
|
report.full_template_path = report.template_path
|
||||||
|
report.generate([package_ahriman], [package_ahriman])
|
||||||
|
send_mock.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
def test_generate_no_empty(configuration: Configuration, package_ahriman: Package, mocker: MockerFixture) -> None:
|
def test_generate_no_empty(configuration: Configuration, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must not generate report with built packages if no_empty_report is set
|
must not generate report with built packages if no_empty_report is set
|
||||||
|
@ -7,13 +7,6 @@ def test_generate(configuration: Configuration, package_ahriman: Package) -> Non
|
|||||||
"""
|
"""
|
||||||
must generate html report
|
must generate html report
|
||||||
"""
|
"""
|
||||||
|
path = configuration.getpath("html", "template_path")
|
||||||
report = JinjaTemplate("html", configuration)
|
report = JinjaTemplate("html", configuration)
|
||||||
assert report.make_html([package_ahriman], extended_report=False)
|
assert report.make_html([package_ahriman], path)
|
||||||
|
|
||||||
|
|
||||||
def test_generate_extended(configuration: Configuration, package_ahriman: Package) -> None:
|
|
||||||
"""
|
|
||||||
must generate extended html report
|
|
||||||
"""
|
|
||||||
report = JinjaTemplate("html", configuration)
|
|
||||||
assert report.make_html([package_ahriman], extended_report=True)
|
|
||||||
|
@ -69,7 +69,7 @@ def test_download_key(gpg: GPG, mocker: MockerFixture) -> None:
|
|||||||
must download the key from public server
|
must download the key from public server
|
||||||
"""
|
"""
|
||||||
requests_mock = mocker.patch("requests.get")
|
requests_mock = mocker.patch("requests.get")
|
||||||
gpg.download_key("keys.gnupg.net", "0xE989490C")
|
gpg.download_key("pgp.mit.edu", "0xE989490C")
|
||||||
requests_mock.assert_called_once()
|
requests_mock.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
@ -79,7 +79,7 @@ def test_download_key_failure(gpg: GPG, mocker: MockerFixture) -> None:
|
|||||||
"""
|
"""
|
||||||
mocker.patch("requests.get", side_effect=requests.exceptions.HTTPError())
|
mocker.patch("requests.get", side_effect=requests.exceptions.HTTPError())
|
||||||
with pytest.raises(requests.exceptions.HTTPError):
|
with pytest.raises(requests.exceptions.HTTPError):
|
||||||
gpg.download_key("keys.gnupg.net", "0xE989490C")
|
gpg.download_key("pgp.mit.edu", "0xE989490C")
|
||||||
|
|
||||||
|
|
||||||
def test_import_key(gpg: GPG, mocker: MockerFixture) -> None:
|
def test_import_key(gpg: GPG, mocker: MockerFixture) -> None:
|
||||||
@ -89,7 +89,7 @@ def test_import_key(gpg: GPG, mocker: MockerFixture) -> None:
|
|||||||
mocker.patch("ahriman.core.sign.gpg.GPG.download_key", return_value="key")
|
mocker.patch("ahriman.core.sign.gpg.GPG.download_key", return_value="key")
|
||||||
check_output_mock = mocker.patch("ahriman.core.sign.gpg.GPG._check_output")
|
check_output_mock = mocker.patch("ahriman.core.sign.gpg.GPG._check_output")
|
||||||
|
|
||||||
gpg.import_key("keys.gnupg.net", "0xE989490C")
|
gpg.import_key("pgp.mit.edu", "0xE989490C")
|
||||||
check_output_mock.assert_has_calls([
|
check_output_mock.assert_has_calls([
|
||||||
mock.call("gpg", "--import", input_data="key", exception=None, logger=pytest.helpers.anyvar(int)),
|
mock.call("gpg", "--import", input_data="key", exception=None, logger=pytest.helpers.anyvar(int)),
|
||||||
mock.call("gpg", "--quick-lsign-key", "0xE989490C", exception=None, logger=pytest.helpers.anyvar(int))
|
mock.call("gpg", "--quick-lsign-key", "0xE989490C", exception=None, logger=pytest.helpers.anyvar(int))
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
|
import configparser
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
import pytest
|
||||||
from pytest_mock import MockerFixture
|
from pytest_mock import MockerFixture
|
||||||
|
|
||||||
from ahriman.core.configuration import Configuration
|
from ahriman.core.configuration import Configuration
|
||||||
@ -49,6 +51,25 @@ def test_absolute_path_for_relative(configuration: Configuration) -> None:
|
|||||||
assert result.name == path.name
|
assert result.name == path.name
|
||||||
|
|
||||||
|
|
||||||
|
def test_path_with_fallback(configuration: Configuration) -> None:
|
||||||
|
"""
|
||||||
|
must return fallback path
|
||||||
|
"""
|
||||||
|
path = Path("a")
|
||||||
|
assert configuration.getpath("some", "option", fallback=path).name == str(path)
|
||||||
|
assert configuration.getpath("some", "option", fallback=None) is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_path_without_fallback(configuration: Configuration) -> None:
|
||||||
|
"""
|
||||||
|
must raise exception without fallback
|
||||||
|
"""
|
||||||
|
with pytest.raises(configparser.NoSectionError):
|
||||||
|
assert configuration.getpath("some", "option")
|
||||||
|
with pytest.raises(configparser.NoOptionError):
|
||||||
|
assert configuration.getpath("build", "option")
|
||||||
|
|
||||||
|
|
||||||
def test_dump(configuration: Configuration) -> None:
|
def test_dump(configuration: Configuration) -> None:
|
||||||
"""
|
"""
|
||||||
dump must not be empty
|
dump must not be empty
|
||||||
|
@ -59,14 +59,10 @@ def test_get_local_files(s3: S3, resource_path_root: Path) -> None:
|
|||||||
Path("models/package_ahriman_srcinfo"),
|
Path("models/package_ahriman_srcinfo"),
|
||||||
Path("models/package_tpacpi-bat-git_srcinfo"),
|
Path("models/package_tpacpi-bat-git_srcinfo"),
|
||||||
Path("models/package_yay_srcinfo"),
|
Path("models/package_yay_srcinfo"),
|
||||||
Path("web/templates/search-line.jinja2"),
|
|
||||||
Path("web/templates/build-status.jinja2"),
|
Path("web/templates/build-status.jinja2"),
|
||||||
Path("web/templates/login-form.jinja2"),
|
Path("web/templates/email-index.jinja2"),
|
||||||
Path("web/templates/login-form-hide.jinja2"),
|
|
||||||
Path("web/templates/repo-index.jinja2"),
|
Path("web/templates/repo-index.jinja2"),
|
||||||
Path("web/templates/sorttable.jinja2"),
|
|
||||||
Path("web/templates/style.jinja2"),
|
Path("web/templates/style.jinja2"),
|
||||||
Path("web/templates/search.jinja2"),
|
|
||||||
])
|
])
|
||||||
|
|
||||||
local_files = list(sorted(s3.get_local_files(resource_path_root).keys()))
|
local_files = list(sorted(s3.get_local_files(resource_path_root).keys()))
|
||||||
|
@ -16,6 +16,18 @@ def test_build_status_enum_badges_color() -> None:
|
|||||||
assert status.badges_color() in SUPPORTED_COLORS
|
assert status.badges_color() in SUPPORTED_COLORS
|
||||||
|
|
||||||
|
|
||||||
|
def test_build_status_enum_bootstrap_color() -> None:
|
||||||
|
"""
|
||||||
|
status color must be one of shields.io supported
|
||||||
|
"""
|
||||||
|
SUPPORTED_COLORS = [
|
||||||
|
"primary", "secondary", "success", "danger", "warning", "info", "light", "dark"
|
||||||
|
]
|
||||||
|
|
||||||
|
for status in BuildStatusEnum:
|
||||||
|
assert status.bootstrap_color() in SUPPORTED_COLORS
|
||||||
|
|
||||||
|
|
||||||
def test_build_status_init_1() -> None:
|
def test_build_status_init_1() -> None:
|
||||||
"""
|
"""
|
||||||
must construct status object from None
|
must construct status object from None
|
||||||
|
Loading…
Reference in New Issue
Block a user