|
9 | 9 | "os" |
10 | 10 | "os/exec" |
11 | 11 | "strings" |
12 | | - "text/template" |
13 | 12 | "time" |
14 | 13 |
|
15 | 14 | "github.com/hashicorp/go-multierror" |
@@ -84,17 +83,14 @@ func runSteps(ctx context.Context, rf RepoFetcher, wc WorkspaceCreator, repo *gr |
84 | 83 | } |
85 | 84 | defer os.Remove(runScriptFile.Name()) |
86 | 85 |
|
87 | | - // Parse step.Run as a template... |
88 | | - tmpl, err := parseAsTemplate("step-run", step.Run, &stepContext) |
89 | | - if err != nil { |
| 86 | + // Parse step.Run as a template and render it into a buffer and the |
| 87 | + // temp file we just created. |
| 88 | + var runScript bytes.Buffer |
| 89 | + out := io.MultiWriter(&runScript, runScriptFile) |
| 90 | + if err := renderTemplate("step-run", step.Run, out, &stepContext); err != nil { |
90 | 91 | return nil, errors.Wrap(err, "parsing step run") |
91 | 92 | } |
92 | 93 |
|
93 | | - // ... and render it into a buffer and the temp file we just created. |
94 | | - var runScript bytes.Buffer |
95 | | - if err := tmpl.Execute(io.MultiWriter(&runScript, runScriptFile), stepContext); err != nil { |
96 | | - return nil, errors.Wrap(err, "executing template") |
97 | | - } |
98 | 94 | if err := runScriptFile.Close(); err != nil { |
99 | 95 | return nil, errors.Wrap(err, "closing temporary file") |
100 | 96 | } |
@@ -333,159 +329,3 @@ func (e stepFailedErr) SingleLineError() string { |
333 | 329 |
|
334 | 330 | return strings.Split(out, "\n")[0] |
335 | 331 | } |
336 | | - |
337 | | -func parseAsTemplate(name, input string, stepCtx *StepContext) (*template.Template, error) { |
338 | | - return template.New(name).Delims("${{", "}}").Funcs(stepCtx.ToFuncMap()).Parse(input) |
339 | | -} |
340 | | - |
341 | | -func renderMap(m map[string]string, stepCtx *StepContext) (map[string]string, error) { |
342 | | - rendered := make(map[string]string, len(m)) |
343 | | - |
344 | | - for k, v := range rendered { |
345 | | - var out bytes.Buffer |
346 | | - |
347 | | - tmpl, err := parseAsTemplate(k, v, stepCtx) |
348 | | - if err != nil { |
349 | | - return rendered, err |
350 | | - } |
351 | | - |
352 | | - if err := tmpl.Execute(&out, stepCtx); err != nil { |
353 | | - return rendered, err |
354 | | - } |
355 | | - |
356 | | - rendered[k] = out.String() |
357 | | - } |
358 | | - |
359 | | - return rendered, nil |
360 | | -} |
361 | | - |
362 | | -// StepContext represents the contextual information available when executing a |
363 | | -// step that's defined in a campaign spec. |
364 | | -type StepContext struct { |
365 | | - PreviousStep StepResult |
366 | | - Repository graphql.Repository |
367 | | -} |
368 | | - |
369 | | -// ToFuncMap returns a template.FuncMap to access fields on the StepContext in a |
370 | | -// text/template. |
371 | | -func (stepCtx *StepContext) ToFuncMap() template.FuncMap { |
372 | | - return template.FuncMap{ |
373 | | - "join": func(list []string, sep string) string { |
374 | | - return strings.Join(list, sep) |
375 | | - }, |
376 | | - "split": func(s string, sep string) []string { |
377 | | - return strings.Split(s, sep) |
378 | | - }, |
379 | | - "previous_step": func() map[string]interface{} { |
380 | | - result := map[string]interface{}{ |
381 | | - "modified_files": stepCtx.PreviousStep.ModifiedFiles(), |
382 | | - "added_files": stepCtx.PreviousStep.AddedFiles(), |
383 | | - "deleted_files": stepCtx.PreviousStep.DeletedFiles(), |
384 | | - "renamed_files": stepCtx.PreviousStep.RenamedFiles(), |
385 | | - } |
386 | | - |
387 | | - if stepCtx.PreviousStep.Stdout != nil { |
388 | | - result["stdout"] = stepCtx.PreviousStep.Stdout.String() |
389 | | - } else { |
390 | | - result["stdout"] = "" |
391 | | - } |
392 | | - |
393 | | - if stepCtx.PreviousStep.Stderr != nil { |
394 | | - result["stderr"] = stepCtx.PreviousStep.Stderr.String() |
395 | | - } else { |
396 | | - result["stderr"] = "" |
397 | | - } |
398 | | - |
399 | | - return result |
400 | | - }, |
401 | | - "repository": func() map[string]interface{} { |
402 | | - return map[string]interface{}{ |
403 | | - "search_result_paths": stepCtx.Repository.SearchResultPaths(), |
404 | | - "name": stepCtx.Repository.Name, |
405 | | - } |
406 | | - }, |
407 | | - } |
408 | | -} |
409 | | - |
410 | | -// StepResult represents the result of a previously executed step. |
411 | | -type StepResult struct { |
412 | | - // files are the changes made to files by the step. |
413 | | - files *StepChanges |
414 | | - |
415 | | - // Stdout is the output produced by the step on standard out. |
416 | | - Stdout *bytes.Buffer |
417 | | - // Stderr is the output produced by the step on standard error. |
418 | | - Stderr *bytes.Buffer |
419 | | -} |
420 | | - |
421 | | -// StepChanges are the changes made to files by a previous step in a repository. |
422 | | -type StepChanges struct { |
423 | | - Modified []string |
424 | | - Added []string |
425 | | - Deleted []string |
426 | | - Renamed []string |
427 | | -} |
428 | | - |
429 | | -// ModifiedFiles returns the files modified by a step. |
430 | | -func (r StepResult) ModifiedFiles() []string { |
431 | | - if r.files != nil { |
432 | | - return r.files.Modified |
433 | | - } |
434 | | - return []string{} |
435 | | -} |
436 | | - |
437 | | -// AddedFiles returns the files added by a step. |
438 | | -func (r StepResult) AddedFiles() []string { |
439 | | - if r.files != nil { |
440 | | - return r.files.Added |
441 | | - } |
442 | | - return []string{} |
443 | | -} |
444 | | - |
445 | | -// DeletedFiles returns the files deleted by a step. |
446 | | -func (r StepResult) DeletedFiles() []string { |
447 | | - if r.files != nil { |
448 | | - return r.files.Deleted |
449 | | - } |
450 | | - return []string{} |
451 | | -} |
452 | | - |
453 | | -// RenamedFiles returns the new name of files that have been renamed by a step. |
454 | | -func (r StepResult) RenamedFiles() []string { |
455 | | - if r.files != nil { |
456 | | - return r.files.Renamed |
457 | | - } |
458 | | - return []string{} |
459 | | -} |
460 | | - |
461 | | -func parseGitStatus(out []byte) (StepChanges, error) { |
462 | | - result := StepChanges{} |
463 | | - |
464 | | - stripped := strings.TrimSpace(string(out)) |
465 | | - if len(stripped) == 0 { |
466 | | - return result, nil |
467 | | - } |
468 | | - |
469 | | - for _, line := range strings.Split(stripped, "\n") { |
470 | | - if len(line) < 4 { |
471 | | - return result, fmt.Errorf("git status line has unrecognized format: %q", line) |
472 | | - } |
473 | | - |
474 | | - file := line[3:] |
475 | | - |
476 | | - switch line[0] { |
477 | | - case 'M': |
478 | | - result.Modified = append(result.Modified, file) |
479 | | - case 'A': |
480 | | - result.Added = append(result.Added, file) |
481 | | - case 'D': |
482 | | - result.Deleted = append(result.Deleted, file) |
483 | | - case 'R': |
484 | | - files := strings.Split(file, " -> ") |
485 | | - newFile := files[len(files)-1] |
486 | | - result.Renamed = append(result.Renamed, newFile) |
487 | | - } |
488 | | - } |
489 | | - |
490 | | - return result, nil |
491 | | -} |
0 commit comments