fix: retry Gitea PaC webhook mirror sync
Pipelines as Code CI / hwlab-web-probe-sentinel-nc01- Success

This commit is contained in:
Codex
2026-07-09 15:15:55 +02:00
parent 07b69e7b10
commit 99b394691b
4 changed files with 69 additions and 5 deletions
@@ -13,6 +13,9 @@ const giteaUsername = requiredEnv("GITEA_USERNAME");
const giteaPassword = requiredEnv("GITEA_PASSWORD");
const webhookSecret = requiredEnv("GITHUB_WEBHOOK_SECRET");
const giteaBaseUrl = requiredEnv("UNIDESK_GITEA_SERVICE_BASE_URL").replace(/\/+$/u, "");
const retryMaxAttempts = positiveIntegerEnv("UNIDESK_GITEA_WEBHOOK_RETRY_MAX_ATTEMPTS");
const retryInitialDelayMs = positiveIntegerEnv("UNIDESK_GITEA_WEBHOOK_RETRY_INITIAL_DELAY_MS");
const retryMaxDelayMs = positiveIntegerEnv("UNIDESK_GITEA_WEBHOOK_RETRY_MAX_DELAY_MS");
const githubAuthHeader = `Authorization: Basic ${Buffer.from(`x-access-token:${githubToken}`).toString("base64")}`;
const giteaAuthHeader = `Authorization: Basic ${Buffer.from(`${giteaUsername}:${giteaPassword}`).toString("base64")}`;
@@ -60,7 +63,7 @@ createServer(async (req, res) => {
state.pending = false;
writeJson(res, 202, { ok: true, event, repository, ref, repo: repo.key, disposition: "accepted", valuesPrinted: false });
setImmediate(() => {
runSyncLoop(repo, state, startedAt);
void runSyncLoop(repo, state, startedAt);
});
} catch (error) {
log({ event: "github-to-gitea-sync-error", ok: false, error: String(error?.message || error), valuesPrinted: false });
@@ -79,14 +82,33 @@ function syncState(key) {
return state;
}
function runSyncLoop(repo, state, startedAt) {
async function runSyncLoop(repo, state, startedAt) {
let attempt = 0;
try {
do {
state.pending = false;
attempt += 1;
const sync = syncRepository(repo);
log({ event: "github-to-gitea-sync", repo: repo.key, ok: sync.ok, sourceCommit: sync.sourceCommit, snapshotRef: sync.snapshotRef, error: sync.error ? redact(sync.error) : null, attempt, pendingRerun: state.pending, elapsedMs: Date.now() - startedAt, valuesPrinted: false });
for (let syncAttempt = 1; syncAttempt <= retryMaxAttempts; syncAttempt += 1) {
const sync = syncRepository(repo);
const willRetry = !sync.ok && syncAttempt < retryMaxAttempts;
log({
event: "github-to-gitea-sync",
repo: repo.key,
ok: sync.ok,
sourceCommit: sync.sourceCommit,
snapshotRef: sync.snapshotRef,
error: sync.error ? redact(sync.error) : null,
attempt,
syncAttempt,
maxSyncAttempts: retryMaxAttempts,
willRetry,
pendingRerun: state.pending,
elapsedMs: Date.now() - startedAt,
valuesPrinted: false,
});
if (sync.ok || !willRetry) break;
await sleep(retryDelayMs(syncAttempt));
}
} while (state.pending);
} catch (error) {
log({ event: "github-to-gitea-sync-error", repo: repo.key, ok: false, error: redact(String(error?.message || error)), attempt, elapsedMs: Date.now() - startedAt, valuesPrinted: false });
@@ -95,6 +117,14 @@ function runSyncLoop(repo, state, startedAt) {
}
}
function retryDelayMs(syncAttempt) {
return Math.min(retryMaxDelayMs, retryInitialDelayMs * (2 ** Math.max(0, syncAttempt - 1)));
}
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
function syncRepository(repo) {
const work = mkdtempSync(join(tmpdir(), `gitea-gh-sync-${repo.key}-`));
try {
@@ -174,3 +204,9 @@ function requiredEnv(name) {
if (!value) throw new Error(`missing env ${name}`);
return value;
}
function positiveIntegerEnv(name) {
const value = Number.parseInt(requiredEnv(name), 10);
if (!Number.isFinite(value) || value < 1) throw new Error(`invalid positive integer env ${name}`);
return value;
}