import { defineCommand } from "citty"; import { apiRequest, type ApiRequestInit, type ApiRequestOptions, } from "../lib/api"; import { ctx } from "../lib/context"; import { resolveTrustIdentity } from "../lib/trust"; import { runUp } from "./up"; type CiRequest = ( path: string, init?: ApiRequestInit, options?: ApiRequestOptions, ) => Promise; function requireApiKey(value: string | undefined): string { const token = value?.trim(); if (!token) throw new Error("KUBER_API_KEY or --api-key is required for kuber ci"); return token; } export function apiKeyRequest(token: string): CiRequest { return (path, init = {}, options = {}) => { const headers = new Headers(init.headers); headers.set("authorization", `Bearer ${token}`); return apiRequest( path, { ...init, headers }, { ...options, authenticated: false }, ); }; } export async function runCi( build: boolean, grantTrust: boolean, token: string, request: CiRequest = apiKeyRequest(token), ): Promise { const { project, cwd } = ctx(); const trust = await resolveTrustIdentity(project, cwd); if (grantTrust) { await request(`/workspaces/${encodeURIComponent(project)}/trust`, { method: "POST", json: { fingerprint: trust.fingerprint }, }); } await runUp(build, request, { trust: grantTrust ? trust : undefined }); } export const ci = defineCommand({ meta: { name: "ci", description: "Deploy using an API key without a session", }, args: { apiKey: { type: "string", description: "API key (defaults to KUBER_API_KEY)", }, trust: { type: "boolean", description: "Grant current directory trust before deploying", }, build: { type: "boolean", default: true, negativeDescription: "Resolve existing images without building", }, }, async run({ args }) { await runCi( args.build, Boolean(args.trust), requireApiKey(args.apiKey ?? process.env.KUBER_API_KEY), ); }, });