Skip to content

Conversation

ViveliDuCh
Copy link
Member

@ViveliDuCh ViveliDuCh commented Sep 17, 2025

Fixes #6811

Apply project name transformation in the templates' generated code using the same normalization logic as in Aspire.Hosting.AppHost.targets. This ensures that when the --aspire flag is used, the generated AppHost/Program.cs references normalized class names (e.g., some_name instead of some.name), resolving build errors caused by mismatched type or namespace references.

Add execution tests to verify the fix for templates with dots or other non-identifier characters in their names.

Microsoft Reviewers: Open in CodeFlow

…-aspire projects. Add execution tests for the fix.
@ViveliDuCh ViveliDuCh requested a review from a team as a code owner September 17, 2025 23:01
@Copilot Copilot AI review requested due to automatic review settings September 17, 2025 23:01
@github-actions github-actions bot added the area-ai-templates Microsoft.Extensions.AI.Templates label Sep 17, 2025
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR addresses build errors that occur when using the --aspire flag with project names containing dots or other non-identifier characters. The fix applies Aspire's project name normalization logic to templates to ensure generated code references match the normalized class names.

  • Adds project name normalization transforms to template configuration
  • Introduces a new test case to verify the fix works with project names containing dots
  • Ensures consistency between template-generated code and Aspire's AppHost target behavior

Reviewed Changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
AIChatWebExecutionTests.cs Adds test case for project names with dots to verify normalization fix
template.json Implements project name normalization transforms using regex replacement

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

@ViveliDuCh ViveliDuCh added the area-ai Microsoft.Extensions.AI libraries label Sep 17, 2025
"forms": {
"projectNameTransform_Normalize": {
"identifier": "replace",
"pattern": "[^a-zA-Z0-9_]",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this pattern different from the pattern that the Aspire code generator uses in their _GeneratedClassNameFixupRegex? If our pattern matches differently from theirs in any scenario, different flavors of this bug will remain.

https://github.com/dotnet/aspire/blob/d651991486f3c2fb81ea6ea7dead2fbe98260de1/src/Aspire.Hosting.AppHost/build/Aspire.Hosting.AppHost.in.targets#L8

If this pattern is a simplified version of their pattern that works completely, then the description below should be expanded to explain why our pattern is different from theirs. Imagine their pattern being updated in the future for some reason; we'd need to know how to reapply our fix here to match their updated logic.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great catch! Using the same regex in one step wasn't working for getting the same transformation but, after checking it again, I was indeed leaving some cases out with the reduction. I now split aspires regex logic in 2 steps instead and it works completely. Thanks!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Terrific. The test should cover each element of logic from the regex by using a name that matches each class of character the regex does a replace on while also verifying things that should not be matched/replaced. So any of these cases you found during re-test can be added into that name used in the unit test.

"type": "derived",
"valueSource": "name",
"valueTransform": "projectNameTransform_Formatting",
"replaces": "ChatWithCustomData_CSharp_Web"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest updating the template to use a different (Aspire-specific) token here, similar to how we have a data-ChatWithCustomData-CSharp.Web- token above, so that it's less likely we'll introduce a false-positive match on this token in the future where this replacer shouldn't apply.

Suggested change
"replaces": "ChatWithCustomData_CSharp_Web"
"replaces": "ChatWithCustomData_CSharp_Web_AspireClassName"

(And then use that token in the template source file as well).

@jeffhandley jeffhandley removed the area-ai Microsoft.Extensions.AI libraries label Sep 18, 2025
@ViveliDuCh ViveliDuCh requested a review from jozkee September 19, 2025 00:14
Comment on lines +81 to +96
[Theory]
[InlineData("Ollama")]
[InlineData("OpenAI")]
[InlineData("AzureOpenAI")]
[InlineData("GitHubModels")]
public async Task CreateRestoreAndBuild_ProjectNameVariants(string provider)
{
string[] projectNames = new[]
{
"dot.name",
"space name",
"mix.ed-dash_name 123",
".1My.Projec-",
"1Project123",
"project.123"
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should create a new MemberData method that combines projectNames with providers.

public static IEnumerable<object[]> GetProjectNameVariants()
{
    foreach (string provider in new[] { "Ollama", "OpenAI", "AzureOpenAI", "GitHubModels" })
    {
        foreach (string projectName in new[]
        {
            "dot.name",
            "space name",
            "mix.ed-dash_name 123",
            ".1My.Projec-",
            "1Project123",
            "project.123"
        })
        {
            yield return new object[] { provider, projectName };
        }
    }
}

[Theory]
[MemberData(nameof(GetProjectNameVariants))]
public async Task CreateRestoreAndBuild_ProjectNameVariants(string provider, string projectName)
{
    // ...
}

"mix.ed-dash_name 123",
".1My.Projec-",
"1Project123",
"project.123"
Copy link
Member

@jozkee jozkee Sep 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a more comprehensive theory, e.g.

// leading
.1abc // dot number is replaced with two underscores + number
.abc
#abc // alt character to dot
..abc // double non-word char
##abc
.1.2abc

// mid
abc.1abc
abc.abc
abc#abc

// trailing
// trailing dots in windows are non-standard, so we test with an alt character
abc#
abc##
abc.1
abc.1.2

// single non-word character
#

// no match
abc
_a_b_c
abc1
Abc1abc

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-ai-templates Microsoft.Extensions.AI.Templates
Projects
None yet
Development

Successfully merging this pull request may close these issues.

MEAI chat web template with --aspire flag: Class name mismatch in AppHost Program.cs causes build error
3 participants