[PR #336] [MERGED] fix: stop false-positive orphan archiving and heal sync 405s on archived-* renamed mirrors (#331) #6301

Closed
opened 2026-07-12 09:52:33 -05:00 by GiteaMirror · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/RayLabsHQ/gitea-mirror/pull/336
Author: @arunavo4
Created: 7/2/2026
Status: Merged
Merged: 7/2/2026
Merged by: @arunavo4

Base: mainHead: fix/331-archived-rename-sync


📝 Commits (1)

  • d552e20 fix: stop false-positive orphan archiving and heal sync 405s on archived-* renamed mirrors (#331)

📊 Changes

6 files changed (+963 additions, -38 deletions)

View changed files

src/lib/gitea-archive.test.ts (+245 -0)
📝 src/lib/gitea-enhanced.test.ts (+380 -5)
📝 src/lib/gitea-enhanced.ts (+99 -6)
📝 src/lib/gitea.ts (+44 -18)
src/lib/repository-cleanup-service.test.ts (+52 -0)
📝 src/lib/repository-cleanup-service.ts (+143 -9)

📄 Description

Fixes the remaining half of #331: repos getting renamed to archived-* in Forgejo/Gitea even though they still exist on GitHub, and every subsequent sync of those repos failing with HTTP 405.

Root cause

The archiving was deterministic, not transient. Repos added via the "+" Add Repository dialog are stored with isStarred: false and a foreign owner — a combination that can never appear in the authenticated bulk fetches (listForAuthenticatedUser with owner,collaborator,organization_member affiliation, plus the starred list). So the orphan-cleanup pass (startup + every 6h + every scheduler cycle, default action archive) flagged each of them as "no longer on GitHub" and archived them within one cycle. That's why fresh repos were archived within the hour — 9 of the reporter's 10 renamed repos are alive and unarchived on GitHub.

The 405 is a redirect artifact. Archiving renames the repo to archived-{name} in Forgejo but the DB kept the old name. Requests to the old name get a 301; fetch follows it silently — GETs succeed against the new repo, but the POST to /mirror-sync is method-downgraded to GET per the WHATWG spec and lands on a POST-only endpoint → 405 Method Not Allowed.

The fix (three parts)

  1. Confirm before archiving (repository-cleanup-service.ts) — a repo missing from the bulk list is only orphaned if a targeted per-repo check (checkRepoIsStarredByAuthenticatedUser / repos.get) returns a clean 404. Any other outcome (found, rate limit, network error) fails safe and logs a warning.
  2. Persist the rename (gitea.ts) — archiveGiteaRepo() returns the actual post-rename name and the cleanup service records it in mirroredLocation, so the DB never points at a 301.
  3. Self-heal the installed base (gitea-enhanced.ts) — the sync candidate loop now adopts the canonical owner/name from the GET response body before POSTing (defeats silent redirects for any Gitea-side rename), and archived repos additionally try an archived-{name} fallback guarded by an original_url source match. Archived repos stay archived: no mirror-interval PATCH, status remains archived, matching the documented Manual Sync contract ("disables automatic syncs — use Manual Sync when you want to refresh").

Also hardens mirrorGitHubReleasesToGitea to derive GitHub coordinates from fullName, so a Gitea-side name can never leak into GitHub API calls.

Verification

  • End-to-end on real Forgejo 15.0.3 rootless (the reporter's setup): reproduced the exact 405 pre-fix; post-fix the broken state (stale DB name → renamed archived-* mirror) syncs successfully — access log shows GET stale → 301, GET canonical → 200, POST canonical /mirror-sync → 200 OK, and mirror_updated advances. Archived repos keep interval 0s with no PATCH issued; a non-archived renamed repo heals and gets the configured interval (8h0m0s).
  • Tests: 18 new unit tests (gitea-archive.test.ts, repository-cleanup-service.test.ts, extended gitea-enhanced.test.ts). Full suite: 381 pass / 0 fail.

Fixes #331


🔄 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/336 **Author:** [@arunavo4](https://github.com/arunavo4) **Created:** 7/2/2026 **Status:** ✅ Merged **Merged:** 7/2/2026 **Merged by:** [@arunavo4](https://github.com/arunavo4) **Base:** `main` ← **Head:** `fix/331-archived-rename-sync` --- ### 📝 Commits (1) - [`d552e20`](https://github.com/RayLabsHQ/gitea-mirror/commit/d552e205689c180cc7afbb9bb5bf0e131186e64e) fix: stop false-positive orphan archiving and heal sync 405s on archived-* renamed mirrors (#331) ### 📊 Changes **6 files changed** (+963 additions, -38 deletions) <details> <summary>View changed files</summary> ➕ `src/lib/gitea-archive.test.ts` (+245 -0) 📝 `src/lib/gitea-enhanced.test.ts` (+380 -5) 📝 `src/lib/gitea-enhanced.ts` (+99 -6) 📝 `src/lib/gitea.ts` (+44 -18) ➕ `src/lib/repository-cleanup-service.test.ts` (+52 -0) 📝 `src/lib/repository-cleanup-service.ts` (+143 -9) </details> ### 📄 Description Fixes the remaining half of #331: repos getting renamed to `archived-*` in Forgejo/Gitea even though they still exist on GitHub, and every subsequent sync of those repos failing with **HTTP 405**. ## Root cause **The archiving was deterministic, not transient.** Repos added via the "+" Add Repository dialog are stored with `isStarred: false` and a foreign owner — a combination that can *never* appear in the authenticated bulk fetches (`listForAuthenticatedUser` with `owner,collaborator,organization_member` affiliation, plus the starred list). So the orphan-cleanup pass (startup + every 6h + every scheduler cycle, default action `archive`) flagged each of them as "no longer on GitHub" and archived them within one cycle. That's why fresh repos were archived within the hour — 9 of the reporter's 10 renamed repos are alive and unarchived on GitHub. **The 405 is a redirect artifact.** Archiving renames the repo to `archived-{name}` in Forgejo but the DB kept the old name. Requests to the old name get a 301; `fetch` follows it silently — GETs succeed against the *new* repo, but the POST to `/mirror-sync` is method-downgraded to GET per the WHATWG spec and lands on a POST-only endpoint → 405 Method Not Allowed. ## The fix (three parts) 1. **Confirm before archiving** (`repository-cleanup-service.ts`) — a repo missing from the bulk list is only orphaned if a targeted per-repo check (`checkRepoIsStarredByAuthenticatedUser` / `repos.get`) returns a clean 404. Any other outcome (found, rate limit, network error) fails safe and logs a warning. 2. **Persist the rename** (`gitea.ts`) — `archiveGiteaRepo()` returns the actual post-rename name and the cleanup service records it in `mirroredLocation`, so the DB never points at a 301. 3. **Self-heal the installed base** (`gitea-enhanced.ts`) — the sync candidate loop now adopts the canonical owner/name from the GET response body before POSTing (defeats silent redirects for *any* Gitea-side rename), and archived repos additionally try an `archived-{name}` fallback guarded by an `original_url` source match. Archived repos stay archived: no mirror-interval PATCH, status remains `archived`, matching the documented Manual Sync contract ("disables automatic syncs — use Manual Sync when you want to refresh"). Also hardens `mirrorGitHubReleasesToGitea` to derive GitHub coordinates from `fullName`, so a Gitea-side name can never leak into GitHub API calls. ## Verification - **End-to-end on real Forgejo 15.0.3 rootless** (the reporter's setup): reproduced the exact 405 pre-fix; post-fix the broken state (stale DB name → renamed `archived-*` mirror) syncs successfully — access log shows `GET stale → 301`, `GET canonical → 200`, `POST canonical /mirror-sync → 200 OK`, and `mirror_updated` advances. Archived repos keep interval `0s` with no PATCH issued; a non-archived renamed repo heals and gets the configured interval (`8h0m0s`). - **Tests**: 18 new unit tests (`gitea-archive.test.ts`, `repository-cleanup-service.test.ts`, extended `gitea-enhanced.test.ts`). Full suite: **381 pass / 0 fail**. Fixes #331 --- <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-07-12 09:52:33 -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#6301