5
5
"context"
6
6
"encoding/json"
7
7
"fmt"
8
- "net/url"
9
8
"os"
10
9
"text/template"
11
10
"text/template/parse"
@@ -234,57 +233,50 @@ func ProcessTmplWithDatasourcesGomplate(
234
233
ignoreMissingTemplateValues bool ,
235
234
) (string , error ) {
236
235
237
- /* Since there's no API method for this in Gomplate 3.11.8, have to set up env var
238
- The .Option("missingkey=default") approach isn't used to avoid having our own render logic.
239
- Instead we rely on standard gomplate.NewRenderer()
240
- */
241
236
if ignoreMissingTemplateValues {
242
237
os .Setenv ("GOMPLATE_MISSINGKEY" , "default" )
243
238
defer os .Unsetenv ("GOMPLATE_MISSINGKEY" )
244
239
}
245
240
246
- // Write merged data to a temporary JSON file
247
- // Required to make no changes to the original templates used with build-harness
248
-
241
+ // 1) Write the 'inner' data
249
242
rawJSON , err := json .Marshal (mergedData )
250
243
if err != nil {
251
244
return "" , fmt .Errorf ("failed to marshal merged data to JSON: %w" , err )
252
245
}
253
-
254
246
tmpfile , err := os .CreateTemp ("" , "gomplate-data-*.json" )
255
247
if err != nil {
256
248
return "" , fmt .Errorf ("failed to create temp data file for gomplate: %w" , err )
257
249
}
258
250
tmpName := tmpfile .Name ()
259
251
defer os .Remove (tmpName )
260
252
261
- if _ , err = tmpfile .Write (rawJSON ); err != nil {
262
- _ = tmpfile .Close ()
253
+ if _ , err : = tmpfile .Write (rawJSON ); err != nil {
254
+ tmpfile .Close ()
263
255
return "" , fmt .Errorf ("failed to write JSON to temp file: %w" , err )
264
256
}
265
- if err = tmpfile .Close (); err != nil {
257
+ if err : = tmpfile .Close (); err != nil {
266
258
return "" , fmt .Errorf ("failed to close temp data file: %w" , err )
267
259
}
268
260
269
- // This is the file URL, it is referenced in .Env.README_YAML
270
- fileURL , err := url .Parse ("file://" + tmpName )
261
+ fileURL , err := toFileURL (tmpName )
271
262
if err != nil {
272
- return "" , fmt .Errorf ("failed to parse temp file path: %w" , err )
263
+ return "" , fmt .Errorf ("failed to convert temp file path to file URL : %w" , err )
273
264
}
274
265
275
- // Build the top-level data object that includes Env
276
- topLevel := make (map [string ]interface {})
277
-
278
- // Add .Env.README_YAML to point to fileURL
279
- topLevel ["Env" ] = map [string ]interface {}{
280
- "README_YAML" : fileURL .String (),
266
+ finalFileUrl , err := fixWindowsFileURL (fileURL )
267
+ if err != nil {
268
+ return "" , err
281
269
}
282
270
283
- // Could be refactored later to avoid the second temp file, but this is more straighforward
284
-
271
+ // 2) Write the 'outer' top-level
272
+ topLevel := map [string ]interface {}{
273
+ "Env" : map [string ]interface {}{
274
+ "README_YAML" : fileURL ,
275
+ },
276
+ }
285
277
outerJSON , err := json .Marshal (topLevel )
286
278
if err != nil {
287
- return "" , fmt . Errorf ( "failed to marshal top-level data: %w" , err )
279
+ return "" , err
288
280
}
289
281
290
282
tmpfile2 , err := os .CreateTemp ("" , "gomplate-top-level-*.json" )
@@ -295,30 +287,40 @@ func ProcessTmplWithDatasourcesGomplate(
295
287
defer os .Remove (tmpName2 )
296
288
297
289
if _ , err = tmpfile2 .Write (outerJSON ); err != nil {
298
- _ = tmpfile2 .Close ()
299
- return "" , fmt .Errorf ("failed to write top-level JSON to temp file : %w" , err )
290
+ tmpfile2 .Close ()
291
+ return "" , fmt .Errorf ("failed to write top-level JSON: %w" , err )
300
292
}
301
293
if err = tmpfile2 .Close (); err != nil {
302
- return "" , fmt .Errorf ("failed to close top-level temp data file : %w" , err )
294
+ return "" , fmt .Errorf ("failed to close top-level JSON : %w" , err )
303
295
}
304
296
305
- topLevelFileURL , err := url . Parse ( "file://" + tmpName2 )
297
+ topLevelFileURL , err := toFileURL ( tmpName2 )
306
298
if err != nil {
307
- return "" , fmt .Errorf ("failed to parse top-level temp file path: %w" , err )
299
+ return "" , fmt .Errorf ("failed to convert top-level temp file path to file URL : %w" , err )
308
300
}
309
301
310
- // Build the gomplate Options to point the entire "dot" context at the second file
302
+ // This step is crucial on Windows:
303
+ finalTopLevelFileURL , err := fixWindowsFileURL (topLevelFileURL )
304
+ if err != nil {
305
+ return "" , err
306
+ }
307
+
308
+ // 3) Construct Gomplate Options
311
309
opts := gomplate.Options {
312
310
Context : map [string ]gomplate.Datasource {
313
311
"." : {
314
- URL : topLevelFileURL ,
312
+ URL : finalTopLevelFileURL ,
313
+ },
314
+ "config" : {
315
+ URL : finalFileUrl ,
315
316
},
316
317
},
317
318
LDelim : "{{" ,
318
319
RDelim : "}}" ,
319
320
Funcs : template.FuncMap {},
320
321
}
321
322
323
+ // 4) Render
322
324
renderer := gomplate .NewRenderer (opts )
323
325
324
326
var buf bytes.Buffer
@@ -328,8 +330,7 @@ func ProcessTmplWithDatasourcesGomplate(
328
330
Writer : & buf ,
329
331
}
330
332
331
- err = renderer .RenderTemplates (context .Background (), []gomplate.Template {tpl })
332
- if err != nil {
333
+ if err := renderer .RenderTemplates (context .Background (), []gomplate.Template {tpl }); err != nil {
333
334
return "" , fmt .Errorf ("failed to render template: %w" , err )
334
335
}
335
336
0 commit comments