Show warning when detected two function apps in the same workspace (#1124)

This commit is contained in:
Eric Jizba 2019-03-26 18:40:42 -07:00 коммит произвёл GitHub
Родитель efea34b476
Коммит 4810e0ebae
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
6 изменённых файлов: 74 добавлений и 47 удалений

Просмотреть файл

@ -734,6 +734,11 @@
"type": "string",
"description": "%azFunc.deploySubpathDescription%"
},
"azureFunctions.projectSubpath": {
"scope": "resource",
"type": "string",
"description": "%azFunc.projectSubpathDescription%"
},
"azureFunctions.showCoreToolsWarning": {
"type": "boolean",
"description": "%azFunc.showCoreToolsWarningDescription%",

Просмотреть файл

@ -31,6 +31,7 @@
"azFunc.appSettings.toggleSlotSetting": "Toggle as Slot Setting",
"azFunc.pickProcess": "Pick Process",
"azFunc.deploySubpathDescription": "The default subpath of a workspace folder to use when deploying. If set, you will not be prompted for the folder path when deploying.",
"azFunc.projectSubpathDescription": "The default subpath of a workspace folder to use for project operations. This is only necessary if you have multiple projects in one workspace. See https://aka.ms/AA4nmfy for more information.",
"azFunc.showCoreToolsWarningDescription": "Show a warning if your installed version of Azure Functions Core Tools is out-of-date.",
"azFunc.showMultiCoreToolsWarningDescription": "Show a warning if multiple installs of Azure Functions Core Tools are detected.",
"azFunc.show64BitWarningDescription": "Show a warning to install a 64-bit version of the Azure Functions Core Tools when you create a .NET Framework project.",

Просмотреть файл

@ -5,12 +5,16 @@
import * as fse from 'fs-extra';
import * as path from 'path';
import { DialogResponses, IActionContext } from 'vscode-azureextensionui';
import { MessageItem } from 'vscode';
import { DialogResponses, IActionContext, IAzureQuickPickItem } from 'vscode-azureextensionui';
import { hostFileName } from '../../constants';
import { ext } from '../../extensionVariables';
import { localize } from '../../localize';
import { getFuncExtensionSetting, updateWorkspaceSetting } from '../../ProjectSettings';
import { createNewProject } from './createNewProject';
const projectSubpathKey: string = 'projectSubpath';
// Use 'host.json' as an indicator that this is a functions project
export async function isFunctionProject(folderPath: string): Promise<boolean> {
return await fse.pathExists(path.join(folderPath, hostFileName));
@ -18,21 +22,49 @@ export async function isFunctionProject(folderPath: string): Promise<boolean> {
/**
* Checks root folder and subFolders one level down
* If a single function project is found, returns that path
* If a single function project is found, returns that path.
* If multiple projects are found, prompt to pick the project.
*/
export async function tryGetFunctionProjectRoot(folderPath: string): Promise<string | undefined> {
if (await isFunctionProject(folderPath)) {
return folderPath;
} else {
const subpaths: string[] = await fse.readdir(folderPath);
const matchingSubpaths: string[] = [];
await Promise.all(subpaths.map(async (subpath: string) => {
if (await isFunctionProject(path.join(folderPath, subpath))) {
matchingSubpaths.push(subpath);
export async function tryGetFunctionProjectRoot(folderPath: string, suppressPrompt: boolean = false): Promise<string | undefined> {
let subpath: string | undefined = getFuncExtensionSetting(projectSubpathKey, folderPath);
if (!subpath) {
if (await isFunctionProject(folderPath)) {
return folderPath;
} else {
const subpaths: string[] = await fse.readdir(folderPath);
const matchingSubpaths: string[] = [];
await Promise.all(subpaths.map(async s => {
if (await isFunctionProject(path.join(folderPath, s))) {
matchingSubpaths.push(s);
}
}));
if (matchingSubpaths.length === 1) {
subpath = matchingSubpaths[0];
} else if (matchingSubpaths.length !== 0 && !suppressPrompt) {
subpath = await promptForProjectSubpath(folderPath, matchingSubpaths);
} else {
return undefined;
}
}));
return matchingSubpaths.length === 1 ? path.join(folderPath, matchingSubpaths[0]) : undefined;
}
}
return path.join(folderPath, subpath);
}
async function promptForProjectSubpath(workspacePath: string, matchingSubpaths: string[]): Promise<string> {
const message: string = localize('detectedMultipleProject', 'Detected multiple function projects in the same workspace folder. You must either set the default or use a multi-root workspace.');
const learnMoreLink: string = 'https://aka.ms/AA4nmfy';
const setDefault: MessageItem = { title: localize('setDefault', 'Set default') };
// No need to check result - cancel will throw a UserCancelledError
await ext.ui.showWarningMessage(message, { learnMoreLink }, setDefault);
const picks: IAzureQuickPickItem<string>[] = matchingSubpaths.map(p => { return { label: p, description: workspacePath, data: p }; });
const placeHolder: string = localize('selectProject', 'Select the default project subpath');
const subpath: string = (await ext.ui.showQuickPick(picks, { placeHolder })).data;
await updateWorkspaceSetting(projectSubpathKey, subpath, workspacePath);
return subpath;
}
/**

Просмотреть файл

@ -8,7 +8,7 @@ import * as os from 'os';
import * as path from 'path';
import * as vscode from 'vscode';
import * as appservice from 'vscode-azureappservice';
import { AzureTreeItem, callWithTelemetryAndErrorHandling, DialogResponses, IActionContext, IAzureUserInput, TelemetryProperties, UserCancelledError } from 'vscode-azureextensionui';
import { AzureTreeItem, callWithTelemetryAndErrorHandling, DialogResponses, IActionContext, TelemetryProperties, UserCancelledError } from 'vscode-azureextensionui';
import { deploySubpathSetting, dotnetPublishTaskLabel, extensionPrefix, extInstallTaskName, javaPackageTaskLabel, packTaskName, preDeployTaskSetting, ProjectLanguage, ProjectRuntime, ScmType } from '../constants';
import { ext } from '../extensionVariables';
import { addLocalFuncTelemetry } from '../funcCoreTools/getLocalFuncCoreToolsVersion';
@ -85,7 +85,7 @@ export async function deploy(this: IActionContext, target?: vscode.Uri | string
}
await verifyWebContentSettings(node, telemetryProperties);
await verifyRuntimeIsCompatible(runtime, ext.ui, ext.outputChannel, client, telemetryProperties);
await verifyRuntimeIsCompatible(runtime, client);
const siteConfig: WebSiteManagementModels.SiteConfigResource = await client.getSiteConfig();
const isZipDeploy: boolean = siteConfig.scmType !== ScmType.LocalGit && siteConfig !== ScmType.GitHub;
@ -248,7 +248,7 @@ async function appendDeploySubpathSetting(targetPath: string): Promise<string> {
/**
* NOTE: If we can't recognize the Azure runtime (aka it's undefined), just assume it's compatible
*/
async function verifyRuntimeIsCompatible(localRuntime: ProjectRuntime, ui: IAzureUserInput, outputChannel: vscode.OutputChannel, client: appservice.SiteClient, telemetryProperties: TelemetryProperties): Promise<void> {
async function verifyRuntimeIsCompatible(localRuntime: ProjectRuntime, client: appservice.SiteClient): Promise<void> {
const appSettings: WebSiteManagementModels.StringDictionary = await client.listApplicationSettings();
if (appSettings.properties) {
const rawAzureRuntime: string = appSettings.properties.FUNCTIONS_EXTENSION_VERSION;
@ -256,20 +256,16 @@ async function verifyRuntimeIsCompatible(localRuntime: ProjectRuntime, ui: IAzur
if (azureRuntime !== undefined && azureRuntime !== localRuntime) {
const message: string = localize('incompatibleRuntime', 'The remote runtime "{0}" is not compatible with your local runtime "{1}".', rawAzureRuntime, localRuntime);
const updateRemoteRuntime: vscode.MessageItem = { title: localize('updateRemoteRuntime', 'Update remote runtime') };
const result: vscode.MessageItem = await ui.showWarningMessage(message, { modal: true }, updateRemoteRuntime, DialogResponses.learnMore, DialogResponses.cancel);
if (result === DialogResponses.learnMore) {
await openUrl('https://aka.ms/azFuncRuntime');
telemetryProperties.cancelStep = 'learnMoreRuntime';
throw new UserCancelledError();
} else {
const newAppSettings: { [key: string]: string } = await getCliFeedAppSettings(localRuntime);
for (const key of Object.keys(newAppSettings)) {
const value: string = newAppSettings[key];
outputChannel.appendLine(localize('updateFunctionRuntime', 'Updating "{0}" to "{1}"...', key, value));
appSettings.properties[key] = value;
}
await client.updateApplicationSettings(appSettings);
const learnMoreLink: string = 'https://aka.ms/azFuncRuntime';
// No need to check result - cancel will throw a UserCancelledError
await ext.ui.showWarningMessage(message, { modal: true, learnMoreLink }, updateRemoteRuntime);
const newAppSettings: { [key: string]: string } = await getCliFeedAppSettings(localRuntime);
for (const key of Object.keys(newAppSettings)) {
const value: string = newAppSettings[key];
ext.outputChannel.appendLine(localize('updateFunctionRuntime', 'Updating "{0}" to "{1}"...', key, value));
appSettings.properties[key] = value;
}
await client.updateApplicationSettings(appSettings);
}
}
}

Просмотреть файл

@ -14,7 +14,6 @@ import { tryGetLocalRuntimeVersion } from '../../funcCoreTools/tryGetLocalRuntim
import { localize } from '../../localize';
import { convertStringToRuntime, getFuncExtensionSetting, updateGlobalSetting, updateWorkspaceSetting } from '../../ProjectSettings';
import { nonNullValue } from '../../utils/nonNull';
import { openUrl } from '../../utils/openUrl';
import { createVirtualEnviornment } from '../createNewProject/ProjectCreateStep/PythonProjectCreateStep';
import { tryGetFunctionProjectRoot } from '../createNewProject/verifyIsProject';
import { initProjectForVSCode } from './initProjectForVSCode';
@ -22,6 +21,8 @@ import { initProjectForVSCode } from './initProjectForVSCode';
export async function verifyVSCodeConfigOnActivate(actionContext: IActionContext, folders: vscode.WorkspaceFolder[] | undefined): Promise<void> {
actionContext.suppressTelemetry = true;
actionContext.properties.isActivationEvent = 'true';
actionContext.suppressErrorDisplay = true; // Swallow errors when verifying debug config. No point in showing an error if we can't understand the project anyways
if (folders) {
for (const folder of folders) {
const workspacePath: string = folder.uri.fsPath;
@ -31,7 +32,6 @@ export async function verifyVSCodeConfigOnActivate(actionContext: IActionContext
if (isInitializedProject(projectPath)) {
actionContext.properties.isInitialized = 'true';
actionContext.suppressErrorDisplay = true; // Swallow errors when verifying debug config. No point in showing an error if we can't understand the project anyways
const projectLanguage: string | undefined = getFuncExtensionSetting(projectLanguageSetting, workspacePath);
actionContext.properties.projectLanguage = projectLanguage;
@ -80,13 +80,11 @@ export async function verifyInitForVSCode(actionContext: IActionContext, fsPath:
async function promptToInitializeProject(workspacePath: string): Promise<boolean> {
const settingKey: string = 'showProjectWarning';
if (getFuncExtensionSetting<boolean>(settingKey)) {
const learnMoreLink: string = 'https://aka.ms/azFuncProject';
const message: string = localize('uninitializedWarning', 'Detected an Azure Functions Project in folder "{0}" that may have been created outside of VS Code. Initialize for optimal use with VS Code?', path.basename(workspacePath));
const result: vscode.MessageItem = await ext.ui.showWarningMessage(message, DialogResponses.yes, DialogResponses.dontWarnAgain, DialogResponses.learnMore);
const result: vscode.MessageItem = await ext.ui.showWarningMessage(message, { learnMoreLink }, DialogResponses.yes, DialogResponses.dontWarnAgain);
if (result === DialogResponses.dontWarnAgain) {
await updateGlobalSetting(settingKey, false);
} else if (result === DialogResponses.learnMore) {
await openUrl('https://aka.ms/azFuncProject');
return await promptToInitializeProject(workspacePath);
} else {
return true;
}
@ -156,17 +154,12 @@ async function verifyJavaDeployConfigIsValid(projectLanguage: ProjectLanguage |
async function promptToUpdateProject(fsPath: string, settingKey: string, message: string, learnMoreLink: string): Promise<boolean> {
if (getFuncExtensionSetting<boolean>(settingKey)) {
const updateConfig: vscode.MessageItem = { title: localize('reinit', 'Reinitialize Project') };
let result: vscode.MessageItem;
do {
result = await ext.ui.showWarningMessage(message, updateConfig, DialogResponses.dontWarnAgain, DialogResponses.learnMore);
if (result === DialogResponses.dontWarnAgain) {
await updateWorkspaceSetting(settingKey, false, fsPath);
} else if (result === DialogResponses.learnMore) {
await openUrl(learnMoreLink);
} else {
return true;
}
} while (result === DialogResponses.learnMore);
const result: vscode.MessageItem = await ext.ui.showWarningMessage(message, { learnMoreLink }, updateConfig, DialogResponses.dontWarnAgain);
if (result === DialogResponses.dontWarnAgain) {
await updateWorkspaceSetting(settingKey, false, fsPath);
} else {
return true;
}
}
return false;

Просмотреть файл

@ -39,7 +39,7 @@ export class FuncTaskProvider implements TaskProvider {
const result: Task[] = [];
if (workspace.workspaceFolders) {
for (const folder of workspace.workspaceFolders) {
const projectRoot: string | undefined = await tryGetFunctionProjectRoot(folder.uri.fsPath);
const projectRoot: string | undefined = await tryGetFunctionProjectRoot(folder.uri.fsPath, true /* suppressPrompt */);
if (projectRoot) {
result.push(getExtensionInstallTask(folder, projectRoot));
const language: string | undefined = getFuncExtensionSetting(projectLanguageSetting, folder.uri.fsPath);