Skip to content
This repository was archived by the owner on Jul 20, 2026. It is now read-only.

Commit 5372c56

Browse files
authored
Refactor push image to be a wizard, show ACR create pick (#4146)
1 parent 0c33ed0 commit 5372c56

13 files changed

Lines changed: 292 additions & 133 deletions

src/commands/images/pushImage.ts

Lines changed: 0 additions & 99 deletions
This file was deleted.
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import { AzureWizardPromptStep, IAzureQuickPickItem } from '@microsoft/vscode-azext-utils';
7+
import { CommonRegistry } from '@microsoft/vscode-docker-registries';
8+
import * as vscode from 'vscode';
9+
import { ext } from '../../../extensionVariables';
10+
import { UnifiedRegistryItem } from '../../../tree/registries/UnifiedRegistryTreeDataProvider';
11+
import { createAzureRegistry } from '../../registries/azure/createAzureRegistry';
12+
import { PushImageWizardContext } from './PushImageWizardContext';
13+
14+
export class CreatePickAcrPromptStep extends AzureWizardPromptStep<PushImageWizardContext> {
15+
public async prompt(wizardContext: PushImageWizardContext): Promise<void> {
16+
const acrs = await ext.registriesRoot.getChildren(wizardContext.azureSubscriptionNode) as UnifiedRegistryItem<CommonRegistry>[];
17+
const picks: IAzureQuickPickItem<string | UnifiedRegistryItem<CommonRegistry>>[] = acrs.map(acr => <IAzureQuickPickItem<UnifiedRegistryItem<CommonRegistry>>>{ label: acr.wrappedItem.label, data: acr });
18+
picks.push({ label: vscode.l10n.t('$(plus) Create new Azure Container Registry...'), data: 'create' });
19+
20+
const response = await wizardContext.ui.showQuickPick(picks, { placeHolder: vscode.l10n.t('Select an Azure Container Registry to push to') });
21+
22+
if (response.data === 'create') {
23+
const createdAcrName = await createAzureRegistry(wizardContext, wizardContext.azureSubscriptionNode);
24+
25+
const acrNodes = await ext.registriesRoot.getChildren(wizardContext.azureSubscriptionNode) as UnifiedRegistryItem<CommonRegistry>[];
26+
const selectedAcrNode = acrNodes.find(acrNode => acrNode.wrappedItem.label === createdAcrName);
27+
wizardContext.connectedRegistry = selectedAcrNode;
28+
} else {
29+
wizardContext.connectedRegistry = response.data as UnifiedRegistryItem<CommonRegistry>;
30+
}
31+
}
32+
33+
public shouldPrompt(wizardContext: PushImageWizardContext): boolean {
34+
return !!wizardContext.azureSubscriptionNode;
35+
}
36+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See LICENSE.md in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import { AzureWizardPromptStep } from '@microsoft/vscode-azext-utils';
7+
import { tagImage } from '../tagImage';
8+
import { PushImageWizardContext } from './PushImageWizardContext';
9+
10+
export class FinalTagPromptStep extends AzureWizardPromptStep<PushImageWizardContext> {
11+
public async prompt(wizardContext: PushImageWizardContext): Promise<void> {
12+
wizardContext.finalTag = await tagImage(wizardContext, wizardContext.node, wizardContext.connectedRegistry);
13+
}
14+
15+
public shouldPrompt(wizardContext: PushImageWizardContext): boolean {
16+
return !wizardContext.finalTag;
17+
}
18+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See LICENSE.md in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import { AzureWizardPromptStep, IActionContext, NoResourceFoundError } from '@microsoft/vscode-azext-utils';
7+
import { parseDockerLikeImageName } from '@microsoft/vscode-container-client';
8+
import { CommonRegistry } from '@microsoft/vscode-docker-registries';
9+
import * as vscode from 'vscode';
10+
import { ext } from '../../../extensionVariables';
11+
import { NormalizedImageNameInfo } from '../../../tree/images/NormalizedImageNameInfo';
12+
import { AzureSubscriptionRegistryItem, isAzureSubscriptionRegistryItem } from '../../../tree/registries/Azure/AzureRegistryDataProvider';
13+
import { UnifiedRegistryItem } from '../../../tree/registries/UnifiedRegistryTreeDataProvider';
14+
import { registryExperience } from '../../../utils/registryExperience';
15+
import { PushImageWizardContext } from './PushImageWizardContext';
16+
17+
export class GetRegistryTargetPromptStep extends AzureWizardPromptStep<PushImageWizardContext> {
18+
public async configureBeforePrompt(wizardContext: PushImageWizardContext): Promise<void> {
19+
// If the image is qualified (has a slash), we'll look for a matching registry in the tree view
20+
if (this.registryIsDeterminate(wizardContext.initialTag)) {
21+
wizardContext.connectedRegistry = await this.tryGetConnectedRegistryForPath(wizardContext, wizardContext.initialTag);
22+
}
23+
}
24+
25+
public async prompt(wizardContext: PushImageWizardContext): Promise<void> {
26+
try {
27+
const pickedNode = await registryExperience<CommonRegistry | AzureSubscriptionRegistryItem>(wizardContext, { contextValueFilter: { include: [/commonregistry/i, /azuresubscription/i] } });
28+
if (isAzureSubscriptionRegistryItem(pickedNode.wrappedItem)) {
29+
// An Azure subscription node was chosen. The CreatePickAcrPromptStep will be activated, instead of the generic RecursiveQuickPickStep that would normally be used.
30+
wizardContext.azureSubscriptionNode = pickedNode as UnifiedRegistryItem<AzureSubscriptionRegistryItem>;
31+
} else {
32+
wizardContext.connectedRegistry = pickedNode as UnifiedRegistryItem<CommonRegistry>;
33+
}
34+
} catch (error) {
35+
if (error instanceof NoResourceFoundError) {
36+
// Do nothing, move on without a selected registry
37+
} else {
38+
// Rethrow
39+
throw error;
40+
}
41+
}
42+
}
43+
44+
public shouldPrompt(wizardContext: PushImageWizardContext): boolean {
45+
return !wizardContext.connectedRegistry && !this.registryIsDeterminate(wizardContext.initialTag) && this.shouldPromptForRegistry;
46+
}
47+
48+
private async tryGetConnectedRegistryForPath(context: IActionContext, baseImagePath: string): Promise<UnifiedRegistryItem<CommonRegistry> | undefined> {
49+
const baseImageNameInfo = parseDockerLikeImageName(baseImagePath);
50+
const normalizedImageNameInfo = new NormalizedImageNameInfo(baseImageNameInfo);
51+
52+
const allRegistries = await vscode.window.withProgress({
53+
location: vscode.ProgressLocation.Notification,
54+
title: vscode.l10n.t('Determining registry to push to...'),
55+
}, () => ext.registriesTree.getConnectedRegistries(normalizedImageNameInfo.normalizedRegistry));
56+
57+
return allRegistries.find((registry) => registry.wrappedItem.baseUrl.authority === normalizedImageNameInfo.normalizedRegistry);
58+
}
59+
60+
private get shouldPromptForRegistry(): boolean {
61+
return vscode.workspace.getConfiguration('docker').get('promptForRegistryWhenPushingImages', true);
62+
}
63+
64+
private registryIsDeterminate(imageName: string): boolean {
65+
return imageName.includes('/');
66+
}
67+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See LICENSE.md in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import { AzureWizardExecuteStep } from '@microsoft/vscode-azext-utils';
7+
import { ext } from '../../../extensionVariables';
8+
import { TaskCommandRunnerFactory } from '../../../runtimes/runners/TaskCommandRunnerFactory';
9+
import { addImageTaggingTelemetry } from '../tagImage';
10+
import { PushImageWizardContext } from './PushImageWizardContext';
11+
12+
export class ImagePushStep extends AzureWizardExecuteStep<PushImageWizardContext> {
13+
public priority: number = 300;
14+
15+
public async execute(wizardContext: PushImageWizardContext): Promise<void> {
16+
addImageTaggingTelemetry(wizardContext, wizardContext.finalTag, '');
17+
18+
const client = await ext.runtimeManager.getClient();
19+
const taskCRF = new TaskCommandRunnerFactory(
20+
{
21+
taskName: wizardContext.finalTag
22+
}
23+
);
24+
25+
await taskCRF.getCommandRunner()(
26+
client.pushImage({ imageRef: wizardContext.finalTag })
27+
);
28+
}
29+
30+
public shouldExecute(wizardContext: PushImageWizardContext): boolean {
31+
return true;
32+
}
33+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See LICENSE.md in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import { IActionContext } from '@microsoft/vscode-azext-utils';
7+
import { CommonRegistry } from '@microsoft/vscode-docker-registries';
8+
import { ImageTreeItem } from '../../../tree/images/ImageTreeItem';
9+
import { AzureSubscriptionRegistryItem } from '../../../tree/registries/Azure/AzureRegistryDataProvider';
10+
import { UnifiedRegistryItem } from '../../../tree/registries/UnifiedRegistryTreeDataProvider';
11+
12+
export interface PushImageWizardContext extends IActionContext {
13+
connectedRegistry?: UnifiedRegistryItem<CommonRegistry>;
14+
finalTag?: string;
15+
16+
initialTag: string;
17+
node: ImageTreeItem;
18+
19+
azureSubscriptionNode?: UnifiedRegistryItem<AzureSubscriptionRegistryItem>;
20+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See LICENSE.md in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import { AzureWizardExecuteStep } from '@microsoft/vscode-azext-utils';
7+
import { parseDockerLikeImageName } from '@microsoft/vscode-container-client';
8+
import * as vscode from 'vscode';
9+
import { NormalizedImageNameInfo } from '../../../tree/images/NormalizedImageNameInfo';
10+
import { PushImageWizardContext } from './PushImageWizardContext';
11+
12+
export class RegistryLoginStep extends AzureWizardExecuteStep<PushImageWizardContext> {
13+
public priority: number = 200;
14+
15+
public async execute(wizardContext: PushImageWizardContext): Promise<void> {
16+
await vscode.commands.executeCommand('vscode-docker.registries.logInToDockerCli', wizardContext.connectedRegistry);
17+
}
18+
19+
public shouldExecute(wizardContext: PushImageWizardContext): boolean {
20+
// If a registry was found/chosen and is still the same as the final tag's registry, try logging in
21+
if (!wizardContext.connectedRegistry) {
22+
return false;
23+
}
24+
25+
const baseAuthority = wizardContext.connectedRegistry.wrappedItem.baseUrl.authority;
26+
const desiredRegistry = new NormalizedImageNameInfo(parseDockerLikeImageName(wizardContext.finalTag)).normalizedRegistry;
27+
return desiredRegistry === baseAuthority;
28+
}
29+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See LICENSE.md in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import { AzureWizard, IActionContext } from '@microsoft/vscode-azext-utils';
7+
import * as vscode from 'vscode';
8+
import { ext } from '../../../extensionVariables';
9+
import { ImageTreeItem } from '../../../tree/images/ImageTreeItem';
10+
import { CreatePickAcrPromptStep } from './CreatePickAcrPromptStep';
11+
import { FinalTagPromptStep } from './FinalTagPromptStep';
12+
import { GetRegistryTargetPromptStep } from './GetRegistryTargetPromptStep';
13+
import { ImagePushStep } from './ImagePushStep';
14+
import { PushImageWizardContext } from './PushImageWizardContext';
15+
import { RegistryLoginStep } from './RegistryLoginStep';
16+
17+
export async function pushImage(context: IActionContext, node: ImageTreeItem | undefined): Promise<void> {
18+
if (!node) {
19+
await ext.imagesTree.refresh(context);
20+
node = await ext.imagesTree.showTreeItemPicker<ImageTreeItem>(ImageTreeItem.contextValue, {
21+
...context,
22+
noItemFoundErrorMessage: vscode.l10n.t('No images are available to push'),
23+
});
24+
}
25+
26+
const wizardContext = context as PushImageWizardContext;
27+
wizardContext.initialTag = node.fullTag;
28+
wizardContext.node = node;
29+
30+
const wizard = new AzureWizard(wizardContext, {
31+
promptSteps: [
32+
new GetRegistryTargetPromptStep(),
33+
new CreatePickAcrPromptStep(),
34+
new FinalTagPromptStep(),
35+
],
36+
executeSteps: [
37+
new RegistryLoginStep(),
38+
new ImagePushStep(),
39+
],
40+
showLoadingPrompt: true,
41+
});
42+
43+
await wizard.prompt();
44+
await wizard.execute();
45+
}

src/commands/registerCommands.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ import { copyFullTag } from "./images/copyFullTag";
3737
import { inspectImage } from "./images/inspectImage";
3838
import { pruneImages } from "./images/pruneImages";
3939
import { pullImage } from "./images/pullImage";
40-
import { pushImage } from "./images/pushImage";
40+
import { pushImage } from "./images/pushImage/pushImage";
4141
import { removeImage } from "./images/removeImage";
4242
import { removeImageGroup } from "./images/removeImageGroup";
4343
import { runAzureCliImage } from "./images/runAzureCliImage";

0 commit comments

Comments
 (0)