Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

g1t.sh
44 linesCodeBlameRaw
1import type { DeliveryMetrics, UsageReport } from "@g1t/contracts";
2
3export function deliveryTotals(rows: DeliveryMetrics[]) {
4 return rows.reduce((total, row) => ({
5 opened: total.opened + row.opened, merged: total.merged + row.merged,
6 active: total.active + row.active, unreviewed: total.unreviewed + row.unreviewed,
7 queued: total.queued + row.queued, checking: total.checking + row.checking,
8 failed: total.failed + row.failed, leadSeconds: total.leadSeconds + row.leadSeconds,
9 reviewSeconds: total.reviewSeconds + row.reviewSeconds, reviewed: total.reviewed + row.reviewed,
10 }), { opened: 0, merged: 0, active: 0, unreviewed: 0, queued: 0, checking: 0, failed: 0, leadSeconds: 0, reviewSeconds: 0, reviewed: 0 });
11}
12
13/** How billing names a project's usage: its repository, `owner/name`, as Usage filters it. */
14export function usageKey(repo: { namespace: string; name: string }): string {
15 return `${repo.namespace}/${repo.name}`.toLowerCase();
16}
17
18export function projectCosts(report: UsageReport | null): Map<string, number> {
19 const costs = new Map<string, number>();
20 for (const product of report?.products ?? []) for (const meter of product.meters) {
21 for (const row of meter.byProject) costs.set(row.project, (costs.get(row.project) ?? 0) + row.micros);
22 }
23 return costs;
24}
25
26export function duration(seconds: number | null): string {
27 if (seconds == null || !Number.isFinite(seconds)) return "—";
28 if (seconds < 60) return "<1m";
29 if (seconds < 3600) return `${Math.round(seconds / 60)}m`;
30 if (seconds < 86400) return `${(seconds / 3600).toFixed(1)}h`;
31 return `${(seconds / 86400).toFixed(1)}d`;
32}
33
34/** Merge service batches and fill quiet UTC days so gaps never disappear. */
35export function deliveryDays(from: string, until: string, rows: { day: string; merged: number }[]) {
36 const counts = new Map<string, number>();
37 for (const row of rows) counts.set(row.day, (counts.get(row.day) ?? 0) + row.merged);
38 const days = [];
39 for (let at = Date.parse(`${from}T00:00:00Z`); at <= Date.parse(`${until}T00:00:00Z`); at += 86_400_000) {
40 const day = new Date(at).toISOString().slice(0, 10);
41 days.push({ day, merged: counts.get(day) ?? 0 });
42 }
43 return days;
44}