GitHub's listForAuthenticatedUser defaults to returning every repo the user has access to (owner + collaborator + organization_member), which imports collab repos as noise for users who only want their own. Adds an includeCollaboratorRepos toggle (default on, preserves existing behavior). When off, scopes the affiliation filter to owner only.
This is the proper rewrite of #279, which was closed because the field was added to the UI type only and never plumbed through the schema, mappers, or cleanup service.
What's wired through
Schema (src/lib/db/schema.ts): includeCollaboratorRepos: z.boolean().default(true) in githubConfigSchema — opt-out, no migration needed (Zod default fills in for existing rows).
UI type (src/types/config.ts): added to GitHubConfig.
Mappers (src/lib/utils/config-mapper.ts): UI↔DB round-trips with ?? true fallbacks for legacy rows missing the field.
Read site (src/lib/github.ts): getGithubRepositories now accepts an includeCollaboratorReposOverride param and picks affiliation between "owner" and "owner,collaborator".
Cleanup safety (src/lib/repository-cleanup-service.ts): forces includeCollaboratorReposOverride: true so toggling the option off does not mark previously-mirrored collab repos as orphans (which would archive/delete them depending on orphanedRepoAction).
Env loader (src/lib/env-config-loader.ts): new INCLUDE_COLLABORATOR_REPOS env var (default true, tri-state parser so unset falls through to existing config / schema default rather than clobbering to false).
UI (src/components/config/GitHubMirrorSettings.tsx): checkbox under "Include private repositories", Users icon to match neighbors, defaults checked.
Docs (docs/ENVIRONMENT_VARIABLES.md): new env var documented under Repository Selection.
Why default true
Defaulting to true preserves what every existing user sees today. Defaulting to false would silently drop collab repos for anyone who currently mirrors them — that's the same silent-breaking-change problem that sank #279.
Tests
src/lib/utils/config-mapper.test.ts: round-trips the field UI→DB→UI in both true and false cases, plus the missing-field-defaults-to-true case, plus a Zod default check.
src/lib/github-affiliation.test.ts: asserts affiliation flips correctly across the four states (default, true, false, override).
Test plan
bun test (246 pass, 0 fail)
Verified in browser at /config: checkbox renders in the right place, defaults to checked, toggle works both directions
Manual end-to-end: connect a real GitHub account with collab repos, save with toggle off, run a sync, confirm only owned repos imported
Manual end-to-end: with collab repos already mirrored, toggle off, run sync + cleanup, confirm collab repos are NOT marked orphaned (cleanup-service override working)
🔄 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/283
**Author:** [@arunavo4](https://github.com/arunavo4)
**Created:** 5/4/2026
**Status:** ✅ Merged
**Merged:** 5/4/2026
**Merged by:** [@arunavo4](https://github.com/arunavo4)
**Base:** `main` ← **Head:** `feat/include-collaborator-repos`
---
### 📝 Commits (1)
- [`48942b9`](https://github.com/RayLabsHQ/gitea-mirror/commit/48942b906f090cf3741454e817ec1d3ab2c0f16a) feat: add option to exclude collaborator repos from import (closes #279)
### 📊 Changes
**10 files changed** (+217 additions, -5 deletions)
<details>
<summary>View changed files</summary>
📝 `docs/ENVIRONMENT_VARIABLES.md` (+1 -0)
📝 `src/components/config/GitHubMirrorSettings.tsx` (+21 -0)
📝 `src/lib/db/schema.ts` (+1 -0)
📝 `src/lib/env-config-loader.ts` (+7 -0)
➕ `src/lib/github-affiliation.test.ts` (+75 -0)
📝 `src/lib/github.ts` (+12 -1)
📝 `src/lib/repository-cleanup-service.ts` (+5 -2)
📝 `src/lib/utils/config-mapper.test.ts` (+92 -2)
📝 `src/lib/utils/config-mapper.ts` (+2 -0)
📝 `src/types/config.ts` (+1 -0)
</details>
### 📄 Description
## Summary
GitHub's `listForAuthenticatedUser` defaults to returning every repo the user has access to (owner + collaborator + organization_member), which imports collab repos as noise for users who only want their own. Adds an `includeCollaboratorRepos` toggle (default **on**, preserves existing behavior). When off, scopes the affiliation filter to `owner` only.
This is the proper rewrite of #279, which was closed because the field was added to the UI type only and never plumbed through the schema, mappers, or cleanup service.
## What's wired through
- **Schema** (`src/lib/db/schema.ts`): `includeCollaboratorRepos: z.boolean().default(true)` in `githubConfigSchema` — opt-out, no migration needed (Zod default fills in for existing rows).
- **UI type** (`src/types/config.ts`): added to `GitHubConfig`.
- **Mappers** (`src/lib/utils/config-mapper.ts`): UI↔DB round-trips with `?? true` fallbacks for legacy rows missing the field.
- **Read site** (`src/lib/github.ts`): `getGithubRepositories` now accepts an `includeCollaboratorReposOverride` param and picks `affiliation` between `"owner"` and `"owner,collaborator"`.
- **Cleanup safety** (`src/lib/repository-cleanup-service.ts`): forces `includeCollaboratorReposOverride: true` so toggling the option off does **not** mark previously-mirrored collab repos as orphans (which would archive/delete them depending on `orphanedRepoAction`).
- **Env loader** (`src/lib/env-config-loader.ts`): new `INCLUDE_COLLABORATOR_REPOS` env var (default `true`, tri-state parser so unset falls through to existing config / schema default rather than clobbering to `false`).
- **UI** (`src/components/config/GitHubMirrorSettings.tsx`): checkbox under "Include private repositories", `Users` icon to match neighbors, defaults checked.
- **Docs** (`docs/ENVIRONMENT_VARIABLES.md`): new env var documented under Repository Selection.
## Why default true
Defaulting to `true` preserves what every existing user sees today. Defaulting to `false` would silently drop collab repos for anyone who currently mirrors them — that's the same silent-breaking-change problem that sank #279.
## Tests
- `src/lib/utils/config-mapper.test.ts`: round-trips the field UI→DB→UI in both true and false cases, plus the missing-field-defaults-to-true case, plus a Zod default check.
- `src/lib/github-affiliation.test.ts`: asserts `affiliation` flips correctly across the four states (default, true, false, override).
## Test plan
- [x] `bun test` (246 pass, 0 fail)
- [x] Verified in browser at `/config`: checkbox renders in the right place, defaults to checked, toggle works both directions
- [ ] Manual end-to-end: connect a real GitHub account with collab repos, save with toggle off, run a sync, confirm only owned repos imported
- [ ] Manual end-to-end: with collab repos already mirrored, toggle off, run sync + cleanup, confirm collab repos are NOT marked orphaned (cleanup-service override working)
---
<sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
📋 Pull Request Information
Original PR: https://github.com/RayLabsHQ/gitea-mirror/pull/283
Author: @arunavo4
Created: 5/4/2026
Status: ✅ Merged
Merged: 5/4/2026
Merged by: @arunavo4
Base:
main← Head:feat/include-collaborator-repos📝 Commits (1)
48942b9feat: add option to exclude collaborator repos from import (closes #279)📊 Changes
10 files changed (+217 additions, -5 deletions)
View changed files
📝
docs/ENVIRONMENT_VARIABLES.md(+1 -0)📝
src/components/config/GitHubMirrorSettings.tsx(+21 -0)📝
src/lib/db/schema.ts(+1 -0)📝
src/lib/env-config-loader.ts(+7 -0)➕
src/lib/github-affiliation.test.ts(+75 -0)📝
src/lib/github.ts(+12 -1)📝
src/lib/repository-cleanup-service.ts(+5 -2)📝
src/lib/utils/config-mapper.test.ts(+92 -2)📝
src/lib/utils/config-mapper.ts(+2 -0)📝
src/types/config.ts(+1 -0)📄 Description
Summary
GitHub's
listForAuthenticatedUserdefaults to returning every repo the user has access to (owner + collaborator + organization_member), which imports collab repos as noise for users who only want their own. Adds anincludeCollaboratorRepostoggle (default on, preserves existing behavior). When off, scopes the affiliation filter toowneronly.This is the proper rewrite of #279, which was closed because the field was added to the UI type only and never plumbed through the schema, mappers, or cleanup service.
What's wired through
src/lib/db/schema.ts):includeCollaboratorRepos: z.boolean().default(true)ingithubConfigSchema— opt-out, no migration needed (Zod default fills in for existing rows).src/types/config.ts): added toGitHubConfig.src/lib/utils/config-mapper.ts): UI↔DB round-trips with?? truefallbacks for legacy rows missing the field.src/lib/github.ts):getGithubRepositoriesnow accepts anincludeCollaboratorReposOverrideparam and picksaffiliationbetween"owner"and"owner,collaborator".src/lib/repository-cleanup-service.ts): forcesincludeCollaboratorReposOverride: trueso toggling the option off does not mark previously-mirrored collab repos as orphans (which would archive/delete them depending onorphanedRepoAction).src/lib/env-config-loader.ts): newINCLUDE_COLLABORATOR_REPOSenv var (defaulttrue, tri-state parser so unset falls through to existing config / schema default rather than clobbering tofalse).src/components/config/GitHubMirrorSettings.tsx): checkbox under "Include private repositories",Usersicon to match neighbors, defaults checked.docs/ENVIRONMENT_VARIABLES.md): new env var documented under Repository Selection.Why default true
Defaulting to
truepreserves what every existing user sees today. Defaulting tofalsewould silently drop collab repos for anyone who currently mirrors them — that's the same silent-breaking-change problem that sank #279.Tests
src/lib/utils/config-mapper.test.ts: round-trips the field UI→DB→UI in both true and false cases, plus the missing-field-defaults-to-true case, plus a Zod default check.src/lib/github-affiliation.test.ts: assertsaffiliationflips correctly across the four states (default, true, false, override).Test plan
bun test(246 pass, 0 fail)/config: checkbox renders in the right place, defaults to checked, toggle works both directions🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.