Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
87841ca
Initial scaffold for deployContainerApp
MicroFish91 Dec 18, 2024
1088867
Improve wizard title
MicroFish91 Dec 18, 2024
3e85ad6
Add retries for failed updates
MicroFish91 Dec 18, 2024
e0a7003
Update throw condition
MicroFish91 Dec 18, 2024
c35bd08
Improve activity children and output logs
MicroFish91 Dec 19, 2024
ef87f0f
Add todo
MicroFish91 Dec 19, 2024
631be79
Add resource output logs
MicroFish91 Dec 19, 2024
b537741
Add comment
MicroFish91 Dec 19, 2024
146661f
Merge branch 'mwf/adorable-plum' of https://github.com/microsoft/vsco…
MicroFish91 Dec 19, 2024
e4ede57
Fix a log message
MicroFish91 Dec 19, 2024
7906366
Always prompt registry
MicroFish91 Dec 19, 2024
7c8d543
Improvements to finding recommended pick
MicroFish91 Dec 19, 2024
b8a81fb
Impl
MicroFish91 Dec 20, 2024
a5db872
Add fix for imageSourceListStep
MicroFish91 Dec 20, 2024
c9292be
Merge branch 'mwf/homely-emerald' of https://github.com/microsoft/vsc…
MicroFish91 Dec 20, 2024
1feaddd
Improve placeHolder prompt
MicroFish91 Dec 20, 2024
a139430
Fix deployImage
MicroFish91 Dec 20, 2024
f2cd6a7
Merge branch 'mwf/homely-emerald' of https://github.com/microsoft/vsc…
MicroFish91 Dec 20, 2024
300a5da
Merge with main
MicroFish91 Dec 21, 2024
2d05935
Improve acr pick recommendations
MicroFish91 Dec 24, 2024
209d1ac
Only sort if sr exists
MicroFish91 Dec 24, 2024
e2c87c6
Add draft boolean to context and register more children for editConta…
MicroFish91 Dec 24, 2024
7a8787c
Update editContainerImage
MicroFish91 Dec 24, 2024
eff6574
Improve comment
MicroFish91 Dec 24, 2024
5b22cc9
Improve comment again
MicroFish91 Dec 24, 2024
bd3e538
Add guard clauses to key commands
MicroFish91 Dec 24, 2024
57661f6
Small improvements
MicroFish91 Dec 24, 2024
d2eddbb
Make pick description search case insensitive
MicroFish91 Dec 24, 2024
4ec09bb
Change to pickUtils
MicroFish91 Dec 24, 2024
85eba7d
Consolidate with pickUtils
MicroFish91 Dec 26, 2024
85d8d1c
Merge branch 'mwf/fiscal-beige' of https://github.com/microsoft/vscod…
MicroFish91 Dec 26, 2024
ac5b4dc
Merge branch 'mwf/obliged-gold' of https://github.com/microsoft/vscod…
MicroFish91 Dec 26, 2024
31db97f
Initial functions api support
MicroFish91 Dec 26, 2024
6f7cb74
Update changelog
MicroFish91 Dec 27, 2024
75df8de
Update suppress option
MicroFish91 Dec 27, 2024
6a98700
Feedback
MicroFish91 Dec 27, 2024
08428a0
Remove container app name
MicroFish91 Dec 27, 2024
7849f25
Merge branch 'mwf/gentle-amber' of https://github.com/microsoft/vscod…
MicroFish91 Dec 27, 2024
5faccfc
Update deployImageApi
MicroFish91 Dec 27, 2024
21cf2c0
Add todos
MicroFish91 Dec 27, 2024
a5ddc53
Update changelog
MicroFish91 Dec 30, 2024
6a433d9
Add activity children and support template updates
MicroFish91 Dec 30, 2024
316ea8c
Add parent resource reference
MicroFish91 Dec 30, 2024
d3c884c
Make command consistent with other deploy commands
MicroFish91 Dec 30, 2024
4604735
Scaffold environment variable preconfigure logic
MicroFish91 Dec 30, 2024
979d280
Change prompt
MicroFish91 Dec 31, 2024
64d07ab
Add output logs and prompt verbiage/key
MicroFish91 Dec 31, 2024
80b2964
Small fixes for activity log output
MicroFish91 Jan 7, 2025
a94731e
Merge with main
MicroFish91 Jan 10, 2025
bf9d0e8
Fix merge
MicroFish91 Jan 10, 2025
a086062
Add env path to description
MicroFish91 Jan 10, 2025
8ac696e
Update quick pick labels
MicroFish91 Jan 10, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { ContainerAppVerifyStep } from "./azureResources/ContainerAppVerifyStep"
import { ContainerRegistryVerifyStep } from "./azureResources/ContainerRegistryVerifyStep";
import { ResourceGroupVerifyStep } from "./azureResources/ResourceGroupVerifyStep";
import { DockerfileValidateStep } from "./filePaths/DockerfileValidateStep";
import { EnvUseRemoteConfigurationPromptStep } from "./filePaths/EnvUseRemoteConfigurationPromptStep";
import { EnvValidateStep } from "./filePaths/EnvValidateStep";
import { SrcValidateStep } from "./filePaths/SrcValidateStep";

Expand Down Expand Up @@ -52,6 +53,9 @@ export class DeploymentConfigurationListStep extends AzureWizardPromptStep<Works
context.activityChildren ??= [];

return {
promptSteps: [
new EnvUseRemoteConfigurationPromptStep(),
],
executeSteps: [
new DockerfileValidateStep(),
new SrcValidateStep(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export abstract class AzureResourceVerifyStepBase extends AzureWizardExecuteStep
protected abstract verifyResource(context: WorkspaceDeploymentConfigurationContext): Promise<void>;

public shouldExecute(context: WorkspaceDeploymentConfigurationContext): boolean {
return !!context.deploymentConfigurationSettings?.[this.deploymentSettingsKey] && !context?.[this.contextKey];
return !!context.deploymentConfigurationSettings?.[this.deploymentSettingsKey];
}

public createSuccessOutput(context: WorkspaceDeploymentConfigurationContext): ExecuteActivityOutput {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@ export class ContainerAppVerifyStep extends AzureResourceVerifyStepBase {
protected contextKey = 'containerApp' as const;

protected async verifyResource(context: WorkspaceDeploymentConfigurationContext): Promise<void> {
await ContainerAppVerifyStep.verifyContainerApp(context);
}

static async verifyContainerApp(context: WorkspaceDeploymentConfigurationContext): Promise<void> {
if (context.containerApp) {
return;
}

const client: ContainerAppsAPIClient = await createContainerAppsAPIClient(context);
const containerApp: ContainerApp = await client.containerApps.get(nonNullValueAndProp(context.resourceGroup, 'name'), nonNullValueAndProp(context.deploymentConfigurationSettings, 'containerApp'));
context.containerApp = ContainerAppItem.CreateContainerAppModel(containerApp);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@ export class ResourceGroupVerifyStep extends AzureResourceVerifyStepBase {
protected contextKey = 'resourceGroup' as const;

protected async verifyResource(context: WorkspaceDeploymentConfigurationContext): Promise<void> {
await ResourceGroupVerifyStep.verifyResourceGroup(context);
}

static async verifyResourceGroup(context: WorkspaceDeploymentConfigurationContext): Promise<void> {
if (context.resourceGroup) {
return;
}

const resourceGroups: ResourceGroup[] = await ResourceGroupListStep.getResourceGroups(context);
context.resourceGroup = resourceGroups.find(rg => rg.name === context.deploymentConfigurationSettings?.resourceGroup);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { type EnvironmentVar } from "@azure/arm-appcontainers";
import { activitySuccessContext, activitySuccessIcon, AzureWizardPromptStep, createUniversallyUniqueContextValue, GenericTreeItem, nonNullProp, nonNullValueAndProp, type IAzureQuickPickItem } from "@microsoft/vscode-azext-utils";
import * as deepEqual from "deep-eql";
import * as path from "path";
import { ext } from "../../../../../extensionVariables";
import { localize } from "../../../../../utils/localize";
import { EnvFileListStep } from "../../../../image/imageSource/EnvFileListStep";
import { type WorkspaceDeploymentConfigurationContext } from "../WorkspaceDeploymentConfigurationContext";
import { ContainerAppVerifyStep } from "../azureResources/ContainerAppVerifyStep";
import { ResourceGroupVerifyStep } from "../azureResources/ResourceGroupVerifyStep";

export const useRemoteConfigurationKey: string = 'useRemoteConfiguration';
export const useRemoteConfigurationLabel: string = localize('useRemoteConfiguration', 'Remote env configuration');
export const useRemoteConfigurationOutputMessage: string = localize('usingRemoteConfiguration', 'Using the existing remote env configuration.');

export class EnvUseRemoteConfigurationPromptStep<T extends WorkspaceDeploymentConfigurationContext> extends AzureWizardPromptStep<T> {
private shouldPromptEnvVars?: boolean;

public async configureBeforePrompt(context: T): Promise<void> {
const envPath: string | undefined = context.deploymentConfigurationSettings?.envPath;
if (!envPath || envPath === useRemoteConfigurationKey) {
this.shouldPromptEnvVars = false;
return;
}

// Verify the resource group and container app ahead of time so we can inspect the current environment variables
try {
await ResourceGroupVerifyStep.verifyResourceGroup(context);
await ContainerAppVerifyStep.verifyContainerApp(context);
} catch {
this.shouldPromptEnvVars = false;
return;
}

const rootPath: string = nonNullProp(context, 'rootFolder').uri.fsPath;
const fullPath: string = path.join(rootPath, envPath);
const configVars: EnvironmentVar[] = await EnvFileListStep.parseEnvironmentVariablesFromEnvPath(fullPath);
const currentVars: EnvironmentVar[] = context.containerApp?.template?.containers?.[0]?.env ?? [];
this.shouldPromptEnvVars = !deepEqual(configVars, currentVars);
}

public async prompt(context: T): Promise<void> {
const envPath: string = nonNullValueAndProp(context.deploymentConfigurationSettings, 'envPath');
const useEnvFile: string = localize('useEnvFile', 'Local config');
const useExistingConfig: string = localize('useExistingConfig', 'Remote config');

const picks: IAzureQuickPickItem<string>[] = [
{ label: useEnvFile, data: useEnvFile, description: envPath },
{ label: useExistingConfig, data: useExistingConfig, description: context.containerApp?.name },
];

const result: string = (await context.ui.showQuickPick(picks, {
placeHolder: localize('selectSourcePrompt', 'Detected conflicts between local and remote environment variables. Select source.'),
suppressPersistence: true,
})).data;

if (result === useEnvFile) {
// Do nothing, later steps will verify the file path
} else if (result === useExistingConfig) {
context.envPath = '';
context.activityChildren?.push(
new GenericTreeItem(undefined, {
contextValue: createUniversallyUniqueContextValue(['envUseExistingConfigurationPromptStepItem', activitySuccessContext]),
label: useRemoteConfigurationLabel,
iconPath: activitySuccessIcon,
})
);
ext.outputChannel.appendLog(useRemoteConfigurationOutputMessage);
}
}

public shouldPrompt(): boolean {
return !!this.shouldPromptEnvVars;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,82 @@
* Licensed under the MIT License. See License.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { activityFailContext, activityFailIcon, activitySuccessContext, activitySuccessIcon, AzExtFsExtra, AzureWizardExecuteStep, createUniversallyUniqueContextValue, GenericTreeItem, nonNullProp, nonNullValueAndProp, type ExecuteActivityOutput } from "@microsoft/vscode-azext-utils";
import * as path from "path";
import { type Progress } from "vscode";
import { localize } from "../../../../../utils/localize";
import { type WorkspaceDeploymentConfigurationContext } from "../WorkspaceDeploymentConfigurationContext";
import { FilePathsVerifyStep } from "./FilePathsVerifyStep";
import { useRemoteConfigurationKey, useRemoteConfigurationLabel, useRemoteConfigurationOutputMessage } from "./EnvUseRemoteConfigurationPromptStep";
import { verifyingFilePaths } from "./FilePathsVerifyStep";

export class EnvValidateStep extends FilePathsVerifyStep {
priority: number = 120;
export class EnvValidateStep<T extends WorkspaceDeploymentConfigurationContext> extends AzureWizardExecuteStep<T> {
public priority: number = 120;
private configEnvPath: string;

deploymentSettingskey = 'envPath' as const;
contextKey = 'envPath' as const;
fileType = 'environment variables';
public async execute(context: T, progress: Progress<{ message?: string; increment?: number; }>): Promise<void> {
this.options.continueOnFail = true;
progress.report({ message: verifyingFilePaths });

public shouldExecute(context: WorkspaceDeploymentConfigurationContext): boolean {
return !context.envPath;
this.configEnvPath = nonNullValueAndProp(context.deploymentConfigurationSettings, 'envPath');
if (this.configEnvPath === useRemoteConfigurationKey) {
context.envPath = '';
return;
}

const rootPath: string = nonNullProp(context, 'rootFolder').uri.fsPath;
if (!context.envPath && this.configEnvPath) {
const fullPath: string = path.join(rootPath, this.configEnvPath);
if (await this.verifyFilePath(fullPath)) {
context.envPath = fullPath;
}
}
}

public shouldExecute(context: T): boolean {
return context.envPath === undefined;
}

public async verifyFilePath(path: string): Promise<boolean> {
if (await AzExtFsExtra.pathExists(path)) {
return true;
} else {
throw new Error(localize('fileNotFound', 'File not found: {0}', path));
}
}

public createSuccessOutput(context: T): ExecuteActivityOutput {
if (context.envPath === undefined) {
return {};
}

let label: string;
let message: string;
if (context.envPath === '') {
label = useRemoteConfigurationLabel;
message = useRemoteConfigurationOutputMessage;
} else {
label = localize('envPathLabel', 'Env path');
message = localize('envPathSuccessMessage', 'Successfully verified {0} path "{1}".', '.env', context.envPath);
}

return {
item: new GenericTreeItem(undefined, {
contextValue: createUniversallyUniqueContextValue(['envValidateStepSuccessItem', activitySuccessContext]),
label,
iconPath: activitySuccessIcon
}),
message,
};
}

public createFailOutput(): ExecuteActivityOutput {
return {
item: new GenericTreeItem(undefined, {
contextValue: createUniversallyUniqueContextValue(['envValidateStepFailItem', activityFailContext]),
label: localize('envPathLabel', 'Env path'),
iconPath: activityFailIcon
}),
message: localize('envPathFailMessage', 'Failed to verify {0} path "{1}".', '.env', this.configEnvPath),
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import { localize } from "../../../../../utils/localize";
import { type DeploymentConfigurationSettings } from "../../../settings/DeployWorkspaceProjectSettingsV2";
import { type WorkspaceDeploymentConfigurationContext } from "../WorkspaceDeploymentConfigurationContext";

export const verifyingFilePaths: string = localize('verifyingFilePaths', `Verifying file paths...`);

export abstract class FilePathsVerifyStep extends AzureWizardExecuteStep<WorkspaceDeploymentConfigurationContext> {
abstract deploymentSettingskey: keyof DeploymentConfigurationSettings;
abstract contextKey: keyof Pick<WorkspaceDeploymentConfigurationContext, 'srcPath' | 'envPath' | 'dockerfilePath'>;
Expand All @@ -23,7 +25,7 @@ export abstract class FilePathsVerifyStep extends AzureWizardExecuteStep<Workspa

public async execute(context: WorkspaceDeploymentConfigurationContext, progress: Progress<{ message?: string | undefined; increment?: number | undefined }>): Promise<void> {
this.options.continueOnFail = true;
progress.report({ message: localize('verifyingFilePaths', `Verifying file paths...`) });
progress.report({ message: verifyingFilePaths });

const rootPath: string = nonNullProp(context, 'rootFolder').uri.fsPath;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import * as path from "path";
import { type Progress, type WorkspaceFolder } from "vscode";
import { relativeSettingsFilePath } from "../../../constants";
import { localize } from "../../../utils/localize";
import { useRemoteConfigurationKey } from "../deploymentConfiguration/workspace/filePaths/EnvUseRemoteConfigurationPromptStep";
import { type DeploymentConfigurationSettings } from "../settings/DeployWorkspaceProjectSettingsV2";
import { dwpSettingUtilsV2 } from "../settings/dwpSettingUtilsV2";
import { type DeployWorkspaceProjectInternalContext } from "./DeployWorkspaceProjectInternalContext";
Expand All @@ -30,7 +31,7 @@ export class DeployWorkspaceProjectSaveSettingsStep extends AzureWizardExecuteSt
type: 'AcrDockerBuildRequest',
dockerfilePath: path.relative(rootFolder.uri.fsPath, nonNullProp(context, 'dockerfilePath')),
srcPath: path.relative(rootFolder.uri.fsPath, context.srcPath || rootFolder.uri.fsPath) || ".",
envPath: context.envPath ? path.relative(rootFolder.uri.fsPath, context.envPath) : "",
envPath: this.getEnvPath(rootFolder, context.envPath),
resourceGroup: context.resourceGroup?.name,
containerApp: context.containerApp?.name,
containerRegistry: context.registry?.name,
Expand All @@ -49,6 +50,16 @@ export class DeployWorkspaceProjectSaveSettingsStep extends AzureWizardExecuteSt
return !!context.shouldSaveDeploySettings;
}

private getEnvPath(rootFolder: WorkspaceFolder, envPath: string | undefined): string {
if (envPath === undefined) {
return '';
} else if (envPath === '') {
return useRemoteConfigurationKey;
} else {
return path.relative(rootFolder.uri.fsPath, envPath);
}
}

public createSuccessOutput(context: DeployWorkspaceProjectInternalContext): ExecuteActivityOutput {
context.telemetry.properties.didSaveSettings = 'true';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { AzureWizardPromptStep, nonNullProp } from "@microsoft/vscode-azext-util
import * as path from "path";
import { type WorkspaceFolder } from "vscode";
import { localize } from "../../../utils/localize";
import { useRemoteConfigurationKey } from "../deploymentConfiguration/workspace/filePaths/EnvUseRemoteConfigurationPromptStep";
import { type DeploymentConfigurationSettings } from "../settings/DeployWorkspaceProjectSettingsV2";
import { dwpSettingUtilsV2 } from "../settings/dwpSettingUtilsV2";
import { type DeployWorkspaceProjectInternalContext } from "./DeployWorkspaceProjectInternalContext";
Expand All @@ -20,6 +21,15 @@ export class ShouldSaveDeploySettingsPromptStep extends AzureWizardPromptStep<De
const settings: DeploymentConfigurationSettings[] | undefined = await dwpSettingUtilsV2.getWorkspaceDeploymentConfigurations(rootFolder);
const setting: DeploymentConfigurationSettings | undefined = settings?.[context.configurationIdx];

let hasNewEnvPath: boolean;
if (context.envPath) {
hasNewEnvPath = convertRelativeToAbsolutePath(rootPath, setting?.envPath) !== context.envPath;
} else if (context.envPath === '') {
hasNewEnvPath = setting?.envPath !== useRemoteConfigurationKey;
} else {
hasNewEnvPath = context.envPath !== setting?.envPath;
}

const hasNewResourceGroupSetting: boolean = (!!context.newResourceGroupName && setting?.resourceGroup !== context.newResourceGroupName) ||
(!!context.resourceGroup && setting?.resourceGroup !== context.resourceGroup.name);
const hasNewContainerAppSetting: boolean = (!!context.newContainerAppName && setting?.containerApp !== context.newContainerAppName) ||
Expand All @@ -31,7 +41,7 @@ export class ShouldSaveDeploySettingsPromptStep extends AzureWizardPromptStep<De
!setting?.label ||
setting?.type !== 'AcrDockerBuildRequest' ||
(context.dockerfilePath && convertRelativeToAbsolutePath(rootPath, setting?.dockerfilePath) !== context.dockerfilePath) ||
(context.envPath && convertRelativeToAbsolutePath(rootPath, setting?.envPath) !== context.envPath) ||
hasNewEnvPath ||
(context.srcPath && convertRelativeToAbsolutePath(rootPath, setting?.srcPath) !== context.srcPath) ||
hasNewResourceGroupSetting ||
hasNewContainerAppSetting ||
Expand Down
Loading