merge main
This commit is contained in:
Коммит
d179f12e41
|
@ -37,7 +37,7 @@ parameters:
|
|||
- name: enableLongRunningTests
|
||||
displayName: Enable Long Running Tests
|
||||
type: boolean
|
||||
default: false
|
||||
default: true
|
||||
|
||||
extends:
|
||||
template: v1/1ES.Official.PipelineTemplate.yml@1esPipelines
|
||||
|
|
|
@ -74,9 +74,9 @@
|
|||
"MOCHA_timeout": "0", // Disable time-outs
|
||||
"DEBUGTELEMETRY": "v",
|
||||
"NODE_DEBUG": "",
|
||||
"ENABLE_LONG_RUNNING_TESTS": "",
|
||||
"FUNC_PATH": "func",
|
||||
"AZFUNC_UPDATE_BACKUP_TEMPLATES": ""
|
||||
"AZFUNC_UPDATE_BACKUP_TEMPLATES": "",
|
||||
"AzCode_EnableLongRunningTestsLocal": "",
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Change Log
|
||||
|
||||
## 1.15.1 - 2024-06-18
|
||||
## 1.15.1 - 2024-06-19
|
||||
|
||||
### Changed
|
||||
* [[4182]](https://github.com/microsoft/vscode-azurefunctions/pull/4182) Display a warning when attempting to deploy a containerized function app
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -2,7 +2,7 @@
|
|||
"name": "vscode-azurefunctions",
|
||||
"displayName": "Azure Functions",
|
||||
"description": "%azureFunctions.description%",
|
||||
"version": "1.15.1",
|
||||
"version": "1.15.2-alpha.0",
|
||||
"publisher": "ms-azuretools",
|
||||
"icon": "resources/azure-functions.png",
|
||||
"aiKey": "0c6ae279ed8443289764825290e4f9e2-1a736e7c-1324-4338-be46-fc2a58ae4d14-7255",
|
||||
|
@ -1191,7 +1191,7 @@
|
|||
"copy-webpack-plugin": "^6.0.3",
|
||||
"eslint": "^8.42.0",
|
||||
"eslint-plugin-import": "^2.27.5",
|
||||
"gulp": "^4.0.2",
|
||||
"gulp": "^5.0.0",
|
||||
"gulp-chmod": "^2.0.0",
|
||||
"gulp-decompress": "^2.0.3",
|
||||
"gulp-filter": "^5.1.0",
|
||||
|
@ -1219,7 +1219,7 @@
|
|||
"@azure/core-client": "^1.7.3",
|
||||
"@azure/core-rest-pipeline": "^1.11.0",
|
||||
"@azure/storage-blob": "^12.5.0",
|
||||
"@microsoft/vscode-azext-azureappservice": "^3.2.1",
|
||||
"@microsoft/vscode-azext-azureappservice": "^3.3.0",
|
||||
"@microsoft/vscode-azext-azureappsettings": "^0.2.1",
|
||||
"@microsoft/vscode-azext-azureutils": "^3.0.0",
|
||||
"@microsoft/vscode-azext-serviceconnector": "^0.1.3",
|
||||
|
|
Двоичный файл не отображается.
Двоичный файл не отображается.
|
@ -1 +1 @@
|
|||
4.75.1
|
||||
4.83.0
|
|
@ -1 +1 @@
|
|||
1.35.0
|
||||
1.36.0
|
|
@ -1 +1 @@
|
|||
4.75.1
|
||||
4.83.0
|
|
@ -1,9 +1,7 @@
|
|||
import { type Site } from "@azure/arm-appservice";
|
||||
import { createHttpHeaders, createPipelineRequest } from "@azure/core-rest-pipeline";
|
||||
import { createGenericClient, uiUtils, type AzExtPipelineResponse, type AzExtRequestPrepareOptions } from "@microsoft/vscode-azext-azureutils";
|
||||
import { getResourceGroupFromId, uiUtils } from "@microsoft/vscode-azext-azureutils";
|
||||
import { callWithTelemetryAndErrorHandling, nonNullProp, nonNullValue, nonNullValueAndProp, type IActionContext, type ISubscriptionContext } from "@microsoft/vscode-azext-utils";
|
||||
import { type AppResource, type AppResourceResolver } from "@microsoft/vscode-azext-utils/hostapi";
|
||||
import { type FunctionAppConfig } from "./commands/createFunctionApp/FunctionAppCreateStep";
|
||||
import { ResolvedFunctionAppResource } from "./tree/ResolvedFunctionAppResource";
|
||||
import { ResolvedContainerizedFunctionAppResource } from "./tree/containerizedFunctionApp/ResolvedContainerizedFunctionAppResource";
|
||||
import { createWebSiteClient } from "./utils/azureClients";
|
||||
|
@ -21,31 +19,26 @@ export class FunctionAppResolver implements AppResourceResolver {
|
|||
if (this.siteCacheLastUpdated < Date.now() - 1000 * 3) {
|
||||
this.siteCache.clear();
|
||||
const sites = await uiUtils.listAllIterator(client.webApps.list());
|
||||
const sites20231201 = await getSites20231201(context, subContext);
|
||||
await Promise.all(sites.map(async (site): Promise<void> => {
|
||||
const id = nonNullProp(site, 'id').toLowerCase();
|
||||
// check for required properties that sometime don't exist in the LIST operation
|
||||
if (!site.defaultHostName) {
|
||||
// if this required property doesn't exist, try getting the full site payload
|
||||
site = await client.webApps.get(nonNullProp(site, 'resourceGroup'), nonNullProp(site, 'name'))
|
||||
this.siteCache.set(id, site);
|
||||
}
|
||||
|
||||
const s = sites20231201.find(s => s.id?.toLowerCase() === site.id?.toLowerCase());
|
||||
this.siteCache.set(id, Object.assign(site, { isFlex: !!s?.properties?.functionAppConfig }));
|
||||
|
||||
}));
|
||||
this.siteCacheLastUpdated = Date.now();
|
||||
for (const site of sites) {
|
||||
this.siteCache.set(resource.id, site);
|
||||
this.siteCacheLastUpdated = Date.now();
|
||||
}
|
||||
}
|
||||
|
||||
const site = this.siteCache.get(nonNullProp(resource, 'id').toLowerCase());
|
||||
let site = this.siteCache.get(nonNullProp(resource, 'id').toLowerCase());
|
||||
// check for required properties that sometime don't exist in the LIST operation
|
||||
if (!site || !site.defaultHostName) {
|
||||
// if this required property doesn't exist, try getting the full site payload
|
||||
site = await client.webApps.get(getResourceGroupFromId(resource.id), resource.name);
|
||||
this.siteCache.set(resource.id, site);
|
||||
}
|
||||
|
||||
if (nonNullValueAndProp(site, 'kind') === 'functionapp,linux,container,azurecontainerapps') {
|
||||
const fullSite = await client.webApps.get(nonNullValueAndProp(site, 'resourceGroup'), nonNullValueAndProp(site, 'name'));
|
||||
return ResolvedContainerizedFunctionAppResource.createResolvedFunctionAppResource(context, subContext, fullSite);
|
||||
}
|
||||
|
||||
return ResolvedFunctionAppResource.createResolvedFunctionAppResource(context, subContext, nonNullValue(site), site?.isFlex);
|
||||
return ResolvedFunctionAppResource.createResolvedFunctionAppResource(context, subContext, nonNullValue(site));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -55,31 +48,3 @@ export class FunctionAppResolver implements AppResourceResolver {
|
|||
&& !resource.kind?.includes('workflowapp'); // exclude logic apps
|
||||
}
|
||||
}
|
||||
|
||||
async function getSites20231201(context: IActionContext, subContext: ISubscriptionContext): Promise<(Site & { properties?: { functionAppConfig: FunctionAppConfig } })[]> {
|
||||
try {
|
||||
const headers = createHttpHeaders({
|
||||
'Content-Type': 'application/json',
|
||||
});
|
||||
|
||||
const armEndpoint = ensureEndingSlash(subContext.environment.resourceManagerEndpointUrl);
|
||||
|
||||
// we need the new api-version to get the functionAppConfig
|
||||
const options: AzExtRequestPrepareOptions = {
|
||||
url: `${armEndpoint}subscriptions/${subContext.subscriptionId}/providers/Microsoft.Web/sites?api-version=2023-12-01`,
|
||||
method: 'GET',
|
||||
headers
|
||||
};
|
||||
|
||||
const client = await createGenericClient(context, subContext);
|
||||
const result = await client.sendRequest(createPipelineRequest(options)) as AzExtPipelineResponse;
|
||||
|
||||
return (result.parsedBody as { value: unknown }).value as (Site & { properties?: { functionAppConfig: FunctionAppConfig } })[] ?? [];
|
||||
} catch (_error) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
function ensureEndingSlash(url: string): string {
|
||||
return url.endsWith('/') ? url : `${url}/`;
|
||||
}
|
||||
|
|
|
@ -4,10 +4,9 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { type NameValuePair, type Site, type SiteConfig, type WebSiteManagementClient } from '@azure/arm-appservice';
|
||||
import { createHttpHeaders, createPipelineRequest, type RequestBodyType } from '@azure/core-rest-pipeline';
|
||||
import { BlobServiceClient } from '@azure/storage-blob';
|
||||
import { ParsedSite, WebsiteOS, type CustomLocation, type IAppServiceWizardContext } from '@microsoft/vscode-azext-azureappservice';
|
||||
import { LocationListStep, createGenericClient, type AzExtPipelineResponse, type AzExtRequestPrepareOptions } from '@microsoft/vscode-azext-azureutils';
|
||||
import { LocationListStep } from '@microsoft/vscode-azext-azureutils';
|
||||
import { AzureWizardExecuteStep, parseError, randomUtils } from '@microsoft/vscode-azext-utils';
|
||||
import { type AppResource } from '@microsoft/vscode-azext-utils/hostapi';
|
||||
import { type Progress } from 'vscode';
|
||||
|
@ -45,9 +44,7 @@ export class FunctionAppCreateStep extends AzureWizardExecuteStep<IFunctionAppWi
|
|||
const siteName: string = nonNullProp(context, 'newSiteName');
|
||||
const rgName: string = nonNullProp(nonNullProp(context, 'resourceGroup'), 'name');
|
||||
|
||||
context.site = context.newFlexSku ?
|
||||
await this.createFlexFunctionApp(context, rgName, siteName, context.newFlexSku) :
|
||||
await this.createFunctionApp(context, rgName, siteName, stack);
|
||||
context.site = await this.createFunctionApp(context, rgName, siteName, stack);
|
||||
context.activityResult = context.site as AppResource;
|
||||
|
||||
const site = new ParsedSite(context.site, context);
|
||||
|
@ -103,26 +100,22 @@ export class FunctionAppCreateStep extends AzureWizardExecuteStep<IFunctionAppWi
|
|||
|
||||
private async getNewFlexSite(context: IFlexFunctionAppWizardContext, sku: Sku): Promise<Site> {
|
||||
const location = await LocationListStep.getLocation(context, webProvider);
|
||||
const site: Site & { properties: FlexFunctionAppProperties } = {
|
||||
const site: Site = {
|
||||
name: context.newSiteName,
|
||||
kind: getSiteKind(context),
|
||||
location: nonNullProp(location, 'name'),
|
||||
properties: {
|
||||
name: context.newSiteName,
|
||||
serverFarmId: context.plan?.id,
|
||||
clientAffinityEnabled: false,
|
||||
siteConfig: await this.getNewSiteConfig(context)
|
||||
},
|
||||
serverFarmId: context.plan?.id,
|
||||
clientAffinityEnabled: false,
|
||||
siteConfig: await this.getNewSiteConfig(context)
|
||||
};
|
||||
|
||||
site.properties.sku = 'FlexConsumption';
|
||||
site.properties.functionAppConfig = {
|
||||
site.functionAppConfig = {
|
||||
deployment: {
|
||||
storage: {
|
||||
type: 'blobContainer',
|
||||
value: `${context.storageAccount?.primaryEndpoints?.blob}app-package-${context.newSiteName?.substring(0, 32)}-${randomUtils.getRandomHexString(7)}`,
|
||||
authentication: {
|
||||
userAssignedIdentityResourceId: null,
|
||||
userAssignedIdentityResourceId: undefined,
|
||||
type: 'StorageAccountConnectionString',
|
||||
storageAccountConnectionStringName: 'DEPLOYMENT_STORAGE_CONNECTION_STRING'
|
||||
}
|
||||
|
@ -136,7 +129,7 @@ export class FunctionAppCreateStep extends AzureWizardExecuteStep<IFunctionAppWi
|
|||
maximumInstanceCount: context.newFlexMaximumInstanceCount ?? sku.maximumInstanceCount.defaultValue,
|
||||
instanceMemoryMB: context.newFlexInstanceMemoryMB ?? sku.instanceMemoryMB.find(im => im.isDefault)?.size ?? 2048,
|
||||
alwaysReady: [],
|
||||
triggers: null
|
||||
triggers: undefined
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -226,39 +219,19 @@ export class FunctionAppCreateStep extends AzureWizardExecuteStep<IFunctionAppWi
|
|||
return newSiteConfig;
|
||||
}
|
||||
|
||||
async createFunctionApp(context: IFunctionAppWizardContext, rgName: string, siteName: string, stack: FullFunctionAppStack): Promise<Site> {
|
||||
async createFunctionApp(context: IFlexFunctionAppWizardContext, rgName: string, siteName: string, stack: FullFunctionAppStack): Promise<Site> {
|
||||
const client: WebSiteManagementClient = await createWebSiteClient(context);
|
||||
return await client.webApps.beginCreateOrUpdateAndWait(rgName, siteName, await this.getNewSite(context, stack));
|
||||
}
|
||||
const site = context.newFlexSku ?
|
||||
await this.getNewFlexSite(context, context.newFlexSku) :
|
||||
await this.getNewSite(context, stack);
|
||||
const result = await client.webApps.beginCreateOrUpdateAndWait(rgName, siteName, site);
|
||||
|
||||
async createFlexFunctionApp(context: IFunctionAppWizardContext, rgName: string, siteName: string, sku: Sku): Promise<Site> {
|
||||
const headers = createHttpHeaders({
|
||||
'Content-Type': 'application/json',
|
||||
});
|
||||
|
||||
const options: AzExtRequestPrepareOptions = {
|
||||
url: `https://management.azure.com/subscriptions/${context.subscriptionId}/resourceGroups/${rgName}/providers/Microsoft.Web/sites/${siteName}?api-version=2023-12-01`,
|
||||
method: 'PUT',
|
||||
body: JSON.stringify(await this.getNewFlexSite(context, sku)) as unknown as RequestBodyType,
|
||||
headers
|
||||
};
|
||||
|
||||
const client = await createGenericClient(context, context);
|
||||
const result = await client.sendRequest(createPipelineRequest(options)) as AzExtPipelineResponse;
|
||||
if (result && result.status >= 200 && result.status < 300) {
|
||||
const site = result.parsedBody as Site & { properties: FlexFunctionAppProperties };
|
||||
if (context.newFlexSku) {
|
||||
const storageConnectionString: string = (await getStorageConnectionString(context)).connectionString;
|
||||
await tryCreateStorageContainer(site, storageConnectionString);
|
||||
const client: WebSiteManagementClient = await createWebSiteClient(context);
|
||||
// the payload for the new API version "2023-12-01" is incompatiable with our current SiteClient so get the old payload
|
||||
try {
|
||||
return await client.webApps.get(rgName, siteName);
|
||||
} catch (_) {
|
||||
// ignore error and fall thru to throw
|
||||
}
|
||||
await tryCreateStorageContainer(result, storageConnectionString);
|
||||
}
|
||||
|
||||
throw new Error(parseError(result.parsedBody).message || localize('failedToCreateFlexFunctionApp', 'Failed to create flex function app "{0}".', siteName));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -280,49 +253,22 @@ function getSiteKind(context: IAppServiceWizardContext): string {
|
|||
}
|
||||
|
||||
// storage container is needed for flex deployment, but it is not created automatically
|
||||
async function tryCreateStorageContainer(site: Site & { properties: FlexFunctionAppProperties }, storageConnectionString: string): Promise<void> {
|
||||
async function tryCreateStorageContainer(site: Site, storageConnectionString: string): Promise<void> {
|
||||
const blobClient = BlobServiceClient.fromConnectionString(storageConnectionString);
|
||||
const containerName = site.properties?.functionAppConfig?.deployment.storage.value.split('/').pop();
|
||||
if (containerName) {
|
||||
const client = blobClient.getContainerClient(containerName);
|
||||
if (!await client.exists()) {
|
||||
await blobClient.createContainer(containerName);
|
||||
const containerUrl: string | undefined = site.functionAppConfig?.deployment?.storage?.value;
|
||||
if (containerUrl) {
|
||||
const containerName = containerUrl.split('/').pop();
|
||||
if (containerName) {
|
||||
const client = blobClient.getContainerClient(containerName);
|
||||
if (!await client.exists()) {
|
||||
await blobClient.createContainer(containerName);
|
||||
} else {
|
||||
ext.outputChannel.appendLog(localize('deploymentStorageExists', 'Deployment storage container "{0}" already exists.', containerName));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ext.outputChannel.appendLog(localize('noDeploymentStorage', 'No deployment storage specified in function app.'));
|
||||
return;
|
||||
}
|
||||
|
||||
type FlexFunctionAppProperties = {
|
||||
containerSize?: number,
|
||||
sku?: 'FlexConsumption',
|
||||
name?: string,
|
||||
serverFarmId?: string,
|
||||
clientAffinityEnabled?: boolean,
|
||||
siteConfig: SiteConfig,
|
||||
reserved?: boolean,
|
||||
functionAppConfig?: FunctionAppConfig
|
||||
};
|
||||
|
||||
// TODO: Temporary until we can get the SDK updated
|
||||
export type FunctionAppConfig = {
|
||||
deployment: {
|
||||
storage: {
|
||||
type: string;
|
||||
value: string;
|
||||
authentication: {
|
||||
type: string;
|
||||
userAssignedIdentityResourceId: string | null;
|
||||
storageAccountConnectionStringName: string | null;
|
||||
};
|
||||
}
|
||||
},
|
||||
runtime: {
|
||||
name?: string,
|
||||
version?: string
|
||||
},
|
||||
scaleAndConcurrency: {
|
||||
alwaysReady: number[],
|
||||
maximumInstanceCount: number,
|
||||
instanceMemoryMB: number,
|
||||
triggers: null
|
||||
}
|
||||
};
|
||||
|
|
|
@ -63,12 +63,12 @@ export class ResolvedFunctionAppResource extends ResolvedFunctionAppBase impleme
|
|||
tooltip?: string | undefined;
|
||||
commandArgs?: unknown[] | undefined;
|
||||
|
||||
public constructor(subscription: ISubscriptionContext, site: Site, isFlex?: boolean) {
|
||||
public constructor(subscription: ISubscriptionContext, site: Site) {
|
||||
super(new ParsedSite(site, subscription))
|
||||
this.data = this.site.rawSite;
|
||||
this._subscription = subscription;
|
||||
this.contextValuesToAdd = [];
|
||||
this._isFlex = !!isFlex;
|
||||
this._isFlex = !!site.functionAppConfig;
|
||||
if (this._isFlex) {
|
||||
this.contextValuesToAdd.push(ResolvedFunctionAppResource.flexContextValue);
|
||||
} else if (this.site.isSlot) {
|
||||
|
@ -91,8 +91,8 @@ export class ResolvedFunctionAppResource extends ResolvedFunctionAppBase impleme
|
|||
}
|
||||
}
|
||||
|
||||
public static createResolvedFunctionAppResource(context: IActionContext, subscription: ISubscriptionContext, site: Site, isFlex?: boolean): ResolvedFunctionAppResource {
|
||||
const resource = new ResolvedFunctionAppResource(subscription, site, isFlex);
|
||||
public static createResolvedFunctionAppResource(context: IActionContext, subscription: ISubscriptionContext, site: Site): ResolvedFunctionAppResource {
|
||||
const resource = new ResolvedFunctionAppResource(subscription, site);
|
||||
void resource.site.createClient(context).then(async (client) => resource.data.siteConfig = await client.getSiteConfig())
|
||||
return resource;
|
||||
}
|
||||
|
|
|
@ -7,11 +7,11 @@ import { runWithInputs } from '@microsoft/vscode-azext-dev';
|
|||
import { type apiUtils } from "@microsoft/vscode-azext-utils";
|
||||
import * as path from 'path';
|
||||
import { extensions, type Extension } from "vscode";
|
||||
import { ProjectLanguage, extensionId, nonNullValue, registerOnActionStartHandler } from '../extension.bundle';
|
||||
import { FuncVersion, ProjectLanguage, extensionId, nonNullValue, registerOnActionStartHandler } from '../extension.bundle';
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { type AzureFunctionsExtensionApi } from '../src/vscode-azurefunctions.api';
|
||||
import { getTestWorkspaceFolder, testFolderPath } from './global.test';
|
||||
import { getJavaScriptValidateOptions, validateProject, type IValidateProjectOptions } from './project/validateProject';
|
||||
import { getCSharpValidateOptions, getJavaScriptValidateOptions, validateProject, type IValidateProjectOptions } from './project/validateProject';
|
||||
|
||||
suite(`AzureFunctionsExtensionApi`, () => {
|
||||
let api: AzureFunctionsExtensionApi;
|
||||
|
@ -73,4 +73,49 @@ suite(`AzureFunctionsExtensionApi`, () => {
|
|||
);
|
||||
await validateProject(folderPath, validateOptions);
|
||||
});
|
||||
|
||||
test('createFunction dotnet with targetFramework', async () => {
|
||||
const functionName: string = 'endpoint1';
|
||||
const language: string = ProjectLanguage.CSharp;
|
||||
const workspaceFolder = getTestWorkspaceFolder();
|
||||
const projectSubpath = 'api';
|
||||
const folderPath: string = path.join(workspaceFolder, projectSubpath);
|
||||
|
||||
await runWithInputs('api.createFunction', [language, /6/i, 'Company.Function', 'Anonymous'], registerOnActionStartHandler, async () => {
|
||||
await api.createFunction({
|
||||
folderPath,
|
||||
functionName,
|
||||
templateId: 'HttpTrigger',
|
||||
languageFilter: /^C\#$/i,
|
||||
functionSettings: { authLevel: 'anonymous' },
|
||||
targetFramework: ['net8.0', 'net7.0', 'net6.0']
|
||||
});
|
||||
});
|
||||
|
||||
const validateOptions: IValidateProjectOptions = getCSharpValidateOptions('net6.0', FuncVersion.v4, 1, projectSubpath, workspaceFolder);
|
||||
// Exclude .git because the test workspace folders are already inside a git repo so we don't do git init.
|
||||
validateOptions.excludedPaths?.push('.git');
|
||||
await validateProject(folderPath, validateOptions);
|
||||
});
|
||||
|
||||
// Intentionally pass a version (8) that hasn't been specified in targetFramework (6 & 7) to verify it isn't a possible pick. In the correct case (when 8 isn't a pick) we throw an error. api.createFunction swallows the error and returns undefined.
|
||||
// In the incorrect case (when 8 is a pick) the test fails since the 2 provided test inputs have already been used, but there are more prompts.
|
||||
test('createFunction with language not in targetFramework', async () => {
|
||||
const functionName: string = 'endpoint1';
|
||||
const language: string = ProjectLanguage.CSharp;
|
||||
const workspaceFolder = getTestWorkspaceFolder();
|
||||
const projectSubpath = 'api';
|
||||
const folderPath: string = path.join(workspaceFolder, projectSubpath);
|
||||
|
||||
await runWithInputs('api.createFunction', [language, /8/i], registerOnActionStartHandler, async () => {
|
||||
await api.createFunction({
|
||||
folderPath,
|
||||
functionName,
|
||||
templateId: 'HttpTrigger',
|
||||
languageFilter: /^C\#$/i,
|
||||
functionSettings: { authLevel: 'anonymous' },
|
||||
targetFramework: ['net7.0', 'net6.0']
|
||||
})
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -16,7 +16,10 @@ import { CentralTemplateProvider, deploySubpathSetting, envUtils, ext, FuncVersi
|
|||
*/
|
||||
export const testFolderPath: string = path.join(os.tmpdir(), `azFuncTest${getRandomHexString()}`);
|
||||
|
||||
export const longRunningTestsEnabled: boolean = envUtils.isEnvironmentVariableSet(process.env.AzCode_UseAzureFederatedCredentials);
|
||||
const longRunningLocalTestsEnabled: boolean = envUtils.isEnvironmentVariableSet(process.env.AzCode_EnableLongRunningTestsLocal);
|
||||
const longRunningRemoteTestsEnabled: boolean = envUtils.isEnvironmentVariableSet(process.env.AzCode_UseAzureFederatedCredentials);
|
||||
|
||||
export const longRunningTestsEnabled: boolean = longRunningLocalTestsEnabled || longRunningRemoteTestsEnabled;
|
||||
export const updateBackupTemplates: boolean = envUtils.isEnvironmentVariableSet(process.env.AZFUNC_UPDATE_BACKUP_TEMPLATES);
|
||||
export const skipStagingTemplateSource: boolean = envUtils.isEnvironmentVariableSet(process.env.SKIP_STAGING_TEMPLATE_SOURCE);
|
||||
|
||||
|
|
|
@ -27,10 +27,11 @@ const testCases: CreateProjectAndDeployTestCase[] = [
|
|||
{ title: 'JavaScript (Model V4)', ...getJavaScriptValidateOptions(true, undefined, undefined, undefined, NodeModelVersion.v4), createProjectInputs: [/Model V4/], deployInputs: [getRotatingNodeVersion()], languageModelVersion: NodeModelVersion.v4 },
|
||||
{ title: 'TypeScript (Model V3)', ...getTypeScriptValidateOptions(), createProjectInputs: [/Model V3/], deployInputs: [getRotatingNodeVersion()], languageModelVersion: NodeModelVersion.v3 },
|
||||
{ title: 'TypeScript (Model V4)', ...getTypeScriptValidateOptions({ modelVersion: NodeModelVersion.v4 }), createProjectInputs: [/Model V4/], deployInputs: [getRotatingNodeVersion()], languageModelVersion: NodeModelVersion.v4 },
|
||||
// Disable Ballerina tests until we figure out how to install Ballerina on the new pipelines
|
||||
// Temporarily disable Ballerina tests until we can install Ballerina on the new pipelines
|
||||
// https://github.com/microsoft/vscode-azurefunctions/issues/4210
|
||||
// { title: 'Ballerina', ...getBallerinaValidateOptions(), createProjectInputs: ["JVM"], deployInputs: [/java.*11/i] },
|
||||
// All C# tests on mac and .NET 6 on windows are consistently timing out for some unknown reason. Will skip for now
|
||||
// { title: 'C# .NET Core 3.1', buildMachineOsToSkip: 'darwin', ...getCSharpValidateOptions('netcoreapp3.1'), createProjectInputs: [/net.*3/i], deployInputs: [/net.*3/i], createFunctionInputs: ['Company.Function'] },
|
||||
{ title: 'C# .NET Framework', buildMachineOsToSkip: 'darwin', ...getCSharpValidateOptions('net48'), createProjectInputs: [/net.*Framework/i], deployInputs: [/net.*Framework/i], createFunctionInputs: ['Company.Function'] },
|
||||
{ title: 'C# .NET 8', ...getCSharpValidateOptions('net8.0', FuncVersion.v4), createProjectInputs: [/net.*8/i], deployInputs: [/net.*8/i], createFunctionInputs: ['Company.Function'] },
|
||||
{ title: 'PowerShell', ...getPowerShellValidateOptions(), deployInputs: [/powershell.*7.4/i] },
|
||||
{ title: 'Python (Model V1)', ...getPythonValidateOptions('.venv'), createProjectInputs: [/Model V1/, /3\.9/], deployInputs: [getRotatingPythonVersion()], languageModelVersion: PythonModelVersion.v1 },
|
||||
|
@ -128,7 +129,6 @@ async function addRoutePrefixToProject(testWorkspacePath: string, routePrefix: s
|
|||
}
|
||||
|
||||
async function validateFunctionUrl(appName: string, functionName: string, routePrefix: string): Promise<void> {
|
||||
// first input matches any item except local project (aka it should match the test subscription)
|
||||
const inputs: (string | RegExp)[] = [appName, functionName];
|
||||
|
||||
let functionUrl: string | undefined;
|
||||
|
@ -145,6 +145,7 @@ async function validateFunctionUrl(appName: string, functionName: string, routeP
|
|||
assert.ok(functionUrl?.includes(routePrefix), `Function url "${functionUrl}" did not include routePrefix "${routePrefix}".`);
|
||||
|
||||
const client: ServiceClient = await createGenericClient(await createTestActionContext(), undefined);
|
||||
// PowerShell functions expect Name capitalized, so we set both
|
||||
const response = await client.sendRequest(createPipelineRequest({ method: 'POST', url: functionUrl!, body: JSON.stringify({ name: "World", Name: "World" }) }));
|
||||
const body: string = nonNullProp(response, 'bodyAsText');
|
||||
assert.ok((body.includes('Hello') && body.includes('World')) || body.includes('Welcome'), 'Expected function response to include "Hello World" or "Welcome"');
|
||||
|
|
|
@ -12,8 +12,8 @@ import { runWithFuncSetting } from '../runWithSetting';
|
|||
import { createAndValidateProject } from './createAndValidateProject';
|
||||
import { PythonModelVersion, getPythonValidateOptions } from './validateProject';
|
||||
|
||||
const modelV1Input = /Model V1/;
|
||||
const modelV2Input = /Model V2/;
|
||||
const modelV1Input: RegExp = /Model V1/;
|
||||
const modelV2Input: RegExp = /Model V2/;
|
||||
|
||||
suite('Create New Python Project (Model V1)', () => {
|
||||
test('skip venv', async () => {
|
||||
|
|
|
@ -98,7 +98,7 @@ export function getTypeScriptValidateOptions(options?: { version?: FuncVersion,
|
|||
return result;
|
||||
}
|
||||
|
||||
export function getCSharpValidateOptions(targetFramework: string, version: FuncVersion = defaultTestFuncVersion, numCsproj: number = 1): IValidateProjectOptions {
|
||||
export function getCSharpValidateOptions(targetFramework: string, version: FuncVersion = defaultTestFuncVersion, numCsproj: number = 1, projectSubpath?: string, workspaceFolder?: string): IValidateProjectOptions {
|
||||
return {
|
||||
language: ProjectLanguage.CSharp,
|
||||
version,
|
||||
|
@ -106,17 +106,17 @@ export function getCSharpValidateOptions(targetFramework: string, version: FuncV
|
|||
'azureFunctions.projectLanguage': ProjectLanguage.CSharp,
|
||||
'azureFunctions.projectRuntime': version,
|
||||
'azureFunctions.preDeployTask': 'publish (functions)',
|
||||
'azureFunctions.deploySubpath': `bin/Release/${targetFramework}/publish`,
|
||||
'azureFunctions.deploySubpath': `${projectSubpath ? `${projectSubpath}/` : ''}bin/Release/${targetFramework}/publish`,
|
||||
'debug.internalConsoleOptions': 'neverOpen',
|
||||
},
|
||||
expectedPaths: [
|
||||
{ globPattern: '*.csproj', numMatches: numCsproj }
|
||||
{ globPattern: `${projectSubpath ? `${projectSubpath}/` : ''}*.csproj`, numMatches: numCsproj }
|
||||
],
|
||||
expectedExtensionRecs: [
|
||||
'ms-dotnettools.csharp'
|
||||
],
|
||||
excludedPaths: [
|
||||
'.funcignore'
|
||||
path.join(projectSubpath ?? '', '.funcignore')
|
||||
],
|
||||
expectedDebugConfigs: [
|
||||
'Attach to .NET Functions'
|
||||
|
@ -127,7 +127,8 @@ export function getCSharpValidateOptions(targetFramework: string, version: FuncV
|
|||
'clean release (functions)',
|
||||
'publish (functions)',
|
||||
'host start'
|
||||
]
|
||||
],
|
||||
workspaceFolder
|
||||
};
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче