Skip to content

Commit 47661a1

Browse files
authored
Open dvc.yaml automatically after adding a pipeline (#3242)
* Open dvc.yaml automatically after adding a pipeline * Fix lint error * Add a comment to direct the user towards the dvc.yaml stages documentation * Cut comment in two and use function to open document in editor
1 parent e6aaa31 commit 47661a1

File tree

4 files changed

+56
-13
lines changed

4 files changed

+56
-13
lines changed

extension/src/experiments/workspace.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -439,7 +439,7 @@ export class WorkspaceExperiments extends BaseWorkspaceWebviews<
439439
if (!trainingScript) {
440440
return false
441441
}
442-
findOrCreateDvcYamlFile(cwd, trainingScript, stageName)
442+
void findOrCreateDvcYamlFile(cwd, trainingScript, stageName)
443443
}
444444
return true
445445
}

extension/src/fileSystem/index.test.ts

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { join, relative, resolve } from 'path'
22
import { appendFileSync, ensureDirSync, ensureFileSync, remove } from 'fs-extra'
3+
import { TextDocument, window, workspace } from 'vscode'
34
import {
45
exists,
56
findAbsoluteDvcRootPath,
@@ -26,6 +27,13 @@ jest.mock('fs-extra', () => {
2627

2728
const mockedAppendFileSync = jest.mocked(appendFileSync)
2829
const mockedEnsureFileSync = jest.mocked(ensureFileSync)
30+
const mockedWorkspace = jest.mocked(workspace)
31+
const mockedWindow = jest.mocked(window)
32+
const mockedOpenTextDocument = jest.fn()
33+
const mockedShowTextDocument = jest.fn()
34+
35+
mockedWorkspace.openTextDocument = mockedOpenTextDocument
36+
mockedWindow.showTextDocument = mockedShowTextDocument
2937

3038
beforeEach(() => {
3139
jest.resetAllMocks()
@@ -169,7 +177,7 @@ describe('getModifiedTime', () => {
169177
describe('findOrCreateDvcYamlFile', () => {
170178
it('should make sure a dvc.yaml file exists', () => {
171179
const cwd = '/cwd'
172-
findOrCreateDvcYamlFile(cwd, '/my/training/script.py', 'train')
180+
void findOrCreateDvcYamlFile(cwd, '/my/training/script.py', 'train')
173181

174182
expect(mockedEnsureFileSync).toHaveBeenCalledWith(`${cwd}/dvc.yaml`)
175183
})
@@ -187,16 +195,29 @@ describe('findOrCreateDvcYamlFile', () => {
187195

188196
it('should add the training script as a train stage in the dvc.yaml file', () => {
189197
const cwd = '/cwd'
190-
findOrCreateDvcYamlFile(cwd, '/my/training/script.py', 'train')
198+
void findOrCreateDvcYamlFile(cwd, '/my/training/script.py', 'train')
191199

192200
expect(mockedAppendFileSync).toHaveBeenCalledWith(
193201
`${cwd}/dvc.yaml`,
194-
expect.stringMatching(/^\s+stages:\s+train:/)
202+
expect.stringMatching(/stages:\s+train:/)
203+
)
204+
})
205+
206+
it('should add a comment to direct the user towards the dvc.yaml stages documentation', () => {
207+
const cwd = '/cwd'
208+
void findOrCreateDvcYamlFile(cwd, '/my/training/script.py', 'train')
209+
210+
expect(mockedAppendFileSync).toHaveBeenCalledWith(
211+
`${cwd}/dvc.yaml`,
212+
expect.stringContaining(
213+
`# Read about DVC pipeline configuration (https://dvc.org/doc/user-guide/project-structure/dvcyaml-files#stages)
214+
# to customize your stages even more`
215+
)
195216
)
196217
})
197218

198219
it('should add the training script as a relative path to the cwd', () => {
199-
findOrCreateDvcYamlFile(
220+
void findOrCreateDvcYamlFile(
200221
'/dir/my_project/',
201222
'/dir/my_project/src/training/train.py',
202223
'train'
@@ -207,7 +228,7 @@ describe('findOrCreateDvcYamlFile', () => {
207228
expect.stringContaining(join('src', 'training', 'train.py'))
208229
)
209230

210-
findOrCreateDvcYamlFile(
231+
void findOrCreateDvcYamlFile(
211232
'/dir/my_project/',
212233
'/dir/my_other_project/train.py',
213234
'train'
@@ -220,7 +241,7 @@ describe('findOrCreateDvcYamlFile', () => {
220241
})
221242

222243
it('should use the jupyter nbconvert command if the training script is a Jupyter notebook', () => {
223-
findOrCreateDvcYamlFile('/', '/train.ipynb', 'train')
244+
void findOrCreateDvcYamlFile('/', '/train.ipynb', 'train')
224245

225246
expect(mockedAppendFileSync).toHaveBeenCalledWith(
226247
expect.anything(),
@@ -233,7 +254,7 @@ describe('findOrCreateDvcYamlFile', () => {
233254
})
234255

235256
it('should use the python command if the training script is not a Jupyter notebook', () => {
236-
findOrCreateDvcYamlFile('/', '/train.py', 'train')
257+
void findOrCreateDvcYamlFile('/', '/train.py', 'train')
237258

238259
expect(mockedAppendFileSync).not.toHaveBeenCalledWith(
239260
expect.anything(),
@@ -244,4 +265,18 @@ describe('findOrCreateDvcYamlFile', () => {
244265
expect.stringContaining(scriptCommand.PYTHON)
245266
)
246267
})
268+
269+
it('should open the dvc.yaml file in the editor', () => {
270+
mockedOpenTextDocument.mockResolvedValue({} as TextDocument)
271+
272+
void findOrCreateDvcYamlFile('/', '/train.py', 'train')
273+
274+
expect(mockedOpenTextDocument).toHaveBeenCalledWith(
275+
expect.objectContaining({
276+
authority: 'dvc.yaml',
277+
path: '/',
278+
scheme: 'file'
279+
})
280+
)
281+
})
247282
})

extension/src/fileSystem/index.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
writeFileSync
1111
} from 'fs-extra'
1212
import { load } from 'js-yaml'
13-
import { Uri } from 'vscode'
13+
import { Uri, workspace, window } from 'vscode'
1414
import { standardizePath } from './path'
1515
import { definedAndNonEmpty } from '../util/array'
1616
import { Logger } from '../common/logger'
@@ -136,6 +136,12 @@ export const scriptCommand = {
136136
PYTHON: 'python'
137137
}
138138

139+
export const openFileInEditor = async (filePath: string) => {
140+
const document = await workspace.openTextDocument(Uri.file(filePath))
141+
await window.showTextDocument(document)
142+
return document
143+
}
144+
139145
export const findOrCreateDvcYamlFile = (
140146
cwd: string,
141147
trainingScript: string,
@@ -148,9 +154,13 @@ export const findOrCreateDvcYamlFile = (
148154
const command = isNotebook ? scriptCommand.JUPYTER : scriptCommand.PYTHON
149155

150156
const pipeline = `
157+
# Read about DVC pipeline configuration (https://dvc.org/doc/user-guide/project-structure/dvcyaml-files#stages)
158+
# to customize your stages even more
151159
stages:
152160
${stageName}:
153161
cmd: ${command} ${relative(cwd, trainingScript)}`
162+
163+
void openFileInEditor(dvcYamlPath)
154164
return appendFileSync(dvcYamlPath, pipeline)
155165
}
156166

extension/src/test/suite/experiments/index.test.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ import { ConfigKey } from '../../../vscode/config'
7373
import { EXPERIMENT_WORKSPACE_ID } from '../../../cli/dvc/contract'
7474
import * as Time from '../../../util/time'
7575
import { AvailableCommands } from '../../../commands/internal'
76+
import { openFileInEditor } from '../../../fileSystem'
7677

7778
suite('Experiments Test Suite', () => {
7879
const disposable = Disposable.fn()
@@ -153,10 +154,7 @@ suite('Experiments Test Suite', () => {
153154
const { experiments } = buildExperiments(disposable)
154155

155156
const windowSpy = spy(window, 'createWebviewPanel')
156-
const uri = Uri.file(resolve(dvcDemoPath, 'train.py'))
157-
158-
const document = await workspace.openTextDocument(uri)
159-
await window.showTextDocument(document)
157+
const document = await openFileInEditor(resolve(dvcDemoPath, 'train.py'))
160158

161159
expect(window.activeTextEditor?.document).to.deep.equal(document)
162160

0 commit comments

Comments
 (0)