Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revamped image push experience #1783

Merged
merged 8 commits into from
Mar 30, 2020
Merged
Show file tree
Hide file tree
Changes from 4 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
21 changes: 5 additions & 16 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@
"onCommand:vscode-docker.registries.pullRepository",
"onCommand:vscode-docker.registries.reconnectRegistry",
"onCommand:vscode-docker.registries.refresh",
"onCommand:vscode-docker.registries.setAsDefault",
"onCommand:vscode-docker.volumes.configureExplorer",
"onCommand:vscode-docker.volumes.inspect",
"onCommand:vscode-docker.volumes.prune",
Expand Down Expand Up @@ -370,7 +369,7 @@
},
{
"command": "vscode-docker.registries.azure.selectSubscriptions",
"when": "view == dockerRegistries && viewItem == azureextensionui.azureSubscription",
"when": "view == dockerRegistries && viewItem == azure;DockerV2;RegistryProvider;",
karolz-ms marked this conversation as resolved.
Show resolved Hide resolved
"group": "inline"
},
{
Expand All @@ -393,11 +392,6 @@
"when": "view == dockerRegistries && viewItem == azureextensionui.azureSubscription",
"group": "regs_1_general@1"
},
{
"command": "vscode-docker.registries.setAsDefault",
"when": "view == dockerRegistries && viewItem =~ /Registry;/",
"group": "regs_reg_1_general@1"
},
{
"command": "vscode-docker.registries.azure.deleteRegistry",
"when": "view == dockerRegistries && viewItem == azure;DockerV2;Registry;",
Expand Down Expand Up @@ -1325,10 +1319,10 @@
"configuration": {
"title": "Docker",
"properties": {
"docker.defaultRegistryPath": {
"type": "string",
"default": "",
"description": "%vscode-docker.config.docker.defaultRegistryPath%"
"docker.promptForRegistryWhenPushingImages": {
"type": "boolean",
"default": true,
"description": "%vscode-docker.config.docker.promptForRegistryWhenPushingImages%"
},
"docker.explorerRefreshInterval": {
"type": "number",
Expand Down Expand Up @@ -2325,11 +2319,6 @@
"dark": "resources/dark/refresh.svg"
}
},
{
"command": "vscode-docker.registries.setAsDefault",
"title": "%vscode-docker.commands.registries.setAsDefault%",
"category": "%vscode-docker.commands.category.dockerRegistries%"
},
{
"command": "vscode-docker.volumes.configureExplorer",
"title": "%vscode-docker.commands.volumes.configureExplorer%",
Expand Down
3 changes: 1 addition & 2 deletions package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@
"vscode-docker.tasks.docker-run.python.args": "Arguments passed to the Python app.",
"vscode-docker.tasks.docker-run.python.wait": "Whether to wait for debugger to attach.",
"vscode-docker.tasks.docker-run.python.debugPort": "The port that the debugger will listen on.",
"vscode-docker.config.docker.promptForRegistryWhenPushingImages": "Whether to prompt for the registry to push to if the image is not explicitly tagged.",
"vscode-docker.config.template.build.template": "The command template.",
"vscode-docker.config.template.build.label": "The label displayed to the user.",
"vscode-docker.config.template.build.match": "The regular expression for choosing the right template. Checked against container name, container's image name, etc.",
Expand Down Expand Up @@ -122,7 +123,6 @@
"vscode-docker.config.template.composeDown.label": "The label displayed to the user.",
"vscode-docker.config.template.composeDown.match": "The regular expression for choosing the right template. Checked against docker-compose YAML files, folder name, etc.",
"vscode-docker.config.template.composeDown.description": "Command templates for `docker-compose down` commands.",
"vscode-docker.config.docker.defaultRegistryPath": "Default registry and path when tagging an image",
"vscode-docker.config.docker.explorerRefreshInterval": "Docker view refresh interval (milliseconds)",
"vscode-docker.config.docker.containers.groupBy": "The property to use to group containers in Docker view: ContainerId, ContainerName, CreatedTime, FullTag, ImageId, Networks, Ports, Registry, Repository, RepositoryName, RepositoryNameAndTag, State, Status, Tag, or None",
"vscode-docker.config.docker.containers.description": "Any secondary properties to display for a container (an array). Possible elements include: ContainerId, ContainerName, CreatedTime, FullTag, ImageId, Networks, Ports, Registry, Repository, RepositoryName, RepositoryNameAndTag, State, Status, and Tag",
Expand Down Expand Up @@ -223,7 +223,6 @@
"vscode-docker.commands.registries.pullImage": "Pull Image",
"vscode-docker.commands.registries.pullRepository": "Pull Repository",
"vscode-docker.commands.registries.refresh": "Refresh",
"vscode-docker.commands.registries.setAsDefault": "Set as Default",
"vscode-docker.commands.volumes.configureExplorer": "Configure Explorer...",
"vscode-docker.commands.volumes.inspect": "Inspect",
"vscode-docker.commands.volumes.prune": "Prune...",
Expand Down
2 changes: 1 addition & 1 deletion src/commands/images/buildImage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export async function buildImage(context: IActionContext, dockerFileUri: vscode.
await delay(500);

addImageTaggingTelemetry(context, suggestedImageName, '.before');
const imageName: string = await getTagFromUserInput(suggestedImageName, !prevImageName);
const imageName: string = await getTagFromUserInput(suggestedImageName);
addImageTaggingTelemetry(context, imageName, '.after');

await ext.context.globalState.update(dockerFileKey, imageName);
Expand Down
71 changes: 46 additions & 25 deletions src/commands/images/pushImage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,48 +5,69 @@

import vscode = require('vscode');
import { IActionContext } from 'vscode-azureextensionui';
import { configurationKeys } from '../../constants';
import { ext } from '../../extensionVariables';
import { localize } from '../../localize';
import { ImageTreeItem } from '../../tree/images/ImageTreeItem';
import { askToSaveRegistryPath } from '../registries/registrySettings';
import { registryExpectedContextValues } from '../../tree/registries/registryContextValues';
import { RegistryTreeItemBase } from '../../tree/registries/RegistryTreeItemBase';
import { addImageTaggingTelemetry, tagImage } from './tagImage';

export async function pushImage(context: IActionContext, node: ImageTreeItem | undefined): Promise<void> {
if (!node) {
node = await ext.imagesTree.showTreeItemPicker<ImageTreeItem>(ImageTreeItem.contextValue, {
...context,
noItemFoundErrorMessage: localize('vscode-docker.commands.images.push.noImages', 'No images are available to push')
noItemFoundErrorMessage: localize('vscode-docker.commands.images.push.noImages', 'No images are available to push'),
karolz-ms marked this conversation as resolved.
Show resolved Hide resolved
});
}

const defaultRegistryPath = vscode.workspace.getConfiguration('docker').get(configurationKeys.defaultRegistryPath);
let connectedRegistry: RegistryTreeItemBase | undefined;

let fullTag: string = node.fullTag;
if (fullTag.includes('/')) {
if (!defaultRegistryPath) {
await askToSaveRegistryPath(fullTag);
if (!node.fullTag.includes('/')) {
// The registry to push to is indeterminate--could be Docker Hub, or could need tagging.
const prompt: boolean = vscode.workspace.getConfiguration('docker').get('promptForRegistryWhenPushingImages', true);

// If the prompt setting is true, we'll ask; if not we'll assume Docker Hub.
if (prompt) {
connectedRegistry = await ext.registriesTree.showTreeItemPicker<RegistryTreeItemBase>(registryExpectedContextValues.all.registry, context);
bwateratmsft marked this conversation as resolved.
Show resolved Hide resolved
} else {
// Try to find a connected Docker Hub registry (primarily for login credentials)
connectedRegistry = await tryGetDockerHubRegistry(context);
}
} else {
let askToPushPrefix: boolean = true;
if (askToPushPrefix && defaultRegistryPath) {
context.telemetry.properties.pushWithoutRepositoryAnswer = 'Cancel';

let tagFirst: vscode.MessageItem = { title: localize('vscode-docker.commands.images.push.tagFirst', 'Tag first') };
let pushAnyway: vscode.MessageItem = { title: localize('vscode-docker.commands.images.push.pushAnyway', 'Push anyway') }
let options: vscode.MessageItem[] = [tagFirst, pushAnyway];
let response: vscode.MessageItem = await ext.ui.showWarningMessage(localize('vscode-docker.commands.images.push.pushDockerHub', 'This will attempt to push to the official public Docker Hub library (docker.io/library), which you may not have permissions for. To push to your own repository, you must tag the image like <docker-id-or-registry-server>/<imagename>'), ...options);
context.telemetry.properties.pushWithoutRepositoryAnswer = response.title;

if (response === tagFirst) {
fullTag = await tagImage(context, node);
}
}
// The registry to push to is determinate. If there's a connected registry in the tree view, we'll try to find it, to perform login ahead of time.
// Registry path is everything up to the last slash.
const baseImagePath = node.fullTag.substring(0, node.fullTag.lastIndexOf('/'));

const progressOptions: vscode.ProgressOptions = {
location: vscode.ProgressLocation.Notification,
title: localize('vscode-docker.commands.images.push.fetchingCreds', 'Fetching login credentials...'),
};

connectedRegistry = await vscode.window.withProgress(progressOptions, async () => await tryGetConnectedRegistryForPath(context, baseImagePath));
}

addImageTaggingTelemetry(context, fullTag, '');
// Give the user a chance to modify the tag however they want
const finalTag = await tagImage(context, node, connectedRegistry);

if (connectedRegistry && finalTag.startsWith(connectedRegistry.baseImagePath)) {
// If a registry was found/chosen and is still the same as the final tag's registry, try logging in
await vscode.commands.executeCommand('vscode-docker.registries.logInToDockerCli', connectedRegistry);
}

const terminal = ext.terminalProvider.createTerminal(fullTag);
terminal.sendText(`docker push ${fullTag}`);
addImageTaggingTelemetry(context, finalTag, '');

// Finally push the image
const terminal = ext.terminalProvider.createTerminal(finalTag);
terminal.sendText(`docker push ${finalTag}`);
terminal.show();
}

async function tryGetConnectedRegistryForPath(context: IActionContext, baseImagePath: string): Promise<RegistryTreeItemBase | undefined> {
const allRegistries = await ext.registriesRoot.getAllConnectedRegistries(context);
return allRegistries.find(r => r.baseImagePath === baseImagePath);
}

async function tryGetDockerHubRegistry(context: IActionContext): Promise<RegistryTreeItemBase | undefined> {
const allRegistries = await ext.registriesRoot.getAllConnectedRegistries(context);
return allRegistries.find(r => r.contextValue.match(registryExpectedContextValues.dockerHub.registry));
}
27 changes: 11 additions & 16 deletions src/commands/images/tagImage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@
import { Image } from 'dockerode';
import * as vscode from 'vscode';
import { IActionContext, TelemetryProperties } from 'vscode-azureextensionui';
import { configurationKeys } from '../../constants';
import { ext } from '../../extensionVariables';
import { localize } from '../../localize';
import { ImageTreeItem } from '../../tree/images/ImageTreeItem';
import { RegistryTreeItemBase } from '../../tree/registries/RegistryTreeItemBase';
import { callDockerodeWithErrorHandling } from '../../utils/callDockerodeWithErrorHandling';
import { extractRegExGroups } from '../../utils/extractRegExGroups';

export async function tagImage(context: IActionContext, node: ImageTreeItem | undefined): Promise<string> {
export async function tagImage(context: IActionContext, node?: ImageTreeItem, registry?: RegistryTreeItemBase): Promise<string> {
if (!node) {
node = await ext.imagesTree.showTreeItemPicker<ImageTreeItem>(ImageTreeItem.contextValue, {
...context,
Expand All @@ -22,7 +22,7 @@ export async function tagImage(context: IActionContext, node: ImageTreeItem | un
}

addImageTaggingTelemetry(context, node.fullTag, '.before');
let newTaggedName: string = await getTagFromUserInput(node.fullTag, true);
let newTaggedName: string = await getTagFromUserInput(node.fullTag, registry?.baseImagePath);
addImageTaggingTelemetry(context, newTaggedName, '.after');

let repo: string = newTaggedName;
Expand All @@ -39,27 +39,22 @@ export async function tagImage(context: IActionContext, node: ImageTreeItem | un
return newTaggedName;
}

export async function getTagFromUserInput(fullTag: string, addDefaultRegistry: boolean): Promise<string> {
const configOptions: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration('docker');
const defaultRegistryPath = configOptions.get(configurationKeys.defaultRegistryPath, '');

export async function getTagFromUserInput(fullTag: string, baseImagePath?: string): Promise<string> {
let opt: vscode.InputBoxOptions = {
ignoreFocusOut: true,
prompt: localize('vscode-docker.commands.images.tag.tagAs', 'Tag image as...'),
};
if (addDefaultRegistry) {
let registryLength: number = fullTag.indexOf('/');
if (defaultRegistryPath.length > 0 && registryLength < 0) {
fullTag = defaultRegistryPath + '/' + fullTag;
registryLength = defaultRegistryPath.length;
}
opt.valueSelection = registryLength < 0 ? undefined : [0, registryLength + 1]; // include the '/'

if (fullTag.includes('/')) {
opt.valueSelection = [0, fullTag.lastIndexOf('/')];
} else if (baseImagePath) {
fullTag = `${baseImagePath}/${fullTag}`;
opt.valueSelection = [0, fullTag.lastIndexOf('/')];
}

opt.value = fullTag;

const nameWithTag: string = await ext.ui.showInputBox(opt);
return nameWithTag;
return await ext.ui.showInputBox(opt);
}

const KnownRegistries: { type: string, regex: RegExp }[] = [
Expand Down
2 changes: 0 additions & 2 deletions src/commands/registerCommands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ import { logInToDockerCli } from "./registries/logInToDockerCli";
import { logOutOfDockerCli } from "./registries/logOutOfDockerCli";
import { pullImage, pullRepository } from "./registries/pullImages";
import { reconnectRegistry } from "./registries/reconnectRegistry";
import { setRegistryAsDefault } from "./registries/registrySettings";
import { configureVolumesExplorer } from "./volumes/configureVolumesExplorer";
import { inspectVolume } from "./volumes/inspectVolume";
import { pruneVolumes } from "./volumes/pruneVolumes";
Expand Down Expand Up @@ -112,7 +111,6 @@ export function registerCommands(): void {
registerWorkspaceCommand('vscode-docker.registries.pullImage', pullImage);
registerWorkspaceCommand('vscode-docker.registries.pullRepository', pullRepository);
registerCommand('vscode-docker.registries.reconnectRegistry', reconnectRegistry);
registerCommand('vscode-docker.registries.setAsDefault', setRegistryAsDefault);

registerCommand('vscode-docker.registries.dockerHub.openInBrowser', openDockerHubInBrowser);

Expand Down
2 changes: 1 addition & 1 deletion src/commands/registries/azure/tasks/scheduleRunRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ async function quickPickImageName(context: IActionContext, rootFolder: vscode.Wo
await delay(500);

addImageTaggingTelemetry(context, suggestedImageName, '.before');
const imageName: string = await getTagFromUserInput(suggestedImageName, false);
const imageName: string = await getTagFromUserInput(suggestedImageName);
addImageTaggingTelemetry(context, imageName, '.after');

await ext.context.globalState.update(dockerFileKey, imageName);
Expand Down
Loading