From 5245d67f37b961eac73f5b5381aae0bb9b292477 Mon Sep 17 00:00:00 2001 From: Arunavo Ray Date: Fri, 24 Oct 2025 07:35:40 +0530 Subject: [PATCH] fix: enforce sequential metadata mirroring --- src/lib/db/schema.ts | 4 ++-- src/lib/env-config-loader.ts | 4 ++-- src/lib/gitea.ts | 37 +++++++++++++++++++++++++------- src/lib/utils/config-defaults.ts | 4 ++-- src/lib/utils/config-mapper.ts | 8 +++---- 5 files changed, 39 insertions(+), 18 deletions(-) diff --git a/src/lib/db/schema.ts b/src/lib/db/schema.ts index 1137050..c95d6e0 100644 --- a/src/lib/db/schema.ts +++ b/src/lib/db/schema.ts @@ -54,8 +54,8 @@ export const giteaConfigSchema = z.object({ .enum(["skip", "reference", "full-copy"]) .default("reference"), // Mirror options - issueConcurrency: z.number().int().min(1).default(3), - pullRequestConcurrency: z.number().int().min(1).default(5), + issueConcurrency: z.number().int().min(1).default(1), + pullRequestConcurrency: z.number().int().min(1).default(1), mirrorReleases: z.boolean().default(false), releaseLimit: z.number().default(10), mirrorMetadata: z.boolean().default(false), diff --git a/src/lib/env-config-loader.ts b/src/lib/env-config-loader.ts index 635b3ab..39e44fd 100644 --- a/src/lib/env-config-loader.ts +++ b/src/lib/env-config-loader.ts @@ -284,10 +284,10 @@ export async function initializeConfigFromEnv(): Promise { releaseLimit: envConfig.mirror.releaseLimit ?? existingConfig?.[0]?.giteaConfig?.releaseLimit ?? 10, issueConcurrency: envConfig.mirror.issueConcurrency && envConfig.mirror.issueConcurrency > 0 ? envConfig.mirror.issueConcurrency - : existingConfig?.[0]?.giteaConfig?.issueConcurrency ?? 3, + : existingConfig?.[0]?.giteaConfig?.issueConcurrency ?? 1, pullRequestConcurrency: envConfig.mirror.pullRequestConcurrency && envConfig.mirror.pullRequestConcurrency > 0 ? envConfig.mirror.pullRequestConcurrency - : existingConfig?.[0]?.giteaConfig?.pullRequestConcurrency ?? 5, + : existingConfig?.[0]?.giteaConfig?.pullRequestConcurrency ?? 1, mirrorMetadata: envConfig.mirror.mirrorMetadata ?? (envConfig.mirror.mirrorIssues || envConfig.mirror.mirrorPullRequests || envConfig.mirror.mirrorLabels || envConfig.mirror.mirrorMilestones) ?? existingConfig?.[0]?.giteaConfig?.mirrorMetadata ?? false, mirrorIssues: envConfig.mirror.mirrorIssues ?? existingConfig?.[0]?.giteaConfig?.mirrorIssues ?? false, mirrorPullRequests: envConfig.mirror.mirrorPullRequests ?? existingConfig?.[0]?.giteaConfig?.mirrorPullRequests ?? false, diff --git a/src/lib/gitea.ts b/src/lib/gitea.ts index 0dec27e..cf343e3 100644 --- a/src/lib/gitea.ts +++ b/src/lib/gitea.ts @@ -1592,11 +1592,17 @@ export const mirrorGitRepoIssuesToGitea = async ({ // Import the processWithRetry function const { processWithRetry } = await import("@/lib/utils/concurrency"); - const rawIssueConcurrency = config.giteaConfig?.issueConcurrency ?? 3; + const rawIssueConcurrency = config.giteaConfig?.issueConcurrency ?? 1; const issueConcurrencyLimit = Number.isFinite(rawIssueConcurrency) ? Math.max(1, Math.floor(rawIssueConcurrency)) - : 3; + : 1; + + if (issueConcurrencyLimit > 1) { + console.warn( + `[Issues] Concurrency is set to ${issueConcurrencyLimit}. This may lead to out-of-order issue creation in Gitea.` + ); + } // Process issues in parallel with concurrency control await processWithRetry( @@ -1670,10 +1676,19 @@ export const mirrorGitRepoIssuesToGitea = async ({ (res) => res.data ); - // Process comments in parallel with concurrency control - if (comments.length > 0) { + // Ensure comments are applied in chronological order to preserve discussion flow + const sortedComments = comments + .slice() + .sort( + (a, b) => + new Date(a.created_at || 0).getTime() - + new Date(b.created_at || 0).getTime() + ); + + // Process comments sequentially to preserve historical ordering + if (sortedComments.length > 0) { await processWithRetry( - comments, + sortedComments, async (comment) => { await httpPost( `${config.giteaConfig!.url}/api/v1/repos/${giteaOwner}/${repoName}/issues/${createdIssue.data.number}/comments`, @@ -1687,7 +1702,7 @@ export const mirrorGitRepoIssuesToGitea = async ({ return comment; }, { - concurrencyLimit: 5, + concurrencyLimit: 1, maxRetries: 2, retryDelay: 1000, onRetry: (_comment, error, attempt) => { @@ -2032,11 +2047,17 @@ export async function mirrorGitRepoPullRequestsToGitea({ const { processWithRetry } = await import("@/lib/utils/concurrency"); - const rawPullConcurrency = config.giteaConfig?.pullRequestConcurrency ?? 5; + const rawPullConcurrency = config.giteaConfig?.pullRequestConcurrency ?? 1; const pullRequestConcurrencyLimit = Number.isFinite(rawPullConcurrency) ? Math.max(1, Math.floor(rawPullConcurrency)) - : 5; + : 1; + + if (pullRequestConcurrencyLimit > 1) { + console.warn( + `[Pull Requests] Concurrency is set to ${pullRequestConcurrencyLimit}. This may lead to out-of-order pull request mirroring in Gitea.` + ); + } let successCount = 0; let failedCount = 0; diff --git a/src/lib/utils/config-defaults.ts b/src/lib/utils/config-defaults.ts index 43db60d..a699321 100644 --- a/src/lib/utils/config-defaults.ts +++ b/src/lib/utils/config-defaults.ts @@ -86,8 +86,8 @@ export async function createDefaultConfig({ userId, envOverrides = {} }: Default addTopics: true, preserveVisibility: false, forkStrategy: "reference", - issueConcurrency: 3, - pullRequestConcurrency: 5, + issueConcurrency: 1, + pullRequestConcurrency: 1, }, include: [], exclude: [], diff --git a/src/lib/utils/config-mapper.ts b/src/lib/utils/config-mapper.ts index 7d02fdc..961c226 100644 --- a/src/lib/utils/config-mapper.ts +++ b/src/lib/utils/config-mapper.ts @@ -89,8 +89,8 @@ export function mapUiToDbConfig( forkStrategy: advancedOptions.skipForks ? "skip" : "reference", // Mirror options from UI - issueConcurrency: giteaConfig.issueConcurrency ?? 3, - pullRequestConcurrency: giteaConfig.pullRequestConcurrency ?? 5, + issueConcurrency: giteaConfig.issueConcurrency ?? 1, + pullRequestConcurrency: giteaConfig.pullRequestConcurrency ?? 1, mirrorReleases: mirrorOptions.mirrorReleases, releaseLimit: mirrorOptions.releaseLimit || 10, mirrorMetadata: mirrorOptions.mirrorMetadata, @@ -134,8 +134,8 @@ export function mapDbToUiConfig(dbConfig: any): { preserveOrgStructure: dbConfig.giteaConfig?.preserveVisibility || false, // Map preserveVisibility mirrorStrategy: dbConfig.githubConfig?.mirrorStrategy || "preserve", // Get from GitHub config personalReposOrg: undefined, // Not stored in current schema - issueConcurrency: dbConfig.giteaConfig?.issueConcurrency ?? 3, - pullRequestConcurrency: dbConfig.giteaConfig?.pullRequestConcurrency ?? 5, + issueConcurrency: dbConfig.giteaConfig?.issueConcurrency ?? 1, + pullRequestConcurrency: dbConfig.giteaConfig?.pullRequestConcurrency ?? 1, }; // Map mirror options from various database fields