Show global default hostname as legacy & update the scopes enum#2182
Show global default hostname as legacy & update the scopes enum#2182MicroFish91 merged 7 commits intomainfrom
global default hostname as legacy & update the scopes enum#2182Conversation
global default hostname as legacy, update the scopes enumglobal default hostname as legacy & update the scopes enum
There was a problem hiding this comment.
Pull request overview
Updates the default-hostname scope UX to label the global hostname option as “Legacy” and renames the corresponding enum member, along with a patch version bump.
Changes:
- Rename
DomainNameLabelScope.GlobaltoDomainNameLabelScope.NoReuse. - Update the hostname-scope quick pick (add “Legacy” label + “Learn more” sentinel handling).
- Adjust site name validation/length logic based on the selected scope.
- Bump package version to
4.0.2.
Reviewed changes
Copilot reviewed 3 out of 4 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| appservice/src/createAppService/SiteNameStep.ts | Updates max-length and async validation logic to align with the updated scope representation. |
| appservice/src/createAppService/SiteDomainNameLabelScopeStep.ts | Renames the scope enum and changes the prompt picks/flow for “Legacy global” and “Learn more”. |
| appservice/package.json | Version bump to 4.0.2. |
| appservice/package-lock.json | Lockfile version bump to 4.0.2. |
Files not reviewed (1)
- appservice/package-lock.json: Language not supported
Comments suppressed due to low confidence (3)
appservice/src/createAppService/SiteNameStep.ts:206
- This branch now identifies the global scope via
!domainNameScope. If the newDomainNameLabelScope.NoReusevalue is used to represent global/legacy behavior, this condition will stop matching and the code will no longer ignore the expected “must be less than … chars” error for long names. Update the condition to explicitly handle the global case (e.g.,domainNameScope === DomainNameLabelScope.NoReuse).
// For global domain scope, if site name input is greater than regionalCNAMaxLength, ignore result of regional CNA because it inherently has a shorter character limit than Global CNA
if (!domainNameScope && siteName.length > regionalCNAMaxLength) {
// Ensure the error message is the expected character validation error message before ignoring it
if (checkNameResponse.message && /must be less than \d{2} chars/i.test(checkNameResponse.message)) {
return undefined;
appservice/src/createAppService/SiteDomainNameLabelScopeStep.ts:47
shouldPromptcurrently returnstruewhennewSiteDomainNameLabelScopeisundefined. Since the “Global default hostname” option sets the scope toundefined, picking that option will cause this step to re-prompt on subsequent wizard passes/backtracking and makes the selection impossible to persist. Track an explicit value for global (e.g.,DomainNameLabelScope.NoReuse) or add a separate flag to indicate the step has already been completed.
public shouldPrompt(context: T): boolean {
return !context.newSiteDomainNameLabelScope;
}
appservice/src/createAppService/SiteNameStep.ts:128
- This max-length check now applies the 43-char regional limit for any truthy
newSiteDomainNameLabelScope. If callers start using the new enum valueDomainNameLabelScope.NoReuse(which represents the global/legacy behavior), it will incorrectly force the shorter regional limit. TreatNoReuseas the global case here (i.e., keep the 60-char limit), and only applyregionalCNAMaxLengthfor the actual reuse scopes.
let maxLength: number = siteNamingRules.maxLength;
if (context.newSiteDomainNameLabelScope) {
maxLength = regionalCNAMaxLength;
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| let validationMessage: string | undefined; | ||
| if (!context.newSiteDomainNameLabelScope || context.newSiteDomainNameLabelScope === DomainNameLabelScope.Global) { | ||
| if (!context.newSiteDomainNameLabelScope) { | ||
| validationMessage ??= await this.asyncValidateGlobalCNA(sdkClient, name); | ||
| } | ||
|
|
||
| if (context.newSiteDomainNameLabelScope) { | ||
| validationMessage ??= await this.asyncValidateRegionalCNA(context, context.newSiteDomainNameLabelScope, name, context.resourceGroup?.name ?? context.newResourceGroupName); | ||
| validationMessage ??= await this.asyncValidateUniqueARMId(context, sdkClient, name, context.resourceGroup?.name ?? context.newResourceGroupName); | ||
| } | ||
| validationMessage ??= await this.asyncValidateRegionalCNA(context, context.newSiteDomainNameLabelScope, name, context.resourceGroup?.name ?? context.newResourceGroupName); | ||
| validationMessage ??= await this.asyncValidateUniqueARMId(context, sdkClient, name, context.resourceGroup?.name ?? context.newResourceGroupName); |
There was a problem hiding this comment.
asyncValidateSiteName treats only undefined as the global/legacy case (asyncValidateGlobalCNA runs only when the scope is falsy), but the enum now has an explicit NoReuse member. If newSiteDomainNameLabelScope is ever set to NoReuse, global CNA validation will be skipped and asyncValidateRegionalCNA will send autoGeneratedDomainNameLabelScope: 'NoReuse' vs undefined depending on the caller, changing validation behavior. It would be more robust to treat NoReuse as the global case everywhere (including telemetry), and avoid relying on undefined to mean “global selected.”
This issue also appears in the following locations of the same file:
- line 202
- line 125
There was a problem hiding this comment.
This is probably true, but I would like for us to revisit the requirements once we add new picks (if ever) as that will be when we will have the requirements and means for more thorough testing. Since it's not in the current list of picks, I'll omit the check here. But I think it's definitely a good idea to leave an explicit note in the picks section stating this expectation.
There was a problem hiding this comment.
| const learnMore = 'learnMore'; | ||
| const picks: IAzureQuickPickItem<DomainNameLabelScope | typeof learnMore | undefined>[] = [ | ||
| // Matching the portal which doesn't yet offer ResourceGroup and Subscription level domain scope | ||
| { label: vscode.l10n.t('Secure unique default hostname'), description: vscode.l10n.t('Tenant Scope'), data: DomainNameLabelScope.Tenant }, | ||
| { label: vscode.l10n.t('Global default hostname'), description: vscode.l10n.t('Global'), data: DomainNameLabelScope.Global }, | ||
| { label: vscode.l10n.t('$(link-external) Learn more about unique default hostname'), data: undefined }, | ||
| { label: vscode.l10n.t('Global default hostname'), description: vscode.l10n.t('Legacy'), data: undefined }, | ||
| { label: vscode.l10n.t('$(link-external) Learn more about unique default hostname'), data: learnMore }, | ||
| ]; |
There was a problem hiding this comment.
Global default hostname is represented as data: undefined, which means selecting it leaves context.newSiteDomainNameLabelScope unset. That breaks downstream logic that treats “unset” as “needs prompting”, and also prevents sending the explicit NoReuse value to APIs/telemetry. Consider using DomainNameLabelScope.NoReuse for this pick (and reserving undefined for “not chosen yet”).
See below for a potential fix:
const picks: IAzureQuickPickItem<DomainNameLabelScope | typeof learnMore>[] = [
// Matching the portal which doesn't yet offer ResourceGroup and Subscription level domain scope
{ label: vscode.l10n.t('Secure unique default hostname'), description: vscode.l10n.t('Tenant Scope'), data: DomainNameLabelScope.Tenant },
{ label: vscode.l10n.t('Global default hostname'), description: vscode.l10n.t('Legacy'), data: DomainNameLabelScope.NoReuse },
{ label: vscode.l10n.t('$(link-external) Learn more about unique default hostname'), data: learnMore },
];
const learnMoreUrl: string = 'https://aka.ms/AAu7lhs';
let result: DomainNameLabelScope | typeof learnMore;
There was a problem hiding this comment.
They are not, in fact, the same. Found out recently that the Global being referenced actually should be something more akin to Legacy. Although both legacy and noreuse ensure global uniqueness, my understanding is that noreuse doesn't free up the resource afterwards. So legacy is in fact equivalent to undefined and not noreuse, although there are many similarities.
Unfortunately this does mean shouldPrompt could trigger re-prompts when users select legacy if the step gets re-run. I don't have a great fix for that but I honestly don't think it's a big enough deal to sink effort into addressing it.


There's a reference to a PR which is still applicable but that you can't easily see via the diff because it's at the top with the method definition.
Rules for validation:
#1882 (comment)
TODO: Add testing in Functions and App Service later for the Tenant and Legacy scenarios.