102 lines
3.7 KiB
TypeScript
102 lines
3.7 KiB
TypeScript
import type { TaskPR, WorkflowAttention, WorkflowAttentionState } from "@/lib/types/github";
|
|
import { parseStrictRfc3339Timestamp } from "@/lib/utils/strict-timestamp";
|
|
|
|
type WorkflowAttentionCarrier = {
|
|
state: string;
|
|
head_sha?: string | null;
|
|
workflow_attention?: WorkflowAttention | null;
|
|
};
|
|
|
|
function isAttentionState(state: WorkflowAttentionState): boolean {
|
|
return state === "approval_required" || state === "action_required";
|
|
}
|
|
|
|
function currentWorkflowObservation(
|
|
carrier: WorkflowAttentionCarrier,
|
|
attention: WorkflowAttention | null | undefined,
|
|
): WorkflowAttention | null {
|
|
if (
|
|
carrier.state !== "open" ||
|
|
!carrier.head_sha ||
|
|
!attention ||
|
|
attention.head_sha !== carrier.head_sha
|
|
) {
|
|
return null;
|
|
}
|
|
return attention;
|
|
}
|
|
|
|
function normalizeWorkflowAttention(attention: WorkflowAttention | null): WorkflowAttention | null {
|
|
if (!attention || attention.state === "none") return null;
|
|
if (attention.runs != null) return attention;
|
|
return { ...attention, runs: attention.runs ?? [] };
|
|
}
|
|
|
|
export function getCurrentWorkflowAttention(
|
|
carrier: WorkflowAttentionCarrier,
|
|
): WorkflowAttention | null {
|
|
return normalizeWorkflowAttention(
|
|
currentWorkflowObservation(carrier, carrier.workflow_attention),
|
|
);
|
|
}
|
|
|
|
function suppliedObservationIsAtLeastAsFresh(
|
|
stored: WorkflowAttention,
|
|
supplied: WorkflowAttention,
|
|
): boolean {
|
|
const storedTimestamp = parseStrictRfc3339Timestamp(stored.observed_at);
|
|
const suppliedTimestamp = parseStrictRfc3339Timestamp(supplied.observed_at);
|
|
if (suppliedTimestamp === null) return storedTimestamp === null;
|
|
if (storedTimestamp === null) return true;
|
|
return suppliedTimestamp >= storedTimestamp;
|
|
}
|
|
|
|
/**
|
|
* Chooses the best observation for a surface that has both stored TaskPR data
|
|
* and an optional live feedback response. A failed live read must not hide a
|
|
* stored same-head positive observation, while an authoritative empty result
|
|
* must clear it.
|
|
*/
|
|
export function getWorkflowAttentionForDisplay(
|
|
carrier: WorkflowAttentionCarrier,
|
|
suppliedAttention?: WorkflowAttention | null,
|
|
): WorkflowAttention | null {
|
|
const stored = currentWorkflowObservation(carrier, carrier.workflow_attention);
|
|
if (suppliedAttention == null) return normalizeWorkflowAttention(stored);
|
|
const incoming = currentWorkflowObservation(carrier, suppliedAttention);
|
|
if (!incoming) return normalizeWorkflowAttention(stored);
|
|
const useIncoming = stored === null || suppliedObservationIsAtLeastAsFresh(stored, incoming);
|
|
const selected = useIncoming ? incoming : stored;
|
|
if (useIncoming && incoming.state === "unknown" && stored && isAttentionState(stored.state)) {
|
|
return normalizeWorkflowAttention({ ...stored, stale: true });
|
|
}
|
|
return normalizeWorkflowAttention(selected);
|
|
}
|
|
|
|
export function getActiveWorkflowAttention(
|
|
carrier: WorkflowAttentionCarrier,
|
|
attention: WorkflowAttention | null | undefined = carrier.workflow_attention,
|
|
): WorkflowAttention | null {
|
|
const current = normalizeWorkflowAttention(currentWorkflowObservation(carrier, attention));
|
|
return current && isAttentionState(current.state) ? current : null;
|
|
}
|
|
|
|
export function getTaskPRWorkflowAttention(pr: TaskPR): WorkflowAttention | null {
|
|
return getActiveWorkflowAttention(pr);
|
|
}
|
|
|
|
export function workflowAttentionWorkflowNames(attention: WorkflowAttention): string {
|
|
const names = attention.runs.map((run) => run.name.trim()).filter(Boolean);
|
|
return names.join(", ");
|
|
}
|
|
|
|
export function isWorkflowApprovalRequired(attention: WorkflowAttention): boolean {
|
|
return attention.state === "approval_required";
|
|
}
|
|
|
|
export function workflowAttentionReasonKey(reason: string): string {
|
|
return reason === "approval_required"
|
|
? "github:workflowApprovalRequired"
|
|
: "github:workflowNeedsAttention";
|
|
}
|