diff --git a/src/lib/github-affiliation.test.ts b/src/lib/github-affiliation.test.ts index dca5f44..c977f5d 100644 --- a/src/lib/github-affiliation.test.ts +++ b/src/lib/github-affiliation.test.ts @@ -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"); + } }); }); diff --git a/src/lib/github.ts b/src/lib/github.ts index 680a54e..a1507e3 100644 --- a/src/lib/github.ts +++ b/src/lib/github.ts @@ -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,