feat(deploy): trigger JD agent after human merge

This commit is contained in:
冰朔 2026-08-06 13:35:10 +08:00
commit 564859b1ce
9 changed files with 517 additions and 8 deletions

View file

@ -9,6 +9,7 @@ const { MapGate } = require("./map-gate");
const { sendSmtpMail } = require("./smtp-mailer");
const { executeRegisteredAction } = require("./action-client");
const { enqueueDeploymentEvent } = require("./deployment-event");
const { processForgejoMergeWebhook } = require("./forgejo-merge-deployment");
const { GuanghuRouter, loadDevices } = require("./guanghu-router");
const { HoloLakeSessionManager } = require("./hololake-session");
const { GhdrAuthorizer } = require("./ghdr-authorizer");
@ -177,6 +178,8 @@ function createApp(options = {}) {
);
const deploymentQueueDir = options.deploymentQueueDir || process.env.LAKE_LAMP_DEPLOYMENT_EVENT_DIR || "/var/lib/guanghu/deployment-events";
const deploymentRegistryFile = options.deploymentRegistryFile || process.env.LAKE_LAMP_DEPLOYMENT_REPOSITORIES || "/etc/guanghu/lake-lamp/deployment-repositories.json";
const forgejoMergeSecret = String(options.forgejoMergeSecret || process.env.LAKE_LAMP_FORGEJO_MERGE_SECRET || "");
const forgejoMergeMergers = options.forgejoMergeMergers || splitCsv(process.env.LAKE_LAMP_FORGEJO_MERGE_MERGERS || "bingshuo");
const executeAction = options.executeAction || executeRegisteredAction;
let ghdrAuthorizer = null;
const getGhdrAuthorizer = options.getGhdrAuthorizer || (() => {
@ -296,6 +299,10 @@ function createApp(options = {}) {
knowledge_snapshot: Boolean(hololakeKnowledgeProvider),
ai_gateway: Boolean(hololakeAiGateway),
},
merge_deployment_gate: {
configured: forgejoMergeSecret.length >= 32,
mode: "human-merged-pull-request-only",
},
});
if (req.method === "GET" && url.pathname === "/api/public/capabilities") return json(res, 200, {
schema: "guanghu.lake-lamp-public-workorder/v1",
@ -1087,6 +1094,26 @@ function createApp(options = {}) {
}
}
if (req.method === "POST" && url.pathname === "/api/deployment/forgejo-merge") {
if (forgejoMergeSecret.length < 32) return json(res, 503, failure("forgejo_merge_gate_unconfigured"));
const rawBody = await readRawBody(req, 1024 * 1024);
const result = await processForgejoMergeWebhook({
rawBody,
signature: req.headers["x-forgejo-signature"] || req.headers["x-gitea-signature"],
eventName: req.headers["x-forgejo-event"] || req.headers["x-gitea-event"],
delivery: req.headers["x-forgejo-delivery"] || req.headers["x-gitea-delivery"],
}, {
secret: forgejoMergeSecret,
allowedMergers: forgejoMergeMergers,
queueDir: deploymentQueueDir,
deploymentRepositories: options.deploymentRepositories || loadDeploymentRepositories(deploymentRegistryFile),
forgejoApiBase: options.forgejoApiBase || process.env.LAKE_LAMP_FORGEJO_API_BASE || "http://127.0.0.1:3340/api/v1",
listFiles: options.listForgejoPullFiles,
enqueue: options.enqueueMergeDeploymentEvent,
});
return json(res, result.status || (result.ok ? 202 : 400), result);
}
if (req.method === "POST" && url.pathname === "/api/deployment/dispatch") {
const body = await readJson(req);
if (!body) return json(res, 400, failure("invalid_json"));
@ -1315,6 +1342,26 @@ function readJson(req, maxBytes = 32 * 1024) {
});
}
function readRawBody(req, maxBytes) {
return new Promise((resolve, reject) => {
const chunks = [];
let received = 0;
req.on("data", chunk => {
received += chunk.length;
if (received > maxBytes) {
const error = new Error("body too large");
error.code = "BODY_TOO_LARGE";
reject(error);
req.destroy();
return;
}
chunks.push(chunk);
});
req.on("end", () => resolve(Buffer.concat(chunks)));
req.on("error", reject);
});
}
function readBinaryBody(req, destination, maxBytes, options = {}) {
return new Promise((resolve, reject) => {
let received = 0;