Skip to content

Repos exist in Gitea but missing from Mirror DB #284

Description

@nikhilbadyal

I ran into a state where many repos existed in Gitea, but Gitea Mirror didn’t have them in its database. Because cleanup only checks the Mirror DB, these repos were never detected as orphaned and cleanup reported “0 orphaned repos.”

What happened / evidence

  • Manual cleanup API reports:
    • orphanedCount: 0, processedCount: 0
Image
  • Docker logs show cleanup running successfully but finding no orphaned repos.
  • DB query showed zero rows for affected repos:
    • select ... from repositories where full_name like '%shoutrrr%'[]
  • Gitea still had those repos, including nikhil/abc.

This makes it impossible to keep Gitea and the Mirror DB consistent. Cleanup and UI state both rely on the DB, so “untracked” repos become invisible to maintenance features.

Expected behavior
If repos exist in Gitea, there should be a way to reconcile or detect DB mismatches.

Actual behavior
Cleanup reports zero orphans because the repos are not in the Mirror DB, even though they exist in Gitea.

Request / Feature Idea
Please add a “force reconcile” or “sync sanity check” feature that:

  • Compares Gitea repos vs Mirror DB.
  • Flags missing DB rows or missing Gitea repos.
  • Optionally fixes the DB or archives/deletes the extra repos.
  • Reports a summary of abnormalities.

This is what i did to fix.

docker exec -i gitea-mirror sh -lc 'cat > /tmp/cleanup.mjs' <<'EOF'
import { Database } from "bun:sqlite";

const url = process.env.GITEA_URL;
const token = process.env.GITEA_TOKEN;
const owner = process.env.GITEA_OWNER;
const ownerType = process.env.GITEA_OWNER_TYPE || "users";
const mode = process.env.MODE || "dry";

if (!url || !token || !owner) {
  console.error("Missing env vars");
  process.exit(1);
}

const db = new Database("/app/data/gitea-mirror.db");
const trackedRows = db.query(
  "select mirrored_location from repositories where mirrored_location is not null and mirrored_location != ''"
).all();

const tracked = new Set(
  trackedRows.map((r) => String(r.mirrored_location).trim().toLowerCase())
);

async function fetchPage(page) {
  const api = url + "/api/v1/" + ownerType + "/" + owner + "/repos?limit=50&page=" + page;
  const res = await fetch(api, {
    headers: { Authorization: "token " + token },
  });
  if (!res.ok) throw new Error("Gitea " + res.status + " " + res.statusText);
  return res.json();
}

let page = 1;
const candidates = [];

while (true) {
  const repos = await fetchPage(page);
  if (!Array.isArray(repos) || repos.length === 0) break;
  for (const repo of repos) {
    const repoOwner = (repo.owner && repo.owner.login ? repo.owner.login : owner).toLowerCase();
    const full = (repoOwner + "/" + repo.name).toLowerCase();
    if (repo.mirror === true && !tracked.has(full)) {
      candidates.push({ owner: repoOwner, name: repo.name });
    }
  }
  page++;
}

console.log("Candidates: " + candidates.length);
for (const c of candidates) console.log(c.owner + "/" + c.name);

if (mode !== "delete") process.exit(0);

for (const c of candidates) {
  const api = url + "/api/v1/repos/" + c.owner + "/" + c.name;
  const res = await fetch(api, {
    method: "DELETE",
    headers: { Authorization: "token " + token },
  });
  console.log(c.owner + "/" + c.name + " -> " + res.status);
}
EOF





docker exec -it gitea-mirror sh -lc '
export GITEA_URL=""
export GITEA_TOKEN=""
export GITEA_OWNER=""
export GITEA_OWNER_TYPE="users"
export MODE="delete"
bun /tmp/cleanup.mjs
'


> Candidates: 40




docker exec -it gitea-mirror sh -lc '
export GITEA_URL=""
export GITEA_TOKEN=""
export GITEA_OWNER=""
export GITEA_OWNER_TYPE="users"
export MODE="delete"
bun /tmp/cleanup.mjs
'
Candidates: 40



Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions