Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
4,376 changes: 2,491 additions & 1,885 deletions package-lock.json

Large diffs are not rendered by default.

27 changes: 20 additions & 7 deletions src/commands/deployContainerApp/deployContainerApp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import { KnownActiveRevisionsMode } from "@azure/arm-appcontainers";
import { AzureWizard, CopilotUserInput, createSubscriptionContext, nonNullProp, type AzureWizardPromptStep, type IActionContext, type ISubscriptionActionContext, type ISubscriptionContext } from "@microsoft/vscode-azext-utils";
import { ImageSource } from "../../constants";
import { ImageSource, SupportedRegistries, acrDomain } from "../../constants";
import { type ContainerAppItem } from "../../tree/ContainerAppItem";
import { createActivityContext } from "../../utils/activityUtils";
import { isAzdExtensionInstalled } from "../../utils/azdUtils";
Expand All @@ -14,7 +14,7 @@ import { getVerifyProvidersStep } from "../../utils/getVerifyProvidersStep";
import { localize } from "../../utils/localize";
import { pickContainerApp } from "../../utils/pickItem/pickContainerApp";
import { OpenConfirmationViewStep } from "../../webviews/OpenConfirmationViewStep";
import { OpenLoadingViewStep } from "../../webviews/OpenLoadingViewStep";
import { openLoadingViewPanel } from "../../webviews/OpenLoadingViewStep";
import { CommandAttributes } from "../CommandAttributes";
import { ContainerAppOverwriteConfirmStep } from "../ContainerAppOverwriteConfirmStep";
import { deployWorkspaceProject } from "../deployWorkspaceProject/deployWorkspaceProject";
Expand All @@ -39,11 +39,20 @@ export async function deployContainerApp(context: IActionContext, node?: Contain
throw new Error(localize('multipleContainersNotSupported', 'The container app cannot be updated using "{0}" while having more than one active container. Navigate to the specific container instance and execute "{1}" instead.', deployContainerAppCommandName, editContainerCommandName));
}

// Prompt for image source before initializing the wizard in case we need to redirect the call to 'deployWorkspaceProject' instead
const imageSource: ImageSource = await promptImageSource(subscriptionActionContext);
if (imageSource === ImageSource.RemoteAcrBuild) {
return await deployWorkspaceProject(context, item);
let imageSource: ImageSource | undefined;
let registryDomain: SupportedRegistries | undefined;
if (isCopilotUserInput(context)) {
await openLoadingViewPanel(context);
imageSource = ImageSource.ContainerRegistry;
registryDomain = acrDomain;
} else {
imageSource = await promptImageSource(subscriptionActionContext);
if (imageSource === ImageSource.RemoteAcrBuild) {
return await deployWorkspaceProject(context, item);
}
}
// Prompt for image source before initializing the wizard in case we need to redirect the call to 'deployWorkspaceProject' instead


const wizardContext: ContainerAppDeployContext = {
...subscriptionActionContext,
Expand All @@ -52,6 +61,7 @@ export async function deployContainerApp(context: IActionContext, node?: Contain
containerApp: item.containerApp,
managedEnvironment: await getManagedEnvironmentFromContainerApp(subscriptionActionContext, item.containerApp),
imageSource,
registryDomain,
activityAttributes: CommandAttributes.DeployContainerAppContainerRegistry,
};

Expand All @@ -67,7 +77,6 @@ export async function deployContainerApp(context: IActionContext, node?: Contain

const promptSteps: AzureWizardPromptStep<ContainerAppDeployContext>[] = [];
if (wizardContext.ui instanceof CopilotUserInput) {
promptSteps.push(new OpenLoadingViewStep());
confirmationViewDescription = localize('viewDescription', 'Please review AI generated inputs and select any you would like to modify. Note: Any input proceeding the modified input will need to change as well');
confirmationViewTabTitle = localize('deployContainerAppTabTitle', 'Summary - Deploy Image to Container App using Copilot');
title = localize('deployContainerAppWithCopilotTitle', 'Deploy image to container app using copilot');
Expand Down Expand Up @@ -105,3 +114,7 @@ async function promptImageSource(context: ISubscriptionActionContext): Promise<I

return nonNullProp(promptContext, 'imageSource');
}

function isCopilotUserInput(context: IActionContext): boolean {
return context.ui.constructor.name === 'CopilotUserInput';
}
3 changes: 3 additions & 0 deletions src/commands/image/imageSource/EnvFileListStep.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import { type EnvironmentVar } from "@azure/arm-appcontainers";
import { ActivityChildItem, ActivityChildType, AzExtFsExtra, AzureWizardPromptStep, activityInfoContext, activityInfoIcon, activitySuccessContext, activitySuccessIcon, createContextValue, prependOrInsertAfterLastInfoChild, type ActivityInfoChild, type ConfirmationViewProperty } from "@microsoft/vscode-azext-utils";
import { parse, type DotenvParseOutput } from "dotenv";
import { updateLoadingViewProgress } from "src/webviews/SharedViewState";
import { RelativePattern, workspace, type Uri, type WorkspaceFolder } from "vscode";
import { ImageSource, envFileGlobPattern } from "../../../constants";
import { ext } from "../../../extensionVariables";
Expand Down Expand Up @@ -62,6 +63,8 @@ export class EnvFileListStep<T extends EnvFileListContext> extends AzureWizardPr
if (this._setEnvironmentVariableOption) {
this.outputLogs(context, this._setEnvironmentVariableOption);
}

updateLoadingViewProgress(localize('environmentVariables', 'Resolved environment variables'));
}

public shouldPrompt(context: T): boolean {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/

import { AzureWizardPromptStep, type ConfirmationViewProperty, type IAzureQuickPickItem, type IWizardOptions } from "@microsoft/vscode-azext-utils";
import { updateLoadingViewProgress } from "src/webviews/SharedViewState";
import { UIKind, env } from "vscode";
import { acrDomain, dockerHubDomain, type SupportedRegistries } from "../../../../constants";
import { localize } from "../../../../utils/localize";
Expand Down Expand Up @@ -35,6 +36,8 @@ export class ContainerRegistryListStep extends AzureWizardPromptStep<ContainerRe
const pick: IAzureQuickPickItem<SupportedRegistries | undefined> | undefined = await context.ui.showQuickPick(picks, { placeHolder });
this.pickLabel = pick?.label;
context.registryDomain = pick.data;

updateLoadingViewProgress(localize('containerRegistryDomain', 'Selected container registry provider'));
}

public shouldPrompt(context: ContainerRegistryImageSourceContext): boolean {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/

import { AzureWizardPromptStep, type ConfirmationViewProperty } from "@microsoft/vscode-azext-utils";
import { updateLoadingViewProgress } from "src/webviews/SharedViewState";
import { acrDomain, quickStartImageName } from "../../../../constants";
import { parseImageName } from "../../../../utils/imageNameUtils";
import { localize } from "../../../../utils/localize";
Expand Down Expand Up @@ -33,6 +34,8 @@ export class RegistryImageInputStep extends AzureWizardPromptStep<ContainerRegis
})).trim();

context.valuesToMask.push(context.image);

updateLoadingViewProgress(localize('registryImage', 'Resolved registry image'));
}

public shouldPrompt(context: ContainerRegistryImageSourceContext): boolean {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/

import { AzureWizardPromptStep, type ConfirmationViewProperty } from "@microsoft/vscode-azext-utils";
import { updateLoadingViewProgress } from "src/webviews/SharedViewState";
import { type QuickPickItem } from "vscode";
import { loadMoreQp, noMatchingResourcesQp, type QuickPicksCache } from "../../../../constants";
import { localize } from "../../../../utils/localize";
Expand All @@ -25,6 +26,8 @@ export abstract class RegistryRepositoriesListStepBase extends AzureWizardPrompt
} while (result === noMatchingResourcesQp || result === loadMoreQp);

context.repositoryName = result.label;

updateLoadingViewProgress(localize('repository', 'Selected repository'));
}

public shouldPrompt(context: ContainerRegistryImageSourceContext): boolean {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/

import { AzureWizardPromptStep, type ConfirmationViewProperty } from "@microsoft/vscode-azext-utils";
import { updateLoadingViewProgress } from "src/webviews/SharedViewState";
import { type QuickPickItem } from "vscode";
import { loadMoreQp, type QuickPicksCache } from "../../../../constants";
import { localize } from "../../../../utils/localize";
Expand All @@ -20,6 +21,8 @@ export abstract class RepositoryTagListStepBase extends AzureWizardPromptStep<Co
} while (result === loadMoreQp);

context.tag = result.label;

updateLoadingViewProgress(localize('selectedTag', 'Selected image tag'));
}

public shouldPrompt(context: ContainerRegistryImageSourceContext): boolean {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import { type ContainerRegistryManagementClient, type Registry } from "@azure/arm-containerregistry";
import { LocationListStep, ResourceGroupListStep, uiUtils } from "@microsoft/vscode-azext-azureutils";
import { AzureWizardPromptStep, nonNullProp, type AzureWizardExecuteStep, type ConfirmationViewProperty, type IAzureQuickPickItem, type ISubscriptionActionContext, type IWizardOptions } from "@microsoft/vscode-azext-utils";
import { updateLoadingViewProgress } from "src/webviews/SharedViewState";
import { noMatchingResources, noMatchingResourcesQp, registryProvider, registryResourceType } from "../../../../../constants";
import { createContainerRegistryManagementClient } from "../../../../../utils/azureClients";
import { localize } from "../../../../../utils/localize";
Expand Down Expand Up @@ -65,6 +66,8 @@ export class AcrListStep<T extends ContainerRegistryImageSourceContext> extends

this.pickLabel = pick.label;
context.registry = result;

updateLoadingViewProgress(localize('registry', 'Selected container registry'));
}

public shouldPrompt(context: T): boolean {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import { KnownActiveRevisionsMode } from "@azure/arm-appcontainers";
import { AzureWizardPromptStep, type ConfirmationViewProperty } from "@microsoft/vscode-azext-utils";
import { updateLoadingViewProgress } from "src/webviews/SharedViewState";
import { dockerHubDomain } from "../../../../../constants";
import { parseImageName } from "../../../../../utils/imageNameUtils";
import { localize } from "../../../../../utils/localize";
Expand All @@ -21,6 +22,8 @@ export class DockerHubNamespaceInputStep extends AzureWizardPromptStep<Container
})).toLowerCase();

context.valuesToMask.push(context.dockerHubNamespace);

updateLoadingViewProgress(localize('dockerHubNamespace', 'Resolved Docker Hub namespace'));
}

public shouldPrompt(context: ContainerRegistryImageSourceContext): boolean {
Expand Down
3 changes: 3 additions & 0 deletions src/commands/ingress/IngressPromptStep.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/

import { AzureWizardPromptStep, type AzureWizardExecuteStep, type ConfirmationViewProperty, type IWizardOptions } from "@microsoft/vscode-azext-utils";
import { updateLoadingViewProgress } from "src/webviews/SharedViewState";
import { ext } from "../../extensionVariables";
import { localize } from "../../utils/localize";
import { type IngressContext } from "./IngressContext";
Expand All @@ -18,6 +19,8 @@ export class IngressPromptStep extends AzureWizardPromptStep<IngressContext> {
public async prompt(context: IngressContext): Promise<void> {
context.enableIngress = (await context.ui.showQuickPick([{ label: localize('enable', 'Enable'), data: true }, { label: localize('disable', 'Disable'), data: false }],
{ placeHolder: localize('enableIngress', 'Enable ingress for applications that need an HTTP endpoint.') })).data;

updateLoadingViewProgress(localize('ingress', 'Configured ingress settings'));
}

public async configureBeforePrompt(context: IngressContext): Promise<void> {
Expand Down
3 changes: 3 additions & 0 deletions src/commands/ingress/editTargetPort/TargetPortInputStep.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/

import { AzureWizardPromptStep, type ConfirmationViewProperty } from "@microsoft/vscode-azext-utils";
import { updateLoadingViewProgress } from "src/webviews/SharedViewState";
import { localize } from "../../../utils/localize";
import { type IngressContext } from "../IngressContext";
import { getDefaultPort } from "./getDefaultPort";
Expand All @@ -17,6 +18,8 @@ export class TargetPortInputStep extends AzureWizardPromptStep<IngressContext> {
}));

context.telemetry.properties.targetPort = String(context.targetPort);

updateLoadingViewProgress(localize('targetPort', 'Configured target port'));
}

public async configureBeforePrompt(context: IngressContext): Promise<void> {
Expand Down
3 changes: 3 additions & 0 deletions src/commands/ingress/enableIngress/IngressVisibilityStep.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/

import { AzureWizardPromptStep, type ConfirmationViewProperty } from "@microsoft/vscode-azext-utils";
import { updateLoadingViewProgress } from "src/webviews/SharedViewState";
import { IngressConstants } from "../../../constants";
import { localize } from "../../../utils/localize";
import { type IngressContext } from "../IngressContext";
Expand All @@ -19,6 +20,8 @@ export class IngressVisibilityStep<T extends IngressContext> extends AzureWizard
{ label: localize('internal', 'Internal'), description: IngressConstants.internalDesc, data: false }],
{ placeHolder: localize('ingressVisibility', 'Select the HTTP traffic that the endpoint will accept.') })).data;
context.telemetry.properties.enableExternal = context.enableExternal ? 'true' : 'false';

updateLoadingViewProgress(localize('ingressVisibility', 'Set ingress visibility'));
}

public shouldPrompt(context: T): boolean {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/

import { ActivityChildItem, ActivityChildType, activityInfoIcon, AzureWizardPromptStep, createContextValue, nonNullProp, prependOrInsertAfterLastInfoChild, type ActivityInfoChild, type AzureWizardExecuteStep, type ConfirmationViewProperty, type IAzureQuickPickItem, type IWizardOptions } from "@microsoft/vscode-azext-utils";
import { updateLoadingViewProgress } from "src/webviews/SharedViewState";
import { acrDomain, activityInfoContext, type SupportedRegistries } from "../../constants";
import { ext } from "../../extensionVariables";
import { getRegistryDomainFromContext } from "../../utils/imageNameUtils";
Expand Down Expand Up @@ -74,6 +75,7 @@ export class RegistryCredentialsAddConfigurationListStep extends AzureWizardProm

this.pickLabel = pick.label;
context.newRegistryCredentialType = pick.data;
updateLoadingViewProgress(localize('connectionMethod', 'Selected connection method'));
}

public shouldPrompt(context: RegistryCredentialsContext): boolean {
Expand Down
59 changes: 55 additions & 4 deletions src/webviews/LoadingView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,61 @@
*--------------------------------------------------------------------------------------------*/

import { Spinner } from "@fluentui/react-components";
import { useEffect, useState } from 'react';
import './styles/loadingView.scss';
import { LoadingViewCommands } from './webviewConstants';

export const LoadingView = () =>
<div className='loadingView'>
<Spinner labelPosition="below" label="Generating Copilot responses..." />
</div>;
type LoadingViewProgressItem = {
name: string;
completed: boolean;
}

type ProgressMessage = {
command: LoadingViewCommands.AddProgressItem;
name: string;
};

export const LoadingView = () => {
const [progressItems, setProgressItems] = useState<LoadingViewProgressItem[]>([]);

useEffect(() => {
const handleMessage = (event: MessageEvent<ProgressMessage>) => {
const message = event.data;
if (message.command === LoadingViewCommands.AddProgressItem) {
setProgressItems(prev => [
...prev,
{ name: message.name, completed: true }
]);
}
};

window.addEventListener('message', handleMessage);
return () => window.removeEventListener('message', handleMessage);
}, []);

return (
<div className='loadingView'>
<div className='loadingContent'>
<Spinner labelPosition="below" label="Generating Copilot responses..." />
<div className='progressList'>
{progressItems.length > 0 ? (
progressItems.map((item, index) => (
<div key={index} className='progressItem'>
<span className='checkmark codicon codicon-check'></span>
<span className='itemName'>{item.name}</span>
</div>
))
) : (
Array.from({ length: 3 }).map((_, index) => (
<div key={index} className='progressItemPlaceholder'>
<span className='placeholderIcon' />
<span className='placeholderText' style={{ width: `${60 + index * 15}%` }} />
</div>
))
)}
</div>
</div>
</div>
);
};

14 changes: 14 additions & 0 deletions src/webviews/LoadingViewController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,27 @@
import { ViewColumn } from "vscode";
import { ext } from "../extensionVariables";
import { WebviewController } from "./extension-server/WebviewController";
import { LoadingViewCommands } from "./webviewConstants";

export type LoadingViewProgressItem = {
name: string;
completed: boolean;
}

export type LoadingViewControllerType = {
title: string;
items?: LoadingViewProgressItem[];
}

export class LoadingViewController extends WebviewController<LoadingViewControllerType> {
constructor(viewConfiguration: LoadingViewControllerType) {
super(ext.context, viewConfiguration.title, 'loadingView', viewConfiguration, ViewColumn.Active);
}

public addProgressItem(name: string): void {
void this.panel.webview.postMessage({
command: LoadingViewCommands.AddProgressItem,
name
});
}
}
15 changes: 3 additions & 12 deletions src/webviews/OpenConfirmationViewStep.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,11 @@

import { AzExtUserInput, AzureWizardPromptStep, CopilotUserInput, GoBackError, openUrl, UserCancelledError, type ConfirmationViewProperty, type IActionContext } from "@microsoft/vscode-azext-utils";
import * as vscode from 'vscode';
import { type WebviewPanel } from 'vscode';
import { localize } from "../utils/localize";
import { ConfirmationViewController } from "./ConfirmationViewController";
import { resetSharedState, SharedState } from "./SharedViewState";

export const SharedState = {
itemsToClear: 0,
cancelled: true,
copilotClicked: false,
editingPicks: false,
currentPanel: undefined as WebviewPanel | undefined,
};
export { SharedState } from "./SharedViewState";

export class OpenConfirmationViewStep<T extends IActionContext> extends AzureWizardPromptStep<T> {
private readonly viewConfig: () => ConfirmationViewProperty[];
Expand Down Expand Up @@ -114,10 +108,7 @@ export class OpenConfirmationViewStep<T extends IActionContext> extends AzureWiz
}
});
// reset the shared state
SharedState.itemsToClear = 0;
SharedState.cancelled = true;
SharedState.copilotClicked = false;
SharedState.editingPicks = false;
resetSharedState();
});
}

Expand Down
Loading
Loading