[PR #327] [MERGED] feat(github): add organization allowlist to mirror only selected orgs #7240

Closed
opened 2026-07-16 01:49:01 -05:00 by GiteaMirror · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/RayLabsHQ/gitea-mirror/pull/327
Author: @arunavo4
Created: 6/19/2026
Status: Merged
Merged: 6/19/2026
Merged by: @arunavo4

Base: mainHead: feat/org-include-allowlist


📝 Commits (1)

  • 0e50f8b feat(github): add organization allowlist to mirror only selected orgs

📊 Changes

8 files changed (+244 additions, -9 deletions)

View changed files

📝 docs/ENVIRONMENT_VARIABLES.md (+1 -0)
📝 src/components/config/GitHubMirrorSettings.tsx (+91 -0)
📝 src/lib/env-config-loader.ts (+8 -1)
📝 src/lib/github-affiliation.test.ts (+89 -0)
📝 src/lib/github.ts (+22 -1)
📝 src/lib/repository-cleanup-service.ts (+11 -5)
📝 src/lib/utils/config-mapper.ts (+21 -2)
📝 src/types/config.ts (+1 -0)

📄 Description

Problem

Repository discovery requested the organization_member affiliation unconditionally, so repos from every org a user belongs to were imported — even orgs they never explicitly added. skipPersonalRepos only dropped user-owned repos and left org repos unfiltered.

This surprised users who expected "skip personal repos / only mirror org repos" to mean "only the orgs I chose." Reported on #304 (follow-up to the v3.18.0 skipPersonalRepos toggle):

But if I'm part of organizations I didn't explicitly add, their repos are synced as well!

Fix

Wire up the previously-dormant includeOrganizations config field as an opt-in allowlist:

  • When non-empty, only repos owned by the listed organizations are imported.
  • When empty, all org repos are imported (backward-compatible — no behavior change for existing users).
  • Owned and collaborator repos are never restricted by the allowlist, so it composes cleanly with skipPersonalRepos.

This is opt-in and complementary to the existing per-org Ignore action (#323, opt-out) — users in many orgs can now pick a small set to mirror instead of ignoring everything else one by one.

Changes

  • getGithubRepositories: filter org-owned repos by the allowlist.
  • Cleanup safety: new includeAllOrgsOverride flag so repository-cleanup-service bypasses the allowlist and never false-orphans (archive/delete) a previously-mirrored repo from an org the user later removes from the list — same guard pattern as the existing includeCollaboratorReposOverride.
  • UI: "Limit to specific organizations" tag input under Filtering & Behavior (next to Skip personal repositories).
  • Env var: INCLUDE_ORGANIZATIONS (comma-separated).
  • Mapper: case-insensitive dedup + trim + blank-drop on the UI↔DB round-trip.
  • Tests: 7 new unit tests (filter on/off, case-insensitivity, personal/collab untouched, composition with skipPersonalRepos, blank handling, cleanup override).

Testing

CI-equivalent run on a clean checkout (bun test 346 pass / bun test:migrations / astro build) all green.

Live end-to-end discovery against a real multi-org account:

Scenario Total Personal Collab Orgs
Empty allowlist (status quo) 242 134 14 6
allowlist=[OrgA] 208 134 14 1 (other 5 orgs dropped)
allowlist + skipPersonalRepos 74 0 14 1
allowlist + cleanup override 242 134 14 6 (no false-orphan)

Closes the org-membership gap discussed in #304.


🔄 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/327 **Author:** [@arunavo4](https://github.com/arunavo4) **Created:** 6/19/2026 **Status:** ✅ Merged **Merged:** 6/19/2026 **Merged by:** [@arunavo4](https://github.com/arunavo4) **Base:** `main` ← **Head:** `feat/org-include-allowlist` --- ### 📝 Commits (1) - [`0e50f8b`](https://github.com/RayLabsHQ/gitea-mirror/commit/0e50f8b7a4e2c0153a4b336b834f80878c64d8db) feat(github): add organization allowlist to mirror only selected orgs ### 📊 Changes **8 files changed** (+244 additions, -9 deletions) <details> <summary>View changed files</summary> 📝 `docs/ENVIRONMENT_VARIABLES.md` (+1 -0) 📝 `src/components/config/GitHubMirrorSettings.tsx` (+91 -0) 📝 `src/lib/env-config-loader.ts` (+8 -1) 📝 `src/lib/github-affiliation.test.ts` (+89 -0) 📝 `src/lib/github.ts` (+22 -1) 📝 `src/lib/repository-cleanup-service.ts` (+11 -5) 📝 `src/lib/utils/config-mapper.ts` (+21 -2) 📝 `src/types/config.ts` (+1 -0) </details> ### 📄 Description ## Problem Repository discovery requested the `organization_member` affiliation unconditionally, so repos from **every** org a user belongs to were imported — even orgs they never explicitly added. `skipPersonalRepos` only dropped user-owned repos and left org repos unfiltered. This surprised users who expected "skip personal repos / only mirror org repos" to mean "only the orgs I chose." Reported on #304 (follow-up to the v3.18.0 `skipPersonalRepos` toggle): > But if I'm part of organizations I didn't explicitly add, their repos are synced as well! ## Fix Wire up the previously-dormant `includeOrganizations` config field as an **opt-in allowlist**: - When **non-empty**, only repos owned by the listed organizations are imported. - When **empty**, all org repos are imported (**backward-compatible** — no behavior change for existing users). - Owned and collaborator repos are **never** restricted by the allowlist, so it composes cleanly with `skipPersonalRepos`. This is opt-in and complementary to the existing per-org **Ignore** action (#323, opt-out) — users in many orgs can now pick a small set to mirror instead of ignoring everything else one by one. ## Changes - **`getGithubRepositories`**: filter org-owned repos by the allowlist. - **Cleanup safety**: new `includeAllOrgsOverride` flag so `repository-cleanup-service` bypasses the allowlist and never false-orphans (archive/delete) a previously-mirrored repo from an org the user later removes from the list — same guard pattern as the existing `includeCollaboratorReposOverride`. - **UI**: "Limit to specific organizations" tag input under *Filtering & Behavior* (next to *Skip personal repositories*). - **Env var**: `INCLUDE_ORGANIZATIONS` (comma-separated). - **Mapper**: case-insensitive dedup + trim + blank-drop on the UI↔DB round-trip. - **Tests**: 7 new unit tests (filter on/off, case-insensitivity, personal/collab untouched, composition with `skipPersonalRepos`, blank handling, cleanup override). ## Testing CI-equivalent run on a clean checkout (`bun test` 346 pass / `bun test:migrations` / `astro build`) all green. Live end-to-end discovery against a real multi-org account: | Scenario | Total | Personal | Collab | Orgs | |---|---|---|---|---| | Empty allowlist (status quo) | 242 | 134 | 14 | **6** | | allowlist=`[OrgA]` | 208 | 134 | 14 | **1** (other 5 orgs dropped) | | allowlist + skipPersonalRepos | 74 | **0** | 14 | 1 | | allowlist + cleanup override | 242 | 134 | 14 | **6** (no false-orphan) | Closes the org-membership gap discussed in #304. --- <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-16 01:49:01 -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#7240