import { repoDb } from "../scoped-db"; import { badRequest } from "../errors"; import { branchSha, listRefs } from "../git/refs"; import { labelUrl } from "./config"; import { createSiteDeployment, loadSiteForRepo } from "./deploy"; import { previewLabel } from "./naming"; import type { RepoRow } from "~/lib/types"; export { createScratchFromFiles } from "./scratch"; export { createScratchRepo } from "./scratch-git"; export type { ScratchResult } from "./scratch-core"; export type SiteDeployResult = { deploymentId: string; environment: "production" | "preview"; url: string | null }; /** * Stable entry point for deploying a repo site from a branch (UI, MCP, API). The site's branch goes to production, * any other branch to its preview host. No permission checks: callers must have checked `contents:write`. */ export async function deploySiteRef( repo: RepoRow, input: { ref?: string; actorId: string | null }, ): Promise { const site = await loadSiteForRepo(repo.id); if (!site) throw badRequest("Sites are not enabled for this repository"); if (site.disabled_reason) throw badRequest(`This site is disabled: ${site.disabled_reason}`); const branch = (input.ref ?? site.branch).replace(/^refs\/heads\//, ""); const production = branch === site.branch; if (!production && site.previews !== 1) throw badRequest("Branch previews are disabled for this site"); const environment = production ? "production" : "preview"; const label = production ? site.label : previewLabel(site.label, branch); const ref = `refs/heads/${branch}`; const base = { site, environment, label, branch, ref, creatorId: input.actorId } as const; let deploymentId: string; if (site.source !== "workflow") { const sha = branchSha(await listRefs(repo), branch); if (!sha) throw badRequest(`Branch ${branch} does not exist`); deploymentId = (await createSiteDeployment({ ...base, sha, artifactId: null, source: "branch" })).id; } else { const artifact = await repoDb(repo.id).first<{ id: string; sha: string }>( `SELECT a.id, r.sha FROM job_artifacts a JOIN workflow_runs r ON r.id = a.run_id WHERE r.repo_id = ? AND r.source_repo_id = r.repo_id AND r.untrusted = 0 AND r.ref = ? AND r.conclusion = 'success' AND r.event != 'pull_request' AND a.name = ? ORDER BY r.created_at DESC LIMIT 1`, repo.id, ref, site.artifact_name, ); if (!artifact) throw badRequest(`No successful trusted run on ${branch} uploaded "${site.artifact_name}"`); deploymentId = ( await createSiteDeployment({ ...base, sha: artifact.sha, artifactId: artifact.id, source: "artifact" }) ).id; } return { deploymentId, environment, url: labelUrl(label) }; }