107 lines · 4.8 KB
import { WorkflowEntrypoint } from "cloudflare:workers";
import type { WorkflowEvent, WorkflowStep } from "cloudflare:workers";
import { repoDb } from "../scoped-db";
import { siteDb } from "./store";
import { loadRepoById } from "../repos";
import { SCRATCH_LIMITS, STATIC_LIMITS } from "./config";
import { DeployError, extractArtifact, planGitFiles, totalBytes, uploadGitBatch } from "./collect";
import type { CollectedFiles, PlannedFile } from "./collect";
import { activateSiteDeployment, failSiteDeployment, loadSiteById, loadSiteDeployment } from "./deploy";
import { planBlockReason } from "../metering/quota";

export type SitesDeployParams = { deploymentId: string };

type Plan =
  | { kind: "branch"; artifactsName: string; sha: string; folder: string; maxFiles: number; maxBytes: number }
  | { kind: "artifact"; r2Key: string; folder: string }
  | { kind: "skip"; error: string | null };

type Outcome<T> = { ok: true; value: T } | { ok: false; error: string };

async function attempt<T>(work: () => Promise<T>): Promise<Outcome<T>> {
  try {
    return { ok: true, value: await work() };
  } catch (error) {
    if (error instanceof DeployError) return { ok: false, error: error.message };
    throw error;
  }
}

async function start(deploymentId: string): Promise<Plan> {
  const deployment = await loadSiteDeployment(deploymentId);
  if (!deployment || deployment.status !== "queued") return { kind: "skip", error: null };
  const site = await loadSiteById(deployment.site_id);
  const repo = site?.repo_id ? await loadRepoById(site.repo_id) : null;
  if (!site || !repo) return { kind: "skip", error: "The site is no longer linked to a repository" };
  if (site.owner_id && site.scratch !== 1) {
    const blocked = await planBlockReason(site.owner_id, "storage.sites");
    if (blocked) return { kind: "skip", error: blocked };
  }
  await siteDb(site).run("UPDATE site_deployments SET status = 'building' WHERE id = ?", deploymentId);