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

.NET SDK Build Prompt For Project File Every Time #3993

Merged
merged 16 commits into from
Jul 11, 2023
Merged
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
12 changes: 12 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -1524,6 +1524,18 @@
"required": [
"appProject"
]
},
"dockerRun": {
"description": "%vscode-docker.tasks.dotnet-container-sdk.dockerRun.description%",
"properties": {
"containerName": {
"type": "string",
"description": "%vscode-docker.tasks.dotnet-container-sdk.dockerRun.containerName%"
}
},
"required": [
"containerName"
]
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@
"vscode-docker.tasks.docker-compose.dockerCompose.envFile.description": "File of environment variables read in and applied to the Docker containers.",
"vscode-docker.tasks.docker-compose.dockerCompose.files.description": "The docker-compose files to include, in order.",
"vscode-docker.tasks.docker-compose.dockerCompose.projectName.description": "Alternate project name to use when naming and labeling Docker objects. If using an alternate project name when composing up, the same project name must be specified when composing down.",
"vscode-docker.tasks.dotnet-container-sdk.dockerRun.description": "Options for running the Docker container used for debugging.",
"vscode-docker.tasks.dotnet-container-sdk.dockerRun.containerName": "Name of the container used for debugging.",
"vscode-docker.config.docker.promptForRegistryWhenPushingImages": "Prompt for registry selection 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.",
Expand Down
7 changes: 6 additions & 1 deletion resources/netCore/GetProjectProperties.targets
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,18 @@
</PropertyGroup>

<Target Name="GetProjectProperties" DependsOnTargets="$(GetProjectPropertiesDependsOn)">
<PropertyGroup>
<InferImageName>$(ContainerRepository)</InferImageName>
<InferImageName Condition=" '$(InferImageName)' == '' ">$(ContainerImageName)</InferImageName>
</PropertyGroup>
<WriteLinesToFile
File="$(InfoOutputPath)"
Lines="$(AssemblyName).dll
$(TargetFramework)$(TargetFrameworks.Split(';')[0])
$(OutputPath)$(AssemblyName).dll
$(ContainerWorkingDirectory)/$(AssemblyName).dll
$(SDKContainerSupportEnabled)"
$(SDKContainerSupportEnabled)
$(InferImageName)"
Overwrite="True" />
</Target>
</Project>
152 changes: 74 additions & 78 deletions src/debugging/netSdk/NetSdkDebugHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,31 @@
* Licensed under the MIT License. See LICENSE.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { IActionContext } from "@microsoft/vscode-azext-utils";
import * as path from "path";
import { Uri, WorkspaceFolder, commands, l10n, tasks } from "vscode";
import { WorkspaceFolder, commands, l10n, tasks } from "vscode";
import { ext } from "../../extensionVariables";
import { NetChooseBuildTypeContext, netContainerBuild } from "../../scaffolding/wizard/net/NetContainerBuild";
import { AllNetContainerBuildOptions, NetContainerBuildOptionsKey } from "../../scaffolding/wizard/net/NetSdkChooseBuildStep";
import { getDefaultContainerName } from "../../tasks/TaskHelper";
import { netSdkRunTaskProvider } from "../../tasks/netSdk/NetSdkRunTaskProvider";
import { NetSdkRunTaskDefinition, netSdkRunTaskProvider } from "../../tasks/netSdk/NetSdkRunTaskProvider";
import { normalizeArchitectureToRidArchitecture, normalizeOsToRidOs } from "../../tasks/netSdk/netSdkTaskUtils";
import { NetCoreTaskHelper } from "../../tasks/netcore/NetCoreTaskHelper";
import { getNetCoreProjectInfo } from "../../utils/netCoreUtils";
import { getDockerOSType } from "../../utils/osUtils";
import { PlatformOS } from "../../utils/platform";
import { quickPickProjectFileItem } from "../../utils/quickPickFile";
import { unresolveWorkspaceFolder } from "../../utils/resolveVariables";
import { DockerDebugContext, DockerDebugScaffoldContext } from "../DebugHelper";
import { resolveVariables, unresolveWorkspaceFolder } from "../../utils/resolveVariables";
import { DockerDebugContext, DockerDebugScaffoldContext, ResolvedDebugConfiguration, inferContainerName } from "../DebugHelper";
import { DockerDebugConfiguration } from "../DockerDebugConfigurationProvider";
import { NetCoreDebugHelper, NetCoreDebugScaffoldingOptions } from "../netcore/NetCoreDebugHelper";
import { NetCoreDebugHelper, NetCoreDebugScaffoldingOptions, NetCoreProjectProperties } from "../netcore/NetCoreDebugHelper";

export class NetSdkDebugHelper extends NetCoreDebugHelper {

private static projPath: string | undefined;
interface NetSdkProjectProperties extends NetCoreProjectProperties {
containerWorkingDirectory: string;
isSdkContainerSupportEnabled: boolean;
imageName: string;
}

public async provideDebugConfigurations(context: DockerDebugScaffoldContext, options?: NetCoreDebugScaffoldingOptions): Promise<DockerDebugConfiguration[]> {
export class NetSdkDebugHelper extends NetCoreDebugHelper {

public override async provideDebugConfigurations(context: DockerDebugScaffoldContext, options?: NetCoreDebugScaffoldingOptions): Promise<DockerDebugConfiguration[]> {
const configurations: DockerDebugConfiguration[] = [];

const netCoreBuildContext: NetChooseBuildTypeContext = {
Expand All @@ -35,16 +36,17 @@ export class NetSdkDebugHelper extends NetCoreDebugHelper {
workspaceFolder: context.folder,
};

await netContainerBuild(netCoreBuildContext);
await netContainerBuild(netCoreBuildContext); // prompt user whether to use .NET container SDK build
if (netCoreBuildContext?.containerBuildOptions === AllNetContainerBuildOptions[1]) {
const appProjectAbsolutePath = await this.inferProjPath(context.actionContext, context.folder);
options = options || {};
options.appProject = options.appProject || await NetCoreTaskHelper.inferAppProject(context); // This method internally checks the user-defined input first

configurations.push({
name: 'Docker .NET Container SDK Launch',
type: 'docker',
request: 'launch',
netCore: {
appProject: unresolveWorkspaceFolder(appProjectAbsolutePath, context.folder),
appProject: unresolveWorkspaceFolder(options.appProject, context.folder),
buildWithSdk: true,
},
});
Expand All @@ -55,67 +57,47 @@ export class NetSdkDebugHelper extends NetCoreDebugHelper {
return configurations;
}

public async afterResolveDebugConfiguration(context: DockerDebugContext, debugConfiguration: DockerDebugConfiguration): Promise<void> {
const { task, promise } = netSdkRunTaskProvider.createNetSdkRunTask(
{
netCore: {
appProject: await this.inferProjPath(context.actionContext, context.folder),
}
}
);

await tasks.executeTask(task);

public override async resolveDebugConfiguration(context: DockerDebugContext, debugConfiguration: DockerDebugConfiguration): Promise<ResolvedDebugConfiguration | undefined> {
try {
await promise;
return await super.resolveDebugConfiguration(context, debugConfiguration);
} catch (error) {
await NetSdkDebugHelper.clearWorkspaceState();
await ext.context.workspaceState.update(NetContainerBuildOptionsKey, '');
throw error;
}
}

public static async clearWorkspaceState(): Promise<void> {
await ext.context.workspaceState.update(NetContainerBuildOptionsKey, ''); // clear the workspace state
NetSdkDebugHelper.projPath = undefined; // clear the static projPath variable
return Promise.resolve();
}

protected override async inferAppOutput(debugConfiguration: DockerDebugConfiguration): Promise<string> {
const ridOS = await normalizeOsToRidOs();
const ridArchitecture = await normalizeArchitectureToRidArchitecture();
const additionalProperties = `/p:ContainerRuntimeIdentifier="${ridOS}-${ridArchitecture}"`;

let projectInfo: string[];
try {
projectInfo = await getNetCoreProjectInfo('GetProjectProperties', debugConfiguration.netCore?.appProject, additionalProperties);
} catch (error) {
await NetSdkDebugHelper.clearWorkspaceState();
throw error;
}

if (projectInfo.length >= 5) { // if .NET has support for SDK Build
// fifth is whether .NET Web apps supports SDK Containers
if (projectInfo[4] === 'true') {
return ridOS === 'win' // fourth is output path
? path.win32.normalize(projectInfo[3])
: path.posix.normalize(projectInfo[3]);
} else {
await NetSdkDebugHelper.clearWorkspaceState();
throw new Error(l10n.t("Your current project configuration or .NET SDK version doesn't support SDK Container build. Please choose a compatible project or update .NET SDK."));
public async afterResolveDebugConfiguration(context: DockerDebugContext, debugConfiguration: DockerDebugConfiguration): Promise<void> {
const runDefinition: Omit<NetSdkRunTaskDefinition, "type"> = {
netCore: {
appProject: debugConfiguration.netCore.appProject,
},
dockerRun: {
image: context.runDefinition.dockerRun.image,
}
}
};

await NetSdkDebugHelper.clearWorkspaceState();
throw new Error(l10n.t('Unable to determine assembly output path.'));
const { task, promise } = netSdkRunTaskProvider.createNetSdkRunTask(runDefinition);
await tasks.executeTask(task);
await promise;
}

protected override async loadExternalInfo(context: DockerDebugContext, debugConfiguration: DockerDebugConfiguration): Promise<{ configureSsl: boolean, containerName: string, platformOS: PlatformOS }> {
NetSdkDebugHelper.projPath = debugConfiguration.netCore.appProject;
const projectProperties = await this.getProjectProperties(debugConfiguration, context.folder);
debugConfiguration.netCore.appOutput = await this.normalizeAppOutput(projectProperties.containerWorkingDirectory, projectProperties.isSdkContainerSupportEnabled);
context.runDefinition = {
...context.runDefinition,
dockerRun: {
containerName: inferContainerName(debugConfiguration, context, projectProperties.imageName, "dev"),
image: projectProperties.imageName,
},
netCore: {
enableDebugging: true,
}
};

const associatedTask = context.runDefinition;
return {
configureSsl: !!(associatedTask?.netCore?.configureSsl),
containerName: this.inferDotNetSdkContainerName(debugConfiguration),
configureSsl: false,
containerName: context.runDefinition.dockerRun.containerName,
platformOS: await getDockerOSType() === "windows" ? 'Windows' : 'Linux',
};
}
Expand All @@ -124,24 +106,38 @@ export class NetSdkDebugHelper extends NetCoreDebugHelper {
return appOutput;
}

private inferDotNetSdkContainerName(debugConfiguration: DockerDebugConfiguration): string {
const projFileUri = Uri.file(path.dirname(debugConfiguration.netCore.appProject));
return getDefaultContainerName(path.basename(projFileUri.fsPath), "dev");
}
protected override async getProjectProperties(debugConfiguration: DockerDebugConfiguration, folder?: WorkspaceFolder): Promise<NetSdkProjectProperties> {
const ridOS = await normalizeOsToRidOs();
const ridArchitecture = await normalizeArchitectureToRidArchitecture();
const additionalProperties = `/p:ContainerRuntimeIdentifier="${ridOS}-${ridArchitecture}"`;
const resolvedAppProject = resolveVariables(debugConfiguration.netCore?.appProject, folder);

const projectInfo = await getNetCoreProjectInfo('GetProjectProperties', resolvedAppProject, additionalProperties);

/**
* @returns the project path stored in the static variable projPath if it exists,
* otherwise prompts the user to select a .csproj file and stores the path
* in the static variable projPath
*/
private async inferProjPath(context: IActionContext, folder: WorkspaceFolder): Promise<string> {
if (NetSdkDebugHelper.projPath) {
return NetSdkDebugHelper.projPath;
if (projectInfo.length < 6 || !projectInfo[5]) {
throw new Error(l10n.t("Your current project configuration or .NET SDK version doesn't support SDK Container build. Please choose a compatible project or update .NET SDK."));
}

const projFileItem = await quickPickProjectFileItem(context, undefined, folder, 'No project file could be found.');
NetSdkDebugHelper.projPath = projFileItem.absoluteFilePath; // save the path for future use
return projFileItem.absoluteFilePath;
const projectProperties: NetSdkProjectProperties = {
assemblyName: projectInfo[0],
targetFramework: projectInfo[1],
appOutput: projectInfo[2],
containerWorkingDirectory: projectInfo[3],
isSdkContainerSupportEnabled: projectInfo[4] === 'true',
imageName: projectInfo[5],
};

return projectProperties;
}

private async normalizeAppOutput(unnormalizedContainerWorkingDirectory: string, isSdkContainerSupportEnabled: boolean): Promise<string> {
if (isSdkContainerSupportEnabled) {
return await getDockerOSType() === 'windows' // fourth is output path
? path.win32.normalize(unnormalizedContainerWorkingDirectory)
: path.posix.normalize(unnormalizedContainerWorkingDirectory);
} else {
throw new Error(l10n.t("Your current project configuration or .NET SDK version doesn't support SDK Container build. Please choose a compatible project or update .NET SDK."));
}
}
}

Expand Down
32 changes: 25 additions & 7 deletions src/debugging/netcore/NetCoreDebugHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ export interface NetCoreDebugScaffoldingOptions {
appProject?: string;
}

export interface NetCoreProjectProperties {
assemblyName: string;
targetFramework: string;
appOutput: string;
}

export class NetCoreDebugHelper implements DebugHelper {
public async provideDebugConfigurations(context: DockerDebugScaffoldContext, options?: NetCoreDebugScaffoldingOptions): Promise<DockerDebugConfiguration[]> {
options = options || {};
Expand Down Expand Up @@ -178,13 +184,8 @@ export class NetCoreDebugHelper implements DebugHelper {
}

protected async inferAppOutput(debugConfiguration: DockerDebugConfiguration): Promise<string> {
const projectInfo = await getNetCoreProjectInfo('GetProjectProperties', debugConfiguration.netCore?.appProject);

if (projectInfo.length < 3) {
throw new Error(l10n.t('Unable to determine assembly output path.'));
}

return projectInfo[2]; // First line is assembly name, second is target framework, third+ are output path(s)
const projectProperties = await this.getProjectProperties(debugConfiguration);
return projectProperties.appOutput;
}

protected inferAppContainerOutput(appOutput: string, platformOS: PlatformOS): string {
Expand All @@ -205,6 +206,23 @@ export class NetCoreDebugHelper implements DebugHelper {
};
}

protected async getProjectProperties(debugConfiguration: DockerDebugConfiguration): Promise<NetCoreProjectProperties> {
const projectInfo = await getNetCoreProjectInfo('GetProjectProperties', debugConfiguration.netCore?.appProject);

if (projectInfo.length < 3) {
throw new Error(l10n.t('Unable to determine assembly output path.'));
}

// First line is assembly name, second is target framework, third+ are output path(s)
const projectProperties: NetCoreProjectProperties = {
assemblyName: projectInfo[0],
targetFramework: projectInfo[1],
appOutput: projectInfo[2]
};

return projectProperties;
}

private async acquireDebuggers(platformOS: PlatformOS): Promise<void> {
await window.withProgress(
{
Expand Down
9 changes: 4 additions & 5 deletions src/tasks/netSdk/NetSdkRunTaskProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { NetSdkRunTaskType, getNetSdkBuildCommand, getNetSdkRunCommand } from '.

const NetSdkDebugTaskName = 'debug';

export type NetSdkRunTask = NetCoreRunTaskDefinition;
export type NetSdkRunTaskDefinition = NetCoreRunTaskDefinition;

export class NetSdkRunTaskProvider extends DockerTaskProvider {

Expand All @@ -28,10 +28,9 @@ export class NetSdkRunTaskProvider extends DockerTaskProvider {
const projectPath = task.definition.netCore?.appProject;
const isProjectWebApp = await NetCoreTaskHelper.isWebApp(projectPath);
const projectFolderPath = path.dirname(projectPath);
const projectFolderName = path.basename(projectFolderPath);

// use dotnet to build the image
const buildCommand = await getNetSdkBuildCommand(isProjectWebApp, projectFolderName);
const buildCommand = await getNetSdkBuildCommand(isProjectWebApp, task.definition.dockerRun.image);
await context.terminal.execAsyncInTerminal(
buildCommand,
{
Expand All @@ -42,7 +41,7 @@ export class NetSdkRunTaskProvider extends DockerTaskProvider {
);

// use docker run to run the image
const runCommand = await getNetSdkRunCommand(isProjectWebApp, projectFolderName);
const runCommand = await getNetSdkRunCommand(isProjectWebApp, task.definition.dockerRun.image);
await context.terminal.execAsyncInTerminal(
runCommand,
{
Expand All @@ -55,7 +54,7 @@ export class NetSdkRunTaskProvider extends DockerTaskProvider {
return Promise.resolve();
}

public createNetSdkRunTask(options?: Omit<NetSdkRunTask, "type">): { task: Task, promise: Promise<number> } {
public createNetSdkRunTask(options?: Omit<NetSdkRunTaskDefinition, "type">): { task: Task, promise: Promise<number> } {
let task: Task;
const definition = {
...options,
Expand Down
Loading