From bb842d018f777cf9cf4ef9bf066312c165d94949 Mon Sep 17 00:00:00 2001 From: Matt Seddon <37993418+mattseddon@users.noreply.github.com> Date: Sat, 29 Apr 2023 08:15:20 +1000 Subject: [PATCH 01/15] Remove commit and share and share as branch options (#3771) --- extension/package.json | 50 ------- extension/src/cli/git/constants.ts | 4 - extension/src/cli/git/executor.test.ts | 99 -------------- extension/src/cli/git/executor.ts | 23 +--- extension/src/commands/external.ts | 4 - extension/src/experiments/commands/index.ts | 94 +------------ .../src/experiments/commands/register.ts | 42 ------ extension/src/experiments/webview/messages.ts | 11 -- extension/src/telemetry/constants.ts | 4 - .../src/test/suite/experiments/index.test.ts | 114 ---------------- .../test/suite/experiments/workspace.test.ts | 123 ------------------ extension/src/webview/contract.ts | 10 -- .../src/experiments/components/App.test.tsx | 10 +- .../components/table/body/RowContextMenu.tsx | 8 -- 14 files changed, 6 insertions(+), 590 deletions(-) delete mode 100644 extension/src/cli/git/executor.test.ts diff --git a/extension/package.json b/extension/package.json index 102cde70e4..fbaa91e797 100644 --- a/extension/package.json +++ b/extension/package.json @@ -382,18 +382,6 @@ "command": "dvc.setupWorkspace", "category": "DVC" }, - { - "title": "Share Experiment as Branch", - "command": "dvc.shareExperimentAsBranch", - "category": "DVC", - "icon": "$(repo-push)" - }, - { - "title": "Commit and Share Experiment", - "command": "dvc.shareExperimentAsCommit", - "category": "DVC", - "icon": "$(repo-push)" - }, { "title": "Show Commands", "command": "dvc.showCommands", @@ -517,18 +505,6 @@ "category": "DVC", "icon": "$(play)" }, - { - "title": "Share as Branch", - "command": "dvc.views.experiments.shareExperimentAsBranch", - "category": "DVC", - "icon": "$(repo-push)" - }, - { - "title": "Commit and Share", - "command": "dvc.views.experiments.shareExperimentAsCommit", - "category": "DVC", - "icon": "$(repo-push)" - }, { "title": "Share to Studio", "command": "dvc.views.experiments.shareExperimentToStudio", @@ -846,14 +822,6 @@ "command": "dvc.showCommands", "when": "false" }, - { - "command": "dvc.shareExperimentAsBranch", - "when": "dvc.commands.available && dvc.project.available && !dvc.experiment.running" - }, - { - "command": "dvc.shareExperimentAsCommit", - "when": "dvc.commands.available && dvc.project.available && !dvc.experiment.running" - }, { "command": "dvc.showExperiments", "when": "dvc.commands.available && dvc.project.available" @@ -914,14 +882,6 @@ "command": "dvc.views.experiments.resetAndRunCheckpointExperiment", "when": "false" }, - { - "command": "dvc.views.experiments.shareExperimentAsBranch", - "when": "false" - }, - { - "command": "dvc.views.experiments.shareExperimentAsCommit", - "when": "false" - }, { "command": "dvc.views.experiments.shareExperimentToStudio", "when": "false" @@ -1191,16 +1151,6 @@ "group": "1_share@0", "when": "view == dvc.views.experimentsTree && dvc.commands.available && viewItem == experiment && !dvc.experiment.running" }, - { - "command": "dvc.views.experiments.shareExperimentAsCommit", - "group": "1_share@1", - "when": "view == dvc.views.experimentsTree && dvc.commands.available && viewItem == experiment && !dvc.experiment.running" - }, - { - "command": "dvc.views.experiments.shareExperimentAsBranch", - "group": "1_share@2", - "when": "view == dvc.views.experimentsTree && dvc.commands.available && viewItem == experiment && !dvc.experiment.running" - }, { "command": "dvc.views.experiments.runExperiment", "group": "2_modify@1", diff --git a/extension/src/cli/git/constants.ts b/extension/src/cli/git/constants.ts index b630fc0c51..4acea7e092 100644 --- a/extension/src/cli/git/constants.ts +++ b/extension/src/cli/git/constants.ts @@ -13,13 +13,11 @@ export enum Command { ADD = 'add', BRANCH = 'branch', CLEAN = 'clean', - COMMIT = 'commit', DIFF = 'diff', INITIALIZE = 'init', LOG = 'log', LS_FILES = 'ls-files', LS_REMOTE = 'ls-remote', - PUSH = 'push', RESET = 'reset', REV_PARSE = 'rev-parse', REV_LIST = 'rev-list' @@ -35,7 +33,6 @@ export enum Flag { FULL_HISTORY = '--full-history', GET_URL = '--get-url', HARD = '--hard', - MESSAGE = '-m', NAME_ONLY = '--name-only', NO_EMPTY_DIRECTORY = '--no-empty-directory', NO_MERGE = '--no-merge', @@ -45,7 +42,6 @@ export enum Flag { QUIET = '-q', RAW_WITH_NUL = '-z', SEPARATE_WITH_NULL = '-z', - SET_UPSTREAM = '--set-upstream', SHOW_TOPLEVEL = '--show-toplevel' } diff --git a/extension/src/cli/git/executor.test.ts b/extension/src/cli/git/executor.test.ts deleted file mode 100644 index 703188f431..0000000000 --- a/extension/src/cli/git/executor.test.ts +++ /dev/null @@ -1,99 +0,0 @@ -import { EventEmitter } from 'vscode' -import { Disposable, Disposer } from '@hediet/std/disposable' -import { GitExecutor } from './executor' -import { createProcess } from '../../process/execution' -import { CliResult, CliStarted } from '..' -import { getMockedProcess } from '../../test/util/jest' -import { standardizePath } from '../../fileSystem/path' - -jest.mock('vscode') -jest.mock('@hediet/std/disposable') -jest.mock('../../process/execution') - -const mockedDisposable = jest.mocked(Disposable) - -const mockedCreateProcess = jest.mocked(createProcess) - -beforeEach(() => { - jest.resetAllMocks() -}) - -describe('GitExecutor', () => { - mockedDisposable.fn.mockReturnValueOnce({ - track: function (disposable: T): T { - return disposable - }, - untrack: function (disposable: T): T { - return disposable - } - } as unknown as (() => void) & Disposer) - - const gitExecutor = new GitExecutor({ - processCompleted: { - event: jest.fn(), - fire: jest.fn() - } as unknown as EventEmitter, - processStarted: { - event: jest.fn(), - fire: jest.fn() - } as unknown as EventEmitter - }) - - const cwd = standardizePath(__dirname) - - describe('pushBranch', () => { - it('should call createProcess with the correct parameters to push a branch', async () => { - const branchName = 'my-branch' - mockedCreateProcess.mockReturnValueOnce( - getMockedProcess( - `branch '${branchName}' set up to track 'origin/${branchName}'.` - ) - ) - - await gitExecutor.pushBranch(cwd, branchName) - expect(mockedCreateProcess).toHaveBeenCalledWith({ - args: ['push', '--set-upstream', 'origin', branchName], - cwd, - executable: 'git' - }) - }) - - it('should call createProcess with the correct parameters to push the current branch', async () => { - mockedCreateProcess.mockReturnValueOnce( - getMockedProcess('Everything up-to-date') - ) - - await gitExecutor.pushBranch(cwd) - expect(mockedCreateProcess).toHaveBeenCalledWith({ - args: ['push', '--set-upstream', 'origin', 'HEAD'], - cwd, - executable: 'git' - }) - }) - }) - - describe('stageAndCommit', () => { - it('should call createProcess with the correct parameters to stage all files and then commit', async () => { - const message = 'best experiment' - mockedCreateProcess.mockReturnValueOnce(getMockedProcess(cwd)) - mockedCreateProcess - .mockReturnValueOnce(getMockedProcess('')) - .mockReturnValueOnce( - getMockedProcess(`[current-branch 67effdbc] ${message}`) - ) - - await gitExecutor.stageAndCommit(cwd, message) - expect(mockedCreateProcess).toHaveBeenCalledTimes(3) - expect(mockedCreateProcess).toHaveBeenCalledWith({ - args: ['add', '.'], - cwd, - executable: 'git' - }) - expect(mockedCreateProcess).toHaveBeenCalledWith({ - args: ['commit', '-m', message], - cwd, - executable: 'git' - }) - }) - }) -}) diff --git a/extension/src/cli/git/executor.ts b/extension/src/cli/git/executor.ts index 008e4b3ea4..536635f073 100644 --- a/extension/src/cli/git/executor.ts +++ b/extension/src/cli/git/executor.ts @@ -1,14 +1,12 @@ import { GitCli } from '.' -import { Args, Command, Commit, DEFAULT_REMOTE, Flag } from './constants' +import { Command, Commit, Flag } from './constants' import { getOptions } from './options' import { typeCheckCommands } from '..' export const autoRegisteredCommands = { GIT_INIT: 'gitInit', - GIT_PUSH_BRANCH: 'pushBranch', GIT_RESET_WORKSPACE: 'resetWorkspace', GIT_STAGE_ALL: 'stageAll', - GIT_STAGE_AND_COMMIT: 'stageAndCommit', GIT_UNSTAGE_ALL: 'reset' } as const @@ -24,16 +22,6 @@ export class GitExecutor extends GitCli { return this.executeProcess(options) } - public pushBranch(cwd: string, branchName?: string) { - const args: Args = [Command.PUSH, Flag.SET_UPSTREAM, DEFAULT_REMOTE] - - args.push((branchName || Commit.HEAD) as Commit) - - const options = getOptions(cwd, ...args) - - return this.executeProcess(options) - } - public reset(cwd: string, ...args: (Flag | Commit)[]) { const options = getOptions(cwd, Command.RESET, ...args) @@ -60,13 +48,4 @@ export class GitExecutor extends GitCli { return this.executeProcess(options) } - - public async stageAndCommit(cwd: string, message: string) { - await this.stageAll(cwd) - - const args = [Command.COMMIT, Flag.MESSAGE, message] as Args - const options = getOptions(cwd, ...args) - - return this.executeProcess(options) - } } diff --git a/extension/src/commands/external.ts b/extension/src/commands/external.ts index 9ab290075f..78ed077eec 100644 --- a/extension/src/commands/external.ts +++ b/extension/src/commands/external.ts @@ -7,8 +7,6 @@ export enum RegisteredCliCommands { EXPERIMENT_RESET_AND_RUN = 'dvc.resetAndRunCheckpointExperiment', EXPERIMENT_RESUME = 'dvc.resumeCheckpointExperiment', EXPERIMENT_RUN = 'dvc.runExperiment', - EXPERIMENT_SHARE_AS_BRANCH = 'dvc.shareExperimentAsBranch', - EXPERIMENT_SHARE_AS_COMMIT = 'dvc.shareExperimentAsCommit', QUEUE_EXPERIMENT = 'dvc.queueExperiment', QUEUE_KILL = 'dvc.stopQueuedExperiments', QUEUE_START = 'dvc.startExperimentsQueue', @@ -18,8 +16,6 @@ export enum RegisteredCliCommands { EXPERIMENT_VIEW_BRANCH = 'dvc.views.experiments.branchExperiment', EXPERIMENT_VIEW_PUSH = 'dvc.views.experiments.pushExperiment', EXPERIMENT_VIEW_REMOVE = 'dvc.views.experiments.removeExperiment', - EXPERIMENT_VIEW_SHARE_AS_BRANCH = 'dvc.views.experiments.shareExperimentAsBranch', - EXPERIMENT_VIEW_SHARE_AS_COMMIT = 'dvc.views.experiments.shareExperimentAsCommit', EXPERIMENT_VIEW_SHOW_LOGS = 'dvc.views.experiments.showLogs', EXPERIMENT_VIEW_STOP = 'dvc.views.experiments.stopQueueExperiment', diff --git a/extension/src/experiments/commands/index.ts b/extension/src/experiments/commands/index.ts index 12c559664a..2df5b3cd2c 100644 --- a/extension/src/experiments/commands/index.ts +++ b/extension/src/experiments/commands/index.ts @@ -1,4 +1,4 @@ -import { Progress, commands } from 'vscode' +import { commands } from 'vscode' import { AvailableCommands, InternalCommands } from '../../commands/internal' import { Toast } from '../../vscode/toast' import { WorkspaceExperiments } from '../workspace' @@ -15,98 +15,6 @@ export const getBranchExperimentCommand = input ) -const applyAndPush = async ( - internalCommands: InternalCommands, - progress: Progress<{ increment: number; message: string }>, - cwd: string, - name: string -): Promise => { - await Toast.runCommandAndIncrementProgress( - () => - internalCommands.executeCommand( - AvailableCommands.EXPERIMENT_APPLY, - cwd, - name - ), - progress, - 25 - ) - - return Toast.runCommandAndIncrementProgress( - () => internalCommands.executeCommand(AvailableCommands.PUSH, cwd), - progress, - 25 - ) -} - -export const getShareExperimentAsBranchCommand = - (internalCommands: InternalCommands) => - async (cwd: string, name: string, input: string) => { - await Toast.showProgress('Sharing Branch', async progress => { - progress.report({ increment: 0 }) - - await Toast.runCommandAndIncrementProgress( - () => - internalCommands.executeCommand( - AvailableCommands.EXPERIMENT_BRANCH, - cwd, - name, - input - ), - progress, - 25 - ) - - await applyAndPush(internalCommands, progress, cwd, name) - - await Toast.runCommandAndIncrementProgress( - () => - internalCommands.executeCommand( - AvailableCommands.GIT_PUSH_BRANCH, - cwd, - input - ), - progress, - 25 - ) - - return Toast.delayProgressClosing() - }) - } - -export const getShareExperimentAsCommitCommand = - (internalCommands: InternalCommands) => - async (cwd: string, name: string, input: string) => { - await Toast.showProgress('Sharing Commit', async progress => { - progress.report({ increment: 0 }) - - await applyAndPush(internalCommands, progress, cwd, name) - - await Toast.runCommandAndIncrementProgress( - () => - internalCommands.executeCommand( - AvailableCommands.GIT_STAGE_AND_COMMIT, - cwd, - input - ), - progress, - 25 - ) - - await Toast.runCommandAndIncrementProgress( - () => - internalCommands.executeCommand( - AvailableCommands.GIT_PUSH_BRANCH, - cwd - ), - progress, - 25 - ) - - return Toast.delayProgressClosing() - }) - } - export const getShareExperimentToStudioCommand = (internalCommands: InternalCommands, setup: Setup) => ({ dvcRoot, id }: { dvcRoot: string; id: string }) => { diff --git a/extension/src/experiments/commands/register.ts b/extension/src/experiments/commands/register.ts index dc32fc11ff..183a25bdb8 100644 --- a/extension/src/experiments/commands/register.ts +++ b/extension/src/experiments/commands/register.ts @@ -1,7 +1,5 @@ import { getBranchExperimentCommand, - getShareExperimentAsBranchCommand, - getShareExperimentAsCommitCommand, getShareExperimentToStudioCommand } from '.' import { pickGarbageCollectionFlags } from '../quickPick' @@ -167,46 +165,6 @@ const registerExperimentInputCommands = ( id ) ) - - internalCommands.registerExternalCliCommand( - RegisteredCliCommands.EXPERIMENT_SHARE_AS_BRANCH, - () => - experiments.getCwdExpNameAndInputThenRun( - getShareExperimentAsBranchCommand(internalCommands), - Title.ENTER_BRANCH_NAME - ) - ) - - internalCommands.registerExternalCliCommand( - RegisteredCliCommands.EXPERIMENT_VIEW_SHARE_AS_BRANCH, - ({ dvcRoot, id }: ExperimentDetails) => - experiments.getInputAndRun( - getShareExperimentAsBranchCommand(internalCommands), - Title.ENTER_BRANCH_NAME, - dvcRoot, - id - ) - ) - - internalCommands.registerExternalCliCommand( - RegisteredCliCommands.EXPERIMENT_SHARE_AS_COMMIT, - () => - experiments.getCwdExpNameAndInputThenRun( - getShareExperimentAsCommitCommand(internalCommands), - Title.ENTER_COMMIT_MESSAGE - ) - ) - - internalCommands.registerExternalCliCommand( - RegisteredCliCommands.EXPERIMENT_VIEW_SHARE_AS_COMMIT, - ({ dvcRoot, id }: ExperimentDetails) => - experiments.getInputAndRun( - getShareExperimentAsCommitCommand(internalCommands), - Title.ENTER_COMMIT_MESSAGE, - dvcRoot, - id - ) - ) } const registerExperimentQuickPickCommands = ( diff --git a/extension/src/experiments/webview/messages.ts b/extension/src/experiments/webview/messages.ts index e6dbd9a4f2..7ed3447e97 100644 --- a/extension/src/experiments/webview/messages.ts +++ b/extension/src/experiments/webview/messages.ts @@ -174,17 +174,6 @@ export class WebviewMessages { case MessageFromWebviewType.OPEN_PLOTS_WEBVIEW: return this.showPlotsToSide() - case MessageFromWebviewType.SHARE_EXPERIMENT_AS_BRANCH: - return commands.executeCommand( - RegisteredCliCommands.EXPERIMENT_VIEW_SHARE_AS_BRANCH, - { dvcRoot: this.dvcRoot, id: message.payload } - ) - case MessageFromWebviewType.SHARE_EXPERIMENT_AS_COMMIT: - return commands.executeCommand( - RegisteredCliCommands.EXPERIMENT_VIEW_SHARE_AS_COMMIT, - { dvcRoot: this.dvcRoot, id: message.payload } - ) - case MessageFromWebviewType.SET_EXPERIMENTS_FOR_PLOTS: return this.setSelectedExperiments(message.payload) diff --git a/extension/src/telemetry/constants.ts b/extension/src/telemetry/constants.ts index 02dddc9ea9..f55c7f1a2c 100644 --- a/extension/src/telemetry/constants.ts +++ b/extension/src/telemetry/constants.ts @@ -133,8 +133,6 @@ export interface IEventNamePropertyMapping { [EventName.EXPERIMENT_RUN]: undefined [EventName.EXPERIMENT_RESET_AND_RUN]: undefined [EventName.EXPERIMENT_SELECT]: undefined - [EventName.EXPERIMENT_SHARE_AS_BRANCH]: undefined - [EventName.EXPERIMENT_SHARE_AS_COMMIT]: undefined [EventName.EXPERIMENT_SHOW]: undefined [EventName.EXPERIMENT_SORT_ADD_STARRED]: undefined [EventName.EXPERIMENT_SORT_ADD]: undefined @@ -146,8 +144,6 @@ export interface IEventNamePropertyMapping { [EventName.EXPERIMENT_VIEW_BRANCH]: undefined [EventName.EXPERIMENT_VIEW_PUSH]: undefined [EventName.EXPERIMENT_VIEW_REMOVE]: undefined - [EventName.EXPERIMENT_VIEW_SHARE_AS_BRANCH]: undefined - [EventName.EXPERIMENT_VIEW_SHARE_AS_COMMIT]: undefined [EventName.EXPERIMENT_VIEW_SHARE_TO_STUDIO]: undefined [EventName.EXPERIMENT_VIEW_SHOW_LOGS]: undefined [EventName.EXPERIMENT_VIEW_STOP]: undefined diff --git a/extension/src/test/suite/experiments/index.test.ts b/extension/src/test/suite/experiments/index.test.ts index 955ed2355b..d779bed334 100644 --- a/extension/src/test/suite/experiments/index.test.ts +++ b/extension/src/test/suite/experiments/index.test.ts @@ -64,7 +64,6 @@ import * as VscodeContext from '../../../vscode/context' import { Title } from '../../../vscode/title' import { EXP_RWLOCK_FILE, ExperimentFlag } from '../../../cli/dvc/constants' import { DvcExecutor } from '../../../cli/dvc/executor' -import { GitExecutor } from '../../../cli/git/executor' import { WorkspacePlots } from '../../../plots/workspace' import { RegisteredCliCommands, @@ -696,119 +695,6 @@ suite('Experiments Test Suite', () => { expect(mockExperimentPush).to.be.calledWithExactly(dvcDemoPath, mockExpId) }).timeout(WEBVIEW_TEST_TIMEOUT) - it('should handle a message to share an experiment as a new branch', async () => { - const { experiments } = buildExperiments(disposable) - await experiments.isReady() - - const mockExperimentId = 'exp-e7a67' - const mockBranch = 'it-is-a-branch-shared-to-the-remote' - const inputEvent = getInputBoxEvent(mockBranch) - - const mockExperimentBranch = stub( - DvcExecutor.prototype, - 'experimentBranch' - ).resolves( - `Git branch '${mockBranch}' has been created from experiment '${mockExperimentId}'. - To switch to the new branch run: - git checkout ${mockBranch}` - ) - const mockExperimentApply = stub( - DvcExecutor.prototype, - 'experimentApply' - ).resolves( - `Changes for experiment '${mockExperimentId}' have been applied to your current workspace.` - ) - const mockPush = stub(DvcExecutor.prototype, 'push').resolves( - '10 files updated.' - ) - const mockGitPush = stub(GitExecutor.prototype, 'pushBranch') - const branchPushedToRemote = new Promise(resolve => - mockGitPush.callsFake(() => { - resolve(undefined) - return Promise.resolve(`${mockBranch} pushed to remote`) - }) - ) - - stubWorkspaceExperimentsGetters(dvcDemoPath, experiments) - - const webview = await experiments.showWebview() - const mockMessageReceived = getMessageReceivedEmitter(webview) - - mockMessageReceived.fire({ - payload: mockExperimentId, - type: MessageFromWebviewType.SHARE_EXPERIMENT_AS_BRANCH - }) - - await inputEvent - await branchPushedToRemote - expect(mockExperimentBranch).to.be.calledWithExactly( - dvcDemoPath, - mockExperimentId, - mockBranch - ) - expect(mockExperimentApply).to.be.calledWithExactly( - dvcDemoPath, - mockExperimentId - ) - expect(mockPush).to.be.calledWithExactly(dvcDemoPath) - expect(mockGitPush).to.be.calledWithExactly(dvcDemoPath, mockBranch) - }).timeout(WEBVIEW_TEST_TIMEOUT) - - it('should handle a message to share an experiment as a commit', async () => { - const { experiments } = buildExperiments(disposable) - await experiments.isReady() - - const mockExperimentId = 'exp-e7a67' - const mockCommitMessage = - 'this is the very best version that I could come up with' - const inputEvent = getInputBoxEvent(mockCommitMessage) - - const mockExperimentApply = stub( - DvcExecutor.prototype, - 'experimentApply' - ).resolves( - `Changes for experiment '${mockExperimentId}' have been applied to your current workspace.` - ) - const mockStageAndCommit = stub( - GitExecutor.prototype, - 'stageAndCommit' - ).resolves(`[current-branch 67effdbc] ${mockCommitMessage}`) - - const mockPush = stub(DvcExecutor.prototype, 'push').resolves( - '100000 files updated.' - ) - const mockGitPush = stub(GitExecutor.prototype, 'pushBranch') - const branchPushedToRemote = new Promise(resolve => - mockGitPush.callsFake(() => { - resolve(undefined) - return Promise.resolve('current-branch pushed to remote') - }) - ) - - stubWorkspaceExperimentsGetters(dvcDemoPath, experiments) - - const webview = await experiments.showWebview() - const mockMessageReceived = getMessageReceivedEmitter(webview) - - mockMessageReceived.fire({ - payload: mockExperimentId, - type: MessageFromWebviewType.SHARE_EXPERIMENT_AS_COMMIT - }) - - await inputEvent - await branchPushedToRemote - expect(mockStageAndCommit).to.be.calledWithExactly( - dvcDemoPath, - mockCommitMessage - ) - expect(mockExperimentApply).to.be.calledWithExactly( - dvcDemoPath, - mockExperimentId - ) - expect(mockPush).to.be.calledWithExactly(dvcDemoPath) - expect(mockGitPush).to.be.calledWithExactly(dvcDemoPath) - }).timeout(WEBVIEW_TEST_TIMEOUT) - it("should be able to handle a message to modify an experiment's params and queue an experiment", async () => { stub(DvcReader.prototype, 'listStages').resolves('train') const { experiments, dvcExecutor } = buildExperiments(disposable) diff --git a/extension/src/test/suite/experiments/workspace.test.ts b/extension/src/test/suite/experiments/workspace.test.ts index be5acdf6b9..669063b93c 100644 --- a/extension/src/test/suite/experiments/workspace.test.ts +++ b/extension/src/test/suite/experiments/workspace.test.ts @@ -13,7 +13,6 @@ import { Experiments } from '../../../experiments' import * as QuickPick from '../../../vscode/quickPick' import { DvcExecutor } from '../../../cli/dvc/executor' import { - bypassProgressCloseDelay, closeAllEditors, getInputBoxEvent, getTimeSafeDisposer, @@ -35,7 +34,6 @@ import { WEBVIEW_TEST_TIMEOUT } from '../timeouts' import { Title } from '../../../vscode/title' import { join } from '../../util/path' import { AvailableCommands } from '../../../commands/internal' -import { GitExecutor } from '../../../cli/git/executor' import { EXPERIMENT_WORKSPACE_ID } from '../../../cli/dvc/contract' import { formatDate } from '../../../util/date' import { DvcReader } from '../../../cli/dvc/reader' @@ -660,127 +658,6 @@ suite('Workspace Experiments Test Suite', () => { }) }) - describe('dvc.shareExperimentAsBranch', () => { - it('should be able to share an experiment as a branch', async () => { - bypassProgressCloseDelay() - stub(DvcReader.prototype, 'listStages').resolves('train') - - const { experiments } = buildExperiments(disposable) - await experiments.isReady() - - const testExperiment = 'exp-83425' - const mockBranch = 'more-brunch' - const inputEvent = getInputBoxEvent(mockBranch) - - stub(window, 'showQuickPick').resolves({ - value: testExperiment - } as QuickPickItemWithValue) - - const mockExperimentBranch = stub( - DvcExecutor.prototype, - 'experimentBranch' - ).resolves( - `Git branch '${mockBranch}' has been created from experiment '${testExperiment}'. - To switch to the new branch run: - git checkout ${mockBranch}` - ) - const mockExperimentApply = stub( - DvcExecutor.prototype, - 'experimentApply' - ).resolves( - `Changes for experiment '${testExperiment}' have been applied to your current workspace.` - ) - const mockPush = stub(DvcExecutor.prototype, 'push').resolves( - '10 files updated.' - ) - const mockGitPush = stub(GitExecutor.prototype, 'pushBranch') - const branchPushedToRemote = new Promise(resolve => - mockGitPush.callsFake(() => { - resolve(undefined) - return Promise.resolve(`${mockBranch} pushed to remote`) - }) - ) - - stubWorkspaceExperimentsGetters(dvcDemoPath, experiments) - - await commands.executeCommand( - RegisteredCliCommands.EXPERIMENT_SHARE_AS_BRANCH - ) - - await inputEvent - await branchPushedToRemote - expect(mockExperimentBranch).to.be.calledWithExactly( - dvcDemoPath, - testExperiment, - mockBranch - ) - expect(mockExperimentApply).to.be.calledWithExactly( - dvcDemoPath, - testExperiment - ) - expect(mockPush).to.be.calledWithExactly(dvcDemoPath) - expect(mockGitPush).to.be.calledWithExactly(dvcDemoPath, mockBranch) - }) - }) - - describe('dvc.shareExperimentAsCommit', () => { - it('should be able to share an experiment as a commit', async () => { - bypassProgressCloseDelay() - stub(DvcReader.prototype, 'listStages').resolves('train') - - const { experiments } = buildExperiments(disposable) - await experiments.isReady() - - const testExperiment = 'exp-83425' - const mockCommit = 'this is the best experiment ever!' - const inputEvent = getInputBoxEvent(mockCommit) - - stub(window, 'showQuickPick').resolves({ - value: testExperiment - } as QuickPickItemWithValue) - - const mockExperimentApply = stub( - DvcExecutor.prototype, - 'experimentApply' - ).resolves( - `Changes for experiment '${testExperiment}' have been applied to your current workspace.` - ) - const mockPush = stub(DvcExecutor.prototype, 'push').resolves( - '191232423 files updated.' - ) - const mockStageAndCommit = stub( - GitExecutor.prototype, - 'stageAndCommit' - ).resolves('') - const mockGitPush = stub(GitExecutor.prototype, 'pushBranch') - const branchPushedToRemote = new Promise(resolve => - mockGitPush.callsFake(() => { - resolve(undefined) - return Promise.resolve(`${mockCommit} pushed to remote`) - }) - ) - - stubWorkspaceExperimentsGetters(dvcDemoPath, experiments) - - await commands.executeCommand( - RegisteredCliCommands.EXPERIMENT_SHARE_AS_COMMIT - ) - - await inputEvent - await branchPushedToRemote - expect(mockExperimentApply).to.be.calledWithExactly( - dvcDemoPath, - testExperiment - ) - expect(mockStageAndCommit).to.be.calledWithExactly( - dvcDemoPath, - mockCommit - ) - expect(mockPush).to.be.calledWithExactly(dvcDemoPath) - expect(mockGitPush).to.be.calledWithExactly(dvcDemoPath) - }) - }) - describe('dvc.removeExperiments', () => { it('should ask the user to pick experiment(s) and then remove selected ones from the workspace', async () => { const mockExperimentId = 'exp-e7a67' diff --git a/extension/src/webview/contract.ts b/extension/src/webview/contract.ts index c7947fd7e7..bc5eae0756 100644 --- a/extension/src/webview/contract.ts +++ b/extension/src/webview/contract.ts @@ -50,8 +50,6 @@ export enum MessageFromWebviewType { SET_EXPERIMENTS_FOR_PLOTS = 'set-experiments-for-plots', SET_EXPERIMENTS_AND_OPEN_PLOTS = 'set-experiments-and-open-plots', SET_STUDIO_SHARE_EXPERIMENTS_LIVE = 'set-studio-share-experiments-live', - SHARE_EXPERIMENT_AS_BRANCH = 'share-experiment-as-branch', - SHARE_EXPERIMENT_AS_COMMIT = 'share-experiment-as-commit', TOGGLE_PLOTS_SECTION = 'toggle-plots-section', REMOVE_CUSTOM_PLOTS = 'remove-custom-plots', REMOVE_STUDIO_TOKEN = 'remove-studio-token', @@ -212,14 +210,6 @@ export type MessageFromWebview = type: MessageFromWebviewType.SET_STUDIO_SHARE_EXPERIMENTS_LIVE payload: boolean } - | { - type: MessageFromWebviewType.SHARE_EXPERIMENT_AS_BRANCH - payload: string - } - | { - type: MessageFromWebviewType.SHARE_EXPERIMENT_AS_COMMIT - payload: string - } | { type: MessageFromWebviewType.SET_EXPERIMENTS_HEADER_HEIGHT } | { type: MessageFromWebviewType.CHECK_CLI_COMPATIBLE } | { type: MessageFromWebviewType.INITIALIZE_DVC } diff --git a/webview/src/experiments/components/App.test.tsx b/webview/src/experiments/components/App.test.tsx index 5c99b02c24..2c9925e18b 100644 --- a/webview/src/experiments/components/App.test.tsx +++ b/webview/src/experiments/components/App.test.tsx @@ -863,8 +863,6 @@ describe('App', () => { 'Apply to Workspace', 'Create new Branch', 'Share to Studio', - 'Commit and Share', - 'Share as Branch', 'Modify and Run', 'Modify and Resume', 'Modify and Queue', @@ -884,7 +882,7 @@ describe('App', () => { fireEvent.contextMenu(row, { bubbles: true }) advanceTimersByTime(100) - expect(screen.getAllByRole('menuitem')).toHaveLength(12) + expect(screen.getAllByRole('menuitem')).toHaveLength(10) fireEvent.click(window, { bubbles: true }) advanceTimersByTime(100) @@ -898,7 +896,7 @@ describe('App', () => { fireEvent.contextMenu(row, { bubbles: true }) advanceTimersByTime(100) - expect(screen.getAllByRole('menuitem')).toHaveLength(12) + expect(screen.getAllByRole('menuitem')).toHaveLength(10) const commit = getRow('main') fireEvent.click(commit, { bubbles: true }) @@ -913,13 +911,13 @@ describe('App', () => { fireEvent.contextMenu(row, { bubbles: true }) advanceTimersByTime(100) - expect(screen.queryAllByRole('menuitem')).toHaveLength(12) + expect(screen.queryAllByRole('menuitem')).toHaveLength(10) fireEvent.contextMenu(within(row).getByText('[exp-e7a67]'), { bubbles: true }) advanceTimersByTime(200) - expect(screen.queryAllByRole('menuitem')).toHaveLength(12) + expect(screen.queryAllByRole('menuitem')).toHaveLength(10) }) it('should present the Remove experiment option for the checkpoint tips', () => { diff --git a/webview/src/experiments/components/table/body/RowContextMenu.tsx b/webview/src/experiments/components/table/body/RowContextMenu.tsx index bac3166559..3778b525c1 100644 --- a/webview/src/experiments/components/table/body/RowContextMenu.tsx +++ b/webview/src/experiments/components/table/body/RowContextMenu.tsx @@ -205,14 +205,6 @@ const getSingleSelectMenuOptions = ( MessageFromWebviewType.SHARE_EXPERIMENT_TO_STUDIO, true ), - hideIfRunningOrNotExperiment( - 'Commit and Share', - MessageFromWebviewType.SHARE_EXPERIMENT_AS_COMMIT - ), - hideIfRunningOrNotExperiment( - 'Share as Branch', - MessageFromWebviewType.SHARE_EXPERIMENT_AS_BRANCH - ), ...getRunResumeOptions( hideIfRunning, projectHasCheckpoints, From 3b1c3722c201eb53570ac0918d670ae3b1e559be Mon Sep 17 00:00:00 2001 From: Matt Seddon <37993418+mattseddon@users.noreply.github.com> Date: Sat, 29 Apr 2023 08:34:22 +1000 Subject: [PATCH 02/15] Stub dvc config calls in integration tests (#3777) --- extension/src/test/suite/extension.test.ts | 1 + extension/src/test/suite/setup/util.ts | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/extension/src/test/suite/extension.test.ts b/extension/src/test/suite/extension.test.ts index 3aff799f9f..8da48a2167 100644 --- a/extension/src/test/suite/extension.test.ts +++ b/extension/src/test/suite/extension.test.ts @@ -94,6 +94,7 @@ suite('Extension Test Suite', () => { const mockExpShow = stub(DvcReader.prototype, 'expShow') const mockDataStatus = stub(DvcReader.prototype, 'dataStatus') const mockPlotsDiff = stub(DvcReader.prototype, 'plotsDiff') + stub(DvcExecutor.prototype, 'config').resolves('') stub(DvcReader.prototype, 'root').resolves('.') diff --git a/extension/src/test/suite/setup/util.ts b/extension/src/test/suite/setup/util.ts index c0be6298fc..64c73a2544 100644 --- a/extension/src/test/suite/setup/util.ts +++ b/extension/src/test/suite/setup/util.ts @@ -33,6 +33,7 @@ export const buildSetup = ( messageSpy, resourceLocator, internalCommands, + dvcExecutor, dvcReader, gitExecutor, gitReader @@ -78,6 +79,8 @@ export const buildSetup = ( }) ) + stub(dvcExecutor, 'config').resolves('') + const setup = disposer.track( new Setup( config, @@ -118,6 +121,7 @@ export const buildSetup = ( export const buildSetupWithWatchers = async (disposer: Disposer) => { const mockEmitter = disposer.track(new EventEmitter()) const mockInternalCommands = { + executeCommand: stub(), registerExternalCliCommand: stub(), registerExternalCommand: stub() } as unknown as InternalCommands From b7a6e32dc677465095434d65a21d84f1a6621e30 Mon Sep 17 00:00:00 2001 From: Matt Seddon <37993418+mattseddon@users.noreply.github.com> Date: Sat, 29 Apr 2023 08:47:45 +1000 Subject: [PATCH 03/15] Rename exp commands to closer match CLI (#3778) --- extension/src/cli/dvc/executor.test.ts | 28 +++++++------- extension/src/cli/dvc/executor.ts | 37 ++++++++----------- extension/src/experiments/commands/index.ts | 9 +---- .../src/experiments/commands/register.ts | 18 +++------ extension/src/experiments/index.ts | 2 +- extension/src/experiments/workspace.ts | 6 +-- .../src/test/suite/experiments/index.test.ts | 19 +++++----- .../test/suite/experiments/model/tree.test.ts | 22 +++++------ .../test/suite/experiments/workspace.test.ts | 27 +++++--------- 9 files changed, 67 insertions(+), 101 deletions(-) diff --git a/extension/src/cli/dvc/executor.test.ts b/extension/src/cli/dvc/executor.test.ts index 642013876f..b181d07c8f 100644 --- a/extension/src/cli/dvc/executor.test.ts +++ b/extension/src/cli/dvc/executor.test.ts @@ -238,13 +238,13 @@ describe('CliExecutor', () => { }) }) - describe('experimentApply', () => { + describe('expApply', () => { it('should call createProcess with the correct parameters to apply an existing experiment to the workspace', async () => { const cwd = '' const stdout = 'Test output that will be passed along' mockedCreateProcess.mockReturnValueOnce(getMockedProcess(stdout)) - const output = await dvcExecutor.experimentApply(cwd, 'exp-test') + const output = await dvcExecutor.expApply(cwd, 'exp-test') expect(output).toStrictEqual(stdout) expect(mockedCreateProcess).toHaveBeenCalledWith({ @@ -256,7 +256,7 @@ describe('CliExecutor', () => { }) }) - describe('experimentBranch', () => { + describe('expBranch', () => { it('should call createProcess with the correct parameters to create a new branch from an existing experiment', async () => { const cwd = __dirname const stdout = @@ -265,7 +265,7 @@ describe('CliExecutor', () => { '\t\tgit checkout some-branch' mockedCreateProcess.mockReturnValueOnce(getMockedProcess(stdout)) - const output = await dvcExecutor.experimentBranch( + const output = await dvcExecutor.expBranch( cwd, 'exp-0898f', 'some-branch' @@ -281,7 +281,7 @@ describe('CliExecutor', () => { }) }) - describe('experimentGarbageCollect', () => { + describe('expGarbageCollect', () => { it('should call createProcess with the correct parameters to garbage collect experiments', async () => { const cwd = __dirname const stdout = @@ -290,7 +290,7 @@ describe('CliExecutor', () => { "Removed 45 experiments. To remove unused cache files use 'dvc gc'. " mockedCreateProcess.mockReturnValueOnce(getMockedProcess(stdout)) - const output = await dvcExecutor.experimentGarbageCollect( + const output = await dvcExecutor.expGarbageCollect( cwd, GcPreserveFlag.WORKSPACE, GcPreserveFlag.QUEUED @@ -306,13 +306,13 @@ describe('CliExecutor', () => { }) }) - describe('experimentPush', () => { + describe('expPush', () => { it('should call createProcess with the correct parameters to push an existing experiment to the remote', async () => { const cwd = __dirname const stdout = '' mockedCreateProcess.mockReturnValueOnce(getMockedProcess(stdout)) - const output = await dvcExecutor.experimentPush(cwd, 'toric-sail') + const output = await dvcExecutor.expPush(cwd, 'toric-sail') expect(output).toStrictEqual(stdout) expect(mockedCreateProcess).toHaveBeenCalledWith({ @@ -324,13 +324,13 @@ describe('CliExecutor', () => { }) }) - describe('experimentRemove', () => { + describe('expRemove', () => { it('should call createProcess with the correct parameters to remove an existing experiment from the workspace', async () => { const cwd = __dirname const stdout = '' mockedCreateProcess.mockReturnValueOnce(getMockedProcess(stdout)) - const output = await dvcExecutor.experimentRemove(cwd, 'exp-dfd12') + const output = await dvcExecutor.expRemove(cwd, 'exp-dfd12') expect(output).toStrictEqual(stdout) expect(mockedCreateProcess).toHaveBeenCalledWith({ @@ -342,13 +342,13 @@ describe('CliExecutor', () => { }) }) - describe('experimentRemoveQueue', () => { + describe('expRemoveQueue', () => { it('should call createProcess with the correct parameters to remove all existing queued experiments from the workspace', async () => { const cwd = __dirname const stdout = '' mockedCreateProcess.mockReturnValueOnce(getMockedProcess(stdout)) - const output = await dvcExecutor.experimentRemoveQueue(cwd) + const output = await dvcExecutor.expRemoveQueue(cwd) expect(output).toStrictEqual(stdout) expect(mockedCreateProcess).toHaveBeenCalledWith({ @@ -360,13 +360,13 @@ describe('CliExecutor', () => { }) }) - describe('experimentRunQueue', () => { + describe('expRunQueue', () => { it('should call createProcess with the correct parameters to queue an experiment for later execution', async () => { const cwd = __dirname const stdout = "Queued experiment 'bbf5c01' for future execution." mockedCreateProcess.mockReturnValueOnce(getMockedProcess(stdout)) - const output = await dvcExecutor.experimentRunQueue(cwd) + const output = await dvcExecutor.expRunQueue(cwd) expect(output).toStrictEqual(stdout) expect(mockedCreateProcess).toHaveBeenCalledWith({ diff --git a/extension/src/cli/dvc/executor.ts b/extension/src/cli/dvc/executor.ts index ecc6c06213..5bd5d88719 100644 --- a/extension/src/cli/dvc/executor.ts +++ b/extension/src/cli/dvc/executor.ts @@ -20,13 +20,13 @@ export const autoRegisteredCommands = { CHECKOUT: 'checkout', COMMIT: 'commit', CONFIG: 'config', - EXPERIMENT_APPLY: 'experimentApply', - EXPERIMENT_BRANCH: 'experimentBranch', - EXPERIMENT_GARBAGE_COLLECT: 'experimentGarbageCollect', - EXPERIMENT_PUSH: 'experimentPush', - EXPERIMENT_QUEUE: 'experimentRunQueue', - EXPERIMENT_REMOVE: 'experimentRemove', - EXPERIMENT_REMOVE_QUEUE: 'experimentRemoveQueue', + EXP_APPLY: 'expApply', + EXP_BRANCH: 'expBranch', + EXP_GARBAGE_COLLECT: 'expGarbageCollect', + EXP_PUSH: 'expPush', + EXP_QUEUE: 'expRunQueue', + EXP_REMOVE: 'expRemove', + EXP_REMOVE_QUEUE: 'expRemoveQueue', INIT: 'init', IS_SCM_COMMAND_RUNNING: 'isScmCommandRunning', MOVE: 'move', @@ -78,7 +78,7 @@ export class DvcExecutor extends DvcCli { return this.executeDvcProcess(cwd, Command.CONFIG, ...args) } - public experimentApply(cwd: string, experimentName: string) { + public expApply(cwd: string, experimentName: string) { return this.executeExperimentProcess( cwd, ExperimentSubCommand.APPLY, @@ -86,11 +86,7 @@ export class DvcExecutor extends DvcCli { ) } - public experimentBranch( - cwd: string, - experimentName: string, - branchName: string - ) { + public expBranch(cwd: string, experimentName: string, branchName: string) { return this.executeExperimentProcess( cwd, ExperimentSubCommand.BRANCH, @@ -99,10 +95,7 @@ export class DvcExecutor extends DvcCli { ) } - public experimentGarbageCollect( - cwd: string, - ...preserveFlags: GcPreserveFlag[] - ) { + public expGarbageCollect(cwd: string, ...preserveFlags: GcPreserveFlag[]) { return this.executeExperimentProcess( cwd, ExperimentSubCommand.GARBAGE_COLLECT, @@ -111,7 +104,7 @@ export class DvcExecutor extends DvcCli { ) } - public experimentPush(cwd: string, id: string) { + public expPush(cwd: string, id: string) { return this.executeExperimentProcess( cwd, ExperimentSubCommand.PUSH, @@ -120,7 +113,7 @@ export class DvcExecutor extends DvcCli { ) } - public experimentRemove(cwd: string, ...experimentNames: string[]) { + public expRemove(cwd: string, ...experimentNames: string[]) { return this.executeExperimentProcess( cwd, ExperimentSubCommand.REMOVE, @@ -128,11 +121,11 @@ export class DvcExecutor extends DvcCli { ) } - public experimentRemoveQueue(cwd: string) { - return this.experimentRemove(cwd, ExperimentFlag.QUEUE) + public expRemoveQueue(cwd: string) { + return this.expRemove(cwd, ExperimentFlag.QUEUE) } - public experimentRunQueue(cwd: string, ...args: Args) { + public expRunQueue(cwd: string, ...args: Args) { return this.executeExperimentProcess( cwd, ExperimentSubCommand.RUN, diff --git a/extension/src/experiments/commands/index.ts b/extension/src/experiments/commands/index.ts index 2df5b3cd2c..1ccf85db11 100644 --- a/extension/src/experiments/commands/index.ts +++ b/extension/src/experiments/commands/index.ts @@ -8,12 +8,7 @@ import { RegisteredCommands } from '../../commands/external' export const getBranchExperimentCommand = (experiments: WorkspaceExperiments) => (cwd: string, name: string, input: string) => - experiments.runCommand( - AvailableCommands.EXPERIMENT_BRANCH, - cwd, - name, - input - ) + experiments.runCommand(AvailableCommands.EXP_BRANCH, cwd, name, input) export const getShareExperimentToStudioCommand = (internalCommands: InternalCommands, setup: Setup) => @@ -31,7 +26,7 @@ export const getShareExperimentToStudioCommand = await Toast.runCommandAndIncrementProgress( () => internalCommands.executeCommand( - AvailableCommands.EXPERIMENT_PUSH, + AvailableCommands.EXP_PUSH, dvcRoot, id ), diff --git a/extension/src/experiments/commands/register.ts b/extension/src/experiments/commands/register.ts index 183a25bdb8..f7764b3f27 100644 --- a/extension/src/experiments/commands/register.ts +++ b/extension/src/experiments/commands/register.ts @@ -24,7 +24,7 @@ const registerExperimentCwdCommands = ( RegisteredCliCommands.QUEUE_EXPERIMENT, () => experiments.pauseUpdatesThenRun(() => - experiments.getCwdThenReport(AvailableCommands.EXPERIMENT_QUEUE) + experiments.getCwdThenReport(AvailableCommands.EXP_QUEUE) ) ) @@ -110,8 +110,7 @@ const registerExperimentCwdCommands = ( internalCommands.registerExternalCliCommand( RegisteredCliCommands.EXPERIMENT_REMOVE_QUEUE, - () => - experiments.getCwdThenReport(AvailableCommands.EXPERIMENT_REMOVE_QUEUE) + () => experiments.getCwdThenReport(AvailableCommands.EXP_REMOVE_QUEUE) ) } @@ -121,24 +120,19 @@ const registerExperimentNameCommands = ( ): void => { internalCommands.registerExternalCliCommand( RegisteredCliCommands.EXPERIMENT_APPLY, - () => - experiments.getCwdAndExpNameThenRun(AvailableCommands.EXPERIMENT_APPLY) + () => experiments.getCwdAndExpNameThenRun(AvailableCommands.EXP_APPLY) ) internalCommands.registerExternalCliCommand( RegisteredCliCommands.EXPERIMENT_VIEW_APPLY, ({ dvcRoot, id }: ExperimentDetails) => - experiments.runCommand(AvailableCommands.EXPERIMENT_APPLY, dvcRoot, id) + experiments.runCommand(AvailableCommands.EXP_APPLY, dvcRoot, id) ) internalCommands.registerExternalCliCommand( RegisteredCliCommands.EXPERIMENT_VIEW_REMOVE, ({ dvcRoot, ids }: { dvcRoot: string; ids: string[] }) => - experiments.runCommand( - AvailableCommands.EXPERIMENT_REMOVE, - dvcRoot, - ...ids - ) + experiments.runCommand(AvailableCommands.EXP_REMOVE, dvcRoot, ...ids) ) } @@ -175,7 +169,7 @@ const registerExperimentQuickPickCommands = ( RegisteredCliCommands.EXPERIMENT_GARBAGE_COLLECT, () => experiments.getCwdAndQuickPickThenRun( - AvailableCommands.EXPERIMENT_GARBAGE_COLLECT, + AvailableCommands.EXP_GARBAGE_COLLECT, pickGarbageCollectionFlags ) ) diff --git a/extension/src/experiments/index.ts b/extension/src/experiments/index.ts index deb902419d..c48904b564 100644 --- a/extension/src/experiments/index.ts +++ b/extension/src/experiments/index.ts @@ -477,7 +477,7 @@ export class Experiments extends BaseRepository { await Toast.showOutput( this.internalCommands.executeCommand( - AvailableCommands.EXPERIMENT_QUEUE, + AvailableCommands.EXP_QUEUE, this.dvcRoot, ...paramsToModify ) diff --git a/extension/src/experiments/workspace.ts b/extension/src/experiments/workspace.ts index 0f8f256382..1bc63ce95e 100644 --- a/extension/src/experiments/workspace.ts +++ b/extension/src/experiments/workspace.ts @@ -185,11 +185,7 @@ export class WorkspaceExperiments extends BaseWorkspaceWebviews< return } - return this.runCommand( - AvailableCommands.EXPERIMENT_REMOVE, - cwd, - ...experimentIds - ) + return this.runCommand(AvailableCommands.EXP_REMOVE, cwd, ...experimentIds) } public async modifyExperimentParamsAndRun( diff --git a/extension/src/test/suite/experiments/index.test.ts b/extension/src/test/suite/experiments/index.test.ts index d779bed334..87ea8c710c 100644 --- a/extension/src/test/suite/experiments/index.test.ts +++ b/extension/src/test/suite/experiments/index.test.ts @@ -551,7 +551,7 @@ suite('Experiments Test Suite', () => { const mockExperimentApply = stub( DvcExecutor.prototype, - 'experimentApply' + 'expApply' ).resolves(undefined) stubWorkspaceExperimentsGetters(dvcDemoPath, experiments) @@ -577,7 +577,7 @@ suite('Experiments Test Suite', () => { const mockExperimentBranch = stub( DvcExecutor.prototype, - 'experimentBranch' + 'expBranch' ).resolves('undefined') stubWorkspaceExperimentsGetters(dvcDemoPath, experiments) @@ -675,9 +675,9 @@ suite('Experiments Test Suite', () => { return 'isat_token' }) ) - const mockExperimentPush = stub(DvcExecutor.prototype, 'experimentPush') + const mockexpPush = stub(DvcExecutor.prototype, 'expPush') const commandExecuted = new Promise(resolve => - mockExperimentPush.callsFake(() => { + mockexpPush.callsFake(() => { resolve(undefined) return Promise.resolve( `Pushed experiment ${mockExpId} to Git remote 'origin'` @@ -692,7 +692,7 @@ suite('Experiments Test Suite', () => { await Promise.all([tokenFound, commandExecuted]) - expect(mockExperimentPush).to.be.calledWithExactly(dvcDemoPath, mockExpId) + expect(mockexpPush).to.be.calledWithExactly(dvcDemoPath, mockExpId) }).timeout(WEBVIEW_TEST_TIMEOUT) it("should be able to handle a message to modify an experiment's params and queue an experiment", async () => { @@ -709,10 +709,9 @@ suite('Experiments Test Suite', () => { stubWorkspaceExperimentsGetters(dvcDemoPath, experiments) stub(experiments, 'pickAndModifyParams').resolves(mockModifiedParams) - const mockQueueExperiment = stub( - dvcExecutor, - 'experimentRunQueue' - ).resolves(undefined) + const mockQueueExperiment = stub(dvcExecutor, 'expRunQueue').resolves( + undefined + ) const webview = await experiments.showWebview() const mockMessageReceived = getMessageReceivedEmitter(webview) @@ -817,7 +816,7 @@ suite('Experiments Test Suite', () => { const mockExperimentRemove = stub( DvcExecutor.prototype, - 'experimentRemove' + 'expRemove' ).resolves(undefined) mockMessageReceived.fire({ diff --git a/extension/src/test/suite/experiments/model/tree.test.ts b/extension/src/test/suite/experiments/model/tree.test.ts index 2c9ebcf550..c30ae742aa 100644 --- a/extension/src/test/suite/experiments/model/tree.test.ts +++ b/extension/src/test/suite/experiments/model/tree.test.ts @@ -256,7 +256,7 @@ suite('Experiments Tree Test Suite', () => { const mockExperimentRemove = stub( DvcExecutor.prototype, - 'experimentRemove' + 'expRemove' ).resolves('') stubPrivatePrototypeMethod( @@ -280,7 +280,7 @@ suite('Experiments Tree Test Suite', () => { const mockExperimentRemove = stub( DvcExecutor.prototype, - 'experimentRemove' + 'expRemove' ).resolves('') stubPrivatePrototypeMethod( @@ -309,7 +309,7 @@ suite('Experiments Tree Test Suite', () => { const mockExperimentRemove = stub( DvcExecutor.prototype, - 'experimentRemove' + 'expRemove' ).resolves('') stubPrivatePrototypeMethod( @@ -354,7 +354,7 @@ suite('Experiments Tree Test Suite', () => { const mockExperimentApply = stub( DvcExecutor.prototype, - 'experimentApply' + 'expApply' ).resolves( `Changes for experiment '${mockExperimentId}' have been applied to your current workspace.` ) @@ -378,10 +378,7 @@ suite('Experiments Tree Test Suite', () => { const { experiments } = buildExperiments(disposable) await experiments.isReady() - const mockExperimentBranch = stub( - DvcExecutor.prototype, - 'experimentBranch' - ) + const mockExperimentBranch = stub(DvcExecutor.prototype, 'expBranch') const mockShowInputBox = stub(window, 'showInputBox').resolves(undefined) stub(WorkspaceExperiments.prototype, 'getRepository').returns(experiments) @@ -406,7 +403,7 @@ suite('Experiments Tree Test Suite', () => { const mockExperimentBranch = stub( DvcExecutor.prototype, - 'experimentBranch' + 'expBranch' ).resolves( `Git branch '${mockBranch}' has been created from experiment '${mockExperimentId}'. To switch to the new branch run: @@ -440,10 +437,9 @@ suite('Experiments Tree Test Suite', () => { await experiments.isReady() - const mockExperimentRunQueue = stub( - dvcExecutor, - 'experimentRunQueue' - ).resolves('true') + const mockExperimentRunQueue = stub(dvcExecutor, 'expRunQueue').resolves( + 'true' + ) const [mockGetOnlyOrPickProject] = stubWorkspaceExperimentsGetters( '', diff --git a/extension/src/test/suite/experiments/workspace.test.ts b/extension/src/test/suite/experiments/workspace.test.ts index 669063b93c..38ef50b874 100644 --- a/extension/src/test/suite/experiments/workspace.test.ts +++ b/extension/src/test/suite/experiments/workspace.test.ts @@ -154,10 +154,9 @@ suite('Workspace Experiments Test Suite', () => { const { dvcExecutor, experiments } = buildExperiments(disposable) - const mockExperimentRunQueue = stub( - dvcExecutor, - 'experimentRunQueue' - ).resolves('true') + const mockExperimentRunQueue = stub(dvcExecutor, 'expRunQueue').resolves( + 'true' + ) stubWorkspaceExperimentsGetters(dvcDemoPath, experiments) @@ -325,7 +324,7 @@ suite('Workspace Experiments Test Suite', () => { const mockExperimentRunQueue = stub( DvcExecutor.prototype, - 'experimentRunQueue' + 'expRunQueue' ).resolves('true') stubWorkspaceExperimentsGetters(dvcDemoPath) @@ -339,7 +338,7 @@ suite('Workspace Experiments Test Suite', () => { it('should send a telemetry event containing a duration when an experiment is queued', async () => { stub(DvcReader.prototype, 'listStages').resolves('train') - stub(DvcExecutor.prototype, 'experimentRunQueue').resolves('true') + stub(DvcExecutor.prototype, 'expRunQueue').resolves('true') const mockSendTelemetryEvent = stub(Telemetry, 'sendTelemetryEvent') @@ -371,7 +370,7 @@ suite('Workspace Experiments Test Suite', () => { undefined ) - stub(DvcExecutor.prototype, 'experimentRunQueue').callsFake(() => { + stub(DvcExecutor.prototype, 'expRunQueue').callsFake(() => { throw new Error(mockErrorMessage) }) @@ -554,7 +553,7 @@ suite('Workspace Experiments Test Suite', () => { const mockShowQuickPick = stub(window, 'showQuickPick').resolves({ value: selectedExperiment } as QuickPickItemWithValue) - const mockExperimentApply = stub(DvcExecutor.prototype, 'experimentApply') + const mockExperimentApply = stub(DvcExecutor.prototype, 'expApply') await commands.executeCommand(RegisteredCliCommands.EXPERIMENT_APPLY) @@ -638,7 +637,7 @@ suite('Workspace Experiments Test Suite', () => { const mockExperimentBranch = stub( DvcExecutor.prototype, - 'experimentBranch' + 'expBranch' ).resolves( `Git branch '${mockBranch}' has been created from experiment '${testExperiment}'. To switch to the new branch run: @@ -691,10 +690,7 @@ suite('Workspace Experiments Test Suite', () => { value: secondMockExperimentId } ] as QuickPickReturnValue) - const mockExperimentRemove = stub( - DvcExecutor.prototype, - 'experimentRemove' - ) + const mockExperimentRemove = stub(DvcExecutor.prototype, 'expRemove') await commands.executeCommand(RegisteredCliCommands.EXPERIMENT_REMOVE) expect(mockShowQuickPick).calledWithExactly( @@ -786,10 +782,7 @@ suite('Workspace Experiments Test Suite', () => { stubWorkspaceExperimentsGetters(dvcDemoPath, experiments) - const mockExperimentRemove = stub( - DvcExecutor.prototype, - 'experimentRemove' - ) + const mockExperimentRemove = stub(DvcExecutor.prototype, 'expRemove') await commands.executeCommand( RegisteredCliCommands.EXPERIMENT_REMOVE_QUEUE From 496d2d79bfb6531037eeeca12ae3204a7bb22ba1 Mon Sep 17 00:00:00 2001 From: Matt Seddon <37993418+mattseddon@users.noreply.github.com> Date: Sat, 29 Apr 2023 10:32:55 +1000 Subject: [PATCH 04/15] Add GIT_TERMINAL_PROMPT=0 to DVC environment variables (#3779) --- extension/src/cli/dvc/executor.test.ts | 1 + extension/src/cli/dvc/index.test.ts | 8 +++++++- extension/src/cli/dvc/options.test.ts | 3 +++ extension/src/cli/dvc/options.ts | 1 + extension/src/cli/dvc/reader.test.ts | 1 + 5 files changed, 13 insertions(+), 1 deletion(-) diff --git a/extension/src/cli/dvc/executor.test.ts b/extension/src/cli/dvc/executor.test.ts index b181d07c8f..d4c4f2b4d9 100644 --- a/extension/src/cli/dvc/executor.test.ts +++ b/extension/src/cli/dvc/executor.test.ts @@ -24,6 +24,7 @@ const mockedGetProcessEnv = jest.mocked(getProcessEnv) const mockedEnv = { DVCLIVE_OPEN: 'false', DVC_NO_ANALYTICS: 'true', + GIT_TERMINAL_PROMPT: '0', PATH: '/some/special/path' } diff --git a/extension/src/cli/dvc/index.test.ts b/extension/src/cli/dvc/index.test.ts index 1c42ae1af6..370602508b 100644 --- a/extension/src/cli/dvc/index.test.ts +++ b/extension/src/cli/dvc/index.test.ts @@ -86,7 +86,12 @@ describe('executeDvcProcess', () => { expect(mockedCreateProcess).toHaveBeenCalledWith({ args, cwd, - env: { ...processEnv, DVCLIVE_OPEN: 'false', DVC_NO_ANALYTICS: 'true' }, + env: { + ...processEnv, + DVCLIVE_OPEN: 'false', + DVC_NO_ANALYTICS: 'true', + GIT_TERMINAL_PROMPT: '0' + }, executable: 'dvc' }) }) @@ -129,6 +134,7 @@ describe('executeDvcProcess', () => { env: { DVCLIVE_OPEN: 'false', DVC_NO_ANALYTICS: 'true', + GIT_TERMINAL_PROMPT: '0', PATH: joinEnvPath('/some/path/to', existingPath), SECRET_KEY }, diff --git a/extension/src/cli/dvc/options.test.ts b/extension/src/cli/dvc/options.test.ts index d3c638aceb..c316a8a7e9 100644 --- a/extension/src/cli/dvc/options.test.ts +++ b/extension/src/cli/dvc/options.test.ts @@ -11,6 +11,7 @@ const mockedPATH = '/some/special/path' const mockedEnv = { DVCLIVE_OPEN: 'false', DVC_NO_ANALYTICS: 'true', + GIT_TERMINAL_PROMPT: '0', PATH: mockedPATH } const mockedGetProcessEnv = jest.mocked(getProcessEnv) @@ -42,6 +43,7 @@ describe('getOptions', () => { env: { DVCLIVE_OPEN: 'false', DVC_NO_ANALYTICS: 'true', + GIT_TERMINAL_PROMPT: '0', PATH: joinEnvPath(join('path', 'to', 'python', '.venv'), mockedPATH) }, executable: pythonBinPath @@ -58,6 +60,7 @@ describe('getOptions', () => { env: { DVCLIVE_OPEN: 'false', DVC_NO_ANALYTICS: 'true', + GIT_TERMINAL_PROMPT: '0', PATH: joinEnvPath(join('path', 'to', 'python', '.venv'), mockedPATH) }, executable: cliPath diff --git a/extension/src/cli/dvc/options.ts b/extension/src/cli/dvc/options.ts index 50f8898122..76a26345ba 100644 --- a/extension/src/cli/dvc/options.ts +++ b/extension/src/cli/dvc/options.ts @@ -21,6 +21,7 @@ const getEnv = (pythonBinPath?: string): NodeJS.ProcessEnv => { ...env, DVCLIVE_OPEN: 'false', DVC_NO_ANALYTICS: 'true', + GIT_TERMINAL_PROMPT: '0', PATH } } diff --git a/extension/src/cli/dvc/reader.test.ts b/extension/src/cli/dvc/reader.test.ts index 87dbcc44e4..9ca8aaf56c 100644 --- a/extension/src/cli/dvc/reader.test.ts +++ b/extension/src/cli/dvc/reader.test.ts @@ -28,6 +28,7 @@ const mockedGetProcessEnv = jest.mocked(getProcessEnv) const mockedEnv = { DVCLIVE_OPEN: 'false', DVC_NO_ANALYTICS: 'true', + GIT_TERMINAL_PROMPT: '0', PATH: '/all/of/the/goodies:/in/my/path' } const JSON_FLAG = '--json' From 920915e5de7380b0ecd93a9d418291ff11c9cfea Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 29 Apr 2023 00:48:07 +0000 Subject: [PATCH 05/15] chore(deps): update dependency eslint to v8.39.0 (#3786) --- package.json | 2 +- yarn.lock | 28 ++++++++++++++-------------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/package.json b/package.json index 0d6e4c7dfc..c73bee0300 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "@typescript-eslint/eslint-plugin": "5.59.0", "@typescript-eslint/parser": "5.59.0", "@vscode/codicons": "0.0.32", - "eslint": "8.38.0", + "eslint": "8.39.0", "eslint-config-prettier": "8.8.0", "eslint-config-prettier-standard": "4.0.1", "eslint-config-standard": "17.0.0", diff --git a/yarn.lock b/yarn.lock index b505632954..6cd76bd49d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2443,10 +2443,10 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@eslint/js@8.38.0": - version "8.38.0" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.38.0.tgz#73a8a0d8aa8a8e6fe270431c5e72ae91b5337892" - integrity sha512-IoD2MfUnOV58ghIHCiil01PcohxjbYR/qCxsoC+xNgUwh1EY8jOOrYmu3d3a71+tJJ23uscEV4X2HJWMsPJu4g== +"@eslint/js@8.39.0": + version "8.39.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.39.0.tgz#58b536bcc843f4cd1e02a7e6171da5c040f4d44b" + integrity sha512-kf9RB0Fg7NZfap83B3QOqOGg9QmD9yBudqQXzzOtn3i4y7ZUXe5ONeW34Gwi+TxhH4mvj72R1Zc300KUMa9Bng== "@fal-works/esbuild-plugin-global-externals@^2.1.2": version "2.1.2" @@ -10474,10 +10474,10 @@ eslint-scope@^4.0.3: esrecurse "^4.1.0" estraverse "^4.1.1" -eslint-scope@^7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.1.1.tgz#fff34894c2f65e5226d3041ac480b4513a163642" - integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw== +eslint-scope@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.0.tgz#f21ebdafda02352f103634b96dd47d9f81ca117b" + integrity sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw== dependencies: esrecurse "^4.3.0" estraverse "^5.2.0" @@ -10516,15 +10516,15 @@ eslint-visitor-keys@^3.4.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.0.tgz#c7f0f956124ce677047ddbc192a68f999454dedc" integrity sha512-HPpKPUBQcAsZOsHAFwTtIKcYlCje62XB7SEAcxjtmW6TD1WVpkS6i6/hOVtTZIl4zGj/mBqpFVGvaDneik+VoQ== -eslint@8.38.0: - version "8.38.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.38.0.tgz#a62c6f36e548a5574dd35728ac3c6209bd1e2f1a" - integrity sha512-pIdsD2jwlUGf/U38Jv97t8lq6HpaU/G9NKbYmpWpZGw3LdTNhZLbJePqxOXGB5+JEKfOPU/XLxYxFh03nr1KTg== +eslint@8.39.0: + version "8.39.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.39.0.tgz#7fd20a295ef92d43809e914b70c39fd5a23cf3f1" + integrity sha512-mwiok6cy7KTW7rBpo05k6+p4YVZByLNjAZ/ACB9DRCu4YDRwjXI01tWHp6KAUWelsBetTxKK/2sHB0vdS8Z2Og== dependencies: "@eslint-community/eslint-utils" "^4.2.0" "@eslint-community/regexpp" "^4.4.0" "@eslint/eslintrc" "^2.0.2" - "@eslint/js" "8.38.0" + "@eslint/js" "8.39.0" "@humanwhocodes/config-array" "^0.11.8" "@humanwhocodes/module-importer" "^1.0.1" "@nodelib/fs.walk" "^1.2.8" @@ -10534,7 +10534,7 @@ eslint@8.38.0: debug "^4.3.2" doctrine "^3.0.0" escape-string-regexp "^4.0.0" - eslint-scope "^7.1.1" + eslint-scope "^7.2.0" eslint-visitor-keys "^3.4.0" espree "^9.5.1" esquery "^1.4.2" From 9e7f2fca23a4a7e5a8fbd33d48079a2065a72388 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 29 Apr 2023 01:05:28 +0000 Subject: [PATCH 06/15] chore(deps): update dependency @types/react to v18.0.38 (#3785) --- package.json | 2 +- webview/package.json | 2 +- yarn.lock | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index c73bee0300..5145d5e7e2 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,7 @@ }, "resolutions": { "decode-uri-component": "0.2.2", - "@types/react": "18.0.37", + "@types/react": "18.0.38", "**/recursive-readdir/minimatch": "6.2.0", "fastify": "3.29.5", "json5": "2.2.3", diff --git a/webview/package.json b/webview/package.json index 9027b1d2c9..f6040fc9e6 100644 --- a/webview/package.json +++ b/webview/package.json @@ -56,7 +56,7 @@ "@types/jest": "29.5.1", "@types/jsdom": "21.1.1", "@types/node": "16.x", - "@types/react": "18.0.37", + "@types/react": "18.0.38", "@types/react-dom": "18.0.11", "@types/react-measure": "2.0.8", "@types/react-virtualized": "9.21.21", diff --git a/yarn.lock b/yarn.lock index 6cd76bd49d..7918edf470 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5331,10 +5331,10 @@ "@types/prop-types" "*" "@types/react" "^17" -"@types/react@*", "@types/react@18.0.37", "@types/react@>=16", "@types/react@^16.9.19", "@types/react@^17": - version "18.0.37" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.37.tgz#7a784e2a8b8f83abb04dc6b9ed9c9b4c0aee9be7" - integrity sha512-4yaZZtkRN3ZIQD3KSEwkfcik8s0SWV+82dlJot1AbGYHCzJkWP3ENBY6wYeDRmKZ6HkrgoGAmR2HqdwYGp6OEw== +"@types/react@*", "@types/react@18.0.38", "@types/react@>=16", "@types/react@^16.9.19", "@types/react@^17": + version "18.0.38" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.38.tgz#02a23bef8848b360a0d1dceef4432c15c21c600c" + integrity sha512-ExsidLLSzYj4cvaQjGnQCk4HFfVT9+EZ9XZsQ8Hsrcn8QNgXtpZ3m9vSIC2MWtx7jHictK6wYhQgGh6ic58oOw== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" From c6c70c898c815da68d7ce067d51c7e2188a84f9c Mon Sep 17 00:00:00 2001 From: Stephanie Roy Date: Fri, 28 Apr 2023 21:24:19 -0400 Subject: [PATCH 07/15] Fix available revisions for multi view plots (#3774) --- extension/src/plots/model/collect.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/extension/src/plots/model/collect.ts b/extension/src/plots/model/collect.ts index 164deb42fa..ea3d56cbe0 100644 --- a/extension/src/plots/model/collect.ts +++ b/extension/src/plots/model/collect.ts @@ -269,8 +269,8 @@ const transformRevisionData = ( ): { revisions: string[]; datapoints: unknown[] } => { const field = multiSourceEncodingUpdate.strokeDash?.field const isMultiSource = !!field - const availableRevisions = selectedRevisions.filter(rev => - Object.keys(revisionData).includes(rev) + const availableRevisions = selectedRevisions.filter( + rev => revisionData[rev]?.[path] ) const transformNeeded = isMultiSource && (isMultiView || isConcatenatedField(field)) @@ -278,7 +278,7 @@ const transformRevisionData = ( if (!transformNeeded) { return { datapoints: selectedRevisions - .flatMap(revision => revisionData?.[revision]?.[path]) + .flatMap(revision => revisionData[revision]?.[path]) .filter(Boolean), revisions: availableRevisions } From 0c58b11c09523d5e6f328a11d338697acd720724 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 30 Apr 2023 15:54:05 +1000 Subject: [PATCH 08/15] chore(deps): update dependency stylelint to v15.6.0 (#3789) --- package.json | 2 +- yarn.lock | 24 ++++++++++++------------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index 5145d5e7e2..dee2607638 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,7 @@ "postcss": "8.4.23", "prettier": "2.8.7", "prettier-config-standard": "5.0.0", - "stylelint": "15.5.0", + "stylelint": "15.6.0", "stylelint-config-standard-scss": "8.0.0", "ts-node": "10.9.1", "turbo": "1.9.3", diff --git a/yarn.lock b/yarn.lock index 7918edf470..1175bbe678 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2264,17 +2264,17 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" -"@csstools/css-parser-algorithms@^2.1.0": +"@csstools/css-parser-algorithms@^2.1.1": version "2.1.1" resolved "https://registry.yarnpkg.com/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.1.1.tgz#7b62e6412a468a2d1096ed267edd1e4a7fd4a119" integrity sha512-viRnRh02AgO4mwIQb2xQNJju0i+Fh9roNgmbR5xEuG7J3TGgxjnE95HnBLgsFJOJOksvcfxOUCgODcft6Y07cA== -"@csstools/css-tokenizer@^2.1.0": +"@csstools/css-tokenizer@^2.1.1": version "2.1.1" resolved "https://registry.yarnpkg.com/@csstools/css-tokenizer/-/css-tokenizer-2.1.1.tgz#07ae11a0a06365d7ec686549db7b729bc036528e" integrity sha512-GbrTj2Z8MCTUv+52GE0RbFGM527xuXZ0Xa5g0Z+YN573uveS4G0qi6WNOMyz3yrFM/jaILTTwJ0+umx81EzqfA== -"@csstools/media-query-list-parser@^2.0.2": +"@csstools/media-query-list-parser@^2.0.4": version "2.0.4" resolved "https://registry.yarnpkg.com/@csstools/media-query-list-parser/-/media-query-list-parser-2.0.4.tgz#466bd254041530dfd1e88bcb1921e8ca4af75b6a" integrity sha512-GyYot6jHgcSDZZ+tLSnrzkR7aJhF2ZW6d+CXH66mjy5WpAQhZD4HDke2OQ36SivGRWlZJpAz7TzbW6OKlEpxAA== @@ -16376,7 +16376,7 @@ postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0: resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== -postcss@8.4.23, postcss@^8.4.21: +postcss@8.4.23, postcss@^8.4.22: version "8.4.23" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.23.tgz#df0aee9ac7c5e53e1075c24a3613496f9e6552ab" integrity sha512-bQ3qMcpF6A/YjR55xtoTr0jGOlnPOKAIMdOWiv0EIT6HVPEaJiJB4NLljSbiHoC2RX7DN5Uvjtpbg1NPdwv1oA== @@ -18771,14 +18771,14 @@ stylelint-scss@^4.6.0: postcss-selector-parser "^6.0.11" postcss-value-parser "^4.2.0" -stylelint@15.5.0: - version "15.5.0" - resolved "https://registry.yarnpkg.com/stylelint/-/stylelint-15.5.0.tgz#f16c238231f3f32e62da8a88969821d237eae8a6" - integrity sha512-jyMO3R1QtE5mUS4v40+Gg+sIQBqe7CF1xPslxycDzNVkIBCUD4O+5F1vLPq16VmunUTv4qG9o2rUKLnU5KkVeQ== +stylelint@15.6.0: + version "15.6.0" + resolved "https://registry.yarnpkg.com/stylelint/-/stylelint-15.6.0.tgz#1d76176dd8b6307bc4645e428ad18ddd15edbafc" + integrity sha512-Cqzpc8tvJm77KaM8qUbhpJ/UYK55Ia0whQXj4b9IId9dlPICO7J8Lyo15SZWiHxKjlvy3p5FQor/3n6i8ignXg== dependencies: - "@csstools/css-parser-algorithms" "^2.1.0" - "@csstools/css-tokenizer" "^2.1.0" - "@csstools/media-query-list-parser" "^2.0.2" + "@csstools/css-parser-algorithms" "^2.1.1" + "@csstools/css-tokenizer" "^2.1.1" + "@csstools/media-query-list-parser" "^2.0.4" "@csstools/selector-specificity" "^2.2.0" balanced-match "^2.0.0" colord "^2.9.3" @@ -18803,7 +18803,7 @@ stylelint@15.5.0: micromatch "^4.0.5" normalize-path "^3.0.0" picocolors "^1.0.0" - postcss "^8.4.21" + postcss "^8.4.22" postcss-media-query-parser "^0.2.3" postcss-resolve-nested-selector "^0.1.1" postcss-safe-parser "^6.0.0" From 71e205bc6451012b83360ffb013ed886b65378d4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 30 Apr 2023 20:24:24 +1000 Subject: [PATCH 09/15] chore(deps): update dependency prettier to v2.8.8 (#3790) --- package.json | 2 +- yarn.lock | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index dee2607638..5a52863fa9 100644 --- a/package.json +++ b/package.json @@ -62,7 +62,7 @@ "npm-run-all": "4.1.5", "nyc": "15.1.0", "postcss": "8.4.23", - "prettier": "2.8.7", + "prettier": "2.8.8", "prettier-config-standard": "5.0.0", "stylelint": "15.6.0", "stylelint-config-standard-scss": "8.0.0", diff --git a/yarn.lock b/yarn.lock index 1175bbe678..0f482d7dcc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -16449,16 +16449,16 @@ prettier-linter-helpers@^1.0.0: dependencies: fast-diff "^1.1.2" -prettier@2.8.7, prettier@^2.8.7: - version "2.8.7" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.7.tgz#bb79fc8729308549d28fe3a98fce73d2c0656450" - integrity sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw== - -prettier@^2.8.0: +prettier@2.8.8, prettier@^2.8.0: version "2.8.8" resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== +prettier@^2.8.7: + version "2.8.7" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.7.tgz#bb79fc8729308549d28fe3a98fce73d2c0656450" + integrity sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw== + pretty-error@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-4.0.0.tgz#90a703f46dd7234adb46d0f84823e9d1cb8f10d6" From d5ad8cd4efb971779122c15f5a24e375dcf7717e Mon Sep 17 00:00:00 2001 From: Matt Seddon <37993418+mattseddon@users.noreply.github.com> Date: Tue, 2 May 2023 07:08:52 +1000 Subject: [PATCH 10/15] Display progress errors to users (#3791) --- extension/src/setup/autoInstall.ts | 42 +++++++------ .../src/test/suite/setup/autoInstall.test.ts | 60 +++++++++++++++++++ extension/src/vscode/toast.ts | 16 +++++ 3 files changed, 101 insertions(+), 17 deletions(-) diff --git a/extension/src/setup/autoInstall.ts b/extension/src/setup/autoInstall.ts index 49b4b43421..b38480e43a 100644 --- a/extension/src/setup/autoInstall.ts +++ b/extension/src/setup/autoInstall.ts @@ -23,25 +23,33 @@ const showInstallProgress = ( Toast.showProgress('Installing packages', async progress => { progress.report({ increment: 0 }) - await Toast.runCommandAndIncrementProgress( - async () => { - await installPackages(root, pythonBinPath, 'dvclive') - return 'DVCLive Installed' - }, - progress, - 25 - ) + try { + await Toast.runCommandAndIncrementProgress( + async () => { + await installPackages(root, pythonBinPath, 'dvclive') + return 'DVCLive Installed' + }, + progress, + 25 + ) + } catch (error: unknown) { + return Toast.reportProgressError(error, progress) + } - await Toast.runCommandAndIncrementProgress( - async () => { - await installPackages(root, pythonBinPath, 'dvc') - return 'DVC Installed' - }, - progress, - 75 - ) + try { + await Toast.runCommandAndIncrementProgress( + async () => { + await installPackages(root, pythonBinPath, 'dvc') + return 'DVC Installed' + }, + progress, + 75 + ) - return Toast.delayProgressClosing() + return Toast.delayProgressClosing() + } catch (error: unknown) { + return Toast.reportProgressError(error, progress) + } }) export const autoInstallDvc = async (): Promise => { diff --git a/extension/src/test/suite/setup/autoInstall.test.ts b/extension/src/test/suite/setup/autoInstall.test.ts index 44276f5dfd..775a75fb32 100644 --- a/extension/src/test/suite/setup/autoInstall.test.ts +++ b/extension/src/test/suite/setup/autoInstall.test.ts @@ -8,6 +8,7 @@ import * as Python from '../../../python' import { autoInstallDvc } from '../../../setup/autoInstall' import * as WorkspaceFolders from '../../../vscode/workspaceFolders' import { bypassProgressCloseDelay } from '../util' +import { Toast } from '../../../vscode/toast' const { getDefaultPython } = Python @@ -89,5 +90,64 @@ suite('Auto Install Test Suite', () => { 'dvclive' ) }) + + it('should show an error message and exit early if DVCLive fails to install', async () => { + bypassProgressCloseDelay() + const cwd = __dirname + stub(PythonExtension, 'getPythonExecutionDetails').resolves(undefined) + stub(Python, 'findPythonBin').resolves(defaultPython) + const mockInstallPackages = stub(Python, 'installPackages').rejects( + new Error('Failed to install DVCLive') + ) + stub(WorkspaceFolders, 'getFirstWorkspaceFolder').returns(cwd) + + const showProgressSpy = spy(window, 'withProgress') + const showErrorSpy = spy(window, 'showErrorMessage') + + await autoInstallDvc() + + expect(showProgressSpy).to.be.called + expect(showErrorSpy).not.to.be.called + expect(mockInstallPackages).to.be.calledOnce + expect(mockInstallPackages).to.be.calledWithExactly( + cwd, + defaultPython, + 'dvclive' + ) + }) + + it('should show an error message if DVC fails to install', async () => { + bypassProgressCloseDelay() + const cwd = __dirname + stub(PythonExtension, 'getPythonExecutionDetails').resolves(undefined) + stub(Python, 'findPythonBin').resolves(defaultPython) + const mockInstallPackages = stub(Python, 'installPackages') + .onFirstCall() + .resolves(undefined) + .onSecondCall() + .rejects(new Error('Network error')) + stub(WorkspaceFolders, 'getFirstWorkspaceFolder').returns(cwd) + + const showProgressSpy = spy(window, 'withProgress') + const showErrorSpy = spy(window, 'showErrorMessage') + const reportProgressErrorSpy = spy(Toast, 'reportProgressError') + + await autoInstallDvc() + + expect(showProgressSpy).to.be.called + expect(showErrorSpy).not.to.be.called + expect(reportProgressErrorSpy).to.be.calledOnce + expect(mockInstallPackages).to.be.calledTwice + expect(mockInstallPackages).to.be.calledWithExactly( + cwd, + defaultPython, + 'dvclive' + ) + expect(mockInstallPackages).to.be.calledWithExactly( + cwd, + defaultPython, + 'dvc' + ) + }) }) }) diff --git a/extension/src/vscode/toast.ts b/extension/src/vscode/toast.ts index d8006950a2..cd4c81579e 100644 --- a/extension/src/vscode/toast.ts +++ b/extension/src/vscode/toast.ts @@ -75,6 +75,22 @@ export class Toast { }) } + static reportProgressError( + error: unknown, + progress: Progress<{ + message?: string | undefined + increment?: number | undefined + }> + ) { + const message = (error as Error)?.message || 'an unexpected error occurred' + + progress.report({ + increment: 0, + message + }) + return Toast.delayProgressClosing(60000) + } + static delayProgressClosing(ms = 5000) { return delay(ms) } From 03019fbbe6a643908e0dfc8ebff9645610d5712b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 1 May 2023 21:54:01 +0000 Subject: [PATCH 11/15] chore(deps): update typescript-eslint monorepo to v5.59.1 (#3795) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 4 +- yarn.lock | 102 +++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 77 insertions(+), 29 deletions(-) diff --git a/package.json b/package.json index 5a52863fa9..322bfb42f9 100644 --- a/package.json +++ b/package.json @@ -34,8 +34,8 @@ "svgr": "yarn workspace dvc-vscode-webview svgr" }, "devDependencies": { - "@typescript-eslint/eslint-plugin": "5.59.0", - "@typescript-eslint/parser": "5.59.0", + "@typescript-eslint/eslint-plugin": "5.59.1", + "@typescript-eslint/parser": "5.59.1", "@vscode/codicons": "0.0.32", "eslint": "8.39.0", "eslint-config-prettier": "8.8.0", diff --git a/yarn.lock b/yarn.lock index 0f482d7dcc..46820f1e67 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5513,15 +5513,15 @@ dependencies: "@types/node" "*" -"@typescript-eslint/eslint-plugin@5.59.0": - version "5.59.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.0.tgz#c0e10eeb936debe5d1c3433cf36206a95befefd0" - integrity sha512-p0QgrEyrxAWBecR56gyn3wkG15TJdI//eetInP3zYRewDh0XS+DhB3VUAd3QqvziFsfaQIoIuZMxZRB7vXYaYw== +"@typescript-eslint/eslint-plugin@5.59.1": + version "5.59.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.1.tgz#9b09ee1541bff1d2cebdcb87e7ce4a4003acde08" + integrity sha512-AVi0uazY5quFB9hlp2Xv+ogpfpk77xzsgsIEWyVS7uK/c7MZ5tw7ZPbapa0SbfkqE0fsAMkz5UwtgMLVk2BQAg== dependencies: "@eslint-community/regexpp" "^4.4.0" - "@typescript-eslint/scope-manager" "5.59.0" - "@typescript-eslint/type-utils" "5.59.0" - "@typescript-eslint/utils" "5.59.0" + "@typescript-eslint/scope-manager" "5.59.1" + "@typescript-eslint/type-utils" "5.59.1" + "@typescript-eslint/utils" "5.59.1" debug "^4.3.4" grapheme-splitter "^1.0.4" ignore "^5.2.0" @@ -5536,14 +5536,14 @@ dependencies: "@typescript-eslint/utils" "5.25.0" -"@typescript-eslint/parser@5.59.0": - version "5.59.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.59.0.tgz#0ad7cd019346cc5d150363f64869eca10ca9977c" - integrity sha512-qK9TZ70eJtjojSUMrrEwA9ZDQ4N0e/AuoOIgXuNBorXYcBDk397D2r5MIe1B3cok/oCtdNC5j+lUUpVB+Dpb+w== +"@typescript-eslint/parser@5.59.1": + version "5.59.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.59.1.tgz#73c2c12127c5c1182d2e5b71a8fa2a85d215cbb4" + integrity sha512-nzjFAN8WEu6yPRDizIFyzAfgK7nybPodMNFGNH0M9tei2gYnYszRDqVA0xlnRjkl7Hkx2vYrEdb6fP2a21cG1g== dependencies: - "@typescript-eslint/scope-manager" "5.59.0" - "@typescript-eslint/types" "5.59.0" - "@typescript-eslint/typescript-estree" "5.59.0" + "@typescript-eslint/scope-manager" "5.59.1" + "@typescript-eslint/types" "5.59.1" + "@typescript-eslint/typescript-estree" "5.59.1" debug "^4.3.4" "@typescript-eslint/scope-manager@5.17.0": @@ -5570,13 +5570,21 @@ "@typescript-eslint/types" "5.59.0" "@typescript-eslint/visitor-keys" "5.59.0" -"@typescript-eslint/type-utils@5.59.0": - version "5.59.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.59.0.tgz#8e8d1420fc2265989fa3a0d897bde37f3851e8c9" - integrity sha512-d/B6VSWnZwu70kcKQSCqjcXpVH+7ABKH8P1KNn4K7j5PXXuycZTPXF44Nui0TEm6rbWGi8kc78xRgOC4n7xFgA== +"@typescript-eslint/scope-manager@5.59.1": + version "5.59.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.59.1.tgz#8a20222719cebc5198618a5d44113705b51fd7fe" + integrity sha512-mau0waO5frJctPuAzcxiNWqJR5Z8V0190FTSqRw1Q4Euop6+zTwHAf8YIXNwDOT29tyUDrQ65jSg9aTU/H0omA== dependencies: - "@typescript-eslint/typescript-estree" "5.59.0" - "@typescript-eslint/utils" "5.59.0" + "@typescript-eslint/types" "5.59.1" + "@typescript-eslint/visitor-keys" "5.59.1" + +"@typescript-eslint/type-utils@5.59.1": + version "5.59.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.59.1.tgz#63981d61684fd24eda2f9f08c0a47ecb000a2111" + integrity sha512-ZMWQ+Oh82jWqWzvM3xU+9y5U7MEMVv6GLioM3R5NJk6uvP47kZ7YvlgSHJ7ERD6bOY7Q4uxWm25c76HKEwIjZw== + dependencies: + "@typescript-eslint/typescript-estree" "5.59.1" + "@typescript-eslint/utils" "5.59.1" debug "^4.3.4" tsutils "^3.21.0" @@ -5595,6 +5603,11 @@ resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.59.0.tgz#3fcdac7dbf923ec5251545acdd9f1d42d7c4fe32" integrity sha512-yR2h1NotF23xFFYKHZs17QJnB51J/s+ud4PYU4MqdZbzeNxpgUr05+dNeCN/bb6raslHvGdd6BFCkVhpPk/ZeA== +"@typescript-eslint/types@5.59.1": + version "5.59.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.59.1.tgz#03f3fedd1c044cb336ebc34cc7855f121991f41d" + integrity sha512-dg0ICB+RZwHlysIy/Dh1SP+gnXNzwd/KS0JprD3Lmgmdq+dJAJnUPe1gNG34p0U19HvRlGX733d/KqscrGC1Pg== + "@typescript-eslint/typescript-estree@5.17.0": version "5.17.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.17.0.tgz#a7cba7dfc8f9cc2ac78c18584e684507df4f2488" @@ -5634,6 +5647,19 @@ semver "^7.3.7" tsutils "^3.21.0" +"@typescript-eslint/typescript-estree@5.59.1": + version "5.59.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.1.tgz#4aa546d27fd0d477c618f0ca00b483f0ec84c43c" + integrity sha512-lYLBBOCsFltFy7XVqzX0Ju+Lh3WPIAWxYpmH/Q7ZoqzbscLiCW00LeYCdsUnnfnj29/s1WovXKh2gwCoinHNGA== + dependencies: + "@typescript-eslint/types" "5.59.1" + "@typescript-eslint/visitor-keys" "5.59.1" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + semver "^7.3.7" + tsutils "^3.21.0" + "@typescript-eslint/utils@5.25.0": version "5.25.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.25.0.tgz#272751fd737733294b4ab95e16c7f2d4a75c2049" @@ -5646,17 +5672,17 @@ eslint-scope "^5.1.1" eslint-utils "^3.0.0" -"@typescript-eslint/utils@5.59.0", "@typescript-eslint/utils@^5.58.0": - version "5.59.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.59.0.tgz#063d066b3bc4850c18872649ed0da9ee72d833d5" - integrity sha512-GGLFd+86drlHSvPgN/el6dRQNYYGOvRSDVydsUaQluwIW3HvbXuxyuD5JETvBt/9qGYe+lOrDk6gRrWOHb/FvA== +"@typescript-eslint/utils@5.59.1": + version "5.59.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.59.1.tgz#d89fc758ad23d2157cfae53f0b429bdf15db9473" + integrity sha512-MkTe7FE+K1/GxZkP5gRj3rCztg45bEhsd8HYjczBuYm+qFHP5vtZmjx3B0yUCDotceQ4sHgTyz60Ycl225njmA== dependencies: "@eslint-community/eslint-utils" "^4.2.0" "@types/json-schema" "^7.0.9" "@types/semver" "^7.3.12" - "@typescript-eslint/scope-manager" "5.59.0" - "@typescript-eslint/types" "5.59.0" - "@typescript-eslint/typescript-estree" "5.59.0" + "@typescript-eslint/scope-manager" "5.59.1" + "@typescript-eslint/types" "5.59.1" + "@typescript-eslint/typescript-estree" "5.59.1" eslint-scope "^5.1.1" semver "^7.3.7" @@ -5672,6 +5698,20 @@ eslint-scope "^5.1.1" eslint-utils "^3.0.0" +"@typescript-eslint/utils@^5.58.0": + version "5.59.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.59.0.tgz#063d066b3bc4850c18872649ed0da9ee72d833d5" + integrity sha512-GGLFd+86drlHSvPgN/el6dRQNYYGOvRSDVydsUaQluwIW3HvbXuxyuD5JETvBt/9qGYe+lOrDk6gRrWOHb/FvA== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@types/json-schema" "^7.0.9" + "@types/semver" "^7.3.12" + "@typescript-eslint/scope-manager" "5.59.0" + "@typescript-eslint/types" "5.59.0" + "@typescript-eslint/typescript-estree" "5.59.0" + eslint-scope "^5.1.1" + semver "^7.3.7" + "@typescript-eslint/visitor-keys@5.17.0": version "5.17.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.17.0.tgz#52daae45c61b0211b4c81b53a71841911e479128" @@ -5696,6 +5736,14 @@ "@typescript-eslint/types" "5.59.0" eslint-visitor-keys "^3.3.0" +"@typescript-eslint/visitor-keys@5.59.1": + version "5.59.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.1.tgz#0d96c36efb6560d7fb8eb85de10442c10d8f6058" + integrity sha512-6waEYwBTCWryx0VJmP7JaM4FpipLsFl9CvYf2foAE8Qh/Y0s+bxWysciwOs0LTBED4JCaNxTZ5rGadB14M6dwA== + dependencies: + "@typescript-eslint/types" "5.59.1" + eslint-visitor-keys "^3.3.0" + "@ungap/promise-all-settled@1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz#aa58042711d6e3275dd37dc597e5d31e8c290a44" From cf1249ef2cb676df64c4ad7078693727b5f3448f Mon Sep 17 00:00:00 2001 From: Matt Seddon <37993418+mattseddon@users.noreply.github.com> Date: Tue, 2 May 2023 08:25:55 +1000 Subject: [PATCH 12/15] Integrate remaining share experiment command with the extension (exp push) (#3781) * integrate remaining share function with the extension (exp push) * self review * rename share to push * remove custom error handling (use offer to show error in output channel) --- README.md | 4 ++ extension/package.json | 15 ++-- extension/src/commands/external.ts | 1 - extension/src/experiments/commands/index.ts | 69 +++++++++++++++---- .../src/experiments/commands/register.ts | 11 ++- extension/src/experiments/webview/messages.ts | 4 +- extension/src/telemetry/constants.ts | 1 - .../src/test/suite/experiments/index.test.ts | 51 +++++++++++--- extension/src/vscode/config.ts | 3 +- extension/src/webview/contract.ts | 4 +- .../src/experiments/components/App.test.tsx | 10 +-- .../components/table/body/RowContextMenu.tsx | 4 +- 12 files changed, 126 insertions(+), 51 deletions(-) diff --git a/README.md b/README.md index d96d24922a..4bfb082df2 100644 --- a/README.md +++ b/README.md @@ -154,6 +154,7 @@ These are the VS Code [settings] available for the Extension: | `dvc.studio.shareExperimentsLive` | Automatically share all new experiment metrics and plots logged with DVCLive to Studio. This option will only take effect once Studio is connected. | | `dvc.focusedProjects` | A subset of paths to the workspace's available DVC projects. Using this option will override project auto-discovery. | | `dvc.doNotShowWalkthroughAfterInstall` | Do not prompt to show the Get Started page after installing. Useful for pre-configured development environments | +| `dvc.doNotRecommendAddStudioToken` | Do not prompt to add a [studio.token] to the global DVC config, which enables automatic sharing of experiments to [Studio]. | | `dvc.doNotRecommendRedHatExtension` | Do not prompt to install the Red Hat YAML extension, which helps with DVC YAML schema validation (`dvc.yaml` and `.dvc` files). | | `dvc.doNotShowCliUnavailable` | Do not warn when the workspace contains a DVC project but the DVC binary is unavailable. | @@ -162,6 +163,9 @@ These are the VS Code [settings] available for the Extension: [python extension]: https://marketplace.visualstudio.com/items?itemName=ms-python.python +[studio.token]: + https://dvc.org/doc/user-guide/project-structure/configuration#studio +[Studio]: https://studio.iterative.ai [workspace level]: https://code.visualstudio.com/docs/getstarted/settings#_workspace-settings diff --git a/extension/package.json b/extension/package.json index fbaa91e797..274f5570eb 100644 --- a/extension/package.json +++ b/extension/package.json @@ -506,8 +506,8 @@ "icon": "$(play)" }, { - "title": "Share to Studio", - "command": "dvc.views.experiments.shareExperimentToStudio", + "title": "Push", + "command": "dvc.views.experiments.pushExperiment", "category": "DVC", "icon": "$(repo-push)" }, @@ -564,6 +564,11 @@ "type": "boolean", "default": null }, + "dvc.doNotRecommendAddStudioToken": { + "description": "Do not prompt to add a studio.token to the global DVC config, which enables automatic sharing of experiments to Studio.", + "type": "boolean", + "default": null + }, "dvc.doNotShowCliUnavailable": { "description": "Do not warn when the workspace contains a DVC project but the DVC binary is unavailable.", "type": "boolean", @@ -599,7 +604,7 @@ "default": null }, "dvc.studio.shareExperimentsLive": { - "description": "Automatically share all new experiment metrics and plots logged with DVCLive to Studio. This option will only take effect once Studio is connected.", + "description": "Automatically share all new experiment metrics and plots logged with DVCLive to Studio. This option will only take effect once Studio is connected (studio.token is set).", "type": "boolean", "default": false } @@ -883,7 +888,7 @@ "when": "false" }, { - "command": "dvc.views.experiments.shareExperimentToStudio", + "command": "dvc.views.experiments.pushExperiment", "when": "false" }, { @@ -1147,7 +1152,7 @@ "when": "view == dvc.views.experimentsTree && dvc.commands.available && viewItem =~ /^(experiment|queued)$/ && !dvc.experiment.running" }, { - "command": "dvc.views.experiments.shareExperimentToStudio", + "command": "dvc.views.experiments.pushExperiment", "group": "1_share@0", "when": "view == dvc.views.experimentsTree && dvc.commands.available && viewItem == experiment && !dvc.experiment.running" }, diff --git a/extension/src/commands/external.ts b/extension/src/commands/external.ts index 78ed077eec..1e951c0d54 100644 --- a/extension/src/commands/external.ts +++ b/extension/src/commands/external.ts @@ -100,7 +100,6 @@ export enum RegisteredCommands { ADD_STUDIO_ACCESS_TOKEN = 'dvc.addStudioAccessToken', UPDATE_STUDIO_ACCESS_TOKEN = 'dvc.updateStudioAccessToken', REMOVE_STUDIO_ACCESS_TOKEN = 'dvc.removeStudioAccessToken', - EXPERIMENT_VIEW_SHARE_TO_STUDIO = 'dvc.views.experiments.shareExperimentToStudio', SETUP_SHOW_STUDIO_CONNECT = 'dvc.showStudioConnect', SETUP_SHOW_STUDIO_SETTINGS = 'dvc.showStudioSettings', diff --git a/extension/src/experiments/commands/index.ts b/extension/src/experiments/commands/index.ts index 1ccf85db11..fac0b50f0b 100644 --- a/extension/src/experiments/commands/index.ts +++ b/extension/src/experiments/commands/index.ts @@ -3,6 +3,13 @@ import { AvailableCommands, InternalCommands } from '../../commands/internal' import { Toast } from '../../vscode/toast' import { WorkspaceExperiments } from '../workspace' import { Setup } from '../../setup' +import { Response } from '../../vscode/response' +import { + ConfigKey, + getConfigValue, + setUserConfigValue +} from '../../vscode/config' +import { STUDIO_URL } from '../../setup/webview/contract' import { RegisteredCommands } from '../../commands/external' export const getBranchExperimentCommand = @@ -10,30 +17,62 @@ export const getBranchExperimentCommand = (cwd: string, name: string, input: string) => experiments.runCommand(AvailableCommands.EXP_BRANCH, cwd, name, input) -export const getShareExperimentToStudioCommand = +const promptToAddStudioToken = async () => { + const response = await Toast.askShowOrCloseOrNever( + `Experiments can be automatically shared to [Studio](${STUDIO_URL}) by setting the studio.token in your config.` + ) + + if (!response || response === Response.CLOSE) { + return + } + if (response === Response.SHOW) { + return commands.executeCommand(RegisteredCommands.SETUP_SHOW_STUDIO_CONNECT) + } + if (response === Response.NEVER) { + return setUserConfigValue(ConfigKey.DO_NOT_RECOMMEND_ADD_STUDIO_TOKEN, true) + } +} + +const convertUrlTextToLink = (stdout: string) => { + const experimentAtRegex = /\sat\s+(https:\/\/studio\.iterative\.ai\/.*$)/ + const match = stdout.match(experimentAtRegex) + if (!(match?.[0] && match?.[1])) { + return stdout + } + return stdout.replace(match[0], ` in [Studio](${match[1]})`) +} + +export const getPushExperimentCommand = (internalCommands: InternalCommands, setup: Setup) => ({ dvcRoot, id }: { dvcRoot: string; id: string }) => { const studioAccessToken = setup.getStudioAccessToken() - if (!studioAccessToken) { - return commands.executeCommand(RegisteredCommands.SETUP_SHOW) + if ( + !( + getConfigValue(ConfigKey.DO_NOT_RECOMMEND_ADD_STUDIO_TOKEN) || + studioAccessToken + ) + ) { + void promptToAddStudioToken() } - return Toast.showProgress('Sharing', async progress => { + return Toast.showProgress('exp push', async progress => { progress.report({ increment: 0 }) - progress.report({ increment: 25, message: 'Running exp push...' }) - - await Toast.runCommandAndIncrementProgress( - () => - internalCommands.executeCommand( - AvailableCommands.EXP_PUSH, - dvcRoot, - id - ), - progress, - 75 + progress.report({ increment: 25, message: `Pushing ${id}...` }) + + const remainingProgress = 75 + + const stdout = await internalCommands.executeCommand( + AvailableCommands.EXP_PUSH, + dvcRoot, + id ) + progress.report({ + increment: remainingProgress, + message: convertUrlTextToLink(stdout) + }) + return Toast.delayProgressClosing(15000) }) } diff --git a/extension/src/experiments/commands/register.ts b/extension/src/experiments/commands/register.ts index f7764b3f27..dcb54486dd 100644 --- a/extension/src/experiments/commands/register.ts +++ b/extension/src/experiments/commands/register.ts @@ -1,7 +1,4 @@ -import { - getBranchExperimentCommand, - getShareExperimentToStudioCommand -} from '.' +import { getBranchExperimentCommand, getPushExperimentCommand } from '.' import { pickGarbageCollectionFlags } from '../quickPick' import { WorkspaceExperiments } from '../workspace' import { AvailableCommands, InternalCommands } from '../../commands/internal' @@ -292,9 +289,9 @@ export const registerExperimentCommands = ( experiments.getRepository(dvcRoot).toggleExperimentStatus(id) ) - internalCommands.registerExternalCommand( - RegisteredCommands.EXPERIMENT_VIEW_SHARE_TO_STUDIO, - getShareExperimentToStudioCommand(internalCommands, setup) + internalCommands.registerExternalCliCommand( + RegisteredCliCommands.EXPERIMENT_VIEW_PUSH, + getPushExperimentCommand(internalCommands, setup) ) internalCommands.registerExternalCliCommand( diff --git a/extension/src/experiments/webview/messages.ts b/extension/src/experiments/webview/messages.ts index 7ed3447e97..a43297c480 100644 --- a/extension/src/experiments/webview/messages.ts +++ b/extension/src/experiments/webview/messages.ts @@ -194,9 +194,9 @@ export class WebviewMessages { case MessageFromWebviewType.ADD_CONFIGURATION: { return this.addConfiguration() } - case MessageFromWebviewType.SHARE_EXPERIMENT_TO_STUDIO: + case MessageFromWebviewType.PUSH_EXPERIMENT: return commands.executeCommand( - RegisteredCommands.EXPERIMENT_VIEW_SHARE_TO_STUDIO, + RegisteredCliCommands.EXPERIMENT_VIEW_PUSH, { dvcRoot: this.dvcRoot, id: message.payload } ) diff --git a/extension/src/telemetry/constants.ts b/extension/src/telemetry/constants.ts index f55c7f1a2c..43c15aab45 100644 --- a/extension/src/telemetry/constants.ts +++ b/extension/src/telemetry/constants.ts @@ -144,7 +144,6 @@ export interface IEventNamePropertyMapping { [EventName.EXPERIMENT_VIEW_BRANCH]: undefined [EventName.EXPERIMENT_VIEW_PUSH]: undefined [EventName.EXPERIMENT_VIEW_REMOVE]: undefined - [EventName.EXPERIMENT_VIEW_SHARE_TO_STUDIO]: undefined [EventName.EXPERIMENT_VIEW_SHOW_LOGS]: undefined [EventName.EXPERIMENT_VIEW_STOP]: undefined [EventName.QUEUE_EXPERIMENT]: undefined diff --git a/extension/src/test/suite/experiments/index.test.ts b/extension/src/test/suite/experiments/index.test.ts index 87ea8c710c..8efd84fe9d 100644 --- a/extension/src/test/suite/experiments/index.test.ts +++ b/extension/src/test/suite/experiments/index.test.ts @@ -10,7 +10,8 @@ import { workspace, Uri, QuickPickItem, - ViewColumn + ViewColumn, + CancellationToken } from 'vscode' import { buildExperiments, stubWorkspaceExperimentsGetters } from './util' import { Disposable } from '../../../extension' @@ -34,6 +35,7 @@ import { import { buildInternalCommands, buildMockExperimentsData, + bypassProgressCloseDelay, closeAllEditors, configurationChangeEvent, experimentsUpdatedEvent, @@ -84,6 +86,8 @@ import { DvcReader } from '../../../cli/dvc/reader' import { DvcViewer } from '../../../cli/dvc/viewer' import { DEFAULT_NB_ITEMS_PER_ROW } from '../../../plots/webview/contract' import { GitReader } from '../../../cli/git/reader' +import { Toast } from '../../../vscode/toast' +import { Response } from '../../../vscode/response' const { openFileInEditor } = FileSystem @@ -633,7 +637,7 @@ suite('Experiments Test Suite', () => { ) }).timeout(WEBVIEW_TEST_TIMEOUT) - it('should handle a message to share an experiment to Studio', async () => { + it('should handle a message to push an experiment', async () => { const { experiments } = buildExperiments(disposable) await experiments.isReady() @@ -643,6 +647,7 @@ suite('Experiments Test Suite', () => { const mockMessageReceived = getMessageReceivedEmitter(webview) const executeCommandSpy = spy(commands, 'executeCommand') + const mockExpPush = stub(DvcExecutor.prototype, 'expPush') const mockGetStudioAccessToken = stub( Setup.prototype, @@ -656,15 +661,24 @@ suite('Experiments Test Suite', () => { }) ) + const mockAskShowOrCloseOrNever = stub(Toast, 'askShowOrCloseOrNever') + + const userPrompted = new Promise(resolve => + mockAskShowOrCloseOrNever.callsFake(() => { + resolve(undefined) + return Promise.resolve(Response.SHOW) + }) + ) + mockMessageReceived.fire({ payload: mockExpId, - type: MessageFromWebviewType.SHARE_EXPERIMENT_TO_STUDIO + type: MessageFromWebviewType.PUSH_EXPERIMENT }) - await tokenNotFound + await Promise.all([tokenNotFound, userPrompted]) expect(executeCommandSpy).to.be.calledWithExactly( - RegisteredCommands.SETUP_SHOW + RegisteredCommands.SETUP_SHOW_STUDIO_CONNECT ) mockGetStudioAccessToken.resetBehavior() @@ -675,24 +689,41 @@ suite('Experiments Test Suite', () => { return 'isat_token' }) ) - const mockexpPush = stub(DvcExecutor.prototype, 'expPush') + + const mockShowProgress = stub(Toast, 'showProgress') + bypassProgressCloseDelay() + + const mockReport = stub() + + mockShowProgress.callsFake((title, callback) => { + expect(title).to.equal('exp push') + + const progress = { report: mockReport } + return callback(progress, {} as CancellationToken) + }) + const commandExecuted = new Promise(resolve => - mockexpPush.callsFake(() => { + mockExpPush.callsFake(() => { resolve(undefined) return Promise.resolve( - `Pushed experiment ${mockExpId} to Git remote 'origin'` + "Experiment major-lamb is up to date on Git remote 'origin'.\nView your experiments at \nhttps://studio.iterative.ai/user/mattseddon/projects/vscode-dvc-demo-ynm6t3jxdx" ) }) ) mockMessageReceived.fire({ payload: mockExpId, - type: MessageFromWebviewType.SHARE_EXPERIMENT_TO_STUDIO + type: MessageFromWebviewType.PUSH_EXPERIMENT }) await Promise.all([tokenFound, commandExecuted]) - expect(mockexpPush).to.be.calledWithExactly(dvcDemoPath, mockExpId) + expect(mockExpPush).to.be.calledWithExactly(dvcDemoPath, mockExpId) + expect(mockReport).to.be.calledWithExactly({ + increment: 75, + message: + "Experiment major-lamb is up to date on Git remote 'origin'.\nView your experiments in [Studio](https://studio.iterative.ai/user/mattseddon/projects/vscode-dvc-demo-ynm6t3jxdx)" + }) }).timeout(WEBVIEW_TEST_TIMEOUT) it("should be able to handle a message to modify an experiment's params and queue an experiment", async () => { diff --git a/extension/src/vscode/config.ts b/extension/src/vscode/config.ts index 6dc49acf7a..b69fc47642 100644 --- a/extension/src/vscode/config.ts +++ b/extension/src/vscode/config.ts @@ -1,12 +1,13 @@ import { ConfigurationTarget, workspace } from 'vscode' export enum ConfigKey { + DO_NOT_RECOMMEND_ADD_STUDIO_TOKEN = 'dvc.doNotRecommendAddStudioToken', DO_NOT_RECOMMEND_RED_HAT = 'dvc.doNotRecommendRedHatExtension', DO_NOT_SHOW_CLI_UNAVAILABLE = 'dvc.doNotShowCliUnavailable', DO_NOT_SHOW_WALKTHROUGH_AFTER_INSTALL = 'dvc.doNotShowWalkthroughAfterInstall', + DVC_PATH = 'dvc.dvcPath', EXP_TABLE_HEAD_MAX_HEIGHT = 'dvc.experimentsTableHeadMaxHeight', FOCUSED_PROJECTS = 'dvc.focusedProjects', - DVC_PATH = 'dvc.dvcPath', PYTHON_PATH = 'dvc.pythonPath', STUDIO_SHARE_EXPERIMENTS_LIVE = 'dvc.studio.shareExperimentsLive' } diff --git a/extension/src/webview/contract.ts b/extension/src/webview/contract.ts index bc5eae0756..4ae7329feb 100644 --- a/extension/src/webview/contract.ts +++ b/extension/src/webview/contract.ts @@ -25,6 +25,7 @@ export enum MessageFromWebviewType { OPEN_PLOTS_WEBVIEW = 'open-plots-webview', OPEN_STUDIO = 'open-studio', OPEN_STUDIO_PROFILE = 'open-studio-profile', + PUSH_EXPERIMENT = 'push-experiment', REMOVE_COLUMN_SORT = 'remove-column-sort', REMOVE_EXPERIMENT = 'remove-experiment', REORDER_COLUMNS = 'reorder-columns', @@ -36,7 +37,6 @@ export enum MessageFromWebviewType { RESIZE_COLUMN = 'resize-column', RESIZE_PLOTS = 'resize-plots', SAVE_STUDIO_TOKEN = 'save-studio-token', - SHARE_EXPERIMENT_TO_STUDIO = 'share-experiment-to-studio', SHOW_EXPERIMENT_LOGS = 'show-experiment-logs', STOP_EXPERIMENT = 'stop-experiment', SORT_COLUMN = 'sort-column', @@ -154,7 +154,7 @@ export type MessageFromWebview = } | { type: MessageFromWebviewType.SHOW_EXPERIMENT_LOGS; payload: string } | { - type: MessageFromWebviewType.SHARE_EXPERIMENT_TO_STUDIO + type: MessageFromWebviewType.PUSH_EXPERIMENT payload: string } | { diff --git a/webview/src/experiments/components/App.test.tsx b/webview/src/experiments/components/App.test.tsx index 2c9925e18b..7cb43dc8f1 100644 --- a/webview/src/experiments/components/App.test.tsx +++ b/webview/src/experiments/components/App.test.tsx @@ -862,7 +862,7 @@ describe('App', () => { 'Show Logs', 'Apply to Workspace', 'Create new Branch', - 'Share to Studio', + 'Push', 'Modify and Run', 'Modify and Resume', 'Modify and Queue', @@ -1014,7 +1014,7 @@ describe('App', () => { }) }) - it('should enable the user to share an experiment to Studio', () => { + it('should enable the user to share an experiment', () => { renderTableWithoutRunningExperiments() const target = screen.getByText('4fb124a') @@ -1023,10 +1023,10 @@ describe('App', () => { advanceTimersByTime(100) const menuitems = screen.getAllByRole('menuitem') const itemLabels = menuitems.map(item => item.textContent) - expect(itemLabels).toContain('Share to Studio') + expect(itemLabels).toContain('Push') const shareOption = menuitems.find(item => - item.textContent?.includes('Share to Studio') + item.textContent?.includes('Push') ) expect(shareOption).toBeDefined() @@ -1035,7 +1035,7 @@ describe('App', () => { expect(sendMessage).toHaveBeenCalledWith({ payload: 'exp-e7a67', - type: MessageFromWebviewType.SHARE_EXPERIMENT_TO_STUDIO + type: MessageFromWebviewType.PUSH_EXPERIMENT }) }) diff --git a/webview/src/experiments/components/table/body/RowContextMenu.tsx b/webview/src/experiments/components/table/body/RowContextMenu.tsx index 3778b525c1..0d81ac6947 100644 --- a/webview/src/experiments/components/table/body/RowContextMenu.tsx +++ b/webview/src/experiments/components/table/body/RowContextMenu.tsx @@ -201,8 +201,8 @@ const getSingleSelectMenuOptions = ( MessageFromWebviewType.CREATE_BRANCH_FROM_EXPERIMENT ), hideIfRunningOrNotExperiment( - 'Share to Studio', - MessageFromWebviewType.SHARE_EXPERIMENT_TO_STUDIO, + 'Push', + MessageFromWebviewType.PUSH_EXPERIMENT, true ), ...getRunResumeOptions( From 651c2f685ab6ae610554178b89b0596e56193692 Mon Sep 17 00:00:00 2001 From: Matt Seddon <37993418+mattseddon@users.noreply.github.com> Date: Tue, 2 May 2023 08:38:44 +1000 Subject: [PATCH 13/15] Add multi-select versions of exp push (#3792) --- extension/package.json | 20 +++- extension/src/experiments/commands/index.ts | 6 +- extension/src/experiments/model/collect.ts | 23 +++-- extension/src/experiments/model/tree.ts | 56 +++++++---- extension/src/experiments/webview/messages.ts | 2 +- .../src/test/suite/experiments/index.test.ts | 4 +- .../test/suite/experiments/model/tree.test.ts | 94 +++++++++++++++++++ extension/src/webview/contract.ts | 2 +- .../src/experiments/components/App.test.tsx | 46 +++++++-- .../components/table/body/RowContextMenu.tsx | 25 +++-- 10 files changed, 222 insertions(+), 56 deletions(-) diff --git a/extension/package.json b/extension/package.json index 274f5570eb..5f4d00611e 100644 --- a/extension/package.json +++ b/extension/package.json @@ -511,6 +511,12 @@ "category": "DVC", "icon": "$(repo-push)" }, + { + "title": "Push", + "command": "dvc.views.experimentsTree.pushExperiment", + "category": "DVC", + "icon": "$(repo-push)" + }, { "title": "Show Logs", "command": "dvc.views.experiments.showLogs", @@ -863,6 +869,14 @@ "command": "dvc.views.experiments.branchExperiment", "when": "false" }, + { + "command": "dvc.views.experiments.pushExperiment", + "when": "false" + }, + { + "command": "dvc.views.experimentsTree.pushExperiment", + "when": "false" + }, { "command": "dvc.views.experiments.queueExperiment", "when": "false" @@ -887,10 +901,6 @@ "command": "dvc.views.experiments.resetAndRunCheckpointExperiment", "when": "false" }, - { - "command": "dvc.views.experiments.pushExperiment", - "when": "false" - }, { "command": "dvc.views.experiments.showLogs", "when": "false" @@ -1152,7 +1162,7 @@ "when": "view == dvc.views.experimentsTree && dvc.commands.available && viewItem =~ /^(experiment|queued)$/ && !dvc.experiment.running" }, { - "command": "dvc.views.experiments.pushExperiment", + "command": "dvc.views.experimentsTree.pushExperiment", "group": "1_share@0", "when": "view == dvc.views.experimentsTree && dvc.commands.available && viewItem == experiment && !dvc.experiment.running" }, diff --git a/extension/src/experiments/commands/index.ts b/extension/src/experiments/commands/index.ts index fac0b50f0b..2d43ae759a 100644 --- a/extension/src/experiments/commands/index.ts +++ b/extension/src/experiments/commands/index.ts @@ -44,7 +44,7 @@ const convertUrlTextToLink = (stdout: string) => { export const getPushExperimentCommand = (internalCommands: InternalCommands, setup: Setup) => - ({ dvcRoot, id }: { dvcRoot: string; id: string }) => { + ({ dvcRoot, ids }: { dvcRoot: string; ids: string[] }) => { const studioAccessToken = setup.getStudioAccessToken() if ( !( @@ -58,14 +58,14 @@ export const getPushExperimentCommand = return Toast.showProgress('exp push', async progress => { progress.report({ increment: 0 }) - progress.report({ increment: 25, message: `Pushing ${id}...` }) + progress.report({ increment: 25, message: `Pushing ${ids.join(' ')}...` }) const remainingProgress = 75 const stdout = await internalCommands.executeCommand( AvailableCommands.EXP_PUSH, dvcRoot, - id + ...ids ) progress.report({ diff --git a/extension/src/experiments/model/collect.ts b/extension/src/experiments/model/collect.ts index 35101876e8..94985d1186 100644 --- a/extension/src/experiments/model/collect.ts +++ b/extension/src/experiments/model/collect.ts @@ -328,10 +328,10 @@ export const collectExperiments = ( return acc } -type DeletableExperimentAccumulator = { [dvcRoot: string]: Set } +type ExperimentTypesAccumulator = { [dvcRoot: string]: Set } const initializeAccumulatorRoot = ( - acc: DeletableExperimentAccumulator, + acc: ExperimentTypesAccumulator, dvcRoot: string ) => { if (!acc[dvcRoot]) { @@ -340,12 +340,12 @@ const initializeAccumulatorRoot = ( } const collectExperimentItem = ( - acc: DeletableExperimentAccumulator, - deletable: Set, + acc: ExperimentTypesAccumulator, + types: Set, experimentItem: ExperimentItem ) => { const { dvcRoot, type, id, label } = experimentItem - if (!deletable.has(type)) { + if (!types.has(type)) { return } initializeAccumulatorRoot(acc, dvcRoot) @@ -357,18 +357,17 @@ const collectExperimentItem = ( acc[dvcRoot].add(id) } -export const collectDeletable = ( - experimentItems: (string | ExperimentItem)[] -): DeletableExperimentAccumulator => { - const deletable = new Set([ExperimentType.EXPERIMENT, ExperimentType.QUEUED]) - - const acc: DeletableExperimentAccumulator = {} +export const collectExperimentType = ( + experimentItems: (string | ExperimentItem)[], + types: Set +): ExperimentTypesAccumulator => { + const acc: ExperimentTypesAccumulator = {} for (const experimentItem of experimentItems) { if (typeof experimentItem === 'string') { continue } - collectExperimentItem(acc, deletable, experimentItem) + collectExperimentItem(acc, types, experimentItem) } return acc diff --git a/extension/src/experiments/model/tree.ts b/extension/src/experiments/model/tree.ts index 9f63199817..0b6693468f 100644 --- a/extension/src/experiments/model/tree.ts +++ b/extension/src/experiments/model/tree.ts @@ -9,7 +9,7 @@ import { Uri } from 'vscode' import { ExperimentType } from '.' -import { collectDeletable, ExperimentItem } from './collect' +import { collectExperimentType, ExperimentItem } from './collect' import { MAX_SELECTED_EXPERIMENTS } from './status' import { getDataFromColumnPaths } from './util' import { WorkspaceExperiments } from '../workspace' @@ -69,7 +69,7 @@ export class ExperimentsTree this.experiments = experiments this.resourceLocator = resourceLocator - this.registerWorkaroundCommand() + this.registerWorkaroundCommands() this.updateDescriptionOnChange() } @@ -123,24 +123,44 @@ export class ExperimentsTree ) } - private registerWorkaroundCommand() { + private registerWorkaroundCommands() { + const callCommandWithSelected = async ( + command: + | RegisteredCliCommands.EXPERIMENT_VIEW_REMOVE + | RegisteredCliCommands.EXPERIMENT_VIEW_PUSH, + experimentItem: ExperimentItem | string, + types: ExperimentType[] + ) => { + const selected = [ + ...this.getSelectedExperimentItems(), + experimentItem + ] as (string | ExperimentItem)[] + + const acc = collectExperimentType(selected, new Set(types)) + + for (const [dvcRoot, ids] of Object.entries(acc)) { + await commands.executeCommand(command, { dvcRoot, ids: [...ids] }) + } + } + commands.registerCommand( 'dvc.views.experimentsTree.removeExperiment', - async experimentItem => { - const selected = [ - ...this.getSelectedExperimentItems(), - experimentItem - ] as (string | ExperimentItem)[] - - const deletable = collectDeletable(selected) - - for (const [dvcRoot, ids] of Object.entries(deletable)) { - await commands.executeCommand( - RegisteredCliCommands.EXPERIMENT_VIEW_REMOVE, - { dvcRoot, ids } - ) - } - } + (experimentItem: ExperimentItem) => + callCommandWithSelected( + RegisteredCliCommands.EXPERIMENT_VIEW_REMOVE, + experimentItem, + [ExperimentType.EXPERIMENT, ExperimentType.QUEUED] + ) + ) + + commands.registerCommand( + 'dvc.views.experimentsTree.pushExperiment', + (experimentItem: ExperimentItem) => + callCommandWithSelected( + RegisteredCliCommands.EXPERIMENT_VIEW_PUSH, + experimentItem, + [ExperimentType.EXPERIMENT] + ) ) } diff --git a/extension/src/experiments/webview/messages.ts b/extension/src/experiments/webview/messages.ts index a43297c480..202e8ce289 100644 --- a/extension/src/experiments/webview/messages.ts +++ b/extension/src/experiments/webview/messages.ts @@ -197,7 +197,7 @@ export class WebviewMessages { case MessageFromWebviewType.PUSH_EXPERIMENT: return commands.executeCommand( RegisteredCliCommands.EXPERIMENT_VIEW_PUSH, - { dvcRoot: this.dvcRoot, id: message.payload } + { dvcRoot: this.dvcRoot, ids: message.payload } ) case MessageFromWebviewType.SHOW_EXPERIMENT_LOGS: diff --git a/extension/src/test/suite/experiments/index.test.ts b/extension/src/test/suite/experiments/index.test.ts index 8efd84fe9d..daff6d1ac2 100644 --- a/extension/src/test/suite/experiments/index.test.ts +++ b/extension/src/test/suite/experiments/index.test.ts @@ -671,7 +671,7 @@ suite('Experiments Test Suite', () => { ) mockMessageReceived.fire({ - payload: mockExpId, + payload: [mockExpId], type: MessageFromWebviewType.PUSH_EXPERIMENT }) @@ -712,7 +712,7 @@ suite('Experiments Test Suite', () => { ) mockMessageReceived.fire({ - payload: mockExpId, + payload: [mockExpId], type: MessageFromWebviewType.PUSH_EXPERIMENT }) diff --git a/extension/src/test/suite/experiments/model/tree.test.ts b/extension/src/test/suite/experiments/model/tree.test.ts index c30ae742aa..9b3ad3764b 100644 --- a/extension/src/test/suite/experiments/model/tree.test.ts +++ b/extension/src/test/suite/experiments/model/tree.test.ts @@ -12,6 +12,7 @@ import { ExperimentType } from '../../../../experiments/model' import { UNSELECTED } from '../../../../experiments/model/status' import { bypassProcessManagerDebounce, + bypassProgressCloseDelay, getFirstArgOfLastCall, getMockNow, getTimeSafeDisposer, @@ -345,6 +346,99 @@ suite('Experiments Tree Test Suite', () => { ) }) + it('should be able to push an experiment with dvc.views.experimentsTree.pushExperiment', async () => { + bypassProgressCloseDelay() + const mockExperimentId = 'exp-to-push' + const mockExperiment = { + dvcRoot: dvcDemoPath, + id: mockExperimentId, + type: ExperimentType.EXPERIMENT + } + + const mockExpPush = stub(DvcExecutor.prototype, 'expPush').resolves('') + + stubPrivatePrototypeMethod( + ExperimentsTree, + 'getSelectedExperimentItems' + ).returns([mockExperiment]) + + await commands.executeCommand( + 'dvc.views.experimentsTree.pushExperiment', + mockExperiment + ) + + expect(mockExpPush).to.be.calledWithExactly(dvcDemoPath, mockExperimentId) + }) + + it('should be able to push the provided experiment with dvc.views.experimentsTree.pushExperiment (if no experiments are selected)', async () => { + bypassProgressCloseDelay() + const mockExperiment = 'exp-to-push' + + const mockExpPush = stub(DvcExecutor.prototype, 'expPush').resolves('') + + stubPrivatePrototypeMethod( + ExperimentsTree, + 'getSelectedExperimentItems' + ).returns([]) + + await commands.executeCommand( + 'dvc.views.experimentsTree.pushExperiment', + { + dvcRoot: dvcDemoPath, + id: mockExperiment, + type: ExperimentType.EXPERIMENT + } + ) + + expect(mockExpPush).to.be.calledWithExactly(dvcDemoPath, mockExperiment) + }) + + it('should be able to push multiple experiments with dvc.views.experimentsTree.pushExperiment', async () => { + bypassProgressCloseDelay() + const mockFirstExperimentId = 'first-exp-pushed' + const mockSecondExperimentId = 'second-exp-pushed' + const mockQueuedExperimentLabel = 'queued-excluded' + + const mockExpPush = stub(DvcExecutor.prototype, 'expPush').resolves('') + + stubPrivatePrototypeMethod( + ExperimentsTree, + 'getSelectedExperimentItems' + ).returns([ + dvcDemoPath, + { + dvcRoot: dvcDemoPath, + label: mockQueuedExperimentLabel, + type: ExperimentType.QUEUED + }, + { + dvcRoot: dvcDemoPath, + id: mockFirstExperimentId, + type: ExperimentType.EXPERIMENT + }, + { + dvcRoot: dvcDemoPath, + id: 'workspace-excluded', + type: ExperimentType.WORKSPACE + } + ]) + + await commands.executeCommand( + 'dvc.views.experimentsTree.pushExperiment', + { + dvcRoot: dvcDemoPath, + id: mockSecondExperimentId, + type: ExperimentType.EXPERIMENT + } + ) + + expect(mockExpPush).to.be.calledWithExactly( + dvcDemoPath, + mockFirstExperimentId, + mockSecondExperimentId + ) + }) + it('should be able to apply an experiment to the workspace with dvc.views.experiments.applyExperiment', async () => { const { experiments } = buildExperiments(disposable) diff --git a/extension/src/webview/contract.ts b/extension/src/webview/contract.ts index 4ae7329feb..3c712cf445 100644 --- a/extension/src/webview/contract.ts +++ b/extension/src/webview/contract.ts @@ -155,7 +155,7 @@ export type MessageFromWebview = | { type: MessageFromWebviewType.SHOW_EXPERIMENT_LOGS; payload: string } | { type: MessageFromWebviewType.PUSH_EXPERIMENT - payload: string + payload: string[] } | { type: MessageFromWebviewType.REMOVE_COLUMN_SORT diff --git a/webview/src/experiments/components/App.test.tsx b/webview/src/experiments/components/App.test.tsx index 7cb43dc8f1..cd2f6d14c2 100644 --- a/webview/src/experiments/components/App.test.tsx +++ b/webview/src/experiments/components/App.test.tsx @@ -801,6 +801,7 @@ describe('App', () => { }) }) + // eslint-disable-next-line sonarjs/cognitive-complexity describe('Row Context Menu', () => { beforeAll(() => { jest.useFakeTimers() @@ -932,7 +933,7 @@ describe('App', () => { expect(itemLabels).toContain('Remove') }) - it('should present the Remove option if multiple checkpoint tip rows are selected', () => { + it('should present the remove option if only experiments are selected', () => { renderTableWithoutRunningExperiments() clickRowCheckbox('4fb124a') @@ -944,10 +945,10 @@ describe('App', () => { advanceTimersByTime(100) const menuitems = screen.getAllByRole('menuitem') const itemLabels = menuitems.map(item => item.textContent) - expect(itemLabels).toContain('Remove Selected Rows') + expect(itemLabels).toContain('Remove Selected') const removeOption = menuitems.find(item => - item.textContent?.includes('Remove Selected Rows') + item.textContent?.includes('Remove Selected') ) expect(removeOption).toBeDefined() @@ -960,6 +961,34 @@ describe('App', () => { }) }) + it('should present the push option if only experiments are selected', () => { + renderTableWithoutRunningExperiments() + + clickRowCheckbox('4fb124a') + clickRowCheckbox('42b8736') + + const target = screen.getByText('4fb124a') + fireEvent.contextMenu(target, { bubbles: true }) + + advanceTimersByTime(100) + const menuitems = screen.getAllByRole('menuitem') + const itemLabels = menuitems.map(item => item.textContent) + expect(itemLabels).toContain('Push Selected') + + const pushOption = menuitems.find(item => + item.textContent?.includes('Push Selected') + ) + + expect(pushOption).toBeDefined() + + pushOption && fireEvent.click(pushOption) + + expect(sendMessage).toHaveBeenCalledWith({ + payload: ['exp-e7a67', 'test-branch'], + type: MessageFromWebviewType.PUSH_EXPERIMENT + }) + }) + it('should present the stop option if only running experiments are selected', () => { renderTable() @@ -1034,7 +1063,7 @@ describe('App', () => { shareOption && fireEvent.click(shareOption) expect(sendMessage).toHaveBeenCalledWith({ - payload: 'exp-e7a67', + payload: ['exp-e7a67'], type: MessageFromWebviewType.PUSH_EXPERIMENT }) }) @@ -1106,8 +1135,13 @@ describe('App', () => { fireEvent.contextMenu(target, { bubbles: true }) advanceTimersByTime(100) - const clearOption = screen.getByText('Clear row selection') - fireEvent.click(clearOption) + + const menuitems = screen.getAllByRole('menuitem') + + const clearOption = menuitems.find(item => + item.textContent?.includes('Clear') + ) + clearOption && fireEvent.click(clearOption) advanceTimersByTime(100) expect(selectedRows().length).toBe(0) diff --git a/webview/src/experiments/components/table/body/RowContextMenu.tsx b/webview/src/experiments/components/table/body/RowContextMenu.tsx index 0d81ac6947..f2000e5fcd 100644 --- a/webview/src/experiments/components/table/body/RowContextMenu.tsx +++ b/webview/src/experiments/components/table/body/RowContextMenu.tsx @@ -50,12 +50,12 @@ const getMultiSelectMenuOptions = ( const selectedIds = selectedRowsList.map(value => value.row.original.id) - const removableRowIds = selectedRowsList + const experimentRowIds = selectedRowsList .filter(value => value.row.depth === 1) .map(value => value.row.original.id) - const hideRemoveOption = - removableRowIds.length !== selectedRowsList.length || hasRunningExperiment + const hideExperimentOnlyOption = + experimentRowIds.length !== selectedRowsList.length || hasRunningExperiment const stoppableRows = selectedRowsList .filter(value => isRunning(value.row.original.status)) @@ -105,17 +105,24 @@ const getMultiSelectMenuOptions = ( true ), experimentMenuOption( - removableRowIds, - 'Remove Selected Rows', + experimentRowIds, + 'Push Selected', + MessageFromWebviewType.PUSH_EXPERIMENT, + hideExperimentOnlyOption, + true + ), + experimentMenuOption( + experimentRowIds, + 'Remove Selected', MessageFromWebviewType.REMOVE_EXPERIMENT, - hideRemoveOption, + hideExperimentOnlyOption, true ), { divider: true, id: 'clear-selection', keyboardShortcut: 'Esc', - label: 'Clear row selection' + label: 'Clear' } ] } @@ -200,9 +207,11 @@ const getSingleSelectMenuOptions = ( 'Create new Branch', MessageFromWebviewType.CREATE_BRANCH_FROM_EXPERIMENT ), - hideIfRunningOrNotExperiment( + experimentMenuOption( + [id], 'Push', MessageFromWebviewType.PUSH_EXPERIMENT, + isNotExperiment, true ), ...getRunResumeOptions( From cad154d074f339034f070ecb487c672fd0f42d8c Mon Sep 17 00:00:00 2001 From: Matt Seddon <37993418+mattseddon@users.noreply.github.com> Date: Tue, 2 May 2023 08:51:57 +1000 Subject: [PATCH 14/15] Add push experiment(s) to the command palette (#3793) --- extension/package.json | 9 ++ extension/src/commands/external.ts | 1 + .../src/experiments/commands/register.ts | 10 +- extension/src/experiments/index.ts | 8 ++ extension/src/experiments/workspace.ts | 18 +++ extension/src/telemetry/constants.ts | 1 + .../test/suite/experiments/workspace.test.ts | 109 +++++++++++++++++- extension/src/vscode/title.ts | 3 +- 8 files changed, 155 insertions(+), 4 deletions(-) diff --git a/extension/package.json b/extension/package.json index 5f4d00611e..403c423337 100644 --- a/extension/package.json +++ b/extension/package.json @@ -296,6 +296,11 @@ "light": "resources/light/queue-experiment.svg" } }, + { + "title": "Push Experiment(s)", + "command": "dvc.pushExperiments", + "category": "DVC" + }, { "title": "Remove Experiment(s)", "command": "dvc.removeExperiments", @@ -761,6 +766,10 @@ "command": "dvc.modifyExperimentParamsResetAndRun", "when": "dvc.commands.available && dvc.project.available && !dvc.experiment.running && dvc.experiment.checkpoints" }, + { + "command": "dvc.pushExperiments", + "when": "dvc.commands.available && dvc.project.available && !dvc.experiment.running" + }, { "command": "dvc.queueExperiment", "when": "dvc.commands.available && dvc.project.available && !dvc.experiment.running" diff --git a/extension/src/commands/external.ts b/extension/src/commands/external.ts index 1e951c0d54..d3de70e56b 100644 --- a/extension/src/commands/external.ts +++ b/extension/src/commands/external.ts @@ -2,6 +2,7 @@ export enum RegisteredCliCommands { EXPERIMENT_APPLY = 'dvc.applyExperiment', EXPERIMENT_BRANCH = 'dvc.branchExperiment', EXPERIMENT_GARBAGE_COLLECT = 'dvc.experimentGarbageCollect', + EXPERIMENT_PUSH = 'dvc.pushExperiments', EXPERIMENT_REMOVE = 'dvc.removeExperiments', EXPERIMENT_REMOVE_QUEUE = 'dvc.removeExperimentQueue', EXPERIMENT_RESET_AND_RUN = 'dvc.resetAndRunCheckpointExperiment', diff --git a/extension/src/experiments/commands/register.ts b/extension/src/experiments/commands/register.ts index dcb54486dd..5625d73ca5 100644 --- a/extension/src/experiments/commands/register.ts +++ b/extension/src/experiments/commands/register.ts @@ -160,7 +160,8 @@ const registerExperimentInputCommands = ( const registerExperimentQuickPickCommands = ( experiments: WorkspaceExperiments, - internalCommands: InternalCommands + internalCommands: InternalCommands, + setup: Setup ): void => { internalCommands.registerExternalCliCommand( RegisteredCliCommands.EXPERIMENT_GARBAGE_COLLECT, @@ -220,6 +221,11 @@ const registerExperimentQuickPickCommands = ( () => experiments.selectQueueTasksToKill() ) + internalCommands.registerExternalCliCommand( + RegisteredCliCommands.EXPERIMENT_PUSH, + () => experiments.selectExperimentsToPush(setup) + ) + internalCommands.registerExternalCliCommand( RegisteredCliCommands.EXPERIMENT_REMOVE, () => experiments.selectExperimentsToRemove() @@ -280,7 +286,7 @@ export const registerExperimentCommands = ( registerExperimentCwdCommands(experiments, internalCommands) registerExperimentNameCommands(experiments, internalCommands) registerExperimentInputCommands(experiments, internalCommands) - registerExperimentQuickPickCommands(experiments, internalCommands) + registerExperimentQuickPickCommands(experiments, internalCommands, setup) registerExperimentRunCommands(experiments, internalCommands, setup) internalCommands.registerExternalCommand( diff --git a/extension/src/experiments/index.ts b/extension/src/experiments/index.ts index c48904b564..def9421933 100644 --- a/extension/src/experiments/index.ts +++ b/extension/src/experiments/index.ts @@ -401,6 +401,14 @@ export class Experiments extends BaseRepository { ) } + public pickExperimentsToPush() { + return pickExperiments( + this.experiments.getExperiments(), + this.getFirstThreeColumnOrder(), + Title.SELECT_EXPERIMENTS_PUSH + ) + } + public async pickAndModifyParams(overrideId?: string) { const id = await this.getExperimentId(overrideId) if (!id) { diff --git a/extension/src/experiments/workspace.ts b/extension/src/experiments/workspace.ts index 1bc63ce95e..945fce327b 100644 --- a/extension/src/experiments/workspace.ts +++ b/extension/src/experiments/workspace.ts @@ -1,6 +1,7 @@ import { EventEmitter, Memento } from 'vscode' import isEmpty from 'lodash.isempty' import { Experiments, ModifiedExperimentAndRunCommandId } from '.' +import { getPushExperimentCommand } from './commands' import { TableData } from './webview/contract' import { Args } from '../cli/dvc/constants' import { @@ -9,6 +10,7 @@ import { InternalCommands } from '../commands/internal' import { ResourceLocator } from '../resourceLocator' +import { Setup } from '../setup' import { Toast } from '../vscode/toast' import { getInput, @@ -172,6 +174,22 @@ export class WorkspaceExperiments extends BaseWorkspaceWebviews< return this.runCommand(AvailableCommands.QUEUE_KILL, cwd, ...taskIds) } + public async selectExperimentsToPush(setup: Setup) { + const dvcRoot = await this.getFocusedOrOnlyOrPickProject() + if (!dvcRoot) { + return + } + + const ids = await this.getRepository(dvcRoot).pickExperimentsToPush() + if (!ids || isEmpty(ids)) { + return + } + + const pushCommand = getPushExperimentCommand(this.internalCommands, setup) + + return pushCommand({ dvcRoot, ids }) + } + public async selectExperimentsToRemove() { const cwd = await this.getFocusedOrOnlyOrPickProject() if (!cwd) { diff --git a/extension/src/telemetry/constants.ts b/extension/src/telemetry/constants.ts index 43c15aab45..a22a2302ac 100644 --- a/extension/src/telemetry/constants.ts +++ b/extension/src/telemetry/constants.ts @@ -127,6 +127,7 @@ export interface IEventNamePropertyMapping { [EventName.EXPERIMENT_FILTERS_REMOVE_ALL]: undefined [EventName.EXPERIMENT_GARBAGE_COLLECT]: undefined [EventName.EXPERIMENT_METRICS_AND_PARAMS_TOGGLE]: undefined + [EventName.EXPERIMENT_PUSH]: undefined [EventName.EXPERIMENT_REMOVE]: undefined [EventName.EXPERIMENT_REMOVE_QUEUE]: undefined [EventName.EXPERIMENT_RESUME]: undefined diff --git a/extension/src/test/suite/experiments/workspace.test.ts b/extension/src/test/suite/experiments/workspace.test.ts index 38ef50b874..47cb36997f 100644 --- a/extension/src/test/suite/experiments/workspace.test.ts +++ b/extension/src/test/suite/experiments/workspace.test.ts @@ -13,6 +13,7 @@ import { Experiments } from '../../../experiments' import * as QuickPick from '../../../vscode/quickPick' import { DvcExecutor } from '../../../cli/dvc/executor' import { + bypassProgressCloseDelay, closeAllEditors, getInputBoxEvent, getTimeSafeDisposer, @@ -657,6 +658,112 @@ suite('Workspace Experiments Test Suite', () => { }) }) + describe('dvc.pushExperiments', () => { + it('should ask the user to pick experiment(s) and then push selected ones to the remote', async () => { + bypassProgressCloseDelay() + const mockExperimentId = 'exp-e7a67' + const secondMockExperimentId = 'exp-83425' + type QuickPickReturnValue = QuickPickItemWithValue[] + stub(Setup.prototype, 'getStudioAccessToken').returns('isat_token') + + const { experiments } = buildExperiments(disposable) + + await experiments.isReady() + + stubWorkspaceExperimentsGetters(dvcDemoPath, experiments) + + const mockShowQuickPick = stub(window, 'showQuickPick') as SinonStub< + [items: readonly QuickPickItem[], options: QuickPickOptionsWithTitle], + Thenable + > + + mockShowQuickPick + .onFirstCall() + .resolves([ + { + value: mockExperimentId + } + ] as QuickPickReturnValue) + .onSecondCall() + .resolves([ + { + value: mockExperimentId + }, + { + value: secondMockExperimentId + } + ] as QuickPickReturnValue) + const mockExpPush = stub(DvcExecutor.prototype, 'expPush') + + await commands.executeCommand(RegisteredCliCommands.EXPERIMENT_PUSH) + expect(mockShowQuickPick).calledWithExactly( + [ + { + description: '[exp-e7a67]', + detail: `Created:${formatDate( + '2020-12-29T15:31:52' + )}, loss:2.0205045, accuracy:0.37241668`, + label: '4fb124a', + value: 'exp-e7a67' + }, + { + description: '[test-branch]', + detail: `Created:${formatDate( + '2020-12-29T15:28:59' + )}, loss:1.9293040, accuracy:0.46680000`, + label: '42b8736', + value: 'test-branch' + }, + { + description: '[exp-83425]', + detail: `Created:${formatDate( + '2020-12-29T15:27:02' + )}, loss:1.7750162, accuracy:0.59265000`, + label: EXPERIMENT_WORKSPACE_ID, + value: 'exp-83425' + }, + { + description: undefined, + detail: 'Created:-, loss:-, accuracy:-', + label: '489fd8b', + value: '489fd8b' + }, + { + description: '[exp-f13bca]', + detail: `Created:${formatDate( + '2020-12-29T15:26:36' + )}, loss:-, accuracy:-`, + label: 'f0f9186', + value: 'exp-f13bca' + }, + { + description: undefined, + detail: `Created:${formatDate( + '2020-12-29T15:25:27' + )}, loss:-, accuracy:-`, + label: '55d492c', + value: '55d492c' + } + ], + { + canPickMany: true, + matchOnDescription: true, + matchOnDetail: true, + title: 'Select Experiment(s) to Push' + } + ) + expect(mockExpPush).to.be.calledWith(dvcDemoPath, mockExperimentId) + + await commands.executeCommand(RegisteredCliCommands.EXPERIMENT_PUSH) + + expect(mockExpPush).to.be.calledWith( + dvcDemoPath, + mockExperimentId, + secondMockExperimentId + ) + }) + }) + describe('dvc.removeExperiments', () => { it('should ask the user to pick experiment(s) and then remove selected ones from the workspace', async () => { const mockExperimentId = 'exp-e7a67' @@ -754,7 +861,7 @@ suite('Workspace Experiments Test Suite', () => { canPickMany: true, matchOnDescription: true, matchOnDetail: true, - title: 'Select Experiments to Remove' + title: 'Select Experiment(s) to Remove' } ) expect(mockExperimentRemove).to.be.calledWith( diff --git a/extension/src/vscode/title.ts b/extension/src/vscode/title.ts index b06a41427f..51398ffb21 100644 --- a/extension/src/vscode/title.ts +++ b/extension/src/vscode/title.ts @@ -17,7 +17,8 @@ export enum Title { SELECT_COLUMNS = 'Select Columns to Display in the Experiments Table', SELECT_EXPERIMENT = 'Select an Experiment', SELECT_EXPERIMENTS = 'Select Experiments', - SELECT_EXPERIMENTS_REMOVE = 'Select Experiments to Remove', + SELECT_EXPERIMENTS_PUSH = 'Select Experiment(s) to Push', + SELECT_EXPERIMENTS_REMOVE = 'Select Experiment(s) to Remove', SELECT_EXPERIMENTS_TO_PLOT = 'Select up to 7 Experiments to Display in Plots', SELECT_FILTERS_TO_REMOVE = 'Select Filter(s) to Remove', SELECT_FOCUSED_PROJECTS = 'Select Project(s) to Focus (set dvc.focusedProjects)', From 2b9f6a76d3326c9df9ff0b4ae83a4401671ad26b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 1 May 2023 23:07:29 +0000 Subject: [PATCH 15/15] chore(deps): update dependency stylelint-config-standard-scss to v9 (#3796) --- package.json | 2 +- yarn.lock | 40 ++++++++++++++++++++-------------------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/package.json b/package.json index 322bfb42f9..bae30c5a6c 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,7 @@ "prettier": "2.8.8", "prettier-config-standard": "5.0.0", "stylelint": "15.6.0", - "stylelint-config-standard-scss": "8.0.0", + "stylelint-config-standard-scss": "9.0.0", "ts-node": "10.9.1", "turbo": "1.9.3", "typescript": "5.0.4" diff --git a/yarn.lock b/yarn.lock index 46820f1e67..dfd3106bd0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -18779,34 +18779,34 @@ style-search@^0.1.0: resolved "https://registry.yarnpkg.com/style-search/-/style-search-0.1.0.tgz#7958c793e47e32e07d2b5cafe5c0bf8e12e77902" integrity sha512-Dj1Okke1C3uKKwQcetra4jSuk0DqbzbYtXipzFlFMZtowbF1x7BKJwB9AayVMyFARvU8EDrZdcax4At/452cAg== -stylelint-config-recommended-scss@^10.0.0: - version "10.0.0" - resolved "https://registry.yarnpkg.com/stylelint-config-recommended-scss/-/stylelint-config-recommended-scss-10.0.0.tgz#06c5c6ad893d2641d7207994de3a5aa2fdcb4078" - integrity sha512-+YvPgUHi0W5mCJCKdupBCIsWPYNbWuJcRmFtSYujwNg+41ljFknhO9bpY6C+oahv659zW7W1AT7i6DQvJYYr1A== +stylelint-config-recommended-scss@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/stylelint-config-recommended-scss/-/stylelint-config-recommended-scss-11.0.0.tgz#7b933ecac99cd3b52d14d1746e3ecd36f421b4b6" + integrity sha512-EDghTDU7aOv2LTsRZvcT1w8mcjUaMhuy+t38iV5I/0Qiu6ixdkRwhLEMul3K/fnB2v9Nwqvb3xpvJfPH+HduDw== dependencies: postcss-scss "^4.0.6" - stylelint-config-recommended "^11.0.0" + stylelint-config-recommended "^12.0.0" stylelint-scss "^4.6.0" -stylelint-config-recommended@^11.0.0: - version "11.0.0" - resolved "https://registry.yarnpkg.com/stylelint-config-recommended/-/stylelint-config-recommended-11.0.0.tgz#b1cb7d71bd92f9b8593f93c2ca6df16ed7d61522" - integrity sha512-SoGIHNI748OCZn6BxFYT83ytWoYETCINVHV3LKScVAWQQauWdvmdDqJC5YXWjpBbxg2E761Tg5aUGKLFOVhEkA== +stylelint-config-recommended@^12.0.0: + version "12.0.0" + resolved "https://registry.yarnpkg.com/stylelint-config-recommended/-/stylelint-config-recommended-12.0.0.tgz#d0993232fca017065fd5acfcb52dd8a188784ef4" + integrity sha512-x6x8QNARrGO2sG6iURkzqL+Dp+4bJorPMMRNPScdvaUK8PsynriOcMW7AFDKqkWAS5wbue/u8fUT/4ynzcmqdQ== -stylelint-config-standard-scss@8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/stylelint-config-standard-scss/-/stylelint-config-standard-scss-8.0.0.tgz#6540562178436346298bd63f67bb3a7d311388f0" - integrity sha512-TDT/gJD/0LUDoUgkjF1uoI/4DfczXHxg7gJVcWT4/JbE6k5hszVuI14reNX+tEwSyMNhcK2BA7izrK+uVAz7XA== +stylelint-config-standard-scss@9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/stylelint-config-standard-scss/-/stylelint-config-standard-scss-9.0.0.tgz#70c66e1179612519fdf6ca1dbff23c804def1b6b" + integrity sha512-yPKpJsrZn4ybuQZx/DkEHuCjw7pJginErE/47dFhCnrvD48IJ4UYec8tSiCuJWMA3HRjbIa3nh5ZeSauDGuVAg== dependencies: - stylelint-config-recommended-scss "^10.0.0" - stylelint-config-standard "^32.0.0" + stylelint-config-recommended-scss "^11.0.0" + stylelint-config-standard "^33.0.0" -stylelint-config-standard@^32.0.0: - version "32.0.0" - resolved "https://registry.yarnpkg.com/stylelint-config-standard/-/stylelint-config-standard-32.0.0.tgz#97179035e967f22a7b7e27f14a74f5d5fc0f0bd6" - integrity sha512-UnGJxYDyYFrIE9CjDMZRkrNh2o4lOtO+MVZ9qG5b8yARfsWho0GMx4YvhHfsv8zKKgHeWX2wfeyxmuoqcaYZ4w== +stylelint-config-standard@^33.0.0: + version "33.0.0" + resolved "https://registry.yarnpkg.com/stylelint-config-standard/-/stylelint-config-standard-33.0.0.tgz#1f7bb299153a53874073e93829e37a475842f0f9" + integrity sha512-eyxnLWoXImUn77+ODIuW9qXBDNM+ALN68L3wT1lN2oNspZ7D9NVGlNHb2QCUn4xDug6VZLsh0tF8NyoYzkgTzg== dependencies: - stylelint-config-recommended "^11.0.0" + stylelint-config-recommended "^12.0.0" stylelint-scss@^4.6.0: version "4.6.0"