fix(cicd): 自动恢复 GitHub webhook 失败投递

This commit is contained in:
Codex
2026-07-12 08:51:06 +02:00
parent c7663f8df7
commit 4629a6fb94
16 changed files with 1458 additions and 19 deletions
@@ -11,6 +11,7 @@ import {
createDurableInboxStore,
createDurableSyncController,
createSyncCoordinator,
readFailedDeliveryRecoveryStatus,
run,
runResult,
syncRepository,
@@ -18,6 +19,45 @@ import {
const zeroDelay = async () => {};
test("primary status exposes a bounded redacted failed-delivery recovery ledger", async () => {
const root = mkdtempSync(join(tmpdir(), "gitea-hook-recovery-status-"));
const statePath = join(root, "ledger.json");
try {
const records = Object.fromEntries(Array.from({ length: 55 }, (_, index) => [`record-${index}`, {
repository: "pikasTech/agentrun",
hookId: 77,
deliveryId: `guid-${index}`,
apiDeliveryId: String(1000 + index),
state: index % 2 === 0 ? "attempted" : "pending",
attempts: index % 3,
cooldownMs: 300_000,
nextAt: "2026-07-12T07:11:40Z",
updatedAt: `2026-07-12T07:${String(index).padStart(2, "0")}:00Z`,
lastStatusCode: 502,
secret: "must-not-leak",
url: "https://must-not-leak.invalid/hook",
}]));
writeFileSync(statePath, JSON.stringify({ version: 1, kind: "GiteaGithubHookRecoveryLedger", records }));
const status = await readFailedDeliveryRecoveryStatus(statePath);
assert.equal(status.ready, true);
assert.equal(status.entries.length, 50);
assert.equal(status.counts.attempted + status.counts.pending, 55);
assert.equal(JSON.stringify(status).includes("must-not-leak"), false);
writeFileSync(statePath, JSON.stringify({
version: 1,
kind: "GiteaGithubHookRecoveryLedger",
records: { corrupt: { ...records["record-0"], state: "unknown-hidden-state" } },
}));
const invalid = await readFailedDeliveryRecoveryStatus(statePath);
assert.equal(invalid.ready, false);
assert.equal(invalid.state, "invalid-or-unreadable");
assert.equal(invalid.errorType, "hook-recovery-ledger-record-invalid");
assert.deepEqual(invalid.entries, []);
} finally {
rmSync(root, { recursive: true, force: true });
}
});
test("explicit entrypoint starts through Kubernetes ConfigMap symlinks", () => {
const root = mkdtempSync(join(tmpdir(), "gitea-sync-main-"));
try {