Pre-launch — Gluecron is in final validation. Public signups and git hosting for non-owner users open after launch review.
CodeIssuesPull RequestsActionsSecurityInsightsSettings
✨ AI
More
Blame · Line-by-line history

post-receive.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.

post-receive.tsBlame139 lines · 1 contributor
fc1817aClaude1/**
2 * Post-receive hook logic.
2c34075Claude3 *
4 * Called after every successful git push. This is gluecron's intelligence layer:
5 * 1. Auto-repair — fix common issues and commit automatically
6 * 2. Push analysis — detect breaking changes, security issues
7 * 3. Health score — recompute repo health
8 * 4. GateTest scan — external security scanning
9 * 5. Crontech deploy — auto-deploy on push to main
10 * 6. Webhooks — fire registered webhook URLs
fc1817aClaude11 */
12
13import { config } from "../lib/config";
2c34075Claude14import { autoRepair } from "../lib/autorepair";
15import { analyzePush, computeHealthScore } from "../lib/intelligence";
fc1817aClaude16
17interface PushRef {
18 oldSha: string;
19 newSha: string;
20 refName: string;
21}
22
23export async function onPostReceive(
24 owner: string,
25 repo: string,
26 refs: PushRef[]
27): Promise<void> {
2c34075Claude28 for (const ref of refs) {
29 if (ref.newSha.startsWith("0000")) continue; // Branch deletion
30 const branchName = ref.refName.replace("refs/heads/", "");
fc1817aClaude31
2c34075Claude32 // 1. Auto-repair (runs first, may create a new commit)
33 try {
34 const repair = await autoRepair(owner, repo, branchName);
35 if (repair.repaired) {
36 console.log(
37 `[autorepair] ${owner}/${repo}@${branchName}: ${repair.repairs.length} repairs committed`
38 );
39 }
40 } catch (err) {
41 console.error(`[autorepair] error:`, err);
42 }
fc1817aClaude43
2c34075Claude44 // 2. Push analysis
45 try {
46 const analysis = await analyzePush(owner, repo, ref.oldSha, ref.newSha);
47 console.log(
48 `[push-analysis] ${owner}/${repo}: ${analysis.summary}`
49 );
50 if (analysis.riskScore > 50) {
51 console.warn(
52 `[push-analysis] HIGH RISK push detected (score: ${analysis.riskScore})`
53 );
54 }
55 if (analysis.breakingChangeSignals.length > 0) {
56 console.warn(
57 `[push-analysis] Breaking changes: ${analysis.breakingChangeSignals.join("; ")}`
58 );
59 }
60 } catch (err) {
61 console.error(`[push-analysis] error:`, err);
62 }
63
64 // 3. Health score (async, don't block)
65 computeHealthScore(owner, repo).then((report) => {
66 console.log(
67 `[health] ${owner}/${repo}: ${report.grade} (${report.score}/100)`
68 );
69 }).catch((err) => {
70 console.error(`[health] error:`, err);
71 });
72 }
73
74 // 4. GateTest scan
75 triggerGateTest(owner, repo, refs).catch((err) =>
76 console.error(`[gatetest] error:`, err)
77 );
78
79 // 5. Crontech deploy on push to main
fc1817aClaude80 const mainPush = refs.find(
81 (r) => r.refName === "refs/heads/main" && !r.newSha.startsWith("0000")
82 );
83 if (mainPush) {
2c34075Claude84 triggerCrontechDeploy(owner, repo, mainPush.newSha).catch((err) =>
85 console.error(`[crontech] error:`, err)
86 );
fc1817aClaude87 }
88}
89
90async function triggerGateTest(
91 owner: string,
92 repo: string,
93 refs: PushRef[]
94): Promise<void> {
95 try {
96 const response = await fetch(config.gatetestUrl, {
97 method: "POST",
98 headers: { "Content-Type": "application/json" },
99 body: JSON.stringify({
100 repository: `${owner}/${repo}`,
101 refs: refs.map((r) => ({
102 ref: r.refName,
103 before: r.oldSha,
104 after: r.newSha,
105 })),
106 source: "gluecron",
107 }),
108 });
109 console.log(
110 `[gatetest] scan triggered for ${owner}/${repo}: ${response.status}`
111 );
112 } catch (err) {
113 console.error(`[gatetest] failed to trigger scan:`, err);
114 }
115}
116
117async function triggerCrontechDeploy(
118 owner: string,
119 repo: string,
120 sha: string
121): Promise<void> {
122 try {
123 const response = await fetch(config.crontechDeployUrl, {
124 method: "POST",
125 headers: { "Content-Type": "application/json" },
126 body: JSON.stringify({
127 repository: `${owner}/${repo}`,
128 sha,
129 branch: "main",
130 source: "gluecron",
131 }),
132 });
133 console.log(
134 `[crontech] deploy triggered for ${owner}/${repo}@${sha.slice(0, 7)}: ${response.status}`
135 );
136 } catch (err) {
137 console.error(`[crontech] failed to trigger deploy:`, err);
138 }
139}