Skip to content

Commit d6c854f

Browse files
author
Tim Eijgelshoven
committed
feat: Add optional function to return manifest of generated files
1 parent 4794dc3 commit d6c854f

File tree

6 files changed

+272
-27
lines changed

6 files changed

+272
-27
lines changed

integration-tests/for_production_example_unix_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
package integration_tests
55

66
import (
7-
"io/ioutil"
7+
"os"
88
"path/filepath"
99
"testing"
1010

@@ -19,9 +19,9 @@ func TestForProductionTerragruntArchitectureBoilerplateExample(t *testing.T) {
1919

2020
forProductionExamplePath := "../examples/for-production/terragrunt-architecture-catalog"
2121

22-
outputBasePath, err := ioutil.TempDir("", "boilerplate-for-production-output")
22+
outputBasePath, err := os.MkdirTemp("", "boilerplate-for-production-output")
2323
require.NoError(t, err)
24-
//defer os.RemoveAll(outputBasePath)
24+
defer os.RemoveAll(outputBasePath)
2525

2626
templateFolder, err := filepath.Abs(filepath.Join(forProductionExamplePath, "blueprints", "reference-architecture"))
2727
require.NoError(t, err)

integration-tests/required_version_test.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
package integration_tests
22

33
import (
4-
"io/ioutil"
54
"os"
65
"testing"
76

87
"github.com/gruntwork-io/boilerplate/cli"
98
"github.com/gruntwork-io/boilerplate/config"
10-
"github.com/gruntwork-io/go-commons/errors"
9+
"github.com/gruntwork-io/boilerplate/errors"
1110
"github.com/gruntwork-io/go-commons/version"
1211
"github.com/stretchr/testify/assert"
1312
"github.com/stretchr/testify/require"
@@ -58,7 +57,7 @@ func TestRequiredVersionUnderTest(t *testing.T) {
5857
func runRequiredVersionExample(t *testing.T, templateFolder string) error {
5958
app := cli.CreateBoilerplateCli()
6059

61-
outputPath, err := ioutil.TempDir("", "boilerplate-test-output-reqver")
60+
outputPath, err := os.MkdirTemp("", "boilerplate-test-output-reqver")
6261
require.NoError(t, err)
6362
defer os.RemoveAll(outputPath)
6463

manifest/manifest.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package manifest
2+
3+
import (
4+
"path/filepath"
5+
)
6+
7+
// GeneratedFile represents a file that was generated by boilerplate
8+
type GeneratedFile struct {
9+
Path string `json:"path"`
10+
}
11+
12+
// Manifest represents the complete list of files generated by boilerplate
13+
type Manifest struct {
14+
OutputDir string
15+
Files []GeneratedFile
16+
}
17+
18+
// NewManifest creates a new manifest
19+
func NewManifest(outputDir string) *Manifest {
20+
return &Manifest{
21+
OutputDir: outputDir,
22+
Files: []GeneratedFile{},
23+
}
24+
}
25+
26+
// AddFile adds a generated file to the manifest
27+
func (m *Manifest) AddFile(outputPath string) error {
28+
// Convert to relative path from output directory
29+
relPath, err := filepath.Rel(m.OutputDir, outputPath)
30+
if err != nil {
31+
relPath = outputPath
32+
}
33+
34+
m.Files = append(m.Files, GeneratedFile{
35+
Path: relPath,
36+
})
37+
return nil
38+
}

manifest/manifest_test.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package manifest
2+
3+
import (
4+
"path/filepath"
5+
"testing"
6+
7+
"github.com/stretchr/testify/assert"
8+
)
9+
10+
func TestNewManifest(t *testing.T) {
11+
t.Parallel()
12+
13+
outputDir := "/test/output"
14+
15+
manifest := NewManifest(outputDir)
16+
17+
assert.Equal(t, outputDir, manifest.OutputDir)
18+
assert.Empty(t, manifest.Files)
19+
}
20+
21+
func TestAddFile(t *testing.T) {
22+
t.Parallel()
23+
24+
outputDir := "/test/output"
25+
manifest := NewManifest(outputDir)
26+
27+
// Test adding a file in the output directory
28+
outputPath := filepath.Join(outputDir, "subdir", "file.txt")
29+
30+
err := manifest.AddFile(outputPath)
31+
32+
assert.NoError(t, err)
33+
assert.Len(t, manifest.Files, 1)
34+
assert.Equal(t, "subdir/file.txt", manifest.Files[0].Path)
35+
}
36+
37+
func TestAddFileOutsideOutputDir(t *testing.T) {
38+
t.Parallel()
39+
40+
outputDir := "/test/output"
41+
manifest := NewManifest(outputDir)
42+
43+
// Test adding a file outside the output directory
44+
outputPath := "/other/dir/file.txt"
45+
46+
err := manifest.AddFile(outputPath)
47+
48+
assert.NoError(t, err)
49+
assert.Len(t, manifest.Files, 1)
50+
expectedRelPath, _ := filepath.Rel(outputDir, outputPath)
51+
assert.Equal(t, expectedRelPath, manifest.Files[0].Path)
52+
}
53+
54+
func TestAddMultipleFiles(t *testing.T) {
55+
t.Parallel()
56+
57+
outputDir := "/test/output"
58+
manifest := NewManifest(outputDir)
59+
60+
// Add first file
61+
err1 := manifest.AddFile(
62+
filepath.Join(outputDir, "file1.txt"),
63+
)
64+
65+
// Add second file
66+
err2 := manifest.AddFile(
67+
filepath.Join(outputDir, "subdir", "file2.txt"),
68+
)
69+
70+
assert.NoError(t, err1)
71+
assert.NoError(t, err2)
72+
assert.Len(t, manifest.Files, 2)
73+
assert.Equal(t, "file1.txt", manifest.Files[0].Path)
74+
assert.Equal(t, "subdir/file2.txt", manifest.Files[1].Path)
75+
}

templates/template_processor.go

Lines changed: 65 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import (
1010

1111
"github.com/gruntwork-io/go-commons/collections"
1212

13+
"github.com/gruntwork-io/boilerplate/manifest"
14+
1315
"github.com/gruntwork-io/boilerplate/config"
1416
"github.com/gruntwork-io/boilerplate/errors"
1517
getter_helper "github.com/gruntwork-io/boilerplate/getter-helper"
@@ -22,12 +24,23 @@ import (
2224
// The name of the variable that contains the current value of the loop in each iteration of for_each
2325
const eachVarName = "__each__"
2426

27+
// Maintaining original function name and signature for backwards compatability
28+
func ProcessTemplate(options, rootOpts *options.BoilerplateOptions, thisDep variables.Dependency) error {
29+
_, err := processTemplateInternal(options, rootOpts, thisDep, false)
30+
return err
31+
}
32+
33+
// ProcessTemplateWithManifest works like ProcessTemplate but also returns the manifest of generated files
34+
func ProcessTemplateWithManifest(options, rootOpts *options.BoilerplateOptions, thisDep variables.Dependency) (*manifest.Manifest, error) {
35+
return processTemplateInternal(options, rootOpts, thisDep, true)
36+
}
37+
2538
// Process the boilerplate template specified in the given options and use the existing variables. This function will
2639
// download remote templates to a temporary working directory, which is cleaned up at the end of the function. This
2740
// function will load any missing variables (either from command line options or by prompting the user), execute all the
2841
// dependent boilerplate templates, and then execute this template. Note that we pass in rootOptions so that template
2942
// dependencies can inspect properties of the root template.
30-
func ProcessTemplate(options, rootOpts *options.BoilerplateOptions, thisDep variables.Dependency) error {
43+
func processTemplateInternal(options, rootOpts *options.BoilerplateOptions, thisDep variables.Dependency, returnManifest bool) (*manifest.Manifest, error) {
3144
// If TemplateFolder is already set, use that directly as it is a local template. Otherwise, download to a temporary
3245
// working directory.
3346
if options.TemplateFolder == "" {
@@ -37,7 +50,7 @@ func ProcessTemplate(options, rootOpts *options.BoilerplateOptions, thisDep vari
3750
os.RemoveAll(workingDir)
3851
}()
3952
if err != nil {
40-
return err
53+
return nil, err
4154
}
4255

4356
// Set the TemplateFolder of the options to the download dir
@@ -46,56 +59,64 @@ func ProcessTemplate(options, rootOpts *options.BoilerplateOptions, thisDep vari
4659

4760
rootBoilerplateConfig, err := config.LoadBoilerplateConfig(rootOpts)
4861
if err != nil {
49-
return err
62+
return nil, err
5063
}
5164
if err := config.EnforceRequiredVersion(rootBoilerplateConfig); err != nil {
52-
return err
65+
return nil, err
5366
}
5467

5568
boilerplateConfig, err := config.LoadBoilerplateConfig(options)
5669
if err != nil {
57-
return err
70+
return nil, err
5871
}
5972
if err := config.EnforceRequiredVersion(boilerplateConfig); err != nil {
60-
return err
73+
return nil, err
6174
}
6275

6376
vars, err := config.GetVariables(options, boilerplateConfig, rootBoilerplateConfig, thisDep)
6477
if err != nil {
65-
return err
78+
return nil, err
6679
}
6780

6881
err = os.MkdirAll(options.OutputFolder, 0777)
6982
if err != nil {
70-
return errors.WithStackTrace(err)
83+
return nil, errors.WithStackTrace(err)
7184
}
7285

7386
err = processHooks(boilerplateConfig.Hooks.BeforeHooks, options, vars)
7487
if err != nil {
75-
return err
88+
return nil, err
7689
}
7790

7891
err = processDependencies(boilerplateConfig.Dependencies, options, boilerplateConfig.GetVariablesMap(), vars)
7992
if err != nil {
80-
return err
93+
return nil, err
8194
}
8295

8396
partials, err := processPartials(boilerplateConfig.Partials, options, vars)
8497
if err != nil {
85-
return err
98+
return nil, err
8699
}
87100

88-
err = processTemplateFolder(boilerplateConfig, options, vars, partials)
101+
var fileManifest *manifest.Manifest
102+
if returnManifest {
103+
fileManifest = manifest.NewManifest(options.OutputFolder)
104+
}
105+
106+
err = processTemplateFolder(boilerplateConfig, options, vars, partials, fileManifest)
89107
if err != nil {
90-
return err
108+
return nil, err
91109
}
92110

93111
err = processHooks(boilerplateConfig.Hooks.AfterHooks, options, vars)
94112
if err != nil {
95-
return err
113+
return nil, err
96114
}
97115

98-
return nil
116+
if returnManifest {
117+
return fileManifest, nil
118+
}
119+
return nil, nil
99120
}
100121

101122
func processPartials(partials []string, opts *options.BoilerplateOptions, vars map[string]interface{}) ([]string, error) {
@@ -455,6 +476,7 @@ func processTemplateFolder(
455476
opts *options.BoilerplateOptions,
456477
variables map[string]interface{},
457478
partials []string,
479+
fileManifest *manifest.Manifest,
458480
) error {
459481
util.Logger.Printf("Processing templates in %s and outputting generated files to %s", opts.TemplateFolder, opts.OutputFolder)
460482

@@ -477,7 +499,7 @@ func processTemplateFolder(
477499
return createOutputDir(path, opts, variables)
478500
} else {
479501
engine := determineTemplateEngine(processedEngines, path)
480-
return processFile(path, opts, variables, partials, engine)
502+
return processFile(path, opts, variables, partials, engine, fileManifest)
481503
}
482504
})
483505
}
@@ -490,16 +512,17 @@ func processFile(
490512
variables map[string]interface{},
491513
partials []string,
492514
engine variables.TemplateEngineType,
515+
fileManifest *manifest.Manifest,
493516
) error {
494517
isText, err := util.IsTextFile(path)
495518
if err != nil {
496519
return err
497520
}
498521

499522
if isText {
500-
return processTemplate(path, opts, variables, partials, engine)
523+
return processTemplate(path, opts, variables, partials, engine, fileManifest)
501524
} else {
502-
return copyFile(path, opts, variables)
525+
return copyFile(path, opts, variables, fileManifest)
503526
}
504527
}
505528

@@ -548,14 +571,24 @@ func outPath(file string, opts *options.BoilerplateOptions, variables map[string
548571
}
549572

550573
// Copy the given file, which is in options.TemplateFolder, to options.OutputFolder
551-
func copyFile(file string, opts *options.BoilerplateOptions, variables map[string]interface{}) error {
574+
func copyFile(file string, opts *options.BoilerplateOptions, variables map[string]interface{}, fileManifest *manifest.Manifest) error {
552575
destination, err := outPath(file, opts, variables)
553576
if err != nil {
554577
return err
555578
}
556579

557580
util.Logger.Printf("Copying %s to %s", file, destination)
558-
return util.CopyFile(file, destination)
581+
if err := util.CopyFile(file, destination); err != nil {
582+
return err
583+
}
584+
585+
if fileManifest != nil {
586+
if err := fileManifest.AddFile(destination); err != nil {
587+
return err
588+
}
589+
}
590+
591+
return nil
559592
}
560593

561594
// Run the template at templatePath, which is in templateFolder, through the Go template engine with the given
@@ -566,6 +599,7 @@ func processTemplate(
566599
vars map[string]interface{},
567600
partials []string,
568601
engine variables.TemplateEngineType,
602+
fileManifest *manifest.Manifest,
569603
) error {
570604
destination, err := outPath(templatePath, opts, vars)
571605
if err != nil {
@@ -588,7 +622,17 @@ func processTemplate(
588622
destination = strings.TrimSuffix(destination, ".jsonnet")
589623
}
590624

591-
return util.WriteFileWithSamePermissions(templatePath, destination, []byte(out))
625+
if err := util.WriteFileWithSamePermissions(templatePath, destination, []byte(out)); err != nil {
626+
return err
627+
}
628+
629+
if fileManifest != nil {
630+
if err := fileManifest.AddFile(destination); err != nil {
631+
return err
632+
}
633+
}
634+
635+
return nil
592636
}
593637

594638
// Return true if this is a path that should not be copied

0 commit comments

Comments
 (0)