Skip to content

Commit 57d1500

Browse files
alexweiningerCopilotNathan Turinski
authored
Fix containerized .NET project creation failing with exit code 73 (#4956)
* Fix containerized .NET project creation failing with exit code 73 When creating a containerized .NET project, `func init` runs first and creates files (.gitignore, .csproj, Program.cs, host.json, etc.). The subsequent `dotnet new func` command then fails with exit code 73 because those files already exist. Pass `--force` to `dotnet new` when creating containerized projects so it overwrites the files created by the preceding `func init` step. Fixes #4955 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address PR feedback: use withFlagArg and always pass --force - Use withFlagArg('--force', ...) instead of withArg('--force') as it is purpose-built for boolean flags. - Always pass force: true since the non-containerized path already prompts the user via confirmOverwriteExisting before reaching dotnet new, so --force is needed to match the confirmed intent. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Changes so that we are not force overwriting the files --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Nathan Turinski <naturins@microsoft.comm>
1 parent 35a2105 commit 57d1500

File tree

1 file changed

+36
-13
lines changed

1 file changed

+36
-13
lines changed

src/commands/createNewProject/ProjectCreateStep/DotnetProjectCreateStep.ts

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6-
import { AzExtFsExtra, DialogResponses, nonNullValueAndProp, type IActionContext } from '@microsoft/vscode-azext-utils';
6+
import { AzExtFsExtra, DialogResponses, nonNullValueAndProp, randomUtils, type IActionContext } from '@microsoft/vscode-azext-utils';
77
import { composeArgs, withArg, withNamedArg } from '@microsoft/vscode-processutils';
8+
import * as fs from 'fs';
9+
import * as os from 'os';
810
import * as path from 'path';
911
import { getMajorVersion, type FuncVersion } from '../../../FuncVersion';
1012
import { ConnectionKey, ProjectLanguage, gitignoreFileName, hostFileName, localSettingsFileName } from '../../../constants';
@@ -35,18 +37,7 @@ export class DotnetProjectCreateStep extends ProjectCreateStepBase {
3537
const projName: string = projectName + language === ProjectLanguage.FSharp ? '.fsproj' : '.csproj';
3638

3739
const workerRuntime = nonNullProp(context, 'workerRuntime');
38-
// For containerized function apps we need to call func init before intialization as we want the .csproj file to be overwritten with the correct version
39-
// currentely the version created by func init is behind the template version
40-
if (context.containerizedProject) {
41-
const runtime = context.workerRuntime?.capabilities.includes('isolated') ? 'dotnet-isolated' : 'dotnet';
42-
const args = composeArgs(
43-
withArg('init'),
44-
withNamedArg('--worker-runtime', runtime),
45-
withNamedArg('--target-framework', runtime === 'dotnet' ? undefined : nonNullValueAndProp(context.workerRuntime, 'targetFramework')), // targetFramework is only supported for dotnet-isolated projects
46-
withArg('--docker'),
47-
)();
48-
await cpUtils.executeCommand(ext.outputChannel, context.projectPath, "func", args);
49-
} else {
40+
if (!context.containerizedProject) {
5041
await this.confirmOverwriteExisting(context, projName);
5142
}
5243

@@ -66,8 +57,40 @@ export class DotnetProjectCreateStep extends ProjectCreateStepBase {
6657
templateArgs.Framework = context.workerRuntime.targetFramework;
6758
}
6859

60+
// Create the project from the .NET template first so files are written into a clean directory
6961
await executeDotnetTemplateCreate(context, version, projTemplateKey, context.projectPath, identity, templateArgs);
7062

63+
// For containerized projects, generate the Dockerfile by running func init --docker in an
64+
// isolated temp directory, then copy only the Dockerfile into the project. This avoids
65+
// func init's internal dotnet new conflicting with files already created by the template.
66+
if (context.containerizedProject) {
67+
const runtime = context.workerRuntime?.capabilities.includes('isolated') ? 'dotnet-isolated' : 'dotnet';
68+
const tempDir = path.join(os.tmpdir(), `azfunc-docker-${randomUtils.getRandomHexString()}`);
69+
try {
70+
await fs.promises.mkdir(tempDir, { recursive: true });
71+
const args = composeArgs(
72+
withArg('init'),
73+
withNamedArg('--worker-runtime', runtime),
74+
withNamedArg('--target-framework', runtime === 'dotnet' ? undefined : nonNullValueAndProp(context.workerRuntime, 'targetFramework')),
75+
withArg('--docker'),
76+
)();
77+
await cpUtils.executeCommand(ext.outputChannel, tempDir, "func", args);
78+
79+
// Copy only the Dockerfile (and .dockerignore if present) into the project
80+
for (const file of ['Dockerfile', '.dockerignore']) {
81+
const src = path.join(tempDir, file);
82+
const dest = path.join(context.projectPath, file);
83+
try {
84+
await fs.promises.copyFile(src, dest);
85+
} catch {
86+
// .dockerignore may not exist, that's fine
87+
}
88+
}
89+
} finally {
90+
await fs.promises.rm(tempDir, { recursive: true, force: true }).catch(() => { /* best-effort cleanup */ });
91+
}
92+
}
93+
7194
await setLocalAppSetting(context, context.projectPath, ConnectionKey.Storage, '', MismatchBehavior.Overwrite);
7295
}
7396

0 commit comments

Comments
 (0)