* Classify the repo structure into one of: single, monorepo, microservices, meta. * * - meta: .gitmodules exists → git submodules mean independent projects are * aggregated here (e.g. org-wide umbrella repos) * - microservices: multiple workspaces each with their own Do
( root: string, workspaces: WorkspaceInfo[], isMonorepo: boolean )
| 427 | * - single: single-project repo with no workspaces |
| 428 | */ |
| 429 | async function classifyRepoType( |
| 430 | root: string, |
| 431 | workspaces: WorkspaceInfo[], |
| 432 | isMonorepo: boolean |
| 433 | ): Promise<RepoType> { |
| 434 | // Meta-repo: git submodules are the definitive signal |
| 435 | if (await fileExists(join(root, ".gitmodules"))) return "meta"; |
| 436 | |
| 437 | if (!isMonorepo || workspaces.length <= 1) return "single"; |
| 438 | |
| 439 | // Microservices: 2+ workspaces each with a Dockerfile, or infra orchestration at root |
| 440 | const infraDirs = ["k8s", "kubernetes", "helm"]; |
| 441 | for (const dir of infraDirs) { |
| 442 | if (await fileExists(join(root, dir))) return "microservices"; |
| 443 | } |
| 444 | |
| 445 | let dockerfileCount = 0; |
| 446 | for (const ws of workspaces) { |
| 447 | if (await fileExists(join(root, ws.path, "Dockerfile"))) { |
| 448 | dockerfileCount++; |
| 449 | if (dockerfileCount >= 2) return "microservices"; |
| 450 | } |
| 451 | } |
| 452 | |
| 453 | return "monorepo"; |
| 454 | } |
| 455 | |
| 456 | async function hasDirectWorkspaceManifest(dir: string): Promise<boolean> { |
| 457 | const directManifestNames = [ |
no test coverage detected