initial commit
This commit is contained in:
77
lib/publish.ts
Normal file
77
lib/publish.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
import { git, MANAGED_PATHS } from "./git";
|
||||
|
||||
/* Publish = commit the managed paths -> push -> ask the live site to
|
||||
* redeploy via its existing POST /api/deploy route (which drops the flag
|
||||
* scripts/deploy-watch.sh polls for). Each step is reported, and push /
|
||||
* deploy are skipped rather than failed when they aren't configured. */
|
||||
|
||||
export type PublishStep = {
|
||||
step: "stage" | "commit" | "push" | "deploy";
|
||||
status: "ok" | "skipped" | "failed";
|
||||
output: string;
|
||||
};
|
||||
|
||||
function pushEnabled(): boolean {
|
||||
return (process.env.GIT_PUSH ?? "true").toLowerCase() !== "false";
|
||||
}
|
||||
|
||||
async function triggerDeploy(): Promise<PublishStep> {
|
||||
const url = process.env.DEPLOY_URL;
|
||||
const secret = process.env.DEPLOY_TRIGGER_SECRET;
|
||||
if (!url || !secret) {
|
||||
return { step: "deploy", status: "skipped", output: "DEPLOY_URL / DEPLOY_TRIGGER_SECRET not set" };
|
||||
}
|
||||
try {
|
||||
const res = await fetch(url, {
|
||||
method: "POST",
|
||||
headers: { Authorization: `Bearer ${secret}` },
|
||||
signal: AbortSignal.timeout(15_000),
|
||||
});
|
||||
const body = await res.text();
|
||||
return {
|
||||
step: "deploy",
|
||||
status: res.ok ? "ok" : "failed",
|
||||
output: `${res.status} ${res.statusText} ${body}`.trim(),
|
||||
};
|
||||
} catch (err) {
|
||||
return { step: "deploy", status: "failed", output: String(err) };
|
||||
}
|
||||
}
|
||||
|
||||
export async function publish(message: string): Promise<{ ok: boolean; steps: PublishStep[] }> {
|
||||
const steps: PublishStep[] = [];
|
||||
const done = (ok: boolean) => ({ ok, steps });
|
||||
|
||||
const add = await git(["add", "-A", "--", ...MANAGED_PATHS]);
|
||||
steps.push({ step: "stage", status: add.ok ? "ok" : "failed", output: add.stderr.trim() });
|
||||
if (!add.ok) return done(false);
|
||||
|
||||
const staged = await git(["diff", "--cached", "--name-only", "--", ...MANAGED_PATHS]);
|
||||
const files = staged.stdout.split("\n").map((f) => f.trim()).filter(Boolean);
|
||||
if (files.length === 0) {
|
||||
steps.push({ step: "commit", status: "skipped", output: "nothing to commit" });
|
||||
} else {
|
||||
// Naming the files commits exactly these, even if other changes are staged.
|
||||
const commit = await git(["commit", "-m", message, "--", ...files]);
|
||||
steps.push({
|
||||
step: "commit",
|
||||
status: commit.ok ? "ok" : "failed",
|
||||
output: (commit.stdout + commit.stderr).trim(),
|
||||
});
|
||||
if (!commit.ok) return done(false);
|
||||
}
|
||||
|
||||
if (!pushEnabled()) {
|
||||
steps.push({ step: "push", status: "skipped", output: "GIT_PUSH=false" });
|
||||
steps.push({ step: "deploy", status: "skipped", output: "push is disabled" });
|
||||
return done(true);
|
||||
}
|
||||
|
||||
const push = await git(["push"], 120_000);
|
||||
steps.push({ step: "push", status: push.ok ? "ok" : "failed", output: (push.stdout + push.stderr).trim() });
|
||||
if (!push.ok) return done(false);
|
||||
|
||||
const deploy = await triggerDeploy();
|
||||
steps.push(deploy);
|
||||
return done(deploy.status !== "failed");
|
||||
}
|
||||
Reference in New Issue
Block a user