Skip to content

Commit

Permalink
Add Git cli calls into output channel (#2225)
Browse files Browse the repository at this point in the history
* add git executor class

* add git reader class

* register git commands as cli commands (offer to show error)

* move constants

* remove git util

* remove arrow functions from new class

* revert exporting of type
  • Loading branch information
mattseddon authored Aug 22, 2022
1 parent e37da4a commit eb156d8
Show file tree
Hide file tree
Showing 37 changed files with 462 additions and 325 deletions.
32 changes: 0 additions & 32 deletions extension/src/cli/command.test.ts

This file was deleted.

17 changes: 8 additions & 9 deletions extension/src/cli/command.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { join } from 'path'
import { Args } from './constants'
import { joinTruthyItems } from '../util/array'

export const getCommandString = (
pythonBinPath: string | undefined,
executable: string,
...args: Args
): string => {
const prefix = pythonBinPath ? join(pythonBinPath, 'python') : undefined
return `${joinTruthyItems([prefix, executable])} ${args.join(' ')}`
export const getCommandString = ({
args,
executable
}: {
args: string[]
executable: string
}): string => {
return `${joinTruthyItems([executable, ...args])}`
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import { EventEmitter } from 'vscode'
import { Disposable, Disposer } from '@hediet/std/disposable'
import { Cli, CliResult, CliStarted, typeCheckCommands } from '.'
import { Command } from './constants'
import { getProcessEnv } from '../env'
import { createProcess } from '../processExecution'
import { getFailingMockedProcess, getMockedProcess } from '../test/util/jest'
import { Config } from '../config'
import { joinEnvPath } from '../util/env'
import { DvcCli } from '.'
import { CliResult, CliStarted, typeCheckCommands } from '..'
import { Command } from '../constants'
import { getProcessEnv } from '../../env'
import { createProcess } from '../../processExecution'
import { getFailingMockedProcess, getMockedProcess } from '../../test/util/jest'
import { Config } from '../../config'
import { joinEnvPath } from '../../util/env'

jest.mock('vscode')
jest.mock('@hediet/std/disposable')
jest.mock('../env')
jest.mock('../processExecution')
jest.mock('../../env')
jest.mock('../../processExecution')

const mockedDisposable = jest.mocked(Disposable)

Expand All @@ -31,7 +32,7 @@ beforeEach(() => {
})

describe('typeCheckCommands', () => {
const cli = { func: jest.fn() } as unknown as Cli
const cli = { func: jest.fn() } as unknown as DvcCli
it('should throw an error when the command is not on the class', () => {
expect(() =>
typeCheckCommands(
Expand All @@ -51,7 +52,7 @@ describe('typeCheckCommands', () => {
})
})

describe('executeProcess', () => {
describe('executeDvcProcess', () => {
it('should pass the correct details to the underlying process given no path to the cli or python binary path', async () => {
const existingPath = joinEnvPath(
'/Users/robot/some/path',
Expand All @@ -62,7 +63,7 @@ describe('executeProcess', () => {
const args = [Command.CHECKOUT]
mockedGetEnv.mockReturnValueOnce(processEnv)
mockedCreateProcess.mockReturnValueOnce(getMockedProcess('done'))
const cli = new Cli(
const cli = new DvcCli(
{
getCliPath: () => undefined,
pythonBinPath: undefined
Expand All @@ -79,7 +80,7 @@ describe('executeProcess', () => {
}
)

await cli.executeProcess(cwd, ...args)
await cli.executeDvcProcess(cwd, ...args)

expect(mockedCreateProcess).toBeCalledWith({
args,
Expand All @@ -101,7 +102,7 @@ describe('executeProcess', () => {
const args = [Command.CHECKOUT]
mockedGetEnv.mockReturnValueOnce(processEnv)
mockedCreateProcess.mockReturnValueOnce(getFailingMockedProcess('I DEED'))
const cli = new Cli(
const cli = new DvcCli(
{
getCliPath: () => '/some/path/to/dvc',
pythonBinPath
Expand All @@ -118,7 +119,7 @@ describe('executeProcess', () => {
}
)

await expect(cli.executeProcess(cwd, ...args)).rejects.toThrow()
await expect(cli.executeDvcProcess(cwd, ...args)).rejects.toThrow()

expect(mockedCreateProcess).toBeCalledWith({
args,
Expand Down
33 changes: 33 additions & 0 deletions extension/src/cli/dvc/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { EventEmitter } from 'vscode'
import { Cli, CliResult, CliStarted } from '..'
import { Config } from '../../config'
import { Args } from '../constants'
import { getOptions } from '../options'

export class DvcCli extends Cli {
public autoRegisteredCommands: string[] = []

protected readonly config: Config

constructor(
config: Config,
emitters?: {
processStarted: EventEmitter<CliStarted>
processCompleted: EventEmitter<CliResult>
}
) {
super(emitters)

this.config = config
}

public executeDvcProcess(cwd: string, ...args: Args): Promise<string> {
const options = getOptions(
this.config.pythonBinPath,
this.config.getCliPath(),
cwd,
...args
)
return this.executeProcess(options)
}
}
6 changes: 3 additions & 3 deletions extension/src/cli/error.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import { ExecutionOptions } from './options'
import { ProcessOptions } from '../processExecution'

export interface MaybeConsoleError extends Error {
stderr?: string
exitCode: number
}

interface CliProcessErrorArgs {
options: ExecutionOptions
options: ProcessOptions
baseError: MaybeConsoleError
message?: string
}

export class CliError extends Error {
public readonly options?: ExecutionOptions
public readonly options?: ProcessOptions
public readonly baseError: Error
public readonly stderr?: string
public readonly exitCode: number | null
Expand Down
9 changes: 5 additions & 4 deletions extension/src/cli/executor.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Cli, typeCheckCommands } from '.'
import { typeCheckCommands } from '.'
import { DvcCli } from './dvc'
import {
Args,
Command,
Expand Down Expand Up @@ -27,7 +28,7 @@ export const autoRegisteredCommands = {
REMOVE: 'remove'
} as const

export class CliExecutor extends Cli {
export class CliExecutor extends DvcCli {
public readonly autoRegisteredCommands = typeCheckCommands(
autoRegisteredCommands,
this
Expand Down Expand Up @@ -130,12 +131,12 @@ export class CliExecutor extends Cli {
}

private executeExperimentProcess(cwd: string, ...args: Args) {
return this.executeProcess(cwd, Command.EXPERIMENT, ...args)
return this.executeDvcProcess(cwd, Command.EXPERIMENT, ...args)
}

private async blockAndExecuteProcess(cwd: string, ...args: Args) {
this.setRunning(true)
const output = await this.executeProcess(cwd, ...args)
const output = await this.executeDvcProcess(cwd, ...args)
this.setRunning(false)
return output
}
Expand Down
40 changes: 40 additions & 0 deletions extension/src/cli/git/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { join } from 'path'

export const DOT_GIT = '.git'
export const DOT_GIT_HEAD = join(DOT_GIT, 'HEAD')
export const DOT_GIT_INDEX = join(DOT_GIT, 'index')
export const GIT_REFS = join(DOT_GIT, 'refs')
export const GIT_LOGS_REFS = join(DOT_GIT, 'logs', 'refs')
export const HEADS_GIT_REFS = join(GIT_REFS, 'heads')

export enum Command {
ADD = 'add',
CLEAN = 'clean',
DIFF = 'diff',
LS_FILES = 'ls-files',
PUSH = 'push',
RESET = 'reset',
REV_PARSE = 'rev-parse'
}

export enum Flag {
DIRECTORIES = '-d',
DIRECTORY = '--directory',
DOT = '.',
EXCLUDE_STANDARD = '--exclude-standard',
FORCE = '-f',
HARD = '--hard',
NAME_ONLY = '--name-only',
NO_EMPTY_DIRECTORY = '--no-empty-directory',
OTHERS = '--others',
QUIET = '-q',
RAW_WITH_NUL = '-z',
SET_UPSTREAM = '--set-upstream',
SHOW_TOPLEVEL = '--show-toplevel'
}

export enum Commit {
HEAD = 'HEAD'
}

export const DEFAULT_REMOTE = 'origin'
57 changes: 57 additions & 0 deletions extension/src/cli/git/executor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { GitCli } from '.'
import { Command, Commit, DEFAULT_REMOTE, Flag } from './constants'
import { getOptions } from './options'
import { typeCheckCommands } from '..'

export const autoRegisteredCommands = {
GIT_PUSH_BRANCH: 'pushBranch',
GIT_RESET_WORKSPACE: 'resetWorkspace',
GIT_STAGE_ALL: 'stageAll',
GIT_UNSTAGE_ALL: 'reset'
} as const

export class GitExecutor extends GitCli {
public readonly autoRegisteredCommands = typeCheckCommands(
autoRegisteredCommands,
this
)

public reset(cwd: string, ...args: (Flag | Commit)[]) {
const options = getOptions(cwd, Command.RESET, ...args)

return this.executeProcess(options)
}

public async resetWorkspace(cwd: string) {
await this.reset(cwd, Flag.HARD, Commit.HEAD)

const options = getOptions(
cwd,
Command.CLEAN,
Flag.FORCE,
Flag.DIRECTORIES,
Flag.QUIET
)

return this.executeProcess(options)
}

public async stageAll(cwd: string) {
const gitRoot = await this.getGitRepositoryRoot(cwd)
const options = getOptions(gitRoot, Command.ADD, Flag.DOT)

return this.executeProcess(options)
}

public pushBranch(cwd: string, branchName: string) {
const options = getOptions(
cwd,
Command.PUSH,
Flag.SET_UPSTREAM,
DEFAULT_REMOTE,
branchName as Commit
)

return this.executeProcess(options)
}
}
11 changes: 11 additions & 0 deletions extension/src/cli/git/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Command, Flag } from './constants'
import { getOptions } from './options'
import { Cli } from '..'

export class GitCli extends Cli {
public getGitRepositoryRoot(cwd: string) {
const options = getOptions(cwd, Command.REV_PARSE, Flag.SHOW_TOPLEVEL)

return this.executeProcess(options)
}
}
11 changes: 11 additions & 0 deletions extension/src/cli/git/options.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Command, Commit, DEFAULT_REMOTE, Flag } from './constants'
import { ProcessOptions } from '../../processExecution'

export const getOptions = (
cwd: string,
...args: (Command | Flag | Commit | typeof DEFAULT_REMOTE)[]
): ProcessOptions => ({
args,
cwd,
executable: 'git'
})
Loading

0 comments on commit eb156d8

Please sign in to comment.