queue: add a build status column

This commit is contained in:
Christoph Reiter
2021-01-22 19:52:15 +01:00
parent 0e95b6bc66
commit 00ec82e7a2
12 changed files with 190 additions and 14 deletions

View File

@@ -39,6 +39,11 @@ CYGWIN_VERSION_CONFIG = [
"", "")
]
BUILD_STATUS_CONFIG = [
("https://github.com/msys2/msys2-autobuild/releases/download/status/status.json",
"", "")
]
UPDATE_INTERVAL = 60 * 5
REQUEST_TIMEOUT = 60
CACHE_LOCAL = False

View File

@@ -170,6 +170,9 @@ class ArchMapping:
self.skipped = set(json_object.get("skipped", []))
BuildStatus = Dict[str, Dict[str, Dict[str, str]]]
class AppState:
def __init__(self) -> None:
@@ -183,6 +186,7 @@ class AppState:
self._arch_versions: Dict[str, Tuple[str, str, int]] = {}
self._arch_mapping: ArchMapping = ArchMapping()
self._cygwin_versions: CygwinVersions = {}
self._build_status: BuildStatus = {}
self._update_etag()
def _update_etag(self) -> None:
@@ -233,6 +237,15 @@ class AppState:
self._arch_mapping = arch_mapping
self._update_etag()
@property
def build_status(self) -> BuildStatus:
return self._build_status
@build_status.setter
def build_status(self, build_status: BuildStatus) -> None:
self._build_status = build_status
self._update_etag()
@property
def cygwin_versions(self) -> CygwinVersions:
return self._cygwin_versions

View File

@@ -12,14 +12,14 @@ from typing import Any, Dict, Tuple, List, Set
import httpx
from .appstate import state, Source, CygwinVersions, ArchMapping, get_repositories, get_arch_names, SrcInfoPackage, Package, DepType
from .appconfig import CYGWIN_VERSION_CONFIG, REQUEST_TIMEOUT, VERSION_CONFIG, ARCH_MAPPING_CONFIG, SRCINFO_CONFIG, UPDATE_INTERVAL
from .appconfig import CYGWIN_VERSION_CONFIG, REQUEST_TIMEOUT, VERSION_CONFIG, ARCH_MAPPING_CONFIG, SRCINFO_CONFIG, UPDATE_INTERVAL, BUILD_STATUS_CONFIG
from .utils import version_is_newer_than, arch_version_to_msys
from . import appconfig
def get_update_urls() -> List[str]:
urls = []
for config in VERSION_CONFIG + SRCINFO_CONFIG + ARCH_MAPPING_CONFIG + CYGWIN_VERSION_CONFIG:
for config in VERSION_CONFIG + SRCINFO_CONFIG + ARCH_MAPPING_CONFIG + CYGWIN_VERSION_CONFIG + BUILD_STATUS_CONFIG:
urls.append(config[0])
for repo in get_repositories():
urls.append(repo.files_url)
@@ -77,6 +77,14 @@ async def update_cygwin_versions() -> None:
state.cygwin_versions = cygwin_versions
async def update_build_status() -> None:
print("update build status")
url = BUILD_STATUS_CONFIG[0][0]
print("Loading %r" % url)
data = await get_content_cached(url, timeout=REQUEST_TIMEOUT)
state.build_status = json.loads(data)
def parse_desc(t: str) -> Dict[str, List[str]]:
d: Dict[str, List[str]] = {}
cat = None
@@ -327,7 +335,8 @@ async def update_loop() -> None:
update_arch_mapping(),
update_cygwin_versions(),
update_source(),
update_sourceinfos()
update_sourceinfos(),
update_build_status(),
])
# update_arch_versions() depends on update_source()
rounds.append([

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -2,6 +2,8 @@
{% block title %}Incoming Package Updates{% endblock %}
{% block inner_content %}
<div class="card mb-3">
<div class="card-header">
<h4 class="card-title">Incoming Package Updates</h4>
@@ -11,7 +13,13 @@
</h6>
</div>
<div class="card-body overflow-auto">
<table class="table table-hover table-sm">
<p class="text-muted">
Packages get automatically built via <a
href="https://github.com/msys2/msys2-autobuild#automated-build-process">msys2-autobuild</a>.
</p>
<table class="table table-hover table-sm">
<thead>
<tr>
<th>Git Change Date</th>
@@ -19,10 +27,11 @@
<th>Repo Version</th>
<th></th>
<th>Git Version</th>
<th>Status</th>
</tr>
</thead>
<tbody>
{% for (srcpkg, s, p) in updates %}
{% for (srcpkg, s, p, status) in updates %}
<tr>
<td><a href="{{ srcpkg.history_url }}">{{ srcpkg.date }}</a></td>
{% if s %}
@@ -34,6 +43,24 @@
{% endif %}
<td></td>
<td>{{ srcpkg.build_version }}</td>
<td class="mytooltip">
{% if status[0].url %}
<a href="{{ status[0].url }}">{{ status[0].status }}</a>
{% else %}
{{ status[0].status }}
{% endif %}
<template class="mytooltip-content">
<dl>
{% for s in status %}
<dt>{{ s.type }}</dt>
<dd>{{ s.status }}
{% if s.details %}
<br>{{ s.details }}
{% endif %}</dd>
{% endfor %}
</dl>
</template>
</td>
</tr>
{% endfor %}
</tbody>

View File

@@ -6,7 +6,8 @@ from __future__ import annotations
import os
import re
import datetime
from typing import Callable, Any, List, Union, Dict, Optional, Tuple, Set
from enum import Enum
from typing import Callable, Any, List, Union, Dict, Optional, Tuple, Set, NamedTuple
import jinja2
@@ -26,6 +27,25 @@ templates = Jinja2Templates(directory=os.path.join(DIR, "templates"))
templates.env.undefined = jinja2.StrictUndefined
class PackageStatus(Enum):
FINISHED = 'finished'
FINISHED_BUT_BLOCKED = 'finished-but-blocked'
FINISHED_BUT_INCOMPLETE = 'finished-but-incomplete'
FAILED_TO_BUILD = 'failed-to-build'
WAITING_FOR_BUILD = 'waiting-for-build'
WAITING_FOR_DEPENDENCIES = 'waiting-for-dependencies'
MANUAL_BUILD_REQUIRED = 'manual-build-required'
UNKNOWN = 'unknown'
PackageBuildStatus = NamedTuple('PackageBuildStatus', [
('type', str),
('status', str),
('details', str),
('url', str),
])
async def get_etag(request: Request) -> str:
return state.etag
@@ -262,16 +282,77 @@ async def outofdate(request: Request, response: Response) -> Response:
}, headers=dict(response.headers))
def get_status_text(key: str) -> str:
try:
status = PackageStatus(key)
except ValueError:
return key
if status == PackageStatus.UNKNOWN:
return "Unknown"
elif status == PackageStatus.FAILED_TO_BUILD:
return "Failed to build"
elif status == PackageStatus.FINISHED:
return "Ready for upload"
elif status == PackageStatus.FINISHED_BUT_BLOCKED:
return "Ready for upload but waiting for dependencies"
elif status == PackageStatus.FINISHED_BUT_INCOMPLETE:
return "Ready for upload but related builds are missing"
elif status == PackageStatus.MANUAL_BUILD_REQUIRED:
return "Manual build required"
elif status == PackageStatus.WAITING_FOR_BUILD:
return "Waiting to be built"
elif status == PackageStatus.WAITING_FOR_DEPENDENCIES:
return "Waiting for dependencies"
else:
return key
def get_status_priority(key: str) -> Tuple[int, str]:
"""We want to show the most important status as the primary one"""
try:
status = PackageStatus(key)
except ValueError:
return (-1, key)
order = [
PackageStatus.UNKNOWN, PackageStatus.FINISHED, PackageStatus.FINISHED_BUT_BLOCKED,
PackageStatus.FINISHED_BUT_INCOMPLETE, PackageStatus.WAITING_FOR_BUILD,
PackageStatus.WAITING_FOR_DEPENDENCIES, PackageStatus.MANUAL_BUILD_REQUIRED,
PackageStatus.FAILED_TO_BUILD]
try:
return (order.index(status), key)
except ValueError:
return (-1, key)
def get_build_status(srcinfo: SrcInfoPackage) -> List[PackageBuildStatus]:
build_status = state.build_status
if srcinfo.pkgbase not in build_status:
return [PackageBuildStatus("unknown", PackageStatus.UNKNOWN.value, "", "")]
all_status = build_status[srcinfo.pkgbase]
results = []
for build_type, status in sorted(all_status.items(), key=lambda i: get_status_priority(i[1]["status"]), reverse=True):
results.append(
PackageBuildStatus(
build_type, get_status_text(status.get("status", "")),
status.get("desc", ""), status.get("url", ""))
)
return results
@router.get('/queue', dependencies=[Depends(Etag(get_etag))])
async def queue(request: Request, response: Response) -> Response:
# Create entries for all packages where the version doesn't match
updates: List[Tuple[SrcInfoPackage, Optional[Source], Optional[Package]]] = []
updates: List[Tuple[SrcInfoPackage, Optional[Source], Optional[Package], List[PackageBuildStatus]]] = []
for s in state.sources.values():
for k, p in sorted(s.packages.items()):
if p.name in state.sourceinfos:
srcinfo = state.sourceinfos[p.name]
if version_is_newer_than(srcinfo.build_version, p.version):
updates.append((srcinfo, s, p))
updates.append((srcinfo, s, p, get_build_status(srcinfo)))
break
# new packages
@@ -287,7 +368,7 @@ async def queue(request: Request, response: Response) -> Response:
for srcinfo in available.values():
grouped[srcinfo.pkgbase] = srcinfo
for srcinfo in grouped.values():
updates.append((srcinfo, None, None))
updates.append((srcinfo, None, None, get_build_status(srcinfo)))
updates.sort(
key=lambda i: (i[0].date, i[0].pkgbase, i[0].pkgname),

View File

@@ -1,5 +1,6 @@
import './style.scss';
import 'bootstrap';
import tippy from 'tippy.js';
class App {
@@ -15,4 +16,12 @@ class App {
};
tippy('.mytooltip', {
allowHTML: true,
theme: 'light-border',
content(reference) {
return reference.querySelector(".mytooltip-content").innerHTML;
},
});
window.App = App;

View File

@@ -28,6 +28,11 @@
"js-tokens": "^4.0.0"
}
},
"@popperjs/core": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.6.0.tgz",
"integrity": "sha512-cPqjjzuFWNK3BSKLm0abspP0sp/IGOli4p5I5fKFAzdS8fvjdOwDCfZqAaIiXd9lPkOWi3SUUfZof3hEb7J/uw=="
},
"@rollup/plugin-commonjs": {
"version": "16.0.0",
"resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-16.0.0.tgz",
@@ -57,6 +62,16 @@
"resolve": "^1.17.0"
}
},
"@rollup/plugin-replace": {
"version": "2.3.4",
"resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-2.3.4.tgz",
"integrity": "sha512-waBhMzyAtjCL1GwZes2jaE9MjuQ/DQF2BatH3fRivUF3z0JBFrU0U6iBNC/4WR+2rLKhaAhPWDNPYp4mI6RqdQ==",
"dev": true,
"requires": {
"@rollup/pluginutils": "^3.1.0",
"magic-string": "^0.25.7"
}
},
"@rollup/pluginutils": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz",
@@ -3538,6 +3553,14 @@
"integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=",
"dev": true
},
"tippy.js": {
"version": "6.2.7",
"resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-6.2.7.tgz",
"integrity": "sha512-k+kWF9AJz5xLQHBi3K/XlmJiyu+p9gsCyc5qZhxxGaJWIW8SMjw1R+C7saUnP33IM8gUhDA2xX//ejRSwqR0tA==",
"requires": {
"@popperjs/core": "^2.4.4"
}
},
"tough-cookie": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",

View File

@@ -1,16 +1,19 @@
{
"devDependencies": {
"popper.js": "^1.16.1",
"@rollup/plugin-commonjs": "^16.0.0",
"@rollup/plugin-node-resolve": "^10.0.0",
"@rollup/plugin-replace": "^2.3.4",
"autoprefixer": "^9.0.0",
"bootstrap": "5.0.0-alpha3",
"node-sass": "^5.0.0",
"popper.js": "^1.16.1",
"rollup": "^2.28.2",
"rollup-plugin-postcss": "^3.1.8",
"rollup-plugin-terser": "^7.0.2"
},
"dependencies": {},
"dependencies": {
"tippy.js": "^6.2.7"
},
"scripts": {
"build": "rollup --config",
"dev": "rollup --config --watch"

View File

@@ -3,6 +3,7 @@ import commonjs from '@rollup/plugin-commonjs';
import {terser} from "rollup-plugin-terser";
import postcss from 'rollup-plugin-postcss';
import autoprefixer from 'autoprefixer';
import replace from '@rollup/plugin-replace';
const dev = process.env.ROLLUP_WATCH === 'true';
@@ -15,6 +16,9 @@ export default {
plugins: [
resolve(),
commonjs(),
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
postcss({
extract: 'index.css',
minimize: true,

View File

@@ -1,5 +1,7 @@
$enable-responsive-font-sizes: true;
@import './node_modules/bootstrap/scss/bootstrap';
@import './node_modules/tippy.js/dist/tippy';
@import './node_modules/tippy.js/themes/light-border';
a {
text-decoration: none;