Skip to content

Commit 5560476

Browse files
authored
Deeply nested repo (#4810)
* Get deeply nested DVC repo as dvc roots * Fix tests * Fix lint error * Add test * Simplify search of roots
1 parent 0a1e45b commit 5560476

File tree

5 files changed

+63
-37
lines changed

5 files changed

+63
-37
lines changed

extension/src/cli/dvc/constants.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ import { join } from 'path'
33
export const UNEXPECTED_ERROR_CODE = 255
44
export const DOT_DVC = '.dvc'
55

6+
const NESTED_DVC = join(DOT_DVC, 'config')
7+
export const FULLY_NESTED_DVC = join('**', NESTED_DVC)
8+
69
export const TEMP_DAG_FILE = join(DOT_DVC, 'tmp', 'dag.md')
710

811
export const TEMP_PLOTS_DIR = join(DOT_DVC, 'tmp', 'plots')

extension/src/fileSystem/index.test.ts

Lines changed: 46 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
import { join, relative, resolve } from 'path'
22
import {
33
appendFileSync,
4-
ensureDirSync,
54
ensureFileSync,
6-
remove,
75
readFileSync,
86
writeFileSync
97
} from 'fs-extra'
@@ -55,8 +53,10 @@ const mockedWorkspace = jest.mocked(workspace)
5553
const mockedWindow = jest.mocked(window)
5654
const mockedOpenTextDocument = jest.fn()
5755
const mockedShowTextDocument = jest.fn()
56+
const mockedFindFiles = jest.fn()
5857

5958
mockedWorkspace.openTextDocument = mockedOpenTextDocument
59+
mockedWorkspace.findFiles = mockedFindFiles
6060
mockedWindow.showTextDocument = mockedShowTextDocument
6161

6262
beforeEach(() => {
@@ -275,41 +275,67 @@ describe('writeTsv', () => {
275275
})
276276

277277
describe('findDvcRootPaths', () => {
278+
const convertAsFindDvcConfigFile = (rootPath: string) => ({
279+
fsPath: join(rootPath, DOT_DVC, 'config')
280+
})
281+
278282
it('should find the dvc root if it exists in the given folder', async () => {
279-
const dvcRoots = await findDvcRootPaths(dvcDemoPath)
283+
mockedFindFiles.mockResolvedValue([convertAsFindDvcConfigFile(dvcDemoPath)])
284+
const dvcRoots = await findDvcRootPaths()
280285

281-
expect(dvcRoots).toStrictEqual([dvcDemoPath])
286+
expect(dvcRoots).toStrictEqual(new Set([dvcDemoPath]))
282287
})
283288

284289
it('should find multiple roots if available one directory below the given folder', async () => {
285290
const parentDir = resolve(dvcDemoPath, '..')
286291
const mockDvcRoot = join(parentDir, 'mockDvc')
287-
ensureDirSync(join(mockDvcRoot, DOT_DVC))
288-
289-
const dvcRoots = await findDvcRootPaths(parentDir)
292+
mockedFindFiles.mockResolvedValue([
293+
convertAsFindDvcConfigFile(dvcDemoPath),
294+
convertAsFindDvcConfigFile(mockDvcRoot)
295+
])
290296

291-
void remove(mockDvcRoot)
297+
const dvcRoots = await findDvcRootPaths()
292298

293-
expect([...dvcRoots]).toStrictEqual([dvcDemoPath, mockDvcRoot])
299+
expect(dvcRoots).toStrictEqual(new Set([dvcDemoPath, mockDvcRoot]))
294300
})
295301

296302
it('should find a mono-repo root as well as sub-roots if available one directory below the given folder', async () => {
297-
const parentDir = dvcDemoPath
298-
const mockFirstDvcRoot = join(parentDir, 'mockFirstDvc')
299-
const mockSecondDvcRoot = join(parentDir, 'mockSecondDvc')
300-
ensureDirSync(join(mockFirstDvcRoot, DOT_DVC))
301-
ensureDirSync(join(mockSecondDvcRoot, DOT_DVC))
303+
const mockFirstDvcRoot = join(dvcDemoPath, 'mockFirstDvc')
304+
const mockSecondDvcRoot = join(dvcDemoPath, 'mockSecondDvc')
305+
mockedFindFiles.mockResolvedValue([
306+
convertAsFindDvcConfigFile(dvcDemoPath),
307+
convertAsFindDvcConfigFile(mockFirstDvcRoot),
308+
convertAsFindDvcConfigFile(mockSecondDvcRoot)
309+
])
302310

303-
const dvcRoots = await findDvcRootPaths(parentDir)
311+
const dvcRoots = await findDvcRootPaths()
304312

305-
void remove(mockFirstDvcRoot)
306-
void remove(mockSecondDvcRoot)
313+
expect(dvcRoots).toStrictEqual(
314+
new Set([dvcDemoPath, mockFirstDvcRoot, mockSecondDvcRoot])
315+
)
316+
})
307317

308-
expect([...dvcRoots]).toStrictEqual([
318+
it('should find deeply nested roots if available', async () => {
319+
const mockFirstDvcRoot = join(
309320
dvcDemoPath,
310-
mockFirstDvcRoot,
311-
mockSecondDvcRoot
321+
'deep',
322+
'deeper',
323+
'really_deep',
324+
'one'
325+
)
326+
const mockSecondDvcRoot = join(dvcDemoPath, 'one_deep', 'two')
327+
328+
mockedFindFiles.mockResolvedValue([
329+
convertAsFindDvcConfigFile(dvcDemoPath),
330+
convertAsFindDvcConfigFile(mockFirstDvcRoot),
331+
convertAsFindDvcConfigFile(mockSecondDvcRoot)
312332
])
333+
334+
const dvcRoots = await findDvcRootPaths()
335+
336+
expect(dvcRoots).toStrictEqual(
337+
new Set([dvcDemoPath, mockFirstDvcRoot, mockSecondDvcRoot])
338+
)
313339
})
314340
})
315341

extension/src/fileSystem/index.ts

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ import {
66
relative,
77
resolve,
88
sep,
9-
format
9+
format,
10+
dirname
1011
} from 'path'
1112
import {
1213
appendFileSync,
@@ -22,13 +23,14 @@ import { Uri, workspace, window, commands, ViewColumn } from 'vscode'
2223
import { csv2json, json2csv } from 'json-2-csv'
2324
import yaml from 'yaml'
2425
import { standardizePath } from './path'
26+
import { findFiles } from './workspace'
2527
import { definedAndNonEmpty, sortCollectedArray } from '../util/array'
2628
import { Logger } from '../common/logger'
2729
import { gitPath } from '../cli/git/constants'
2830
import { createValidInteger } from '../util/number'
2931
import { processExists } from '../process/execution'
3032
import { getFirstWorkspaceFolder } from '../vscode/workspaceFolders'
31-
import { DOT_DVC } from '../cli/dvc/constants'
33+
import { DOT_DVC, FULLY_NESTED_DVC } from '../cli/dvc/constants'
3234
import { delay } from '../util/time'
3335
import { PlotConfigData, PlotConfigDataAxis } from '../pipeline/quickPick'
3436

@@ -65,20 +67,17 @@ export const findSubRootPaths = async (
6567
.map(child => standardizePath(join(cwd, child)))
6668
}
6769

68-
export const findDvcRootPaths = async (cwd: string): Promise<string[]> => {
70+
export const findDvcRootPaths = async (): Promise<Set<string>> => {
6971
const dvcRoots = []
7072

71-
if (isDirectory(join(cwd, DOT_DVC))) {
72-
dvcRoots.push(standardizePath(cwd))
73-
}
74-
75-
const subRoots = await findSubRootPaths(cwd, DOT_DVC)
76-
77-
if (definedAndNonEmpty(subRoots)) {
78-
dvcRoots.push(...subRoots)
73+
const nested = await findFiles(FULLY_NESTED_DVC)
74+
if (definedAndNonEmpty(nested)) {
75+
dvcRoots.push(
76+
...nested.map(nestedRoot => standardizePath(dirname(dirname(nestedRoot))))
77+
)
7978
}
8079

81-
return sortCollectedArray(dvcRoots)
80+
return new Set(sortCollectedArray(dvcRoots))
8281
}
8382

8483
export const findAbsoluteDvcRootPath = async (

extension/src/setup/index.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -467,12 +467,10 @@ export class Setup
467467
}
468468

469469
private async findWorkspaceDvcRoots() {
470-
let dvcRoots: Set<string> = new Set()
470+
const dvcRoots: Set<string> = await findDvcRootPaths()
471471

472472
for (const workspaceFolder of getWorkspaceFolders()) {
473-
const workspaceFolderRoots = await findDvcRootPaths(workspaceFolder)
474-
if (definedAndNonEmpty(workspaceFolderRoots)) {
475-
dvcRoots = new Set([...dvcRoots, ...workspaceFolderRoots])
473+
if ([...dvcRoots].filter(root => root.includes(workspaceFolder))) {
476474
continue
477475
}
478476

extension/src/test/suite/setup/util.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ export const buildSetup = ({
8080
const mockInitializeGit = stub(gitExecutor, 'gitInit')
8181

8282
stub(FileSystem, 'findDvcRootPaths').resolves(
83-
[mockDvcRoot].filter(Boolean) as string[]
83+
new Set([mockDvcRoot].filter(Boolean) as string[])
8484
)
8585

8686
const mockAutoInstallDvc = stub(AutoInstall, 'autoInstallDvc')

0 commit comments

Comments
 (0)