Pre-launch — Gluecron is in final validation. Public signups and git hosting for non-owner users open after launch review.
CodeIssuesDiscussionsWikiPull RequestsProjectsCommitsActionsReleasesContributorsPulse● GatesSecuritySettingsDeploymentsPipelineInsightsAgents✨ Explain✨ Ask AI✨ Workspace✨ Spec✨ Tests▓ Debt Map✨ NL Search🏛 Archaeology
Blame · Line-by-line history

self-bootstrap.ts

Each line is annotated with the commit that last touched it. Click any SHA to jump to that commit and see the surrounding change.

self-bootstrap.tsBlame115 lines · 1 contributor
96942a6Test User1/**
2 * Self-bootstrap — fires on boot if Gluecron's own canonical repo
3 * doesn't yet exist on disk. Idempotent, safe to call every boot.
4 *
5 * This closes the chicken-and-egg gap where:
6 * - The site self-hosts on gluecron.com/ccantynz/Gluecron.com.git
7 * - But that bare repo was never initialized on the Fly volume
8 * - So every `git push` to it returned "Repository not found"
9 * - So nothing ever deployed through Gluecron's own canonical path
10 *
11 * After this boots once, the canonical repo exists, the post-receive
12 * hook is installed, and Gluecron is fully self-sufficient — future
13 * deploys do not require GitHub Actions, flyctl, or any external tool.
14 *
15 * Configuration (env vars, all optional):
16 * SELF_BOOTSTRAP_DISABLED=1 — skip entirely
17 * SELF_BOOTSTRAP_OWNER — default "ccantynz"
18 * SELF_BOOTSTRAP_NAME — default "Gluecron.com"
19 * SELF_BOOTSTRAP_SOURCE — default github mirror URL
20 *
21 * Never throws out of boot. Returns silently on any failure (logged).
22 * Run the explicit script (`bun run scripts/self-host-bootstrap.ts`)
23 * for verbose output and exit codes.
24 */
25
26import { existsSync } from "fs";
27import { mkdir, writeFile, chmod, rm } from "fs/promises";
28import { join } from "path";
29import { tmpdir } from "os";
30import {
31 runBootstrap,
32 sh,
33 type BootstrapArgs,
34 type BootstrapDeps,
35} from "../../scripts/self-host-bootstrap";
36
37const DEFAULT_OWNER = "ccantynz";
38const DEFAULT_NAME = "Gluecron.com";
39const DEFAULT_SOURCE = "https://github.com/ccantynz-alt/Gluecron.com.git";
40
41export async function maybeSelfBootstrap(): Promise<void> {
42 if (process.env.SELF_BOOTSTRAP_DISABLED === "1") {
43 return;
44 }
45
46 // Lazy imports — these touch the live DB / config, and we want
47 // src/lib/self-bootstrap.ts itself to be cheap to import in tests.
48 const { db } = await import("../db");
49 const schemaMod = await import("../db/schema");
50 const { config } = await import("./config");
51
52 const owner = process.env.SELF_BOOTSTRAP_OWNER || DEFAULT_OWNER;
53 const name = process.env.SELF_BOOTSTRAP_NAME || DEFAULT_NAME;
54 const source = process.env.SELF_BOOTSTRAP_SOURCE || DEFAULT_SOURCE;
55
56 // Fast path: if the bare repo already exists on disk, the bootstrap
57 // is a no-op and we skip even the DB roundtrip. The explicit script
58 // is still idempotent if you want to re-verify everything.
59 const barePath = join(config.gitReposPath, owner, `${name}.git`);
60 if (existsSync(join(barePath, "HEAD"))) {
61 return;
62 }
63
64 const args: BootstrapArgs = { owner, name, source, dryRun: false };
65
66 // Quiet log shim — boot output is precious; only print on completion
67 // or failure. The verbose script-mode logs go to stdout when you run
68 // scripts/self-host-bootstrap.ts directly.
69 const lines: string[] = [];
70 const push = (level: string, msg: string) => lines.push(`[${level}] ${msg}`);
71
72 const deps: BootstrapDeps = {
73 db,
74 schema: {
75 users: schemaMod.users,
76 repositories: schemaMod.repositories,
77 siteAdmins: schemaMod.siteAdmins,
78 },
79 reposPath: config.gitReposPath,
80 sh,
81 fsExists: existsSync,
82 fsMkdir: mkdir,
83 fsWrite: (p, body) => writeFile(p, body, "utf8"),
84 fsChmod: chmod,
85 fsRm: rm,
86 log: {
87 say: (m) => push("·", m),
88 ok: (m) => push("ok", m),
89 warn: (m) => push("warn", m),
90 bad: (m) => push("err", m),
91 info: (m) => push("info", m),
92 },
93 tmpRoot: tmpdir(),
94 };
95
96 try {
97 const result = await runBootstrap(args, deps);
98 if (result.ok) {
99 console.log(
100 `[self-bootstrap] canonical ${owner}/${name} initialized from ${source}`
101 );
102 } else {
103 console.warn(
104 `[self-bootstrap] failed: ${result.error || "unknown"} — see scripts/self-host-bootstrap.ts to re-run with verbose output`
105 );
106 // Print the captured log lines so the operator can diagnose
107 // without grepping process state.
108 for (const l of lines.slice(-20)) console.warn(` ${l}`);
109 }
110 } catch (err) {
111 console.warn(
112 `[self-bootstrap] crashed: ${(err as Error).message} — boot continues normally`
113 );
114 }
115}