Skip to content

Commit ccb450c

Browse files
authored
Make suggested images names unique by appending a timestamped tag (#565)
1 parent c267980 commit ccb450c

File tree

18 files changed

+44
-42
lines changed

18 files changed

+44
-42
lines changed

src/commands/deployWorkspaceProject/DeployWorkspaceProjectConfirmStep.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ export class DeployWorkspaceProjectConfirmStep extends OverwriteConfirmStepBase<
4141
resourcesToCreate.join(', ')
4242
);
4343
} else {
44-
confirmMessage = localize('overwriteConfirm', 'The latest deployment of container app "{0}" and the latest image of registry "{1}" will be overwritten.', context.containerApp?.name, context.registry?.name);
45-
outputMessage = localize('overwriteConfirmed', 'User confirmed overwrite of container app "{0}" and the latest image of registry "{1}".', context.containerApp?.name, context.registry?.name);
44+
confirmMessage = localize('overwriteConfirm', 'The latest deployment of container app "{0}" will be overwritten.', context.containerApp?.name);
45+
outputMessage = localize('overwriteConfirmed', 'User confirmed overwrite of container app "{0}".', context.containerApp?.name);
4646
}
4747

4848
if (this.hasUnsupportedFeatures(context)) {

src/commands/deployWorkspaceProject/DeployWorkspaceProjectContext.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import type { SetTelemetryProps } from "../../telemetry/SetTelemetryProps";
88
import type { DeployWorkspaceProjectTelemetryProps as TelemetryProps } from "../../telemetry/commandTelemetryProps";
99
import type { CreateContainerAppBaseContext } from "../createContainerApp/CreateContainerAppContext";
1010
import type { IManagedEnvironmentContext } from "../createManagedEnvironment/IManagedEnvironmentContext";
11-
import { BuildImageInAzureImageSourceBaseContext } from "../image/imageSource/buildImageInAzure/BuildImageInAzureContext";
11+
import { BuildImageInAzureImageSourceBaseContext } from "../image/imageSource/buildImageInAzure/BuildImageInAzureImageSourceContext";
1212
import type { CreateAcrContext } from "../image/imageSource/containerRegistry/acr/createAcr/CreateAcrContext";
1313

1414
// Use intersection typing instead of an interface here to bypass some minor (relatively trivial) type mismatch issues introduced by having to use the 'Partial' utility

src/commands/deployWorkspaceProject/getDefaultValues/DefaultResourcesNameStep.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { localize } from "../../../utils/localize";
1111
import { validateUtils } from "../../../utils/validateUtils";
1212
import { ContainerAppNameStep } from "../../createContainerApp/ContainerAppNameStep";
1313
import { ManagedEnvironmentNameStep } from "../../createManagedEnvironment/ManagedEnvironmentNameStep";
14+
import { ImageNameStep } from "../../image/imageSource/buildImageInAzure/ImageNameStep";
1415
import { RegistryNameStep } from "../../image/imageSource/containerRegistry/acr/createAcr/RegistryNameStep";
1516
import type { DeployWorkspaceProjectContext } from "../DeployWorkspaceProjectContext";
1617

@@ -32,7 +33,7 @@ export class DefaultResourcesNameStep extends AzureWizardPromptStep<DeployWorksp
3233
!context.resourceGroup && (context.newResourceGroupName = resourceBaseName);
3334
!context.managedEnvironment && (context.newManagedEnvironmentName = resourceBaseName);
3435
!context.containerApp && (context.newContainerAppName = resourceBaseName);
35-
context.imageName = `${resourceBaseName}:latest`;
36+
context.imageName = ImageNameStep.getTimestampedImageName(context.containerApp?.name || resourceBaseName);
3637
}
3738

3839
public async configureBeforePrompt(context: DeployWorkspaceProjectContext): Promise<void> {
@@ -55,7 +56,7 @@ export class DefaultResourcesNameStep extends AzureWizardPromptStep<DeployWorksp
5556
!context.managedEnvironment && (context.newManagedEnvironmentName = workspaceName);
5657
!context.registry && (context.newRegistryName = await RegistryNameStep.tryGenerateRelatedName(context, workspaceName));
5758
!context.containerApp && (context.newContainerAppName = workspaceName);
58-
context.imageName = `${context.containerApp?.name || workspaceName}:latest`;
59+
context.imageName = ImageNameStep.getTimestampedImageName(context.containerApp?.name || workspaceName);
5960
}
6061

6162
public shouldPrompt(context: DeployWorkspaceProjectContext): boolean {

src/commands/deployWorkspaceProject/getDefaultValues/getDefaultAcrResources.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@
44
*--------------------------------------------------------------------------------------------*/
55

66
import type { Registry } from "@azure/arm-containerregistry";
7-
import type { ISubscriptionActionContext } from "@microsoft/vscode-azext-utils";
7+
import { nonNullProp, type ISubscriptionActionContext } from "@microsoft/vscode-azext-utils";
88
import { ext } from "../../../extensionVariables";
99
import type { ContainerAppItem } from "../../../tree/ContainerAppItem";
1010
import type { ManagedEnvironmentItem } from "../../../tree/ManagedEnvironmentItem";
1111
import { localize } from "../../../utils/localize";
12+
import { ImageNameStep } from "../../image/imageSource/buildImageInAzure/ImageNameStep";
1213
import { AcrListStep } from "../../image/imageSource/containerRegistry/acr/AcrListStep";
1314
import { DeployWorkspaceProjectSettings } from "../deployWorkspaceProjectSettings";
1415
import { triggerSettingsOverride } from "./getDefaultContextValues";
@@ -36,7 +37,7 @@ export async function getDefaultAcrResources(
3637
ext.outputChannel.appendLog(localize('foundResourceMatch', 'Used saved workspace settings and found an existing container registry.'));
3738
return {
3839
registry: savedRegistry,
39-
imageName: `${settings.containerAppName || savedRegistry.name}:latest`
40+
imageName: ImageNameStep.getTimestampedImageName(settings.containerAppName || nonNullProp(savedRegistry, 'name'))
4041
};
4142
} else {
4243
ext.outputChannel.appendLog(localize('noResourceMatch', 'Used saved workspace settings to search for Azure Container Registry "{0}" but found no match.', settings.containerRegistryName));

src/commands/image/imageSource/buildImageInAzure/BuildImageInAzureContext.ts renamed to src/commands/image/imageSource/buildImageInAzure/BuildImageInAzureImageSourceContext.ts

File renamed without changes.

src/commands/image/imageSource/buildImageInAzure/BuildImageStep.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { acrDomain, activityFailContext, activityFailIcon, activitySuccessContex
1010
import { ExecuteActivityOutput, ExecuteActivityOutputStepBase } from "../../../../utils/activity/ExecuteActivityOutputStepBase";
1111
import { createActivityChildContext } from "../../../../utils/activity/activityUtils";
1212
import { localize } from "../../../../utils/localize";
13-
import { BuildImageInAzureImageSourceContext } from "./BuildImageInAzureContext";
13+
import { BuildImageInAzureImageSourceContext } from "./BuildImageInAzureImageSourceContext";
1414
import { buildImageInAzure } from "./buildImageInAzure";
1515

1616
export class BuildImageStep extends ExecuteActivityOutputStepBase<BuildImageInAzureImageSourceContext> {

src/commands/image/imageSource/buildImageInAzure/DockerFileItemStep.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import { AzureWizardPromptStep, nonNullValue } from '@microsoft/vscode-azext-utils';
77
import { dockerFilePick, dockerfileGlobPattern } from "../../../../constants";
88
import { selectWorkspaceFile } from "../../../../utils/workspaceUtils";
9-
import { BuildImageInAzureImageSourceContext } from './BuildImageInAzureContext';
9+
import { BuildImageInAzureImageSourceContext } from './BuildImageInAzureImageSourceContext';
1010

1111
export class DockerFileItemStep extends AzureWizardPromptStep<BuildImageInAzureImageSourceContext> {
1212
public async prompt(context: BuildImageInAzureImageSourceContext): Promise<void> {

src/commands/image/imageSource/buildImageInAzure/ImageNameStep.ts

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,21 @@
33
* Licensed under the MIT License. See License.md in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6-
import { AzureWizardPromptStep } from "@microsoft/vscode-azext-utils";
7-
import { URI, Utils } from "vscode-uri";
6+
import { AzureWizardPromptStep, nonNullProp } from "@microsoft/vscode-azext-utils";
87
import { localize } from "../../../../utils/localize";
98
import { validateUtils } from "../../../../utils/validateUtils";
10-
import { BuildImageInAzureImageSourceContext } from "./BuildImageInAzureContext";
9+
import { CreateContainerAppContext } from "../../../createContainerApp/CreateContainerAppContext";
10+
import type { BuildImageInAzureImageSourceContext } from "./BuildImageInAzureImageSourceContext";
1111

1212
const maxImageNameLength: number = 46;
1313

1414
export class ImageNameStep extends AzureWizardPromptStep<BuildImageInAzureImageSourceContext> {
1515
public async prompt(context: BuildImageInAzureImageSourceContext): Promise<void> {
16-
const suggestedImageName = await getSuggestedName(context, context.rootFolder.name);
16+
const suggestedImageName = ImageNameStep.getTimestampedImageName(
17+
context.containerApp?.name ||
18+
// Step is also technically reachable from the `createContainerApp` entry point
19+
nonNullProp((context as CreateContainerAppContext), 'newContainerAppName')
20+
);
1721

1822
context.imageName = (await context.ui.showInputBox({
1923
prompt: localize('imageNamePrompt', 'Enter a name for the image'),
@@ -35,19 +39,22 @@ export class ImageNameStep extends AzureWizardPromptStep<BuildImageInAzureImageS
3539

3640
return undefined;
3741
}
38-
}
3942

40-
async function getSuggestedName(context: BuildImageInAzureImageSourceContext, dockerFilePath: string): Promise<string | undefined> {
41-
let suggestedImageName: string | undefined;
42-
suggestedImageName = Utils.dirname(URI.parse(dockerFilePath)).path.split('/').pop();
43-
if (suggestedImageName === '') {
44-
if (context.rootFolder) {
45-
suggestedImageName = Utils.basename(context.rootFolder.uri).toLowerCase().replace(/\s/g, '');
46-
}
43+
static getTimestampedImageName(repositoryName: string): string {
44+
const tag: string = getTimestampTag();
45+
return repositoryName.slice(0, maxImageNameLength - (tag.length + 1)) + ':' + tag;
4746
}
47+
}
48+
49+
function getTimestampTag(): string {
50+
const now = new Date();
51+
52+
const year = now.getFullYear();
53+
const month = (now.getMonth() + 1).toString().padStart(2, '0'); // Months start at 0, so add 1
54+
const day = now.getDate().toString().padStart(2, '0');
55+
const hours = now.getHours().toString().padStart(2, '0');
56+
const minutes = now.getMinutes().toString().padStart(2, '0');
57+
const seconds = now.getSeconds().toString().padStart(2, '0');
4858

49-
const colonTag: string = ':latest';
50-
suggestedImageName = suggestedImageName?.slice(0, maxImageNameLength - colonTag.length);
51-
suggestedImageName += colonTag;
52-
return suggestedImageName;
59+
return `${year}-${month}-${day}_${hours}${minutes}${seconds}`;
5360
}

src/commands/image/imageSource/buildImageInAzure/OSPickStep.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
import { AzureWizardPromptStep, IAzureQuickPickItem } from "@microsoft/vscode-azext-utils";
77
import { localize } from "../../../../utils/localize";
8-
import { BuildImageInAzureImageSourceContext } from "./BuildImageInAzureContext";
8+
import { BuildImageInAzureImageSourceContext } from "./BuildImageInAzureImageSourceContext";
99

1010
export enum AcrBuildSupportedOS {
1111
Windows = 'Windows',

src/commands/image/imageSource/buildImageInAzure/RootFolderStep.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { AzureWizardPromptStep, UserCancelledError } from '@microsoft/vscode-aze
66
import * as vscode from 'vscode';
77
import { isAzdWorkspaceProject } from '../../../../utils/azdUtils';
88
import { localize } from '../../../../utils/localize';
9-
import { BuildImageInAzureImageSourceContext } from './BuildImageInAzureContext';
9+
import { BuildImageInAzureImageSourceContext } from './BuildImageInAzureImageSourceContext';
1010

1111
export class RootFolderStep extends AzureWizardPromptStep<BuildImageInAzureImageSourceContext> {
1212
public async prompt(context: BuildImageInAzureImageSourceContext): Promise<void> {

0 commit comments

Comments
 (0)