Skip to content

Commit ff0979c

Browse files
authored
Add DvcConfig class (#3987)
1 parent e8b9708 commit ff0979c

File tree

10 files changed

+179
-38
lines changed

10 files changed

+179
-38
lines changed
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import { EventEmitter } from 'vscode'
2+
import { Disposable, Disposer } from '@hediet/std/disposable'
3+
import { ConfigKey, Flag, SubCommand } from './constants'
4+
import { DvcConfig } from './config'
5+
import { CliResult, CliStarted } from '..'
6+
import { createProcess } from '../../process/execution'
7+
import { getMockedProcess } from '../../test/util/jest'
8+
import { getProcessEnv } from '../../env'
9+
import { Config } from '../../config'
10+
11+
jest.mock('vscode')
12+
jest.mock('@hediet/std/disposable')
13+
jest.mock('../../env')
14+
jest.mock('../../process/execution')
15+
16+
const mockedDisposable = jest.mocked(Disposable)
17+
18+
const mockedCreateProcess = jest.mocked(createProcess)
19+
const mockedGetProcessEnv = jest.mocked(getProcessEnv)
20+
const mockedEnv = {
21+
DVCLIVE_OPEN: 'false',
22+
DVC_NO_ANALYTICS: 'true',
23+
GIT_TERMINAL_PROMPT: '0',
24+
PATH: '/some/special/path'
25+
}
26+
27+
beforeEach(() => {
28+
jest.resetAllMocks()
29+
mockedGetProcessEnv.mockReturnValueOnce(mockedEnv)
30+
})
31+
32+
describe('DvcConfig', () => {
33+
mockedDisposable.fn.mockReturnValueOnce({
34+
track: function <T>(disposable: T): T {
35+
return disposable
36+
},
37+
untrack: function <T>(disposable: T): T {
38+
return disposable
39+
}
40+
} as unknown as (() => void) & Disposer)
41+
42+
const dvcConfig = new DvcConfig(
43+
{
44+
getCliPath: () => undefined,
45+
getPythonBinPath: () => undefined
46+
} as unknown as Config,
47+
{
48+
processCompleted: {
49+
event: jest.fn(),
50+
fire: jest.fn()
51+
} as unknown as EventEmitter<CliResult>,
52+
processStarted: {
53+
event: jest.fn(),
54+
fire: jest.fn()
55+
} as unknown as EventEmitter<CliStarted>
56+
}
57+
)
58+
59+
describe('config', () => {
60+
it('should call createProcess with the correct parameters to access the config', async () => {
61+
const cwd = __dirname
62+
const stdout = ''
63+
64+
mockedCreateProcess.mockReturnValueOnce(getMockedProcess(stdout))
65+
66+
const output = await dvcConfig.config(cwd, ConfigKey.STUDIO_OFFLINE)
67+
expect(output).toStrictEqual(stdout)
68+
69+
expect(mockedCreateProcess).toHaveBeenCalledWith({
70+
args: ['config', 'studio.offline'],
71+
cwd,
72+
env: mockedEnv,
73+
executable: 'dvc'
74+
})
75+
})
76+
77+
it('should return undefined if the underlying process throws', async () => {
78+
const cwd = __dirname
79+
80+
mockedCreateProcess.mockImplementationOnce(() => {
81+
throw new Error('unable to access DVC')
82+
})
83+
84+
const output = await dvcConfig.config(cwd, ConfigKey.STUDIO_OFFLINE)
85+
expect(output).toStrictEqual(undefined)
86+
})
87+
})
88+
89+
describe('remote', () => {
90+
it('should call createProcess with the correct parameters to access the remote section of the config', async () => {
91+
const cwd = __dirname
92+
const stdout = ''
93+
94+
mockedCreateProcess.mockReturnValueOnce(getMockedProcess(stdout))
95+
96+
const output = await dvcConfig.remote(cwd, SubCommand.LIST, Flag.LOCAL)
97+
expect(output).toStrictEqual(stdout)
98+
99+
expect(mockedCreateProcess).toHaveBeenCalledWith({
100+
args: ['remote', 'list', '--local'],
101+
cwd,
102+
env: mockedEnv,
103+
executable: 'dvc'
104+
})
105+
})
106+
})
107+
})

extension/src/cli/dvc/config.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { DvcCli } from '.'
2+
import { Args, Command } from './constants'
3+
import { typeCheckCommands } from '..'
4+
5+
export const autoRegisteredCommands = {
6+
CONFIG: 'config',
7+
REMOTE: 'remote'
8+
} as const
9+
10+
export class DvcConfig extends DvcCli {
11+
public readonly autoRegisteredCommands = typeCheckCommands(
12+
autoRegisteredCommands,
13+
this
14+
)
15+
16+
public config(cwd: string, ...args: Args) {
17+
return this.executeSafeProcess(cwd, Command.CONFIG, ...args)
18+
}
19+
20+
public remote(cwd: string, ...args: Args) {
21+
return this.executeSafeProcess(cwd, Command.REMOTE, ...args)
22+
}
23+
24+
private async executeSafeProcess(
25+
cwd: string,
26+
command: Command,
27+
...args: Args
28+
) {
29+
try {
30+
return await this.executeDvcProcess(cwd, command, ...args)
31+
} catch {}
32+
}
33+
}

extension/src/cli/dvc/executor.ts

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ export const autoRegisteredCommands = {
1616
ADD: 'add',
1717
CHECKOUT: 'checkout',
1818
COMMIT: 'commit',
19-
CONFIG: 'config',
2019
EXP_APPLY: 'expApply',
2120
EXP_BRANCH: 'expBranch',
2221
EXP_GARBAGE_COLLECT: 'expGarbageCollect',
@@ -32,7 +31,6 @@ export const autoRegisteredCommands = {
3231
QUEUE_KILL: 'queueKill',
3332
QUEUE_START: 'queueStart',
3433
QUEUE_STOP: 'queueStop',
35-
REMOTE: 'remote',
3634
REMOVE: 'remove'
3735
} as const
3836

@@ -56,10 +54,6 @@ export class DvcExecutor extends DvcCli {
5654
return this.blockAndExecuteProcess(cwd, Command.COMMIT, ...args, Flag.FORCE)
5755
}
5856

59-
public config(cwd: string, ...args: Args) {
60-
return this.executeDvcProcess(cwd, Command.CONFIG, ...args)
61-
}
62-
6357
public expApply(cwd: string, experimentName: string) {
6458
return this.executeExperimentProcess(
6559
cwd,
@@ -174,10 +168,6 @@ export class DvcExecutor extends DvcCli {
174168
return this.blockAndExecuteProcess(cwd, Command.REMOVE, ...args)
175169
}
176170

177-
public remote(cwd: string, ...args: Args) {
178-
return this.executeDvcProcess(cwd, Command.REMOTE, ...args)
179-
}
180-
181171
private executeExperimentProcess(cwd: string, ...args: Args) {
182172
return this.executeDvcProcess(cwd, Command.EXPERIMENT, ...args)
183173
}

extension/src/commands/internal.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { commands } from 'vscode'
22
import { RegisteredCliCommands, RegisteredCommands } from './external'
33
import { ICli } from '../cli'
44
import { Args } from '../cli/constants'
5+
import { autoRegisteredCommands as DvcConfigCommands } from '../cli/dvc/config'
56
import { autoRegisteredCommands as DvcExecutorCommands } from '../cli/dvc/executor'
67
import { autoRegisteredCommands as DvcReaderCommands } from '../cli/dvc/reader'
78
import { autoRegisteredCommands as DvcRunnerCommands } from '../cli/dvc/runner'
@@ -19,18 +20,21 @@ type Command = (...args: Args) => unknown | Promise<unknown>
1920

2021
export const AvailableCommands = Object.assign(
2122
{},
23+
DvcConfigCommands,
2224
DvcExecutorCommands,
2325
DvcReaderCommands,
2426
DvcRunnerCommands,
2527
DvcViewerCommands,
2628
GitExecutorCommands,
2729
GitReaderCommands
28-
) as typeof DvcExecutorCommands &
30+
) as typeof DvcConfigCommands &
31+
typeof DvcExecutorCommands &
2932
typeof DvcReaderCommands &
3033
typeof DvcRunnerCommands &
3134
typeof DvcViewerCommands &
3235
typeof GitExecutorCommands &
3336
typeof GitReaderCommands
37+
3438
export type CommandId =
3539
(typeof AvailableCommands)[keyof typeof AvailableCommands]
3640

extension/src/extension.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { commands, env, ExtensionContext, ViewColumn } from 'vscode'
2+
import { DvcConfig } from './cli/dvc/config'
23
import { DvcExecutor } from './cli/dvc/executor'
34
import { DvcRunner } from './cli/dvc/runner'
45
import { DvcReader } from './cli/dvc/reader'
@@ -61,6 +62,7 @@ class Extension extends Disposable {
6162
private readonly plots: WorkspacePlots
6263
private readonly setup: Setup
6364
private readonly repositoriesTree: RepositoriesTree
65+
private readonly dvcConfig: DvcConfig
6466
private readonly dvcExecutor: DvcExecutor
6567
private readonly dvcReader: DvcReader
6668
private readonly dvcRunner: DvcRunner
@@ -84,13 +86,14 @@ class Extension extends Disposable {
8486
this.gitExecutor = this.dispose.track(new GitExecutor())
8587
this.gitReader = this.dispose.track(new GitReader())
8688

89+
this.dvcConfig = this.dispose.track(new DvcConfig(config))
8790
this.dvcExecutor = this.dispose.track(new DvcExecutor(config))
88-
8991
this.dvcReader = this.dispose.track(new DvcReader(config))
9092
this.dvcRunner = this.dispose.track(new DvcRunner(config))
9193
this.dvcViewer = this.dispose.track(new DvcViewer(config))
9294

9395
const clis = [
96+
this.dvcConfig,
9497
this.dvcExecutor,
9598
this.dvcReader,
9699
this.dvcRunner,

extension/src/setup/index.ts

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -714,22 +714,18 @@ export class Setup
714714
}
715715

716716
private accessConfig(cwd: string, ...args: Args) {
717-
return this.accessDvc(AvailableCommands.CONFIG, cwd, ...args)
717+
return this.internalCommands.executeCommand(
718+
AvailableCommands.CONFIG,
719+
cwd,
720+
...args
721+
)
718722
}
719723

720724
private accessRemote(cwd: string, ...args: Args) {
721-
return this.accessDvc(AvailableCommands.REMOTE, cwd, ...args)
722-
}
723-
724-
private async accessDvc(
725-
commandId:
726-
| typeof AvailableCommands.CONFIG
727-
| typeof AvailableCommands.REMOTE,
728-
cwd: string,
729-
...args: Args
730-
) {
731-
try {
732-
return await this.internalCommands.executeCommand(commandId, cwd, ...args)
733-
} catch {}
725+
return this.internalCommands.executeCommand(
726+
AvailableCommands.REMOTE,
727+
cwd,
728+
...args
729+
)
734730
}
735731
}

extension/src/test/suite/extension.test.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { DvcExecutor } from '../../cli/dvc/executor'
2222
import { dvcDemoPath } from '../util'
2323
import { Setup } from '../../setup'
2424
import { Flag } from '../../cli/dvc/constants'
25+
import { DvcConfig } from '../../cli/dvc/config'
2526

2627
suite('Extension Test Suite', () => {
2728
const disposable = Disposable.fn()
@@ -94,7 +95,8 @@ suite('Extension Test Suite', () => {
9495
const mockExpShow = stub(DvcReader.prototype, 'expShow')
9596
const mockDataStatus = stub(DvcReader.prototype, 'dataStatus')
9697
const mockPlotsDiff = stub(DvcReader.prototype, 'plotsDiff')
97-
stub(DvcExecutor.prototype, 'config').resolves('')
98+
stub(DvcConfig.prototype, 'config').resolves('')
99+
stub(DvcConfig.prototype, 'remote').resolves('')
98100

99101
stub(DvcReader.prototype, 'root').resolves('.')
100102

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

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@ import * as Python from '../../../extensions/python'
4545
import { ContextKey } from '../../../vscode/context'
4646
import { Setup } from '../../../setup'
4747
import { SetupSection } from '../../../setup/webview/contract'
48-
import { DvcExecutor } from '../../../cli/dvc/executor'
4948
import { getFirstWorkspaceFolder } from '../../../vscode/workspaceFolders'
5049
import { Response } from '../../../vscode/response'
50+
import { DvcConfig } from '../../../cli/dvc/config'
5151

5252
suite('Setup Test Suite', () => {
5353
const disposable = Disposable.fn()
@@ -741,7 +741,7 @@ suite('Setup Test Suite', () => {
741741
const { setup, mockExecuteCommand, messageSpy } = buildSetup(disposable)
742742
mockExecuteCommand.restore()
743743

744-
const mockConfig = stub(DvcExecutor.prototype, 'config')
744+
const mockConfig = stub(DvcConfig.prototype, 'config')
745745
mockConfig.resolves('')
746746

747747
const executeCommandSpy = spy(commands, 'executeCommand')
@@ -829,7 +829,7 @@ suite('Setup Test Suite', () => {
829829
it('should be able to delete the Studio access token from the global dvc config', async () => {
830830
const mockConfig = stub(
831831
// eslint-disable-next-line @typescript-eslint/no-explicit-any
832-
DvcExecutor.prototype,
832+
DvcConfig.prototype,
833833
'config'
834834
).resolves(undefined)
835835

@@ -909,7 +909,7 @@ suite('Setup Test Suite', () => {
909909

910910
const mockMessageReceived = getMessageReceivedEmitter(webview)
911911

912-
const mockRemote = stub(DvcExecutor.prototype, 'remote')
912+
const mockRemote = stub(DvcConfig.prototype, 'remote')
913913

914914
const remoteAdded = new Promise(resolve =>
915915
mockRemote.callsFake((_, ...args) => {
@@ -948,7 +948,7 @@ suite('Setup Test Suite', () => {
948948
}).timeout(WEBVIEW_TEST_TIMEOUT)
949949

950950
it('should be able to add a remote', async () => {
951-
const mockRemote = stub(DvcExecutor.prototype, 'remote')
951+
const mockRemote = stub(DvcConfig.prototype, 'remote')
952952

953953
const remoteAdded = new Promise(resolve =>
954954
mockRemote.callsFake((_, ...args) => {
@@ -998,7 +998,7 @@ suite('Setup Test Suite', () => {
998998
}).timeout(WEBVIEW_TEST_TIMEOUT)
999999

10001000
it('should be able to rename a remote', async () => {
1001-
const mockRemote = stub(DvcExecutor.prototype, 'remote')
1001+
const mockRemote = stub(DvcConfig.prototype, 'remote')
10021002
const newName = 'better-name'
10031003

10041004
const remoteRenamed = new Promise(resolve =>
@@ -1051,7 +1051,7 @@ suite('Setup Test Suite', () => {
10511051

10521052
const mockMessageReceived = getMessageReceivedEmitter(webview)
10531053

1054-
const mockRemote = stub(DvcExecutor.prototype, 'remote')
1054+
const mockRemote = stub(DvcConfig.prototype, 'remote')
10551055
const projectConfigUrl = 's3://different-url'
10561056

10571057
const remoteModified = new Promise(resolve =>
@@ -1123,7 +1123,7 @@ suite('Setup Test Suite', () => {
11231123

11241124
const mockMessageReceived = getMessageReceivedEmitter(webview)
11251125

1126-
const mockRemote = stub(DvcExecutor.prototype, 'remote')
1126+
const mockRemote = stub(DvcConfig.prototype, 'remote')
11271127

11281128
let calls = 0
11291129

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export const buildSetup = (
3333
messageSpy,
3434
resourceLocator,
3535
internalCommands,
36-
dvcExecutor,
36+
dvcConfig,
3737
dvcReader,
3838
gitExecutor,
3939
gitReader
@@ -44,7 +44,7 @@ export const buildSetup = (
4444

4545
const mockEmitter = disposer.track(new EventEmitter())
4646
stub(dvcReader, 'root').resolves(mockDvcRoot)
47-
const mockRemote = stub(dvcExecutor, 'remote').resolves('')
47+
const mockRemote = stub(dvcConfig, 'remote').resolves('')
4848
const mockVersion = stub(dvcReader, 'version').resolves(MIN_CLI_VERSION)
4949
const mockGlobalVersion = stub(dvcReader, 'globalVersion').resolves(
5050
MIN_CLI_VERSION
@@ -81,7 +81,7 @@ export const buildSetup = (
8181
})
8282
)
8383

84-
const mockConfig = stub(dvcExecutor, 'config').resolves('')
84+
const mockConfig = stub(dvcConfig, 'config').resolves('')
8585

8686
const setup = disposer.track(
8787
new Setup(

0 commit comments

Comments
 (0)