1
0
mirror of git://f0xx.org/android_cast synced 2026-07-29 03:57:50 +03:00

be builders

This commit is contained in:
Anton Afanasyeu
2026-06-05 17:00:26 +02:00
parent a27958b430
commit a656f40922
5 changed files with 197 additions and 6 deletions

View File

@@ -1,6 +1,65 @@
(function () {
'use strict';
var base = document.body.getAttribute('data-base-path') || '';
function buildStatusIconHtml(status) {
var s = String(status || '').toLowerCase();
if (s === 'running' || s === 'queued') {
return '<span class="build-status-icon build-status-icon--running" role="img" aria-label="In progress">'
+ '<span class="build-status-spinner"></span></span>';
}
if (s === 'passed') {
return '<span class="build-status-icon build-status-icon--passed" role="img" aria-label="Succeeded"></span>';
}
return '<span class="build-status-icon build-status-icon--failed" role="img" aria-label="Failed or cancelled">'
+ '<span class="build-status-fail-mark" aria-hidden="true">!</span></span>';
}
function updateBuildStatusCell(cell, status, phase) {
if (!cell) return;
var icon = cell.querySelector('.build-status-icon');
var label = cell.querySelector('.build-status-label');
var html = buildStatusIconHtml(status);
if (icon) {
icon.outerHTML = html;
} else {
cell.insertAdjacentHTML('afterbegin', html);
}
if (label) {
label.textContent = status || '';
}
if (phase !== undefined) {
var row = cell.closest('tr');
if (row) {
var phaseCell = row.querySelector('.build-phase-cell');
if (phaseCell) {
phaseCell.textContent = phase || '';
}
}
}
}
function refreshRecentBuilds() {
var tbody = document.getElementById('builds-recent-tbody');
if (!tbody) return;
fetch(base + '/api/builds.php', { credentials: 'same-origin' })
.then(function (r) { return r.json(); })
.then(function (j) {
if (!j.ok || !j.builds) return;
j.builds.forEach(function (b) {
var cell = tbody.querySelector('[data-build-status-cell="' + b.id + '"]');
updateBuildStatusCell(cell, b.status, b.phase);
});
})
.catch(function () {})
.then(function () {
setTimeout(refreshRecentBuilds, 4000);
});
}
if (document.body.getAttribute('data-view') === 'home' && document.getElementById('builds-recent-tbody')) {
setTimeout(refreshRecentBuilds, 4000);
}
var themeSel = document.getElementById('theme-select');
if (themeSel) {
var cur = localStorage.getItem('crash_console_theme') || 'dark';
@@ -180,6 +239,22 @@
offset = j.log.offset || offset;
renderLog();
}
if (j.build) {
var iconEl = document.getElementById('build-status-icon');
var textEl = document.getElementById('build-status-text');
if (iconEl) {
iconEl.innerHTML = buildStatusIconHtml(j.build.status);
}
if (textEl) {
var errEl = document.getElementById('build-error-message');
var err = j.build.error_message ? (' · <span class="error-text" id="build-error-message">'
+ String(j.build.error_message).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
+ '</span>') : '';
textEl.innerHTML = 'Status: <strong>' + (j.build.status || '') + '</strong> · Phase: '
+ (j.build.phase || '') + err;
}
document.body.setAttribute('data-build-status', j.build.status || '');
}
var running = j.build && j.build.status === 'running';
var eof = j.log && j.log.eof;
if (running || !eof) {

View File

@@ -54,3 +54,35 @@ function json_out(array $data, int $code = 200): void {
function assets_base(): string {
return rtrim((string) cfg('links.crashes', '/app/androidcast_project/crashes/'), '/');
}
/** @return 'running'|'passed'|'failed' */
function build_status_kind(string $status): string {
$s = strtolower(trim($status));
if (in_array($s, ['running', 'queued'], true)) {
return 'running';
}
if ($s === 'passed') {
return 'passed';
}
return 'failed';
}
/** Inline status glyph: spinner (in progress), green check, or red alert triangle. */
function build_status_icon(string $status): string {
$kind = build_status_kind($status);
$labels = [
'running' => 'In progress',
'passed' => 'Succeeded',
'failed' => 'Failed or cancelled',
];
$label = h($labels[$kind] ?? 'Unknown');
if ($kind === 'running') {
return '<span class="build-status-icon build-status-icon--running" role="img" aria-label="' . $label . '">'
. '<span class="build-status-spinner"></span></span>';
}
if ($kind === 'passed') {
return '<span class="build-status-icon build-status-icon--passed" role="img" aria-label="' . $label . '"></span>';
}
return '<span class="build-status-icon build-status-icon--failed" role="img" aria-label="' . $label . '">'
. '<span class="build-status-fail-mark" aria-hidden="true">!</span></span>';
}

View File

@@ -20,10 +20,13 @@ $stepLabels = [
];
?>
<h1>Build <?= h($build['build_code'] ?? '') ?></h1>
<p class="muted">Status: <strong><?= h($status) ?></strong> · Phase: <?= h($phase) ?>
<p class="muted build-status-line" id="build-status-line">
<span id="build-status-icon"><?= build_status_icon($status) ?></span>
<span id="build-status-text">Status: <strong><?= h($status) ?></strong> · Phase: <?= h($phase) ?>
<?php if (!empty($build['error_message'])): ?>
· <span class="error-text"><?= h($build['error_message']) ?></span>
· <span class="error-text" id="build-error-message"><?= h($build['error_message']) ?></span>
<?php endif; ?>
</span>
</p>
<?php
$healthDocker = ($health['docker'] ?? '') === 'ok';

View File

@@ -51,12 +51,17 @@
<th>Created</th>
</tr>
</thead>
<tbody>
<tbody id="builds-recent-tbody">
<?php foreach ($builds as $b): ?>
<tr>
<tr data-build-id="<?= (int) $b['id'] ?>">
<td><a href="<?= h(Auth::basePath()) ?>/?view=build&id=<?= (int) $b['id'] ?>"><?= h($b['build_code']) ?></a></td>
<td><?= h($b['status']) ?></td>
<td><?= h($b['phase']) ?></td>
<td>
<span class="build-status-cell-inner" data-build-status-cell="<?= (int) $b['id'] ?>">
<?= build_status_icon((string) ($b['status'] ?? '')) ?>
<span class="build-status-label"><?= h($b['status']) ?></span>
</span>
</td>
<td class="build-phase-cell" data-build-phase-cell="<?= (int) $b['id'] ?>"><?= h($b['phase']) ?></td>
<td><?= h($b['git_ref'] ?? $b['branch'] ?? '—') ?></td>
<td><?= h($b['ota_channel'] ?? '—') ?></td>
<td><?= h($b['created_at'] ?? '') ?></td>