fix: include organization_member in /user/repos affiliation (#286)

Affiliation was set to "owner,collaborator", omitting repos owned
by orgs the user belongs to. As a result:

- main sync, scheduler, and cleanup never saw org repos
- orgs appeared empty unless manually re-added via /api/sync/organization
- restart archived previously-mirrored org repos as orphans

GitHub's API default is owner,collaborator,organization_member;
restoring it fixes both symptoms with no other code changes.
This commit is contained in:
Eduardo Riguetto (Kralot)
2026-05-16 01:10:40 -03:00
committed by GitHub
parent 680b374c84
commit 7c1f24dc2f
2 changed files with 40 additions and 9 deletions
+33 -8
View File
@@ -39,37 +39,62 @@ function makeOctokit() {
}
describe("getGithubRepositories - affiliation", () => {
test("defaults to owner+collaborator when field is unset (backward compat)", async () => {
test("defaults to owner+collaborator+organization_member when field is unset (backward compat)", async () => {
const { octokit, getCaptured } = makeOctokit();
await getGithubRepositories({ octokit, config: { githubConfig: { owner: "octo" } as any } });
expect(getCaptured()?.affiliation).toBe("owner,collaborator");
expect(getCaptured()?.affiliation).toBe("owner,collaborator,organization_member");
});
test("uses owner only when includeCollaboratorRepos is false", async () => {
test("uses owner+organization_member when includeCollaboratorRepos is false", async () => {
const { octokit, getCaptured } = makeOctokit();
await getGithubRepositories({
octokit,
config: { githubConfig: { owner: "octo", includeCollaboratorRepos: false } as any },
});
expect(getCaptured()?.affiliation).toBe("owner");
expect(getCaptured()?.affiliation).toBe("owner,organization_member");
});
test("uses owner+collaborator when includeCollaboratorRepos is true", async () => {
test("uses owner+collaborator+organization_member when includeCollaboratorRepos is true", async () => {
const { octokit, getCaptured } = makeOctokit();
await getGithubRepositories({
octokit,
config: { githubConfig: { owner: "octo", includeCollaboratorRepos: true } as any },
});
expect(getCaptured()?.affiliation).toBe("owner,collaborator");
expect(getCaptured()?.affiliation).toBe("owner,collaborator,organization_member");
});
test("override forces owner+collaborator regardless of config (used by cleanup)", async () => {
test("override forces owner+collaborator+organization_member regardless of config (used by cleanup)", async () => {
const { octokit, getCaptured } = makeOctokit();
await getGithubRepositories({
octokit,
config: { githubConfig: { owner: "octo", includeCollaboratorRepos: false } as any },
includeCollaboratorReposOverride: true,
});
expect(getCaptured()?.affiliation).toBe("owner,collaborator");
expect(getCaptured()?.affiliation).toBe("owner,collaborator,organization_member");
});
test("always includes organization_member (regression guard for org-repo invisibility)", async () => {
const cases: Array<{ includeCollab?: boolean; override?: boolean }> = [
{},
{ includeCollab: true },
{ includeCollab: false },
{ override: true },
{ includeCollab: false, override: true },
];
for (const c of cases) {
const { octokit, getCaptured } = makeOctokit();
await getGithubRepositories({
octokit,
config: {
githubConfig: {
owner: "octo",
...(c.includeCollab !== undefined && { includeCollaboratorRepos: c.includeCollab }),
} as any,
},
...(c.override !== undefined && { includeCollaboratorReposOverride: c.override }),
});
const aff = String(getCaptured()?.affiliation ?? "");
expect(aff.split(",")).toContain("organization_member");
}
});
});
+7 -1
View File
@@ -249,7 +249,13 @@ export async function getGithubRepositories({
includeCollaboratorReposOverride ??
config.githubConfig?.includeCollaboratorRepos ??
true;
const affiliation = includeCollab ? "owner,collaborator" : "owner";
// Always include organization_member so repos owned by orgs the user
// belongs to are returned. Omitting it caused org repos to be invisible
// to the main sync, the scheduler, and the cleanup service (which then
// archived them on restart as if they had been deleted on GitHub).
const affiliation = includeCollab
? "owner,collaborator,organization_member"
: "owner,organization_member";
const repos = await octokit.paginate(
octokit.repos.listForAuthenticatedUser,