Show warning when detected two function apps in the same workspace (#1124)
This commit is contained in:
Родитель
efea34b476
Коммит
4810e0ebae
|
@ -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);
|
||||
|
|
Загрузка…
Ссылка в новой задаче