fix: explicit slot targeting — SLOT_NAME=production for main app#7630
fix: explicit slot targeting — SLOT_NAME=production for main app#7630rajeshkamal5050 merged 5 commits intomainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
This PR makes App Service slot deployments deterministic by removing auto-routing (deployment history + 1-slot auto-pick) and requiring explicit targeting via AZD_DEPLOY_{SERVICE}_SLOT_NAME, treating production as the main app.
Changes:
- Update App Service deploy target selection to use explicit
SLOT_NAME(withproductionmeaning main app) and fail in--no-promptwhen slots exist but no target is specified. - Add/refresh unit tests covering the new targeting rules and edge cases.
- Align the App Service swap extension UX/flags to accept
production/@productionfor the main app and document the env var inenvironment-variables.md.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| cli/azd/pkg/project/service_target_appservice.go | Replaces heuristic routing with explicit slot targeting + interactive prompt / --no-prompt failure path. |
| cli/azd/pkg/project/service_target_appservice_test.go | Adds table-driven tests for the new slot targeting behavior. |
| cli/azd/extensions/azure.appservice/internal/cmd/swap.go | Updates swap help/prompt labels and normalizes production/@production for the main app; deprecates @main. |
| cli/azd/docs/environment-variables.md | Documents AZD_DEPLOY_{SERVICE}_SLOT_NAME and its behavior when slots exist. |
jongio
left a comment
There was a problem hiding this comment.
Clean design improvement - the deployment-history heuristic was opaque and the 1-slot auto-pick was surprising. Explicit slot targeting is much more predictable.
Core logic in service_target_appservice.go is well-structured: SLOT_NAME first, then no-slots fast path, then no-prompt error, then interactive prompt. Good test coverage (12 cases, all branches).
Things to address:
-
normalizeSlotNamein swap.go puts a deprecation warning (viafmt.Println) inside what should be a pure normalization function. The warning goes to stdout (not stderr), duplicates if both flags are@main, and uses plain fmt instead ofcolor.Yellow()like the rest of the file. -
HasAppServiceDeploymentsinazapi/webapp.gois now dead code - this PR removes its only call site. Consider removing the function and its test, or note it for a follow-up. -
The "Breaking change" section documents the 1-slot auto-pick removal but doesn't mention the first-deployment behavior change: previously, first deploy would push to main app + ALL slots simultaneously. That behavior is also gone now - worth documenting.
Skip playback until recording is refreshed. Test now uses SLOT_NAME=production for initial deploy instead of deploy-to-all. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Replace env var instead of appending duplicate - Skip playback — stale recording has old HasAppServiceDeployments calls - Recording needs re-recording in CI with proper Bicep/proxy setup Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fresh recording with new slot targeting behavior. Playback verified locally (22s). Remove skip and magefile exclusion. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Updated the PR description — breaking changes section now documents both:
@jongio — did you also mean we should add this to the changelog / release notes? |
- Move @main deprecation warning from normalizeSlotName to runSwap, use color.Yellow, warn once - Remove dead HasAppServiceDeployments and its test - Fix no-prompt error to list copy-pasteable values (production, not 'production (main app)') Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Azure Dev CLI Install InstructionsInstall scriptsMacOS/Linux
bash: pwsh: WindowsPowerShell install MSI install Standalone Binary
MSI
Documentationlearn.microsoft.com documentationtitle: Azure Developer CLI reference
|
jongio
left a comment
There was a problem hiding this comment.
Clean design - explicit targeting via SLOT_NAME is much more predictable than the old deployment-history heuristic. The "production = main app" convention aligns with how Azure's ARM API treats the reserved name, so there's zero ambiguity.
Logic is solid: SLOT_NAME takes precedence, then no-slots fast path, then interactive/no-prompt fork. Case-insensitive matching with canonical name return is a nice improvement over the old exact-match. Good test coverage across all branches.
A few minor observations (none blocking):
-
The
Deploy()method still loops overdeployTargets, butdetermineDeploymentTargetsnow always returns exactly one target. The loop andhasSlotslogic still work correctly - just dead complexity you could simplify later. -
The case-insensitive slot matching (
strings.EqualFoldreplacing==) is an improvement worth calling out in the PR description since it's a subtle behavioral change. Could also add a test case likeSLOT_NAME=Stagingmatching astagingslot to lock this in. -
Dead code removal, functional test update, and re-recorded test data all look clean.
wbreza
left a comment
There was a problem hiding this comment.
Design Alignment
Verified against the design proposal on PR #7530. All three requirements are correctly implemented:
- ✅
SLOT_NAME=production→ deploys to main app (case-insensitive, aligns with Azure ARM convention) - ✅
--no-prompt+ slots + no SLOT_NAME → fails with actionable error listing targets includingproduction - ✅ Single env var, no
IGNORE_SLOTS— zero new API surface, no precedence conflicts
What Looks Good
- Clean replacement of deployment-history heuristic with deterministic explicit targeting
- Excellent table-driven test coverage (12 cases, all branches)
- Dead code (
HasAppServiceDeployments) fully removed with zero remaining callers - Swap command aligned with the
productionconvention
Findings
| Priority | Count |
|---|---|
| Medium | 1 |
| Low | 2 |
[Medium] Case-sensitive slot validation in swap vs case-insensitive in deploy (swap.go:isValidSlotName)
isValidSlotName uses slices.Contains (case-sensitive), but the deploy path uses strings.EqualFold. A user who learns SLOT_NAME=Staging works for deploy will find --src Staging fails for swap. Pre-existing gap — not introduced by this PR — worth aligning in a follow-up with slices.ContainsFunc + strings.EqualFold.
[Low] @production accepted but undocumented (swap.go:normalizeSlotName)
normalizeSlotName accepts @production → "" but the flag help text only mentions production. Minor discoverability gap.
[Low] Deploy loop iterates over always-single-element slice (service_target_appservice.go:Deploy)
determineDeploymentTargets now always returns exactly one target, but Deploy() retains the multi-target loop. Dead complexity, already noted by @jongio.
|
Follow-up items tracked in #7650 |
Fixes #7365
Replaces the auto-routing heuristic (deployment history check, 1-slot auto-pick) with explicit slot targeting.
What changed
Deploy (
service_target_appservice.go)SLOT_NAME=production→ deploys to main app (case-insensitive)SLOT_NAME=staging→ deploys to that slot (validated against actual slots)SLOT_NAME=bogus→ error with available slots listed + production hint--no-prompt+ slots exist → fails with error listing targetsHasAppServiceDeployments)Swap (
swap.go)--src production/--dst production→ main app (aligns with deploy)@productionalso accepted@mainstill works but prints deprecation warningDocs
AZD_DEPLOY_{SERVICE}_SLOT_NAMEtoenvironment-variables.mdWhy "production" is safe
Azure ARM API rejects creating a slot named "production":
Not just portal UX — backend enforces it. Zero ambiguity.
Breaking changes
1-slot auto-pick removed: CI pipelines relying on auto-pick (slots exist, no SLOT_NAME set,
--no-prompt) will now get an error. Fix: setAZD_DEPLOY_{SERVICE}_SLOT_NAME=<target>.First-deploy "push to all slots" removed: Previously, first deploy (no deployment history) would deploy to the main app AND all slots simultaneously. Now you always deploy to exactly one target — set
SLOT_NAMEfor each target.Manual testing
Tested against live App Service with staging slot (
rg-azd-slot-test-69352):SLOT_NAME=productionSLOT_NAME=stagingSLOT_NAME=bogus--no-promptUnit tests
12 table-driven tests covering all scenarios above + case insensitivity + edge cases.
Ref: azure-dev-pr/discussions/1775 (Option 4)