Skip to content

Refactor name validation to NameValidationPolicyAnnotation with configurable rules#15728

Merged
JamesNK merged 5 commits intomainfrom
jamesnk/name-validation-suppression-v2
Apr 9, 2026
Merged

Refactor name validation to NameValidationPolicyAnnotation with configurable rules#15728
JamesNK merged 5 commits intomainfrom
jamesnk/name-validation-suppression-v2

Conversation

@JamesNK
Copy link
Copy Markdown
Member

@JamesNK JamesNK commented Mar 31, 2026

Summary

Replaces the boolean SuppressNameValidationAnnotation and the skipValidation constructor parameter on Resource/ExecutableResource with a configurable NameValidationPolicyAnnotation that can individually control each validation rule.

Changes

New: NameValidationPolicyAnnotation

  • Immutable annotation (init-only properties) for customizing name validation rules per resource
  • Properties: MaxLength, ValidateStartsWithLetter, ValidateAllowedCharacters, ValidateNoConsecutiveHyphens, ValidateNoTrailingHyphen
  • Static well-known policies:
    • Default — enforces all standard rules
    • None — disables all rules (replaces the old suppress annotation)

Validation moved to DistributedApplicationBuilder

  • Name validation now happens in AddResource() and Build() via a ValidateResourceName helper
  • The helper reads the NameValidationPolicyAnnotation from the resource (falling back to Default) and passes its settings to ModelName.ValidateName
  • Resource constructor no longer validates names (just rejects null/whitespace)
  • Removed skipValidation internal constructor from Resource and ExecutableResource

ModelName updates

  • Added public default-value constants: DefaultMaxLength (64), DefaultValidateStartsWithLetter, DefaultValidateAllowedCharacters, DefaultValidateNoConsecutiveHyphens, DefaultValidateNoTrailingHyphen
  • Added ValidateName/TryValidateName overloads that accept individual rule parameters
  • Simple overloads and NameValidationPolicyAnnotation property initializers reference these constants as the single source of truth

Usage sites updated

  • PythonInstallerResource, PythonVenvCreatorResource, JavaScriptInstallerResource, ProjectRebuilderResource now add NameValidationPolicyAnnotation.None as an annotation instead of using skipValidation: true or SuppressNameValidationAnnotation

Tests

  • Tests assert Assert.Same(NameValidationPolicyAnnotation.None, policy) to verify the exact static instance
  • Builder tests verify name validation with and without the annotation

Copilot AI review requested due to automatic review settings March 31, 2026 06:22
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 31, 2026

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 15728

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 15728"

@JamesNK JamesNK added the area-app-model Issues pertaining to the APIs in Aspire.Hosting, e.g. DistributedApplication label Mar 31, 2026
Copy link
Copy Markdown
Contributor

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 moves resource name validation (64-character limit enforced by ModelName.ValidateName) out of Resource construction and into DistributedApplicationBuilder.AddResource() and Build(), adding an opt-out marker annotation (SuppressNameValidationAnnotation) for internal “suffix-appending” resources like rebuilders/installers.

Changes:

  • Remove name validation from Resource/ExecutableResource constructors and validate in DistributedApplicationBuilder.AddResource() and Build().
  • Introduce SuppressNameValidationAnnotation and apply it to rebuilder/installer resources to avoid failures when suffixes push names over 64 characters.
  • Add/adjust tests to cover AddResource/Build validation and suppression behavior, plus annotation presence on affected resources.

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tests/Aspire.Hosting.Tests/ProjectResourceTests.cs Adds test asserting the project rebuilder resource carries the suppression annotation.
tests/Aspire.Hosting.Tests/ModelNameTests.cs Removes prior test that relied on constructor-level validation behavior.
tests/Aspire.Hosting.Tests/DistributedApplicationBuilderTests.cs Adds tests for name validation in AddResource/Build, including suppression opt-out.
tests/Aspire.Hosting.Python.Tests/AddPythonAppTests.cs Adds tests asserting Python installer/venv creator resources carry the suppression annotation.
tests/Aspire.Hosting.JavaScript.Tests/PackageInstallationTests.cs Adds test asserting JavaScript installer resource carries the suppression annotation.
src/Aspire.Hosting/ProjectResourceBuilderExtensions.cs Applies suppression annotation to the generated ProjectRebuilderResource.
src/Aspire.Hosting/DistributedApplicationBuilder.cs Adds name validation in AddResource() and Build(), skipping when suppression annotation exists.
src/Aspire.Hosting/ApplicationModel/SuppressNameValidationAnnotation.cs Introduces new public marker annotation type.
src/Aspire.Hosting/ApplicationModel/Resource.cs Removes name validation from base Resource construction.
src/Aspire.Hosting/ApplicationModel/ProjectRebuilderResource.cs Removes use of skip-validation constructor pattern.
src/Aspire.Hosting/ApplicationModel/ExecutableResource.cs Removes skip-validation constructor overload; uses base Resource(name) directly.
src/Aspire.Hosting.Python/PythonVenvCreatorResource.cs Removes use of skip-validation constructor pattern.
src/Aspire.Hosting.Python/PythonInstallerResource.cs Removes use of skip-validation constructor pattern.
src/Aspire.Hosting.Python/PythonAppResourceBuilderExtensions.cs Applies suppression annotation to created Python installer/venv creator resources.
src/Aspire.Hosting.JavaScript/JavaScriptInstallerResource.cs Refactors to primary-constructor style (public surface unchanged).
src/Aspire.Hosting.JavaScript/JavaScriptHostingExtensions.cs Applies suppression annotation to created JavaScript installer resources.

@JamesNK JamesNK force-pushed the jamesnk/name-validation-suppression-v2 branch from fa56879 to ac8add0 Compare March 31, 2026 06:28
@JamesNK JamesNK requested a review from DamianEdwards March 31, 2026 06:31
@davidfowl
Copy link
Copy Markdown
Contributor

Much cleaner.

@JamesNK JamesNK force-pushed the jamesnk/name-validation-suppression-v2 branch from e1838b8 to 39f6d3e Compare March 31, 2026 07:50
@github-actions
Copy link
Copy Markdown
Contributor

Re-running the failed jobs in the CI workflow for this pull request because 1 job was identified as retry-safe transient failures in the CI run attempt.
GitHub was asked to rerun all failed jobs for that attempt, and the rerun is being tracked in the rerun attempt.
The job links below point to the failed attempt jobs that matched the retry-safe transient failure rules.

@JamesNK JamesNK force-pushed the jamesnk/name-validation-suppression-v2 branch 2 times, most recently from 5314738 to 5f5cf68 Compare March 31, 2026 11:29
Copy link
Copy Markdown
Contributor

@karolz-ms karolz-ms left a comment

Choose a reason for hiding this comment

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

This solution completely disables name validation for several "system" resources. I am not sure this is the best approach. I think it makes bugs related to naming harder to catch early.

One alternative would be to introduce "name validation policy" concept, with user resources subject to standard naming policy vs system resources having a different/more relaxed policy. An annotation could be used to indicate the desired policy.

@DamianEdwards
Copy link
Copy Markdown
Member

One alternative would be to introduce "name validation policy" concept, with user resources subject to standard naming policy vs system resources having a different/more relaxed policy. An annotation could be used to indicate the desired policy.

This aligns with another proposal I made a short while back to support extensible naming policies that resources could be applied based on usage, so e.g. if you add Azure then the naming policy of compute resources would be further restricted to ensure they map to Azure resource naming cleanly.

@JamesNK
Copy link
Copy Markdown
Member Author

JamesNK commented Apr 1, 2026

What do yall want here? I don't think having a SuppressNameValidationAnnotation blocks the scenarios meantioned. If you have futher features they should be added as new issues.

Alternatively, if SuppressNameValidationAnnotation is disliked then I could remove it and add a SystemResourceAnnoation. It's only behavior right now would be it disables name validation.

@karolz-ms
Copy link
Copy Markdown
Contributor

I just think suppressing name validation (and introducing an annotation for doing so) is not a good idea in general. If system resources need longer names, then we should have a different name validation policy for them, one that accepts longer names, say 96- or 128-character long.

Whether you want to introduce the name validation policies as part of fixing this issue is related, but different question. If you decide to do that, I would expect to have a NameValidationPolicyAnnotation that identifies the policy to use (by name--ployglot friendly), and that you can add multiple of these annotations to an object with "all must agree" effect. And if there is no name validation policy annotation on the object, it gets default name validation.

If this is too much for now, the suggested SystemResourceAnnotation with the initial implementation being that names can be longer than standard but otherwise meet all other constraints is a better idea IMO. We should also be clear that we reserve the right to evolve the semantics of "system resource" and that Aspire users should refrain from putting that annotation on their objects.

JamesNK added 4 commits April 7, 2026 12:52
…alidationAnnotation

Resource names have a 64-character limit enforced by ModelName.ValidateName.
Internal resources like ProjectRebuilderResource, JavaScriptInstallerResource,
PythonInstallerResource, and PythonVenvCreatorResource append suffixes to
user-provided resource names, which can push them over the limit.

Previously, validation happened in the Resource constructor, requiring internal
skip-validation constructors that couldn't be accessed from external assemblies.

This change moves validation from the Resource constructor to
DistributedApplicationBuilder.AddResource() and Build(), and introduces a
public SuppressNameValidationAnnotation that any resource can use to opt out
of name validation before being added to the app model.
…tation

Replace the boolean SuppressNameValidationAnnotation with a configurable
NameValidationPolicyAnnotation that exposes individual validation rules
(MaxLength, ValidateStartsWithLetter, ValidateAllowedCharacters,
ValidateNoConsecutiveHyphens, ValidateNoTrailingHyphen) as init-only
properties.

Add static readonly fields None (disables all rules) and Default
(enforces all standard rules). Add public default-value constants to
ModelName and use them in both the simple overloads and the annotation
property initializers.
@JamesNK JamesNK force-pushed the jamesnk/name-validation-suppression-v2 branch from 5f5cf68 to f5279b7 Compare April 7, 2026 05:31
@JamesNK JamesNK changed the title Move resource name validation to AddResource/Build with SuppressNameValidationAnnotation Refactor name validation to NameValidationPolicyAnnotation with configurable rules Apr 7, 2026
@JamesNK
Copy link
Copy Markdown
Member Author

JamesNK commented Apr 7, 2026

@karolz-ms @DamianEdwards I added NameValidationPolicyAnnotation. Please take a look.

Right now the code analysis still uses default rules for resource and endpoint arguments. When needed, properties could be placed on ResourceNameAttribute to customize how the name is validated by tooling. There is duplication, but it should be simple to customize both places, and I can't think of a way to have one source of name truth that spans devtime and runtime.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 7, 2026

Re-running the failed jobs in the CI workflow for this pull request because 1 job was identified as retry-safe transient failures in the CI run attempt.
GitHub was asked to rerun all failed jobs for that attempt, and the rerun is being tracked in the rerun attempt.
The job links below point to the failed attempt jobs that matched the retry-safe transient failure rules.

Copy link
Copy Markdown
Member

@IEvangelist IEvangelist left a comment

Choose a reason for hiding this comment

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

This looks clean! Nice job.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 8, 2026

🎬 CLI E2E Test Recordings — 56 recordings uploaded (commit 5513914)

View recordings
Test Recording
AddPackageInteractiveWhileAppHostRunningDetached ▶️ View Recording
AddPackageWhileAppHostRunningDetached ▶️ View Recording
AgentCommands_AllHelpOutputs_AreCorrect ▶️ View Recording
AgentInitCommand_DefaultSelection_InstallsSkillOnly ▶️ View Recording
AgentInitCommand_MigratesDeprecatedConfig ▶️ View Recording
AllPublishMethodsBuildDockerImages ▶️ View Recording
AspireAddPackageVersionToDirectoryPackagesProps ▶️ View Recording
AspireUpdateRemovesAppHostPackageVersionFromDirectoryPackagesProps ▶️ View Recording
Banner_DisplayedOnFirstRun ▶️ View Recording
Banner_DisplayedWithExplicitFlag ▶️ View Recording
Banner_NotDisplayedWithNoLogoFlag ▶️ View Recording
CertificatesClean_RemovesCertificates ▶️ View Recording
CertificatesTrust_WithNoCert_CreatesAndTrustsCertificate ▶️ View Recording
CertificatesTrust_WithUntrustedCert_TrustsCertificate ▶️ View Recording
ConfigSetGet_CreatesNestedJsonFormat ▶️ View Recording
CreateAndRunAspireStarterProject ▶️ View Recording
CreateAndRunAspireStarterProjectWithBundle ▶️ View Recording
CreateAndRunEmptyAppHostProject ▶️ View Recording
CreateAndRunJavaEmptyAppHostProject ▶️ View Recording
CreateAndRunJsReactProject ▶️ View Recording
CreateAndRunPythonReactProject ▶️ View Recording
CreateAndRunTypeScriptEmptyAppHostProject ▶️ View Recording
CreateAndRunTypeScriptStarterProject ▶️ View Recording
CreateJavaAppHostWithViteApp ▶️ View Recording
CreateStartAndStopAspireProject ▶️ View Recording
CreateTypeScriptAppHostWithViteApp ▶️ View Recording
DashboardRunWithOtelTracesReturnsNoTraces ▶️ View Recording
DescribeCommandResolvesReplicaNames ▶️ View Recording
DescribeCommandShowsRunningResources ▶️ View Recording
DetachFormatJsonProducesValidJson ▶️ View Recording
DoctorCommand_DetectsDeprecatedAgentConfig ▶️ View Recording
DoctorCommand_WithSslCertDir_ShowsTrusted ▶️ View Recording
DoctorCommand_WithoutSslCertDir_ShowsPartiallyTrusted ▶️ View Recording
GlobalMigration_HandlesCommentsAndTrailingCommas ▶️ View Recording
GlobalMigration_HandlesMalformedLegacyJson ▶️ View Recording
GlobalMigration_PreservesAllValueTypes ▶️ View Recording
GlobalMigration_SkipsWhenNewConfigExists ▶️ View Recording
GlobalSettings_MigratedFromLegacyFormat ▶️ View Recording
InvalidAppHostPathWithComments_IsHealedOnRun ▶️ View Recording
LegacySettingsMigration_AdjustsRelativeAppHostPath ▶️ View Recording
LogsCommandShowsResourceLogs ▶️ View Recording
PsCommandListsRunningAppHost ▶️ View Recording
PsFormatJsonOutputsOnlyJsonToStdout ▶️ View Recording
PublishWithDockerComposeServiceCallbackSucceeds ▶️ View Recording
RestoreGeneratesSdkFiles ▶️ View Recording
RestoreSupportsConfigOnlyHelperPackageAndCrossPackageTypes ▶️ View Recording
RunFromParentDirectory_UsesExistingConfigNearAppHost ▶️ View Recording
RunWithMissingAwaitShowsHelpfulError ▶️ View Recording
SecretCrudOnDotNetAppHost ▶️ View Recording
SecretCrudOnTypeScriptAppHost ▶️ View Recording
StagingChannel_ConfigureAndVerifySettings_ThenSwitchChannels ▶️ View Recording
StopAllAppHostsFromAppHostDirectory ▶️ View Recording
StopAllAppHostsFromUnrelatedDirectory ▶️ View Recording
StopNonInteractiveMultipleAppHostsShowsError ▶️ View Recording
StopNonInteractiveSingleAppHost ▶️ View Recording
StopWithNoRunningAppHostExitsSuccessfully ▶️ View Recording

📹 Recordings uploaded automatically from CI run #24067278937

@DamianEdwards
Copy link
Copy Markdown
Member

@JamesNK I logged #13404 previously to cover expanding name validation to be more flexible. The thought there was that the validation facets would be set via MSBuild so that they could be discovered at both build time and runtime, although it could also be achieved via attribute constructor parameters on ResourceNameAttribute. Are you saying as it is right now, there is no mismatch because none of the resource types that we're using non-default name validation rules on are manually added by users so the compile-time analysis isn't used?

@JamesNK
Copy link
Copy Markdown
Member Author

JamesNK commented Apr 8, 2026

@JamesNK I logged #13404 previously to cover expanding name validation to be more flexible. The thought there was that the validation facets would be set via MSBuild so that they could be discovered at both build time and runtime, although it could also be achieved via attribute constructor parameters on ResourceNameAttribute. Are you saying as it is right now, there is no mismatch because none of the resource types that we're using non-default name validation rules on are manually added by users so the compile-time analysis isn't used?

As far as I know, the only place where the current resource name rules don't work is length. And that happens when we take a valid name and create another resource from it and add more characters. So it doesn't involve the analyzer.

The problem with an MSBuild property is it would apply globally. You couldn't add a new name rule and only apply it to certain resources. Or disable a rule for existing resources. Although maybe globally setting rules is what's wanted when thinking about deploying to an environment.

Should the approach in this PR change?

@DamianEdwards
Copy link
Copy Markdown
Member

@JamesNK I think this is fine for now. We could maybe do an inherited attribute to enable specific resources to have specific rules that are still visible by the analyzer by having the validation logic be callable by the analyzer on a static method on the derived attribute or some-such. But no need to block for now.

@JamesNK JamesNK merged commit a1bdd7c into main Apr 9, 2026
523 of 526 checks passed
@JamesNK JamesNK deleted the jamesnk/name-validation-suppression-v2 branch April 9, 2026 01:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-app-model Issues pertaining to the APIs in Aspire.Hosting, e.g. DistributedApplication

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants