import { createBobbinClient, type BobbinContext, type QueryValue } from "$lib/api/client"; import type { XrpcRequestInit } from "$lib/api/client"; import { jsonGet } from "$lib/api/_request"; import type * as GetPipeline from "$lib/api/lexicons/types/sh/tangled/ci/getPipeline"; import type * as Pipeline from "$lib/api/lexicons/types/sh/tangled/ci/pipeline"; import type * as QueryPipelines from "$lib/api/lexicons/types/sh/tangled/ci/queryPipelines"; import type { PipelineStatus, PipelineSummary, PipelineTrigger } from "$lib/components/repo/types"; const asParams = (params: object): Record => params as unknown as Record; export const spindleUrl = (host: string): string => { const raw = host.trim().replace(/\/+$/, ""); if (raw.includes("://")) return raw; return raw.startsWith("localhost:") ? `http://${raw}` : `https://${raw}`; }; export const createSpindleClient = (host: string, fetch: typeof globalThis.fetch): BobbinContext => createBobbinClient({ serviceUrl: spindleUrl(host), fetch }); export const queryPipelines = ( ctx: BobbinContext, params: QueryPipelines.$params, init?: XrpcRequestInit ) => jsonGet(ctx, "sh.tangled.ci.queryPipelines", asParams(params), init); export const getPipeline = ( ctx: BobbinContext, params: GetPipeline.$params, init?: XrpcRequestInit ) => jsonGet(ctx, "sh.tangled.ci.getPipeline", asParams(params), init); const toTrigger = (trigger: Pipeline.Main["trigger"], repoDid?: string): PipelineTrigger => { if ("newSha" in trigger) { return { kind: "push", targetRef: trigger.ref.replace(/^refs\/(?:heads|tags)\//, "") }; } if ("targetBranch" in trigger) { return { kind: "pull_request", targetRef: trigger.targetBranch, // fork ci labels are handled by the page resolving the name of the fork first sourceLabel: trigger.sourceBranch ?? "", ...(trigger.sourceRepo && trigger.sourceRepo !== repoDid ? { sourceRepo: trigger.sourceRepo } : {}) }; } return { kind: "manual" }; }; const durationOf = (workflow: Pipeline.Workflow): number => { if (!workflow.startedAt || !workflow.finishedAt) return 0; const started = Date.parse(workflow.startedAt); const finished = Date.parse(workflow.finishedAt); if (Number.isNaN(started) || Number.isNaN(finished)) return 0; return Math.max(0, finished - started); }; export const toPipelineSummary = (pipeline: Pipeline.Main, repoDid?: string): PipelineSummary => ({ id: pipeline.id, sha: pipeline.commit, createdAt: pipeline.createdAt ?? "", trigger: toTrigger(pipeline.trigger, repoDid), workflows: (pipeline.workflows ?? []).map((workflow) => ({ name: workflow.name, status: workflow.status as PipelineStatus, duration: durationOf(workflow), error: workflow.error, startedAt: workflow.startedAt, finishedAt: workflow.finishedAt })) });