[PR #287] [MERGED] fix: reconcile metadata on every sync instead of once per repo #2919

Closed
opened 2026-05-17 19:23:30 -05:00 by GiteaMirror · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/RayLabsHQ/gitea-mirror/pull/287
Author: @riguettodev
Created: 5/15/2026
Status: Merged
Merged: 5/16/2026
Merged by: @arunavo4

Base: mainHead: fix/reconcile-metadata-on-resync


📝 Commits (1)

  • efc3ea0 fix: reconcile metadata on every sync instead of once per repo

📊 Changes

3 files changed (+40 additions, -124 deletions)

View changed files

📝 src/lib/gitea-enhanced.test.ts (+7 -4)
📝 src/lib/gitea-enhanced.ts (+9 -39)
📝 src/lib/gitea.ts (+24 -81)

📄 Description

Summary

After the first successful sync of a repository, subsequent syncs no longer reconcile metadata (issue/PR titles, bodies, labels, milestones). Changes made on GitHub never propagate to Gitea on re-sync, even though the underlying mirror functions are already designed to perform idempotent upserts via [GH-ISSUE #N] / [GH-PR #N] markers.

The log signature is:

[Sync] Issues already mirrored for <repo>; skipping
[Sync] Pull requests already mirrored for <repo>; skipping
[Sync] Labels already mirrored for <repo>; skipping
[Sync] Milestones already mirrored for <repo>; skipping

This is a regression: the exact same behavior was the subject of #165 (Jan 2026), which was fixed by #184. The regression was reintroduced by #266.

Root Cause

PR #184 (Implement incremental issue and PR metadata sync) added marker-based idempotent upserts so re-syncs would update existing entries instead of creating duplicates. That solved #165, where the maintainer wrote:

"It's a mistake on my part. I kept that resyncing for later cause the tracking and de-duplicating across github and gitea was getting too complex."
— @arunavo4 in #165

Later, PR #266 (fix: prevent duplicate issue/PR mirroring, fixes #262) added one-shot guards in gitea.ts and gitea-enhanced.ts:

const shouldMirrorIssuesThisRun =
  !!config.giteaConfig?.mirrorIssues &&
  !skipMetadataForStarred &&
  !metadataState.components.issues; // locks the path off after first run
// same pattern for pullRequests, labels, milestones

The intent was duplicate prevention, but #184 already handled duplicates via marker-based deduplication. The guard turned a reconciling re-sync into a one-shot mirror, re-introducing the original #165 symptom.

The releases mirror path in the same files (shouldMirrorReleases) does not have such a guard and runs unconditionally on every sync — that is the correct pattern, and the model this PR aligns the other metadata paths with.

Fix

Remove the !metadataState.components.X guards in src/lib/gitea.ts and src/lib/gitea-enhanced.ts (4 sites each, 2 files × 2 duplicated code blocks). The metadata mirror paths now run on every sync; the underlying mirror functions handle idempotency via their existing markers and dedup logic.

Per-mirror deduplication is unchanged:

Component Dedup key
Issues [GH-ISSUE #N] marker in Gitea issue title
Pull Requests [GH-PR #N] marker in Gitea issue title
Labels name (Gitea API uniqueness)
Milestones title

Note on labels: the labels mirror path still has its existing !shouldMirrorIssuesThisRun guard, because the issue mirror creates labels inline as a side effect of creating each issue. Calling mirrorGitRepoLabelsToGitea separately would be redundant when issues are about to run. This is unchanged behavior.

Testing

src/lib/gitea-enhanced.test.ts had a test that was actively enforcing the buggy behavior:

// before
expect(mockMirrorGitHubReleasesToGitea).not.toHaveBeenCalled();
expect(mockMirrorGitRepoIssuesToGitea).not.toHaveBeenCalled();
expect(mockMirrorGitRepoPullRequestsToGitea).not.toHaveBeenCalled();
expect(mockMirrorGitRepoLabelsToGitea).not.toHaveBeenCalled();
expect(mockMirrorGitRepoMilestonesToGitea).not.toHaveBeenCalled();

Updated to reflect the corrected behavior:

// after
expect(mockMirrorGitHubReleasesToGitea).not.toHaveBeenCalled();
expect(mockMirrorGitRepoIssuesToGitea).toHaveBeenCalledTimes(1);
expect(mockMirrorGitRepoPullRequestsToGitea).toHaveBeenCalledTimes(1);
expect(mockMirrorGitRepoLabelsToGitea).not.toHaveBeenCalled(); // skipped because issues runs
expect(mockMirrorGitRepoMilestonesToGitea).toHaveBeenCalledTimes(1);
bun test src/lib/gitea-enhanced.test.ts

Steps to Reproduce (pre-fix)

  1. Mirror any repository with issues.
  2. On GitHub, rename an issue title (or edit body / change a label / move milestone).
  3. Trigger a re-sync in gitea-mirror.
  4. Observe [Sync] Issues already mirrored ...; skipping in the logs.
  5. Confirm the change is not present in Gitea.

After the fix, the renamed title is PATCH'd to the existing Gitea issue (matched by [GH-ISSUE #N] marker), no duplicates are created.

  • Fixes #165 — regression reintroduced after the original fix in #184.
  • Regression source: #266.

Possible follow-up (not in this PR)

This change reconciles metadata on every sync. For repos with many issues, that means a small bump in GitHub API usage per sync. A natural optimization is to pass since=metadataState.components.issues.lastSyncedAt to the GitHub issues API so only issues updated since the last successful sync are fetched. That belongs in a separate PR; this one focuses on restoring correctness first.


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/RayLabsHQ/gitea-mirror/pull/287 **Author:** [@riguettodev](https://github.com/riguettodev) **Created:** 5/15/2026 **Status:** ✅ Merged **Merged:** 5/16/2026 **Merged by:** [@arunavo4](https://github.com/arunavo4) **Base:** `main` ← **Head:** `fix/reconcile-metadata-on-resync` --- ### 📝 Commits (1) - [`efc3ea0`](https://github.com/RayLabsHQ/gitea-mirror/commit/efc3ea08610d7e91295abf68c75f878282f9af7b) fix: reconcile metadata on every sync instead of once per repo ### 📊 Changes **3 files changed** (+40 additions, -124 deletions) <details> <summary>View changed files</summary> 📝 `src/lib/gitea-enhanced.test.ts` (+7 -4) 📝 `src/lib/gitea-enhanced.ts` (+9 -39) 📝 `src/lib/gitea.ts` (+24 -81) </details> ### 📄 Description ## Summary After the first successful sync of a repository, subsequent syncs no longer reconcile metadata (issue/PR titles, bodies, labels, milestones). Changes made on GitHub never propagate to Gitea on re-sync, even though the underlying mirror functions are already designed to perform idempotent upserts via `[GH-ISSUE #N]` / `[GH-PR #N]` markers. The log signature is: ``` [Sync] Issues already mirrored for <repo>; skipping [Sync] Pull requests already mirrored for <repo>; skipping [Sync] Labels already mirrored for <repo>; skipping [Sync] Milestones already mirrored for <repo>; skipping ``` This is a regression: the exact same behavior was the subject of #165 (Jan 2026), which was fixed by #184. The regression was reintroduced by #266. ## Root Cause PR #184 (`Implement incremental issue and PR metadata sync`) added marker-based idempotent upserts so re-syncs would update existing entries instead of creating duplicates. That solved #165, where the maintainer wrote: > *"It's a mistake on my part. I kept that resyncing for later cause the tracking and de-duplicating across github and gitea was getting too complex."* > — @arunavo4 in [#165](https://github.com/RayLabsHQ/gitea-mirror/issues/165#issuecomment-3742337429) Later, PR #266 (`fix: prevent duplicate issue/PR mirroring`, fixes #262) added one-shot guards in `gitea.ts` and `gitea-enhanced.ts`: ```ts const shouldMirrorIssuesThisRun = !!config.giteaConfig?.mirrorIssues && !skipMetadataForStarred && !metadataState.components.issues; // locks the path off after first run // same pattern for pullRequests, labels, milestones ``` The intent was duplicate prevention, but #184 already handled duplicates via marker-based deduplication. The guard turned a *reconciling* re-sync into a *one-shot* mirror, re-introducing the original #165 symptom. The releases mirror path in the same files (`shouldMirrorReleases`) does **not** have such a guard and runs unconditionally on every sync — that is the correct pattern, and the model this PR aligns the other metadata paths with. ## Fix Remove the `!metadataState.components.X` guards in `src/lib/gitea.ts` and `src/lib/gitea-enhanced.ts` (4 sites each, 2 files × 2 duplicated code blocks). The metadata mirror paths now run on every sync; the underlying mirror functions handle idempotency via their existing markers and dedup logic. Per-mirror deduplication is unchanged: | Component | Dedup key | |---|---| | Issues | `[GH-ISSUE #N]` marker in Gitea issue title | | Pull Requests | `[GH-PR #N]` marker in Gitea issue title | | Labels | name (Gitea API uniqueness) | | Milestones | title | **Note on labels:** the labels mirror path still has its existing `!shouldMirrorIssuesThisRun` guard, because the issue mirror creates labels inline as a side effect of creating each issue. Calling `mirrorGitRepoLabelsToGitea` separately would be redundant when issues are about to run. This is unchanged behavior. ## Testing `src/lib/gitea-enhanced.test.ts` had a test that was actively enforcing the buggy behavior: ```ts // before expect(mockMirrorGitHubReleasesToGitea).not.toHaveBeenCalled(); expect(mockMirrorGitRepoIssuesToGitea).not.toHaveBeenCalled(); expect(mockMirrorGitRepoPullRequestsToGitea).not.toHaveBeenCalled(); expect(mockMirrorGitRepoLabelsToGitea).not.toHaveBeenCalled(); expect(mockMirrorGitRepoMilestonesToGitea).not.toHaveBeenCalled(); ``` Updated to reflect the corrected behavior: ```ts // after expect(mockMirrorGitHubReleasesToGitea).not.toHaveBeenCalled(); expect(mockMirrorGitRepoIssuesToGitea).toHaveBeenCalledTimes(1); expect(mockMirrorGitRepoPullRequestsToGitea).toHaveBeenCalledTimes(1); expect(mockMirrorGitRepoLabelsToGitea).not.toHaveBeenCalled(); // skipped because issues runs expect(mockMirrorGitRepoMilestonesToGitea).toHaveBeenCalledTimes(1); ``` ``` bun test src/lib/gitea-enhanced.test.ts ``` ## Steps to Reproduce (pre-fix) 1. Mirror any repository with issues. 2. On GitHub, rename an issue title (or edit body / change a label / move milestone). 3. Trigger a re-sync in gitea-mirror. 4. Observe `[Sync] Issues already mirrored ...; skipping` in the logs. 5. Confirm the change is **not** present in Gitea. After the fix, the renamed title is PATCH'd to the existing Gitea issue (matched by `[GH-ISSUE #N]` marker), no duplicates are created. ## Related - Fixes #165 — regression reintroduced after the original fix in #184. - Regression source: #266. ## Possible follow-up (not in this PR) This change reconciles metadata on every sync. For repos with many issues, that means a small bump in GitHub API usage per sync. A natural optimization is to pass `since=metadataState.components.issues.lastSyncedAt` to the GitHub issues API so only issues updated since the last successful sync are fetched. That belongs in a separate PR; this one focuses on restoring correctness first. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
GiteaMirror added the pull-request label 2026-05-17 19:23:30 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/gitea-mirror#2919