aboutsummaryrefslogtreecommitdiffstats
path: root/static/modules/sbbuilder.js
blob: 6285dd51f74fd37fc53c4f2b0f7ba0938148db05 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
// deck-it - sbbuilder panel
// Copyright (C) 2026 Danilo M. <danix@danix.xyz>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

import { offline } from "../offline.js";

const gib = (b) => (b == null ? "?" : (b / 1073741824).toFixed(1));
const pct = (n) => (n == null ? "?" : `${Math.round(n)}%`);
const ago = (ts) => {
  if (!ts) return "—";
  const d = Math.max(0, Math.floor(Date.now() / 1000 - ts));
  if (d < 3600) return `${Math.floor(d / 60)}m`;
  if (d < 86400) return `${Math.floor(d / 3600)}h`;
  return `${Math.floor(d / 86400)}d`;
};

const ACTIVE = ["running", "busy", "starting"];

function tile(label, value, sub) {
  const d = document.createElement("div");
  d.className = "mtile";
  const l = document.createElement("span");
  l.className = "mlabel";
  l.textContent = label;
  const v = document.createElement("span");
  v.className = "mvalue num";
  v.textContent = value;
  const s = document.createElement("span");
  s.className = "msub";
  s.textContent = sub || "";
  d.append(l, v, s);
  return d;
}

export default {
  title: "Build VM",
  interval: 10,

  mount(el) {
    el.innerHTML = `<div class="sbb"></div>`;
  },

  update(state, el) {
    const box = el.querySelector(".sbb");
    if (state.offline) {
      offline(box, {
        icon: "/icons/virt-manager.svg",
        title: "Build VM is off",
        detail: "Start the VM and its jobs and package counts appear here.",
      });
      return;
    }
    box.textContent = "";
    const s = state.stats ?? {};
    const p = s.packages ?? {};
    const sr = s.slackrepo ?? {};
    const results = sr.results ?? {};
    const jobs = state.jobs ?? [];
    const running = jobs.find((j) => ACTIVE.includes(j.status));

    const now = document.createElement("div");
    now.className = "sbb-now";
    now.textContent = running ? `● ${running.kind || "job"}` : "idle";
    now.classList.toggle("warn", Boolean(running));
    box.append(now);

    const overlays = Object.entries(p).filter(([k]) => k !== "total" && k !== "shadowed");
    box.append(
      tile("Packages", String(p.total ?? "?"), overlays.map(([k, v]) => `${k} ${v}`).join(" · ")),
      tile("Failed", String(results.failed ?? 0), `${sr.built ?? "?"} built`),
      tile("Disk free", `${gib(s.disk?.free)}`, `of ${gib(s.disk?.total)} GiB`),
      tile("CPU", pct(s.cpu), `load ${s.load ? s.load[0].toFixed(2) : "?"}`),
    );

    const foot = document.createElement("div");
    foot.className = "mfoot";
    foot.textContent = `orphans ${s.orphans ?? "?"} · ${
      Object.entries(s.sync ?? {}).map(([k, v]) => `${k} ${ago(v)}`).join(" · ") || "no pulls"}`;
    box.append(foot);
  },
};