@@ -26,6 +26,45 @@ type parameterValue struct {
2626Source parameterValueSource
2727}
2828
29+ type ResolverError struct {
30+ Diagnostics hcl.Diagnostics
31+ Parameter map [string ]hcl.Diagnostics
32+ }
33+
34+ // Error is a pretty bad format for these errors. Try to avoid using this.
35+ func (e * ResolverError )Error ()string {
36+ var diags hcl.Diagnostics
37+ diags = diags .Extend (e .Diagnostics )
38+ for _ ,d := range e .Parameter {
39+ diags = diags .Extend (d )
40+ }
41+
42+ return diags .Error ()
43+ }
44+
45+ func (e * ResolverError )HasError ()bool {
46+ if e .Diagnostics .HasErrors () {
47+ return true
48+ }
49+
50+ for _ ,diags := range e .Parameter {
51+ if diags .HasErrors () {
52+ return true
53+ }
54+ }
55+ return false
56+ }
57+
58+ func (e * ResolverError )Extend (parameterName string ,diag hcl.Diagnostics ) {
59+ if e .Parameter == nil {
60+ e .Parameter = make (map [string ]hcl.Diagnostics )
61+ }
62+ if _ ,ok := e .Parameter [parameterName ];! ok {
63+ e .Parameter [parameterName ]= hcl.Diagnostics {}
64+ }
65+ e .Parameter [parameterName ]= e .Parameter [parameterName ].Extend (diag )
66+ }
67+
2968//nolint:revive // firstbuild is a control flag to turn on immutable validation
3069func ResolveParameters (
3170ctx context.Context ,
@@ -35,7 +74,7 @@ func ResolveParameters(
3574previousValues []database.WorkspaceBuildParameter ,
3675buildValues []codersdk.WorkspaceBuildParameter ,
3776presetValues []database.TemplateVersionPresetParameter ,
38- ) (map [string ]string ,hcl. Diagnostics ) {
77+ ) (map [string ]string ,error ) {
3978previousValuesMap := slice .ToMap (previousValues ,func (p database.WorkspaceBuildParameter ) (string ,string ) {
4079return p .Name ,p .Value
4180})
@@ -73,7 +112,10 @@ func ResolveParameters(
73112// always be valid. If there is a case where this is not true, then this has to
74113// be changed to allow the build to continue with a different set of values.
75114
76- return nil ,diags
115+ return nil ,& ResolverError {
116+ Diagnostics :diags ,
117+ Parameter :nil ,
118+ }
77119}
78120
79121// The user's input now needs to be validated against the parameters.
@@ -114,12 +156,16 @@ func ResolveParameters(
114156// are fatal. Additional validation for immutability has to be done manually.
115157output ,diags = renderer .Render (ctx ,ownerID ,values .ValuesMap ())
116158if diags .HasErrors () {
117- return nil ,diags
159+ return nil ,& ResolverError {
160+ Diagnostics :diags ,
161+ Parameter :nil ,
162+ }
118163}
119164
120165// parameterNames is going to be used to remove any excess values that were left
121166// around without a parameter.
122167parameterNames := make (map [string ]struct {},len (output .Parameters ))
168+ parameterError := & ResolverError {}
123169for _ ,parameter := range output .Parameters {
124170parameterNames [parameter .Name ]= struct {}{}
125171
@@ -133,20 +179,22 @@ func ResolveParameters(
133179}
134180
135181// An immutable parameter was changed, which is not allowed.
136- // Add the failed diagnostic to the output.
137- diags = diags .Append (& hcl.Diagnostic {
138- Severity :hcl .DiagError ,
139- Summary :"Immutable parameter changed" ,
140- Detail :fmt .Sprintf ("Parameter %q is not mutable, so it can't be updated after creating a workspace." ,parameter .Name ),
141- Subject :src ,
182+ // Add a failed diagnostic to the output.
183+ parameterError .Extend (parameter .Name , hcl.Diagnostics {
184+ & hcl.Diagnostic {
185+ Severity :hcl .DiagError ,
186+ Summary :"Immutable parameter changed" ,
187+ Detail :fmt .Sprintf ("Parameter %q is not mutable, so it can't be updated after creating a workspace." ,parameter .Name ),
188+ Subject :src ,
189+ },
142190})
143191}
144192}
145193
146194// TODO: Fix the `hcl.Diagnostics(...)` type casting. It should not be needed.
147195if hcl .Diagnostics (parameter .Diagnostics ).HasErrors () {
148- // All validation errors are raised here.
149- diags = diags .Extend (hcl .Diagnostics (parameter .Diagnostics ))
196+ // All validation errors are raised here for each parameter .
197+ parameterError .Extend (parameter . Name , hcl .Diagnostics (parameter .Diagnostics ))
150198}
151199
152200// If the parameter has a value, but it was not set explicitly by the user at any
@@ -175,8 +223,13 @@ func ResolveParameters(
175223}
176224}
177225
226+ if parameterError .HasError () {
227+ // If there are any errors, return them.
228+ return nil ,parameterError
229+ }
230+
178231// Return the values to be saved for the build.
179- return values .ValuesMap (),diags
232+ return values .ValuesMap (),nil
180233}
181234
182235type parameterValueMap map [string ]parameterValue