Skip to content

Commit b9a38cb

Browse files
Copilotvhvb1989
andcommitted
Implement hyphen-to-underscore conversion for build args with infra.parameters prefix
Co-authored-by: vhvb1989 <24213737+vhvb1989@users.noreply.github.com>
1 parent ffa697e commit b9a38cb

2 files changed

Lines changed: 175 additions & 1 deletion

File tree

cli/azd/pkg/project/dotnet_importer.go

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,56 @@ func mapToExpandableStringSlice(m map[string]string, separator string) []osutil.
197197
return result
198198
}
199199

200+
// mapToExpandableStringSliceWithHyphenConversion converts a map of strings to a slice of expandable strings,
201+
// with special handling for build arg values that contain infra.parameters. references.
202+
// For values that match the pattern "{infra.parameters.param-name}", hyphens in the parameter name
203+
// are converted to underscores to match the expected Azure parameter naming convention.
204+
// Each key-value pair in the map is converted to a string in the format "key:value",
205+
// where the separator is specified by the `separator` parameter.
206+
// If the value is an empty string, only the key is included in the resulting slice.
207+
func mapToExpandableStringSliceWithHyphenConversion(m map[string]string, separator string) []osutil.ExpandableString {
208+
var result []osutil.ExpandableString
209+
for key, value := range m {
210+
processedValue := value
211+
if value != "" {
212+
processedValue = convertHyphensInInfraParameters(value)
213+
}
214+
215+
if processedValue == "" {
216+
result = append(result, osutil.NewExpandableString(key))
217+
} else {
218+
result = append(result, osutil.NewExpandableString(key+separator+processedValue))
219+
}
220+
}
221+
return result
222+
}
223+
224+
// convertHyphensInInfraParameters converts hyphens to underscores in parameter names
225+
// within {infra.parameters.*} references to match Azure parameter naming conventions.
226+
func convertHyphensInInfraParameters(value string) string {
227+
const infraParametersPrefix = "{infra.parameters."
228+
229+
// Only process values that start with the infra.parameters. prefix
230+
if !strings.HasPrefix(value, infraParametersPrefix) {
231+
return value
232+
}
233+
234+
// Find the closing brace
235+
closeIndex := strings.Index(value, "}")
236+
if closeIndex == -1 {
237+
return value
238+
}
239+
240+
// Extract the parameter name part after "infra.parameters."
241+
parameterPart := value[len(infraParametersPrefix):closeIndex]
242+
243+
// Convert hyphens to underscores in the parameter name
244+
convertedParameterPart := strings.ReplaceAll(parameterPart, "-", "_")
245+
246+
// Reconstruct the value with the converted parameter name
247+
return infraParametersPrefix + convertedParameterPart + value[closeIndex:]
248+
}
249+
200250
func (ai *DotNetImporter) Services(
201251
ctx context.Context, p *ProjectConfig, svcConfig *ServiceConfig,
202252
) (map[string]*ServiceConfig, error) {
@@ -334,7 +384,7 @@ func (ai *DotNetImporter) Services(
334384
dOptions = DockerProjectOptions{
335385
Path: bContainer.Build.Dockerfile,
336386
Context: bContainer.Build.Context,
337-
BuildArgs: mapToExpandableStringSlice(bArgs, "="),
387+
BuildArgs: mapToExpandableStringSliceWithHyphenConversion(bArgs, "="),
338388
BuildSecrets: bArgsArray,
339389
BuildEnv: reqEnv,
340390
}

cli/azd/pkg/project/dotnet_importer_test.go

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,3 +144,127 @@ func TestBuildArgsArrayAndEnv(t *testing.T) {
144144
assert.ElementsMatch(t, expectedArgs, args)
145145
assert.ElementsMatch(t, expectedEnv, env)
146146
}
147+
148+
func TestMapToExpandableStringSliceWithHyphenConversion(t *testing.T) {
149+
// Test that build args with infra.parameters. prefix get hyphens converted to underscores
150+
buildArgs := map[string]string{
151+
"ARG1": "value1",
152+
"ARG2": "{infra.parameters.param-name}",
153+
"ARG3": "{infra.parameters.another-param-name}",
154+
"ARG4": "{some.other.param-name}", // Should NOT be converted (no infra.parameters. prefix)
155+
"ARG5": "regular-value-with-hyphens", // Should NOT be converted (not a parameter reference)
156+
"ARG6": "{infra.parameters.param_underscore}", // Should remain unchanged (already has underscore)
157+
"ARG7": "{infra.parameters.param-name-with-suffix}",
158+
"ARG8": "",
159+
}
160+
161+
result := mapToExpandableStringSliceWithHyphenConversion(buildArgs, "=")
162+
163+
// Convert result to strings for easier comparison using identity mapping
164+
resultStrings := make([]string, len(result))
165+
for i, item := range result {
166+
resultStrings[i] = item.MustEnvsubst(func(name string) string { return "${" + name + "}" })
167+
}
168+
169+
expected := []string{
170+
"ARG1=value1",
171+
"ARG2={infra.parameters.param_name}", // Hyphen converted to underscore
172+
"ARG3={infra.parameters.another_param_name}", // Hyphens converted to underscores
173+
"ARG4={some.other.param-name}", // NOT converted (wrong prefix)
174+
"ARG5=regular-value-with-hyphens", // NOT converted (not a parameter reference)
175+
"ARG6={infra.parameters.param_underscore}", // Unchanged (already has underscore)
176+
"ARG7={infra.parameters.param_name_with_suffix}", // All hyphens converted
177+
"ARG8", // Empty value
178+
}
179+
180+
assert.ElementsMatch(t, expected, resultStrings)
181+
}
182+
183+
func TestEvaluateArgsWithConfigHyphenHandling(t *testing.T) {
184+
// Test integration with evaluateArgsWithConfig to ensure hyphens in parameter names
185+
// get converted to underscores in infra.parameters references
186+
manifest := apphost.Manifest{
187+
Resources: map[string]*apphost.Resource{
188+
"param-with-hyphens": {
189+
Type: "parameter.v0",
190+
Value: "{param-with-hyphens.inputs.inputParam}",
191+
Inputs: map[string]apphost.Input{
192+
"inputParam": {
193+
Type: "string",
194+
},
195+
},
196+
},
197+
},
198+
}
199+
200+
args := map[string]string{
201+
"BUILD_ARG1": "{param-with-hyphens.value}",
202+
"BUILD_ARG2": "constant-value",
203+
}
204+
205+
// Evaluate args first (this should produce {infra.parameters.param-with-hyphens})
206+
evaluatedArgs, err := evaluateArgsWithConfig(manifest, args)
207+
require.NoError(t, err)
208+
209+
// Now apply hyphen conversion when creating expandable strings
210+
result := mapToExpandableStringSliceWithHyphenConversion(evaluatedArgs, "=")
211+
212+
// Convert to strings for verification
213+
resultStrings := make([]string, len(result))
214+
for i, item := range result {
215+
resultStrings[i] = item.MustEnvsubst(func(name string) string { return "${" + name + "}" })
216+
}
217+
218+
expected := []string{
219+
"BUILD_ARG1={infra.parameters.param_with_hyphens}", // Hyphen converted to underscore
220+
"BUILD_ARG2=constant-value", // Unchanged (not a parameter reference)
221+
}
222+
223+
assert.ElementsMatch(t, expected, resultStrings)
224+
}
225+
226+
func TestConvertHyphensInInfraParameters(t *testing.T) {
227+
tests := []struct {
228+
name string
229+
input string
230+
expected string
231+
}{
232+
{
233+
name: "infra.parameters with hyphens",
234+
input: "{infra.parameters.my-param-name}",
235+
expected: "{infra.parameters.my_param_name}",
236+
},
237+
{
238+
name: "infra.parameters with underscores",
239+
input: "{infra.parameters.my_param_name}",
240+
expected: "{infra.parameters.my_param_name}",
241+
},
242+
{
243+
name: "non-infra parameter reference",
244+
input: "{some.other.param-name}",
245+
expected: "{some.other.param-name}",
246+
},
247+
{
248+
name: "regular string with hyphens",
249+
input: "regular-string-with-hyphens",
250+
expected: "regular-string-with-hyphens",
251+
},
252+
{
253+
name: "empty string",
254+
input: "",
255+
expected: "",
256+
},
257+
{
258+
name: "malformed parameter reference",
259+
input: "{infra.parameters.param-name",
260+
expected: "{infra.parameters.param-name",
261+
},
262+
}
263+
264+
for _, tt := range tests {
265+
t.Run(tt.name, func(t *testing.T) {
266+
result := convertHyphensInInfraParameters(tt.input)
267+
assert.Equal(t, tt.expected, result)
268+
})
269+
}
270+
}

0 commit comments

Comments
 (0)