Skip to content

Commit c3980e0

Browse files
authored
fix: Support external log control (#311)
* fix: Fully swap out the logger to make it configurable by callers * fix: Explicitly plumb logger throughout the entire codebase
1 parent 6adb201 commit c3980e0

31 files changed

Lines changed: 913 additions & 283 deletions

cli/boilerplate_cli.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@ package cli
44
import (
55
"context"
66
"fmt"
7+
"os"
78
"path/filepath"
89
"runtime"
910

1011
"github.com/urfave/cli/v2"
1112

1213
"github.com/gruntwork-io/boilerplate/manifest"
1314
"github.com/gruntwork-io/boilerplate/options"
15+
"github.com/gruntwork-io/boilerplate/pkg/logging"
1416
"github.com/gruntwork-io/boilerplate/templates"
1517
"github.com/gruntwork-io/boilerplate/variables"
1618
"github.com/gruntwork-io/boilerplate/version"
@@ -139,7 +141,9 @@ func runApp(cliContext *cli.Context) error {
139141
// The root boilerplate.yml is not itself a dependency, so we pass an empty Dependency.
140142
emptyDep := variables.Dependency{}
141143

142-
result, err := templates.ProcessTemplateWithContext(ctx, opts, opts, &emptyDep)
144+
l := logging.New(os.Stdout, logging.LevelInfo)
145+
146+
result, err := templates.ProcessTemplateWithContext(ctx, l, opts, opts, &emptyDep)
143147
if err != nil {
144148
return err
145149
}

cmd/wasm/main.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"syscall/js"
99

1010
"github.com/gruntwork-io/boilerplate/options"
11+
"github.com/gruntwork-io/boilerplate/pkg/logging"
1112
"github.com/gruntwork-io/boilerplate/render"
1213
)
1314

@@ -45,7 +46,7 @@ func renderTemplate(this js.Value, args []js.Value) any {
4546
OnMissingConfig: options.Ignore,
4647
}
4748

48-
result, err := render.RenderTemplateFromString("template", templateStr, variables, opts)
49+
result, err := render.RenderTemplateFromString(logging.Discard(), "template", templateStr, variables, opts)
4950
if err != nil {
5051
return js.Global().Get("Error").New(fmt.Sprintf("template rendering failed: %v", err))
5152
}

config/config.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ import (
1313
"gopkg.in/yaml.v3"
1414

1515
"github.com/gruntwork-io/boilerplate/internal/fileutil"
16-
"github.com/gruntwork-io/boilerplate/internal/logging"
1716
"github.com/gruntwork-io/boilerplate/options"
17+
"github.com/gruntwork-io/boilerplate/pkg/logging"
1818
"github.com/gruntwork-io/boilerplate/util"
1919
"github.com/gruntwork-io/boilerplate/variables"
2020
)
@@ -192,12 +192,12 @@ func (config *BoilerplateConfig) MarshalYAML() (any, error) {
192192
}
193193

194194
// LoadBoilerplateConfig loads the boilerplate.yml config contents for the folder specified in the given options.
195-
func LoadBoilerplateConfig(opts *options.BoilerplateOptions) (*BoilerplateConfig, error) {
195+
func LoadBoilerplateConfig(l logging.Logger, opts *options.BoilerplateOptions) (*BoilerplateConfig, error) {
196196
configPath := BoilerplateConfigPath(opts.TemplateFolder)
197197

198198
switch {
199199
case fileutil.PathExists(configPath):
200-
logging.Logger.Printf("Loading boilerplate config from %s", configPath)
200+
l.Debugf("Loading boilerplate config from %s", configPath)
201201

202202
bytes, err := os.ReadFile(configPath)
203203
if err != nil {
@@ -206,7 +206,7 @@ func LoadBoilerplateConfig(opts *options.BoilerplateOptions) (*BoilerplateConfig
206206

207207
return ParseBoilerplateConfig(bytes)
208208
case opts.OnMissingConfig == options.Ignore:
209-
logging.Logger.Printf("Warning: boilerplate config file not found at %s. The %s flag is set, so ignoring. Note that no variables will be available while generating.", configPath, options.OptMissingConfigAction)
209+
l.Warnf("boilerplate config file not found at %s. The %s flag is set, so ignoring. Note that no variables will be available while generating.", configPath, options.OptMissingConfigAction)
210210
return &BoilerplateConfig{}, nil
211211
default:
212212
// If the template URL is similar to a git URL, surface in error message that there may be a misspelling/typo.

config/config_test.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"gopkg.in/yaml.v3"
1515

1616
"github.com/gruntwork-io/boilerplate/options"
17+
"github.com/gruntwork-io/boilerplate/pkg/logging"
1718
"github.com/gruntwork-io/boilerplate/testutil"
1819
"github.com/gruntwork-io/boilerplate/variables"
1920

@@ -621,7 +622,7 @@ func TestParseBoilerplateConfigMultipleHooks(t *testing.T) {
621622
func TestLoadBoilerplateConfigFullConfig(t *testing.T) {
622623
t.Parallel()
623624

624-
actual, err := LoadBoilerplateConfig(testutil.CreateTestOptions("../test-fixtures/config-test/full-config"))
625+
actual, err := LoadBoilerplateConfig(logging.Discard(), testutil.CreateTestOptions("../test-fixtures/config-test/full-config"))
625626
expected := &BoilerplateConfig{
626627
Partials: []string{"../templates/foo"},
627628
Variables: []variables.Variable{
@@ -655,7 +656,7 @@ func TestLoadBoilerplateConfigNoConfig(t *testing.T) {
655656
t.Parallel()
656657

657658
templateFolder := "../test-fixtures/config-test/no-config"
658-
_, err := LoadBoilerplateConfig(testutil.CreateTestOptions(templateFolder))
659+
_, err := LoadBoilerplateConfig(logging.Discard(), testutil.CreateTestOptions(templateFolder))
659660
expectedErr := BoilerplateConfigNotFound(path.Join(templateFolder, "boilerplate.yml"))
660661

661662
assert.ErrorIs(t, err, expectedErr, "Expected error %v but got %v", expectedErr, err)
@@ -667,7 +668,7 @@ func TestLoadBoilerplateConfigNoConfigIgnore(t *testing.T) {
667668
templateFolder := "../test-fixtures/config-test/no-config"
668669
opts := testutil.CreateTestOptions(templateFolder)
669670
opts.OnMissingConfig = options.Ignore
670-
actual, err := LoadBoilerplateConfig(opts)
671+
actual, err := LoadBoilerplateConfig(logging.Discard(), opts)
671672
expected := &BoilerplateConfig{}
672673

673674
require.NoError(t, err, "Unexpected error: %v", err)
@@ -677,7 +678,7 @@ func TestLoadBoilerplateConfigNoConfigIgnore(t *testing.T) {
677678
func TestLoadBoilerplateConfigInvalidConfig(t *testing.T) {
678679
t.Parallel()
679680

680-
_, err := LoadBoilerplateConfig(testutil.CreateTestOptions("../test-fixtures/config-test/invalid-config"))
681+
_, err := LoadBoilerplateConfig(logging.Discard(), testutil.CreateTestOptions("../test-fixtures/config-test/invalid-config"))
681682

682683
require.Error(t, err)
683684

config/get_variables.go

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ import (
1212
"github.com/AlecAivazis/survey/v2/terminal"
1313
ozzo "github.com/go-ozzo/ozzo-validation"
1414
"github.com/gruntwork-io/boilerplate/internal/color"
15-
"github.com/gruntwork-io/boilerplate/internal/logging"
1615
"github.com/gruntwork-io/boilerplate/options"
16+
"github.com/gruntwork-io/boilerplate/pkg/logging"
1717
"github.com/gruntwork-io/boilerplate/render"
1818
"github.com/gruntwork-io/boilerplate/variables"
1919
"github.com/hashicorp/go-multierror"
@@ -24,16 +24,16 @@ const MaxReferenceDepth = 20
2424
// GetVariables gets a value for each of the variables specified in boilerplateConfig, other than those already in existingVariables.
2525
// The value for a variable can come from the user (if the non-interactive option isn't set), the default value in the
2626
// config, or a command line option.
27-
func GetVariables(opts *options.BoilerplateOptions, boilerplateConfig, rootBoilerplateConfig *BoilerplateConfig, thisDep *variables.Dependency) (map[string]any, error) {
28-
return GetVariablesWithContext(context.Background(), opts, boilerplateConfig, rootBoilerplateConfig, thisDep)
27+
func GetVariables(l logging.Logger, opts *options.BoilerplateOptions, boilerplateConfig, rootBoilerplateConfig *BoilerplateConfig, thisDep *variables.Dependency) (map[string]any, error) {
28+
return GetVariablesWithContext(context.Background(), l, opts, boilerplateConfig, rootBoilerplateConfig, thisDep)
2929
}
3030

3131
// GetVariablesWithContext collects variables from the user, variable defaults in the boilerplate.yml config, command line options, and environment
3232
// variables. Variables in Boilerplate can can be used in both the Boilerplate config itself and in the templates.
3333
//
3434
// The value for a variable can come from the user (if the non-interactive option isn't set), the default value in the
3535
// config, or a command line option.
36-
func GetVariablesWithContext(ctx context.Context, opts *options.BoilerplateOptions, boilerplateConfig, rootBoilerplateConfig *BoilerplateConfig, thisDep *variables.Dependency) (map[string]any, error) {
36+
func GetVariablesWithContext(ctx context.Context, l logging.Logger, opts *options.BoilerplateOptions, boilerplateConfig, rootBoilerplateConfig *BoilerplateConfig, thisDep *variables.Dependency) (map[string]any, error) {
3737
renderedVariables := map[string]any{}
3838

3939
// Add a variable for all variables contained in the root config file. This will allow Golang template users
@@ -104,7 +104,7 @@ func GetVariablesWithContext(ctx context.Context, opts *options.BoilerplateOptio
104104
for _, keyOrderPair := range keyAndOrderPairs {
105105
variable := variablesInConfig[keyOrderPair.Key]
106106

107-
unmarshalled, err := GetValueForVariable(variable, variablesInConfig, variablesToRender, opts, 0)
107+
unmarshalled, err := GetValueForVariable(l, variable, variablesInConfig, variablesToRender, opts, 0)
108108
if err != nil {
109109
return nil, err
110110
}
@@ -114,7 +114,7 @@ func GetVariablesWithContext(ctx context.Context, opts *options.BoilerplateOptio
114114

115115
// Pass all the user provided variables through a rendering pipeline to ensure they are evaluated down to
116116
// primitives.
117-
newlyRenderedVariables, err := render.RenderVariablesWithContext(ctx, opts, variablesToRender, renderedVariables)
117+
newlyRenderedVariables, err := render.RenderVariablesWithContext(ctx, l, opts, variablesToRender, renderedVariables)
118118
if err != nil {
119119
return nil, err
120120
}
@@ -135,6 +135,7 @@ func GetVariablesWithContext(ctx context.Context, opts *options.BoilerplateOptio
135135
}
136136

137137
func GetValueForVariable(
138+
l logging.Logger,
138139
variable variables.Variable,
139140
variablesInConfig map[string]variables.Variable,
140141
valuesForPreviousVariables map[string]any,
@@ -161,11 +162,11 @@ func GetValueForVariable(
161162
return nil, MissingReference{VariableName: variable.Name(), ReferenceName: variable.Reference()}
162163
}
163164

164-
return GetValueForVariable(reference, variablesInConfig, valuesForPreviousVariables, opts, referenceDepth+1)
165+
return GetValueForVariable(l, reference, variablesInConfig, valuesForPreviousVariables, opts, referenceDepth+1)
165166
}
166167

167168
// Run the value we receive from getVariable through validations, ensuring values provided by --var-files will also be checked
168-
value, err := getVariable(variable, opts)
169+
value, err := getVariable(l, variable, opts)
169170
if err != nil {
170171
return value, err
171172
}
@@ -183,23 +184,23 @@ func GetValueForVariable(
183184

184185
// Get a value for the given variable. The value can come from the user (if the non-interactive option isn't set), the
185186
// default value in the config, or a command line option.
186-
func getVariable(variable variables.Variable, opts *options.BoilerplateOptions) (any, error) {
187+
func getVariable(l logging.Logger, variable variables.Variable, opts *options.BoilerplateOptions) (any, error) {
187188
valueFromVars, valueSpecifiedInVars := getVariableFromVars(variable, opts)
188189

189190
switch {
190191
case valueSpecifiedInVars:
191-
logging.Logger.Printf("Using value specified via command line options for variable '%s': %s", variable.FullName(), valueFromVars)
192+
l.Debugf("Using value specified via command line options for variable '%s': %s", variable.FullName(), valueFromVars)
192193
return valueFromVars, nil
193194
case opts.NonInteractive && variable.Default() != nil:
194-
logging.Logger.Printf("Using default value for variable '%s': %v", variable.FullName(), variable.Default())
195+
l.Debugf("Using default value for variable '%s': %v", variable.FullName(), variable.Default())
195196
return variable.Default(), nil
196197
case opts.NonInteractive:
197198
return nil, MissingVariableWithNonInteractiveMode(variable.FullName())
198199
case variable.Default() != nil && !variable.Confirm():
199-
logging.Logger.Printf("Using default value for variable '%s': %v", variable.FullName(), variable.Default())
200+
l.Debugf("Using default value for variable '%s': %v", variable.FullName(), variable.Default())
200201
return variable.Default(), nil
201202
default:
202-
return getVariableFromUser(variable, variables.InvalidEntries{})
203+
return getVariableFromUser(l, variable, variables.InvalidEntries{})
203204
}
204205
}
205206

@@ -215,7 +216,7 @@ func getVariableFromVars(variable variables.Variable, opts *options.BoilerplateO
215216
}
216217

217218
// Get the value for the given variable by prompting the user
218-
func getVariableFromUser(variable variables.Variable, invalidEntries variables.InvalidEntries) (any, error) {
219+
func getVariableFromUser(l logging.Logger, variable variables.Variable, invalidEntries variables.InvalidEntries) (any, error) {
219220
// Add a newline for legibility and padding
220221
fmt.Println()
221222

@@ -242,12 +243,12 @@ func getVariableFromUser(variable variables.Variable, invalidEntries variables.I
242243
},
243244
}
244245

245-
return getVariableFromUser(variable, ie)
246+
return getVariableFromUser(l, variable, ie)
246247
}
247248

248249
if value == "" {
249250
// TODO: what if the user wanted an empty string instead of the default?
250-
logging.Logger.Printf("Using default value for variable '%s': %v", variable.FullName(), variable.Default())
251+
l.Debugf("Using default value for variable '%s': %v", variable.FullName(), variable.Default())
251252
return variable.Default(), nil
252253
}
253254

config/get_variables_test.go

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"github.com/stretchr/testify/require"
1111

1212
"github.com/gruntwork-io/boilerplate/options"
13+
"github.com/gruntwork-io/boilerplate/pkg/logging"
1314
"github.com/gruntwork-io/boilerplate/testutil"
1415
"github.com/gruntwork-io/boilerplate/variables"
1516
)
@@ -100,7 +101,7 @@ func TestGetVariableNoMatchNonInteractive(t *testing.T) {
100101
variable := variables.NewStringVariable("foo")
101102
opts := testutil.CreateTestOptionsForShell(true, false)
102103

103-
_, err := getVariable(variable, opts)
104+
_, err := getVariable(logging.Discard(), variable, opts)
104105

105106
require.Error(t, err)
106107
assert.ErrorIs(t, err, MissingVariableWithNonInteractiveMode("foo"), "Expected a MissingVariableWithNonInteractiveMode error but got %s", reflect.TypeOf(err))
@@ -119,7 +120,7 @@ func TestGetVariableInVarsNonInteractive(t *testing.T) {
119120
},
120121
}
121122

122-
actual, err := getVariable(variable, opts)
123+
actual, err := getVariable(logging.Discard(), variable, opts)
123124
expected := "bar"
124125

125126
require.NoError(t, err)
@@ -139,7 +140,7 @@ func TestGetVariableDefaultNonInteractive(t *testing.T) {
139140
},
140141
}
141142

142-
actual, err := getVariable(variable, opts)
143+
actual, err := getVariable(logging.Discard(), variable, opts)
143144
expected := "bar"
144145

145146
require.NoError(t, err)
@@ -154,7 +155,7 @@ func TestGetVariablesNoVariables(t *testing.T) {
154155
rootBoilerplateConfig := &BoilerplateConfig{}
155156
dependency := &variables.Dependency{}
156157

157-
actual, err := GetVariables(opts, boilerplateConfig, rootBoilerplateConfig, dependency)
158+
actual, err := GetVariables(logging.Discard(), opts, boilerplateConfig, rootBoilerplateConfig, dependency)
158159
expected := map[string]any{
159160
"BoilerplateConfigVars": map[string]variables.Variable{},
160161
"BoilerplateConfigDeps": map[string]*variables.Dependency{},
@@ -181,7 +182,7 @@ func TestGetVariablesNoMatchNonInteractive(t *testing.T) {
181182
rootBoilerplateConfig := &BoilerplateConfig{}
182183
dependency := &variables.Dependency{}
183184

184-
_, err := GetVariables(opts, boilerplateConfig, rootBoilerplateConfig, dependency)
185+
_, err := GetVariables(logging.Discard(), opts, boilerplateConfig, rootBoilerplateConfig, dependency)
185186

186187
require.Error(t, err)
187188
assert.ErrorIs(t, err, MissingVariableWithNonInteractiveMode("foo"), "Expected a MissingVariableWithNonInteractiveMode error but got %s", reflect.TypeOf(err))
@@ -208,7 +209,7 @@ func TestGetVariablesMatchFromVars(t *testing.T) {
208209

209210
dependency := &variables.Dependency{}
210211

211-
actual, err := GetVariables(opts, boilerplateConfig, rootBoilerplateConfig, dependency)
212+
actual, err := GetVariables(logging.Discard(), opts, boilerplateConfig, rootBoilerplateConfig, dependency)
212213
expected := map[string]any{
213214
"foo": "bar",
214215
"BoilerplateConfigVars": map[string]variables.Variable{},
@@ -248,7 +249,7 @@ func TestGetVariablesMatchFromVarsAndDefaults(t *testing.T) {
248249

249250
dependency := &variables.Dependency{}
250251

251-
actual, err := GetVariables(opts, boilerplateConfig, rootBoilerplateConfig, dependency)
252+
actual, err := GetVariables(logging.Discard(), opts, boilerplateConfig, rootBoilerplateConfig, dependency)
252253
expected := map[string]any{
253254
"key1": "value1",
254255
"key2": "value2",
@@ -275,7 +276,7 @@ func TestGetVariableInteractiveWithDefaultSkipsPrompt(t *testing.T) {
275276
Vars: map[string]any{},
276277
}
277278

278-
actual, err := getVariable(variable, opts)
279+
actual, err := getVariable(logging.Discard(), variable, opts)
279280
require.NoError(t, err)
280281
assert.Equal(t, "default-val", actual)
281282
}
@@ -294,7 +295,7 @@ func TestGetVariableInteractiveWithDefaultAndConfirmDoesNotSkipPrompt(t *testing
294295
Vars: map[string]any{},
295296
}
296297

297-
actual, err := getVariable(variable, opts)
298+
actual, err := getVariable(logging.Discard(), variable, opts)
298299
require.NoError(t, err)
299300
assert.Equal(t, "default-val", actual)
300301
}
@@ -308,7 +309,7 @@ func TestGetVariableInteractiveFormulaDefaultSkipsPrompt(t *testing.T) {
308309
Vars: map[string]any{},
309310
}
310311

311-
actual, err := getVariable(variable, opts)
312+
actual, err := getVariable(logging.Discard(), variable, opts)
312313
require.NoError(t, err)
313314
assert.Equal(t, "{{ .Primary }}", actual)
314315
}

docs/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
# build output
22
dist/
33

4+
# wasm artifacts fetched from GitHub Releases at build time (see scripts/fetch-wasm.mjs)
5+
public/wasm/
6+
47
# generated types
58
.astro/
69

0 commit comments

Comments
 (0)