diff --git a/CHANGELOG.md b/CHANGELOG.md index 48e0ce4e3..4d8ca7d4e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Fixed +- [EE] Fixed issue where internal GitLab projects were not visible in Sourcebot when permission syncing is enabled. [#857](https://github.com/sourcebot-dev/sourcebot/pull/857) + ## [4.10.26] - 2026-02-05 ### Added diff --git a/docs/docs/features/permission-syncing.mdx b/docs/docs/features/permission-syncing.mdx index ac3250bbe..2feeb5776 100644 --- a/docs/docs/features/permission-syncing.mdx +++ b/docs/docs/features/permission-syncing.mdx @@ -76,6 +76,7 @@ Permission syncing works with **GitLab Self-managed** and **GitLab Cloud**. User **Notes:** - A GitLab [external identity provider](/docs/configuration/idp) must be configured to (1) correlate a Sourcebot user with a GitLab user, and (2) to list repositories that the user has access to for [User driven syncing](/docs/features/permission-syncing#how-it-works). - OAuth tokens require the `read_api` scope in order to use the [List projects for the authenticated user API](https://docs.gitlab.com/ee/api/projects.html#list-all-projects) during [User driven syncing](/docs/features/permission-syncing#how-it-works). +- [Internal GitLab projects](https://docs.gitlab.com/user/public_access/#internal-projects-and-groups) are **not** enforced by permission syncing and therefore are visible to all users. Only [private projects](https://docs.gitlab.com/user/public_access/#private-projects-and-groups) are enforced. # How it works diff --git a/packages/backend/src/ee/accountPermissionSyncer.ts b/packages/backend/src/ee/accountPermissionSyncer.ts index 1fef909d2..aa972fa21 100644 --- a/packages/backend/src/ee/accountPermissionSyncer.ts +++ b/packages/backend/src/ee/accountPermissionSyncer.ts @@ -247,16 +247,15 @@ export class AccountPermissionSyncer { throw new Error(`OAuth token with scopes [${scopes.join(', ')}] is missing the 'read_api' scope required for permission syncing.`); } - // @note: we only care about the private and internal repos since we don't need to build a mapping - // for public repos. + // @note: we only care about the private repos since we don't need to build a + // mapping for public or internal repos. Note that internal repos are _not_ + // enforced by permission syncing and therefore we don't need to fetch them + // here. + // // @see: packages/web/src/prisma.ts - const privateGitLabProjects = await getProjectsForAuthenticatedUser('private', api); - const internalGitLabProjects = await getProjectsForAuthenticatedUser('internal', api); - - const gitLabProjectIds = [ - ...privateGitLabProjects, - ...internalGitLabProjects, - ].map(project => project.id.toString()); + const gitLabProjectIds = ( + await getProjectsForAuthenticatedUser('private', api) + ).map(project => project.id.toString()); const repos = await this.db.repo.findMany({ where: { diff --git a/packages/backend/src/gitlab.ts b/packages/backend/src/gitlab.ts index 14b8702fc..0d74c44a2 100644 --- a/packages/backend/src/gitlab.ts +++ b/packages/backend/src/gitlab.ts @@ -303,7 +303,6 @@ export const getProjectMembers = async (projectId: string, api: InstanceType) => { try { const fetchFn = () => api.Projects.all({ - membership: true, ...(visibility !== 'all' ? { visibility, } : {}), diff --git a/packages/backend/src/repoCompileUtils.ts b/packages/backend/src/repoCompileUtils.ts index e0a44c7e4..12b2a6caf 100644 --- a/packages/backend/src/repoCompileUtils.ts +++ b/packages/backend/src/repoCompileUtils.ts @@ -170,7 +170,13 @@ export const compileGitlabConfig = async ( const cloneUrl = new URL(project.http_url_to_repo); cloneUrl.protocol = new URL(hostUrl).protocol; const isFork = project.forked_from_project !== undefined; - const isPublic = project.visibility === 'public'; + // @note: we consider internal repos to be `public` s.t., + // we don't enforce permission filtering for them and they + // are visible to all users. + // @see: packages/web/src/prisma.ts + const isPublic = + project.visibility === 'public' || + project.visibility === 'internal'; const repoDisplayName = project.path_with_namespace; const repoName = path.join(repoNameRoot, repoDisplayName); // project.avatar_url is not directly accessible with tokens; use the avatar API endpoint if available