diff --git a/commands/azureCommands/delete-registry.ts b/commands/azureCommands/delete-registry.ts index 3b2918c7a8..42182a940d 100644 --- a/commands/azureCommands/delete-registry.ts +++ b/commands/azureCommands/delete-registry.ts @@ -20,9 +20,9 @@ export async function deleteAzureRegistry(context?: AzureRegistryNode): Promise< if (context) { registry = context.registry; } else { - registry = await quickPickACRRegistry(false, 'Select the registry you want to delete'); + registry = await quickPickACRRegistry(false, undefined, 'Select the registry you want to delete'); } - const shouldDelete = await confirmUserIntent(`Are you sure you want to delete ${registry.name} and its associated images?`); + const shouldDelete = await confirmUserIntent(`Are you sure you want to delete ${registry.name} and its associated images?`, true); if (shouldDelete) { let subscription: SubscriptionModels.Subscription = await acrTools.getSubscriptionFromRegistry(registry); let resourceGroup: string = acrTools.getResourceGroupName(registry); diff --git a/commands/azureCommands/delete-repository.ts b/commands/azureCommands/delete-repository.ts index 9e873e9ddd..864a439dfa 100644 --- a/commands/azureCommands/delete-repository.ts +++ b/commands/azureCommands/delete-repository.ts @@ -25,7 +25,7 @@ export async function deleteRepository(context?: AzureRepositoryNode): Promise { - let registryName: string; let registry: Registry; - let imageName: string; + let imageRequest: string; if (context) { // Right Click - registryName = context.registry.loginServer; registry = context.registry; if (context instanceof AzureImageTagNode) { // Right Click on AzureImageNode - imageName = context.label; + imageRequest = context.label; } else if (context instanceof AzureRepositoryNode) { // Right Click on AzureRepositoryNode - imageName = `${context.label} -a`; // Pull all images in repository + imageRequest = `${context.label} -a`; // Pull all images in repository } else { assert.fail(`Unexpected node type`); } } else { // Command Palette - registry = await quickPicks.quickPickACRRegistry(); - registryName = registry.loginServer; - const repository: Repository = await quickPicks.quickPickACRRepository(registry, 'Select the repository of the image you want to pull'); - const image: AzureImage = await quickPicks.quickPickACRImage(repository, 'Select the image you want to pull'); - imageName = `${repository.name}:${image.tag}`; + registry = await quickPickACRRegistry(); + const repository: Repository = await quickPickACRRepository(registry, 'Select the repository of the image you want to pull.'); + const pullAll = await confirmUserIntent(`Do want to pull all tags from '${repository.name}'.`, false); + if (pullAll) { + imageRequest = `${repository.name} -a`; + } else { + const image: AzureImage = await quickPickACRImage(repository, 'Select the image you want to pull'); + imageRequest = image.toString(); + } } // Using loginCredentials function to get the username and password. This takes care of all users, even if they don't have the Azure CLI - const credentials = await acrTools.getLoginCredentials(registry); - const username = credentials.username; - const password = credentials.password; - await pullImage(registryName, imageName, username, password); + const { username, password } = await acrTools.getLoginCredentials(registry); + await pullImage(registry.loginServer, imageRequest, username, password); } -async function pullImage(registryName: string, imageName: string, username: string, password: string): Promise { - // Check if user is logged into Docker and send appropriate commands to terminal - let result = await isLoggedIntoDocker(registryName); - if (!result.loggedIn) { // If not logged in to Docker - let login: vscode.MessageItem = { title: 'Log in to Docker CLI' }; - let msg = `You are not currently logged in to "${registryName}" in the Docker CLI.`; - let response = await vscode.window.showErrorMessage(msg, login) - if (response !== login) { - throw new UserCancelledError(msg); - } - - await new Promise((resolve, reject) => { - let childProcess = exec(`docker login ${registryName} --username ${username} --password-stdin`, (err, stdout, stderr) => { - ext.outputChannel.append(stdout); - ext.outputChannel.append(stderr); - if (err && err.message.match(/error storing credentials.*The stub received bad data/)) { - // Temporary work-around for this error- same as Azure CLI - // See https://github.com/Azure/azure-cli/issues/4843 - reject(new Error(`In order to log in to the Docker CLI using tokens, you currently need to go to \n${result.configPath} and remove "credsStore": "wincred" from the config.json file, then try again. \nDoing this will disable wincred and cause Docker to store credentials directly in the .docker/config.json file. All registries that are currently logged in will be effectly logged out.`)); - } else if (err) { - reject(err); - } else if (stderr) { - reject(stderr); - } +async function pullImage(loginServer: string, imageRequest: string, username: string, password: string): Promise { + // We can't know if the key is still active. So we login into Docker and send appropriate commands to terminal + let dockerConfigPath: string = path.join(process.env.HOMEPATH, '.docker', 'config.json'); - resolve(); - }); + await new Promise((resolve, reject) => { + const dockerLoginCmd = `docker login ${loginServer} --username ${username} --password-stdin`; + let childProcess = exec(dockerLoginCmd, (err, stdout, stderr) => { + ext.outputChannel.append(`${dockerLoginCmd} xxxxxx\n`); + ext.outputChannel.append(stdout); + ext.outputChannel.append(stderr); + if (err && err.message.match(/error storing credentials.*The stub received bad data/)) { + // Temporary work-around for this error- same as Azure CLI + // See https://github.com/Azure/azure-cli/issues/4843 + reject(new Error(`In order to log in to the Docker CLI using tokens, you currently need to go to \n${dockerConfigPath} and remove "credsStore": "wincred" from the config.json file, then try again. \nDoing this will disable wincred and cause Docker to store credentials directly in the .docker/config.json file. All registries that are currently logged in will be effectly logged out.`)); + } else if (err) { + reject(err); + } else if (stderr) { + reject(stderr); + } - childProcess.stdin.write(password); // Prevents insecure password error - childProcess.stdin.end(); + resolve(); }); - } + + childProcess.stdin.write(password); // Prevents insecure password error + childProcess.stdin.end(); + }); const terminal: vscode.Terminal = ext.terminalProvider.createTerminal("docker pull"); terminal.show(); - terminal.sendText(`docker pull ${registryName}/${imageName}`); + terminal.sendText(`docker pull ${loginServer}/${imageRequest}`); } -async function isLoggedIntoDocker(registryName: string): Promise<{ configPath: string, loggedIn: boolean }> { +async function isLoggedIntoDocker(loginServer: string): Promise<{ configPath: string, loggedIn: boolean }> { let home = process.env.HOMEPATH; let configPath: string = path.join(home, '.docker', 'config.json'); let buffer: Buffer; @@ -100,7 +93,7 @@ async function isLoggedIntoDocker(registryName: string): Promise<{ configPath: s buffer = fse.readFileSync(configPath); }); - let index = buffer.indexOf(registryName); + let index = buffer.indexOf(loginServer); let loggedIn = index >= 0; // Returns -1 if user is not logged into Docker return { configPath, loggedIn }; // Returns object with configuration path and boolean indicating if user was logged in or not } diff --git a/commands/azureCommands/run-task.ts b/commands/azureCommands/run-task.ts index 2d5cee18e1..124e33efc7 100644 --- a/commands/azureCommands/run-task.ts +++ b/commands/azureCommands/run-task.ts @@ -1,5 +1,5 @@ -import { TaskRunRequest } from "azure-arm-containerregistry/lib/models"; import { Registry } from "azure-arm-containerregistry/lib/models"; +import { TaskRunRequest } from "azure-arm-containerregistry/lib/models"; import { ResourceGroup } from "azure-arm-resource/lib/resource/models"; import { Subscription } from "azure-arm-resource/lib/subscription/models"; import vscode = require('vscode'); @@ -29,7 +29,7 @@ export async function runTask(context?: TaskNode): Promise { taskName = context.task.name; } else { // Command Palette subscription = await quickPickSubscription(); - registry = await quickPickACRRegistry(); + registry = await quickPickACRRegistry(false, subscription); resourceGroup = await acrTools.getResourceGroup(registry, subscription); taskName = (await quickPickTask(registry, subscription, resourceGroup)).name; } diff --git a/commands/azureCommands/show-task.ts b/commands/azureCommands/show-task.ts index e515eda150..eedee241d1 100644 --- a/commands/azureCommands/show-task.ts +++ b/commands/azureCommands/show-task.ts @@ -20,7 +20,7 @@ export async function showTaskProperties(context?: TaskNode): Promise { task = context.task.name; } else { // Command palette subscription = await quickPickSubscription(); - registry = await quickPickACRRegistry(); + registry = await quickPickACRRegistry(false, subscription); resourceGroup = await acrTools.getResourceGroup(registry, subscription); task = (await quickPickTask(registry, subscription, resourceGroup)).name; } diff --git a/commands/build-image.ts b/commands/build-image.ts index a999631345..0dfef412dd 100644 --- a/commands/build-image.ts +++ b/commands/build-image.ts @@ -5,73 +5,20 @@ import * as path from "path"; import * as vscode from "vscode"; -import { DialogResponses, IActionContext, UserCancelledError } from "vscode-azureextensionui"; -import { DOCKERFILE_GLOB_PATTERN, YAML_GLOB_PATTERN } from '../dockerExtension'; +import { IActionContext } from "vscode-azureextensionui"; import { delay } from "../explorer/utils/utils"; import { ext } from "../extensionVariables"; import { addImageTaggingTelemetry, getTagFromUserInput } from "./tag-image"; +import { quickPickDockerFileItem } from "./utils/quick-pick-file"; import { quickPickWorkspaceFolder } from "./utils/quickPickWorkspaceFolder"; -async function getFileUris(folder: vscode.WorkspaceFolder, globPattern: string): Promise { - return await vscode.workspace.findFiles(new vscode.RelativePattern(folder, globPattern), undefined, 1000, undefined); -} - -export interface Item extends vscode.QuickPickItem { - relativeFilePath: string; - relativeFolderPath: string; -} - -function createFileItem(rootFolder: vscode.WorkspaceFolder, uri: vscode.Uri): Item { - let relativeFilePath = path.join(".", uri.fsPath.substr(rootFolder.uri.fsPath.length)); - - return { - description: undefined, - relativeFilePath: relativeFilePath, - label: relativeFilePath, - relativeFolderPath: path.dirname(relativeFilePath) - }; -} - -export async function resolveFileItem(rootFolder: vscode.WorkspaceFolder, fileUri: vscode.Uri | undefined, globPattern: string, message: string): Promise { - if (fileUri) { - return createFileItem(rootFolder, fileUri); - } - - let uris: vscode.Uri[] = await getFileUris(rootFolder, globPattern); - - if (!uris || uris.length === 0) { - return undefined; - } else { - let items: Item[] = uris.map(uri => createFileItem(rootFolder, uri)); - if (items.length === 1) { - return items[0]; - } else { - const res: vscode.QuickPickItem = await ext.ui.showQuickPick(items, { placeHolder: message }); - return res; - } - } -} - export async function buildImage(actionContext: IActionContext, dockerFileUri: vscode.Uri | undefined): Promise { const configOptions: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration('docker'); const defaultContextPath = configOptions.get('imageBuildContextPath', ''); - let dockerFileItem: Item | undefined; let rootFolder: vscode.WorkspaceFolder = await quickPickWorkspaceFolder('To build Docker files you must first open a folder or workspace in VS Code.'); - while (!dockerFileItem) { - let resolvedItem: Item | undefined = await resolveFileItem(rootFolder, dockerFileUri, DOCKERFILE_GLOB_PATTERN, 'Choose a Dockerfile to build the image.'); - if (resolvedItem) { - dockerFileItem = resolvedItem; - } else { - let msg = "Couldn't find a Dockerfile in your workspace. Would you like to add Docker files to the workspace?"; - actionContext.properties.cancelStep = msg; - await ext.ui.showWarningMessage(msg, DialogResponses.yes, DialogResponses.cancel); - actionContext.properties.cancelStep = undefined; - await vscode.commands.executeCommand('vscode-docker.configure'); - // Try again - } - } + const dockerFileItem = await quickPickDockerFileItem(actionContext, dockerFileUri, rootFolder); let contextPath: string = dockerFileItem.relativeFolderPath; if (defaultContextPath && defaultContextPath !== '') { diff --git a/commands/utils/SourceArchiveUtility.ts b/commands/utils/SourceArchiveUtility.ts index cdc848846e..9db8d36ac6 100644 --- a/commands/utils/SourceArchiveUtility.ts +++ b/commands/utils/SourceArchiveUtility.ts @@ -1,3 +1,4 @@ + import { ContainerRegistryManagementClient } from 'azure-arm-containerregistry/lib/containerRegistryManagementClient'; import { DockerBuildRequest, FileTaskRunRequest, Registry } from 'azure-arm-containerregistry/lib/models'; import { Subscription } from 'azure-arm-resource/lib/subscription/models'; @@ -11,33 +12,31 @@ import { IActionContext, IAzureQuickPickItem } from 'vscode-azureextensionui'; import { ext } from '../../extensionVariables'; import { getBlobInfo, getResourceGroupName, streamLogs } from "../../utils/Azure/acrTools"; import { AzureUtilityManager } from '../../utils/azureUtilityManager'; -import { Item } from '../build-image'; import { quickPickACRRegistry, quickPickSubscription } from './quick-pick-azure'; -import { quickPickDockerFileItem, quickPickImageName, quickPickYamlFileItem } from './quick-pick-image'; +import { Item, quickPickDockerFileItem, quickPickYamlFileItem } from './quick-pick-file'; +import { quickPickImageName } from './quick-pick-image'; import { quickPickWorkspaceFolder } from './quickPickWorkspaceFolder'; const idPrecision = 6; const vcsIgnoreList = ['.git', '.gitignore', '.bzr', 'bzrignore', '.hg', '.hgignore', '.svn'] -export type runRequestType = - | 'DockerBuildRequest' - | 'FileTaskRunRequest'; - -export async function scheduleRunRequest(fileUri: vscode.Uri, requestType: runRequestType, actionContext?: IActionContext): Promise { +export async function scheduleRunRequest(fileUri: vscode.Uri, requestType: 'DockerBuildRequest' | 'FileTaskRunRequest', actionContext?: IActionContext): Promise { //Acquire information. let rootFolder: vscode.WorkspaceFolder; let fileItem: Item; + let imageName: string; if (requestType === 'DockerBuildRequest') { rootFolder = await quickPickWorkspaceFolder("To quick build Docker files you must first open a folder or workspace in VS Code."); fileItem = await quickPickDockerFileItem(actionContext, fileUri, rootFolder); + imageName = await quickPickImageName(actionContext, rootFolder, fileItem); } else if (requestType === 'FileTaskRunRequest') { rootFolder = await quickPickWorkspaceFolder("To run a task from a .yaml file you must first open a folder or workspace in VS Code."); - fileItem = await quickPickYamlFileItem(fileUri, rootFolder); + fileItem = await quickPickYamlFileItem(fileUri, rootFolder, "To run a task from a .yaml file you must have yaml file in your VS Code workspace."); } else { throw new Error("Run Request Type Currently not supported."); } const subscription: Subscription = await quickPickSubscription(); - const registry: Registry = await quickPickACRRegistry(true); + const registry: Registry = await quickPickACRRegistry(true, subscription); const osPick = ['Linux', 'Windows'].map(item => >{ label: item, data: item }); const osType: string = (await ext.ui.showQuickPick(osPick, { 'canPickMany': false, 'placeHolder': 'Select image base OS' })).data; @@ -53,7 +52,6 @@ export async function scheduleRunRequest(fileUri: vscode.Uri, requestType: runRe let runRequest: DockerBuildRequest | FileTaskRunRequest; if (requestType === 'DockerBuildRequest') { - const imageName: string = await quickPickImageName(actionContext, rootFolder, fileItem); runRequest = { type: requestType, imageNames: [imageName], diff --git a/commands/utils/quick-pick-azure.ts b/commands/utils/quick-pick-azure.ts index 310208ccac..c441997089 100644 --- a/commands/utils/quick-pick-azure.ts +++ b/commands/utils/quick-pick-azure.ts @@ -2,8 +2,8 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See LICENSE.md in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Registry } from 'azure-arm-containerregistry/lib/models'; import * as ContainerModels from 'azure-arm-containerregistry/lib/models'; +import { Registry } from 'azure-arm-containerregistry/lib/models'; import { ResourceGroup } from 'azure-arm-resource/lib/resource/models'; import { Location, Subscription } from 'azure-arm-resource/lib/subscription/models'; import * as opn from 'opn'; @@ -45,9 +45,9 @@ export async function quickPickTask(registry: Registry, subscription: Subscripti return desiredTask.data; } -export async function quickPickACRRegistry(canCreateNew: boolean = false, prompt?: string): Promise { +export async function quickPickACRRegistry(canCreateNew: boolean = false, subscription?: Subscription, prompt?: string): Promise { const placeHolder = prompt ? prompt : 'Select registry'; - let registries = await AzureUtilityManager.getInstance().getRegistries(); + let registries = await AzureUtilityManager.getInstance().getRegistries(subscription); let quickPickRegList = registries.map(reg => >{ label: reg.name, data: reg }); let createNewItem: IAzureQuickPickItem = { label: '+ Create new registry', data: undefined }; @@ -139,7 +139,7 @@ export async function quickPickResourceGroup(canCreateNew?: boolean, subscriptio /** Requests confirmation for an action and returns true only in the case that the user types in yes * @param yesOrNoPrompt Should be a yes or no question */ -export async function confirmUserIntent(yesOrNoPrompt: string): Promise { +export async function confirmUserIntent(yesOrNoPrompt: string, cancelWhenNo: boolean): Promise { let opt: vscode.InputBoxOptions = { ignoreFocusOut: true, placeHolder: 'Enter "Yes"', @@ -149,7 +149,9 @@ export async function confirmUserIntent(yesOrNoPrompt: string): Promise let answer = await ext.ui.showInputBox(opt); answer = answer.toLowerCase(); if (answer === 'yes') { - return answer === 'yes'; + return true; + } else if (!cancelWhenNo && answer === 'no') { + return false; } else { throw new UserCancelledError(); } diff --git a/commands/utils/quick-pick-file.ts b/commands/utils/quick-pick-file.ts new file mode 100644 index 0000000000..183dcd01d7 --- /dev/null +++ b/commands/utils/quick-pick-file.ts @@ -0,0 +1,77 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See LICENSE.md in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as path from "path"; +import vscode = require('vscode'); +import { DialogResponses, IActionContext } from 'vscode-azureextensionui'; +import { DOCKERFILE_GLOB_PATTERN, YAML_GLOB_PATTERN } from '../../dockerExtension'; +import { ext } from '../../extensionVariables'; + +export interface Item extends vscode.QuickPickItem { + relativeFilePath: string; + relativeFolderPath: string; +} + +async function getFileUris(folder: vscode.WorkspaceFolder, globPattern: string): Promise { + return await vscode.workspace.findFiles(new vscode.RelativePattern(folder, globPattern), undefined, 1000, undefined); +} + +function createFileItem(rootFolder: vscode.WorkspaceFolder, uri: vscode.Uri): Item { + let relativeFilePath = path.join(".", uri.fsPath.substr(rootFolder.uri.fsPath.length)); + + return { + description: undefined, + relativeFilePath: relativeFilePath, + label: relativeFilePath, + relativeFolderPath: path.dirname(relativeFilePath) + }; +} + +export async function resolveFileItem(rootFolder: vscode.WorkspaceFolder, fileUri: vscode.Uri | undefined, globPattern: string, message: string): Promise { + if (fileUri) { + return createFileItem(rootFolder, fileUri); + } + + let uris: vscode.Uri[] = await getFileUris(rootFolder, globPattern); + + if (!uris || uris.length === 0) { + return undefined; + } else { + let items: Item[] = uris.map(uri => createFileItem(rootFolder, uri)); + if (items.length === 1) { + return items[0]; + } else { + const res: vscode.QuickPickItem = await ext.ui.showQuickPick(items, { placeHolder: message }); + return res; + } + } +} + +export async function quickPickDockerFileItem(actionContext: IActionContext, dockerFileUri: vscode.Uri | undefined, rootFolder: vscode.WorkspaceFolder): Promise { + let dockerFileItem: Item; + + while (!dockerFileItem) { + let resolvedItem: Item | undefined = await resolveFileItem(rootFolder, dockerFileUri, DOCKERFILE_GLOB_PATTERN, 'Choose a Dockerfile to build.'); + if (resolvedItem) { + dockerFileItem = resolvedItem; + } else { + let msg = "Couldn't find a Dockerfile in your workspace. Would you like to add Docker files to the workspace?"; + actionContext.properties.cancelStep = msg; + await ext.ui.showWarningMessage(msg, DialogResponses.yes, DialogResponses.cancel); + actionContext.properties.cancelStep = undefined; + await vscode.commands.executeCommand('vscode-docker.configure'); + // Try again + } + } + return dockerFileItem; +} + +export async function quickPickYamlFileItem(fileUri: vscode.Uri, rootFolder: vscode.WorkspaceFolder, noYamlFileMessage: string): Promise { + const fileItem: Item = await resolveFileItem(rootFolder, fileUri, YAML_GLOB_PATTERN, 'Choose a .yaml file to run.'); + if (!fileItem) { + throw new Error(noYamlFileMessage); + } + return fileItem; +} diff --git a/commands/utils/quick-pick-image.ts b/commands/utils/quick-pick-image.ts index b03672aa5d..8c334f70f1 100644 --- a/commands/utils/quick-pick-image.ts +++ b/commands/utils/quick-pick-image.ts @@ -6,14 +6,13 @@ import * as Docker from 'dockerode'; import * as path from "path"; import vscode = require('vscode'); -import { DialogResponses, IActionContext, parseError, TelemetryProperties } from 'vscode-azureextensionui'; -import { DOCKERFILE_GLOB_PATTERN, YAML_GLOB_PATTERN } from '../../dockerExtension'; -import { showDockerConnectionError, throwDockerConnectionError } from '../../explorer/utils/dockerConnectionError'; +import { IActionContext, TelemetryProperties } from 'vscode-azureextensionui'; +import { throwDockerConnectionError } from '../../explorer/utils/dockerConnectionError'; import { delay } from '../../explorer/utils/utils'; import { ext } from '../../extensionVariables'; -import { Item, resolveFileItem } from '../build-image'; import { addImageTaggingTelemetry, getTagFromUserInput } from '../tag-image'; import { docker } from './docker-endpoint'; +import { Item } from './quick-pick-file'; export interface ImageItem extends vscode.QuickPickItem { label: string; @@ -112,32 +111,3 @@ export async function quickPickImageName(actionContext: IActionContext, rootFold await ext.context.globalState.update(dockerFileKey, imageName); return imageName; } - -export async function quickPickDockerFileItem(actionContext: IActionContext, dockerFileUri: vscode.Uri | undefined, rootFolder: vscode.WorkspaceFolder): Promise { - let dockerFileItem: Item; - - while (!dockerFileItem) { - let resolvedItem: Item | undefined = await resolveFileItem(rootFolder, dockerFileUri, DOCKERFILE_GLOB_PATTERN, 'Choose a Dockerfile to build.'); - if (resolvedItem) { - dockerFileItem = resolvedItem; - } else { - let msg = "Couldn't find a Dockerfile in your workspace. Would you like to add Docker files to the workspace?"; - actionContext.properties.cancelStep = msg; - await ext.ui.showWarningMessage(msg, DialogResponses.yes, DialogResponses.cancel); - actionContext.properties.cancelStep = undefined; - await vscode.commands.executeCommand('vscode-docker.configure'); - // Try again - } - } - return dockerFileItem; -} - -export async function quickPickYamlFileItem(fileUri: vscode.Uri | undefined, rootFolder: vscode.WorkspaceFolder): Promise { - let fileItem: Item; - - let resolvedItem: Item | undefined = await resolveFileItem(rootFolder, fileUri, YAML_GLOB_PATTERN, 'Choose a .yaml file to run.'); - if (resolvedItem) { - fileItem = resolvedItem; - } - return fileItem; -} diff --git a/package.json b/package.json index 741604303b..0594b02e43 100644 --- a/package.json +++ b/package.json @@ -684,7 +684,7 @@ }, { "command": "vscode-docker.acr.viewLogs", - "title": "View Azure Logs", + "title": "View Task Logs", "category": "Docker" }, { @@ -895,10 +895,10 @@ "@types/request-promise-native": "^1.0.15", "@types/semver": "^5.5.0", "adm-zip": "^0.4.11", - "gulp": "^3.9.1", + "gulp": "^4.0.0", "gulp-decompress": "^2.0.2", "gulp-download": "^0.0.1", - "mocha": "^2.3.3", + "mocha": "^5.2.0", "mocha-junit-reporter": "^1.18.0", "mocha-multi-reporters": "^1.1.7", "tslint": "^5.11.0",