From df568926f93812ba46b13b6977768b62012087c9 Mon Sep 17 00:00:00 2001 From: sebastien Date: Fri, 7 Nov 2025 16:08:19 +0100 Subject: [PATCH] add getGitAllRepoRoots API --- .../src/vcs/__tests__/gitUtils.test.ts | 77 +++++++++++++++++++ packages/docusaurus-utils/src/vcs/gitUtils.ts | 22 ++++++ 2 files changed, 99 insertions(+) diff --git a/packages/docusaurus-utils/src/vcs/__tests__/gitUtils.test.ts b/packages/docusaurus-utils/src/vcs/__tests__/gitUtils.test.ts index 3767dbd3ad..01f93631d4 100644 --- a/packages/docusaurus-utils/src/vcs/__tests__/gitUtils.test.ts +++ b/packages/docusaurus-utils/src/vcs/__tests__/gitUtils.test.ts @@ -18,6 +18,7 @@ import { getGitRepoRoot, getGitSuperProjectRoot, getGitSubmodulePaths, + getGitAllRepoRoots, } from '../gitUtils'; class Git { @@ -548,4 +549,80 @@ describe('submodules APIs', () => { ); }); }); + + describe('getGitAllRepoRoots', () => { + it('returns root paths for cwd=superproject', async () => { + const repo = await initTestRepo(); + const cwd = path.join(repo.superproject.repoDir); + await expect(getGitAllRepoRoots(cwd)).resolves.toEqual([ + repo.superproject.repoDir, + path.join(repo.superproject.repoDir, 'submodules', 'submodule1'), + path.join(repo.superproject.repoDir, 'submodules', 'submodule2'), + ]); + }); + + it('returns root paths for cwd=superproject/website/docs', async () => { + const repo = await initTestRepo(); + const cwd = path.join(repo.superproject.repoDir, 'website', 'docs'); + await expect(getGitAllRepoRoots(cwd)).resolves.toEqual([ + repo.superproject.repoDir, + path.join(repo.superproject.repoDir, 'submodules', 'submodule1'), + path.join(repo.superproject.repoDir, 'submodules', 'submodule2'), + ]); + }); + + it('returns root paths for cwd=superproject/submodules', async () => { + const repo = await initTestRepo(); + const cwd = path.join(repo.superproject.repoDir, 'submodules'); + await expect(getGitAllRepoRoots(cwd)).resolves.toEqual([ + repo.superproject.repoDir, + path.join(repo.superproject.repoDir, 'submodules', 'submodule1'), + path.join(repo.superproject.repoDir, 'submodules', 'submodule2'), + ]); + }); + + it('returns root paths for cwd=superproject/submodules/submodule1', async () => { + const repo = await initTestRepo(); + const cwd = path.join( + repo.superproject.repoDir, + 'submodules', + 'submodule1', + ); + await expect(getGitAllRepoRoots(cwd)).resolves.toEqual([ + repo.superproject.repoDir, + path.join(repo.superproject.repoDir, 'submodules', 'submodule1'), + path.join(repo.superproject.repoDir, 'submodules', 'submodule2'), + ]); + }); + + it('returns root paths for cwd=superproject/submodules/submodule2/subDir', async () => { + const repo = await initTestRepo(); + const cwd = path.join( + repo.superproject.repoDir, + 'submodules', + 'submodule2', + 'subDir', + ); + await expect(getGitAllRepoRoots(cwd)).resolves.toEqual([ + repo.superproject.repoDir, + path.join(repo.superproject.repoDir, 'submodules', 'submodule1'), + path.join(repo.superproject.repoDir, 'submodules', 'submodule2'), + ]); + }); + + it('rejects for cwd=doesNotExist', async () => { + const repo = await initTestRepo(); + const cwd = path.join(repo.superproject.repoDir, 'doesNotExist'); + await expect(getGitAllRepoRoots(cwd)).rejects.toThrow( + /Could not get all the git repository root paths/, + ); + }); + + it('rejects for cwd=notTracked', async () => { + const cwd = await os.tmpdir(); + await expect(getGitAllRepoRoots(cwd)).rejects.toThrow( + /Could not get all the git repository root paths/, + ); + }); + }); }); diff --git a/packages/docusaurus-utils/src/vcs/gitUtils.ts b/packages/docusaurus-utils/src/vcs/gitUtils.ts index 7a468a3b36..d2409385ed 100644 --- a/packages/docusaurus-utils/src/vcs/gitUtils.ts +++ b/packages/docusaurus-utils/src/vcs/gitUtils.ts @@ -390,3 +390,25 @@ The command returned exit code ${logger.code(result.exitCode)}: ${logger.subdue( return Promise.all(output.split('\n').map(getSubmodulePath)); } + +// Find the root git repository alongside all its submodules, if any +export async function getGitAllRepoRoots(cwd: string): Promise { + try { + const superProjectRoot = await getGitSuperProjectRoot(cwd); + if (!superProjectRoot) { + return []; + } + let submodulePaths = await getGitSubmodulePaths(superProjectRoot); + submodulePaths = await Promise.all( + submodulePaths.map((submodulePath) => + fs.realpath.native(path.resolve(superProjectRoot, submodulePath)), + ), + ); + return [superProjectRoot, ...submodulePaths]; + } catch (error) { + throw new Error( + `Could not get all the git repository root paths (superproject + submodules) from cwd=${cwd}`, + {cause: error}, + ); + } +}