Bug Description
HooksConfig.UnmarshalYAML fails when an �zure.yaml file contains a mix of single-hook (map) and multi-hook (list) formats in the same hooks: block. This is a pre-existing limitation that becomes user-facing now that multi-hook support exists.
Repro Steps
- Create an �zure.yaml with hooks using mixed formats — some as single maps, some as lists:
name: my-app
hooks:
preprovision:
- run: ./hooks/preprovision.sh
shell: sh
- run: ./hooks/preprovision/main.py
predeploy:
windows:
shell: pwsh
run: 'echo "VITE_API_BASE_URL=$env:API_BASE_URL" > ./src/web/.env.local'
posix:
shell: sh
run: 'echo VITE_API_BASE_URL=\"$API_BASE_URL\" > ./src/web/.env.local'
- Run any azd command that loads the project config (e.g., �zd provision)
Expected Behavior
Both hook formats should parse successfully in the same hooks: block:
- preprovision parsed as a list of 2 hooks
- predeploy parsed as a single hook with platform overrides
Actual Behavior
ERROR: Your azure.yaml file is invalid.
parsing project file: unable to parse azure.yaml file: failed to unmarshal hooks configuration: yaml: unmarshal errors:
line 33: cannot unmarshal !!map into []*ext.HookConfig
line 40: cannot unmarshal !!map into []*ext.HookConfig
Root Cause
HooksConfig.UnmarshalYAML (pkg/ext/hooks_config.go) uses a two-pass approach:
- First attempts map[string]*HookConfig (all maps) — fails if ANY hook uses list format
- Falls back to map[string][]*HookConfig (all lists) — fails if ANY hook uses map format
Neither pass succeeds when the hooks: block contains a mix of both formats.
Suggested Fix
Replace the two-pass approach with per-key type inspection using yaml.Node or map[string]any, then switch on the YAML node kind for each hook entry:
- Mapping node → unmarshal as single *HookConfig, wrap into slice
- Sequence node → unmarshal as []*HookConfig
This would allow any combination of single-hook and multi-hook entries within the same hooks: block.
Workaround
Convert ALL hooks to list format (even single hooks become 1-item lists):
hooks:
preprovision:
- run: ./hooks/preprovision.sh
shell: sh
- run: ./hooks/preprovision/main.py
predeploy:
- windows:
shell: pwsh
run: 'echo "VITE_API_BASE_URL=$env:API_BASE_URL" > ./src/web/.env.local'
posix:
shell: sh
run: 'echo VITE_API_BASE_URL=\"$API_BASE_URL\" > ./src/web/.env.local'
Context
Bug Description
HooksConfig.UnmarshalYAML fails when an �zure.yaml file contains a mix of single-hook (map) and multi-hook (list) formats in the same hooks: block. This is a pre-existing limitation that becomes user-facing now that multi-hook support exists.
Repro Steps
Expected Behavior
Both hook formats should parse successfully in the same hooks: block:
Actual Behavior
Root Cause
HooksConfig.UnmarshalYAML (pkg/ext/hooks_config.go) uses a two-pass approach:
Neither pass succeeds when the hooks: block contains a mix of both formats.
Suggested Fix
Replace the two-pass approach with per-key type inspection using yaml.Node or map[string]any, then switch on the YAML node kind for each hook entry:
This would allow any combination of single-hook and multi-hook entries within the same hooks: block.
Workaround
Convert ALL hooks to list format (even single hooks become 1-item lists):
Context