Something went wrong. Try again.
Find the cast and crew shared by two movies or TV shows crosscredit.haltcase.dev
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188// This short-lived CI command deliberately runs operations in sequence and writes progress to its log./* oxlint-disable node/no-sync, no-console */import { execFileSync } from "node:child_process";import { mkdirSync, mkdtempSync, rmdirSync, unlinkSync, writeFileSync } from "node:fs";import { createRequire } from "node:module";import { dirname, join } from "node:path";
import { previewName } from "./deployment.ts";import { commentOnPreview } from "./preview-comment.ts";
const require = createRequire(import.meta.url);const cf = join(dirname(require.resolve("cf/package.json")), "bin", "cf");
function remoteHead(branch: string): string | undefined { return ( execFileSync("git", ["ls-remote", "--heads", "origin", `refs/heads/${branch}`], { encoding: "utf8" }) .trim() .split(/\s+/)[0] || undefined );}
async function deploy(branch: string, sha: string, preview: boolean): Promise<void> { if (remoteHead(branch) !== sha) { console.info("Skipping deployment: the branch was deleted or has a newer commit.");
return; }
const mode = preview ? "preview" : "production";
execFileSync("pnpm", ["build", "--mode", mode], { stdio: "inherit", env: { ...process.env, CLOUDFLARE_PREVIEW_BUILD: String(preview) } });
if (remoteHead(branch) !== sha) { console.info("Skipping deployment: the branch changed during the build.");
return; }
if (preview) { const output = execFileSync( process.execPath, [ cf, "previews", "deploy", previewName(branch), "--prebuilt", "--mode", mode, "--worker", "crosscredit" ], { encoding: "utf8", stdio: ["ignore", "pipe", "inherit"] } );
await commentOnPreview(output, branch, sha);
return; }
// JSON avoids dotenv escaping issues, and the file is never part of the build. const token = process.env.TMDB_READ_ACCESS_TOKEN;
if (!token) { throw new Error("Configure the TMDB_READ_ACCESS_TOKEN repository secret."); }
mkdirSync(".cloudflare", { recursive: true });
const directory = mkdtempSync(".cloudflare/ci-secrets-"); const secrets = join(directory, "secrets.json");
try { writeFileSync(secrets, JSON.stringify({ TMDB_READ_ACCESS_TOKEN: token }), { mode: 0o600 });
execFileSync( process.execPath, [cf, "deploy", "--prebuilt", "--mode", "production", "--secrets-file", secrets], { stdio: "inherit" } ); } finally { unlinkSync(secrets); rmdirSync(directory); }}
async function deletePreview(branch: string, account: string, token: string): Promise<void> { const name = previewName(branch);
// Use the native endpoint directly so an absent Preview is safe to clean up again. const response = await fetch( `https://api.cloudflare.com/client/v4/accounts/${encodeURIComponent(account)}/workers/workers/crosscredit/previews/${name}`, { method: "DELETE", headers: { Authorization: `Bearer ${token}` }, signal: AbortSignal.timeout(30_000), redirect: "error" } );
if (response.status === 404) { const body: unknown = await response.json();
if ( typeof body === "object" && body !== null && "errors" in body && Array.isArray(body.errors) && body.errors.some( (error: unknown) => typeof error === "object" && error !== null && "code" in error && error.code === 10_025 ) ) { console.info(`Preview ${name} is already absent.`);
return; } }
if (!response.ok) { throw new Error(`Preview deletion failed (HTTP ${response.status}).`); }
console.info(`Deleted Preview ${name}.`);}
async function run(): Promise<void> { const preview = process.env.TANGLED_PIPELINE_KIND === "pull_request"; const action = process.env.CROSSCREDIT_CI_ACTION;
if (action && action !== "delete-preview") { throw new Error("Unknown CI action."); }
const cleanup = action === "delete-preview"; const branch = preview ? process.env.TANGLED_PR_SOURCE_BRANCH : process.env.TANGLED_REF_NAME; const sha = preview ? process.env.TANGLED_PR_SOURCE_SHA : process.env.TANGLED_SHA;
if ( (preview ? process.env.TANGLED_PR_TARGET_BRANCH !== "main" || branch === "main" : process.env.TANGLED_PIPELINE_KIND !== "push" || process.env.TANGLED_REF_TYPE !== "branch" || branch !== "main" || cleanup) || !branch || !sha ) { throw new Error( "Expected a Tangled push to main or a pull request targeting main with a source SHA." ); }
if (preview && !process.env.CLOUDFLARE_API_TOKEN) { console.info("Skipping preview operation: Tangled withheld deployment secrets for this PR.");
return; }
const token = process.env.CLOUDFLARE_API_TOKEN; const account = process.env.CLOUDFLARE_ACCOUNT_ID;
if (!token || !account) { throw new Error("Configure CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID repository secrets."); }
if (cleanup) { await deletePreview(branch, account, token);
return; }
for (const command of ["check", "typecheck", "test"]) { execFileSync("pnpm", [command], { stdio: "inherit" }); }
await deploy(branch, sha, preview);}
// CI entrypoint: finish the requested operation before exiting, including Preview cleanup.// oxlint-disable-next-line node/no-top-level-awaitawait run();