diff --git a/internal/cmd/config.go b/internal/cmd/config.go index 07d933b8dcd..d5e6f371379 100644 --- a/internal/cmd/config.go +++ b/internal/cmd/config.go @@ -2466,11 +2466,6 @@ func (c *Config) runHookPre(hook string) error { func (c *Config) setEncryption() error { switch c.Encryption { case "age": - // Only use builtin age encryption if age encryption is explicitly - // specified. Otherwise, chezmoi would fall back to using age encryption - // (rather than no encryption) if age is not in $PATH, which leads to - // error messages from the builtin age instead of error messages about - // encryption not being configured. c.Age.UseBuiltin = c.UseBuiltinAge.Value(c.useBuiltinAgeAutoFunc) c.encryption = &c.Age case "gpg": @@ -2480,8 +2475,17 @@ func (c *Config) setEncryption() error { // gpg for backwards compatibility. switch { case !reflect.DeepEqual(c.GPG, defaultGPGEncryptionConfig): + c.errorf( + "warning: 'encryption' not set, using gpg configuration. " + + "Check if 'encryption' is correctly set as the top-level key.\n", + ) c.encryption = &c.GPG case !reflect.DeepEqual(c.Age, defaultAgeEncryptionConfig): + c.errorf( + "warning: 'encryption' not set, using age configuration. " + + "Check if 'encryption' is correctly set as the top-level key.\n", + ) + c.Age.UseBuiltin = c.UseBuiltinAge.Value(c.useBuiltinAgeAutoFunc) c.encryption = &c.Age default: c.encryption = chezmoi.NoEncryption{} @@ -2511,7 +2515,7 @@ func (c *Config) setEnvironmentVariables() error { } for key, value := range env { if strings.HasPrefix(key, "CHEZMOI_") { - c.errorf("warning: %s: overriding reserved environment variable", key) + c.errorf("warning: %s: overriding reserved environment variable\n", key) } if err := os.Setenv(key, value); err != nil { return err diff --git a/internal/cmd/config_test.go b/internal/cmd/config_test.go index 098ee3f2134..3b3ac722fe8 100644 --- a/internal/cmd/config_test.go +++ b/internal/cmd/config_test.go @@ -1,6 +1,7 @@ package cmd import ( + "bytes" "fmt" "io" "io/fs" @@ -303,6 +304,178 @@ func TestUpperSnakeCaseToCamelCase(t *testing.T) { } } +func TestIssue3980(t *testing.T) { + tests := []struct { + name string + filename string + contents string + expectsErr bool + warns []string + expectedEncryptionType interface{} + usesBuiltinAge bool + }{ + { + name: "empty_config", + filename: "chezmoi.toml", + contents: chezmoitest.JoinLines( + "", + ), + expectsErr: false, + warns: nil, + expectedEncryptionType: chezmoi.NoEncryption{}, + usesBuiltinAge: false, + }, + { + name: "empty_encryption_no_configs", + filename: "chezmoi.toml", + contents: chezmoitest.JoinLines( + "encryption = \"\"", + ), + expectsErr: false, + warns: nil, + expectedEncryptionType: chezmoi.NoEncryption{}, + usesBuiltinAge: false, + }, + { + name: "valid_age_config", + filename: "chezmoi.toml", + contents: chezmoitest.JoinLines( + "encryption = \"age\"", + "[age]", + "command = \"fakeage\"", + "identity = \"key.txt\"", + ), + expectsErr: false, + warns: nil, + expectedEncryptionType: &chezmoi.AgeEncryption{}, + usesBuiltinAge: true, + }, + { + name: "valid_age_config_no_builtin", + filename: "chezmoi.toml", + contents: chezmoitest.JoinLines( + "encryption = \"age\"", + "useBuiltinAge = \"off\"", + "[age]", + "command = \"fakeage\"", + "identity = \"key.txt\"", + ), + expectsErr: false, + warns: nil, + expectedEncryptionType: &chezmoi.AgeEncryption{}, + usesBuiltinAge: false, + }, + { + name: "valid_gpg_config", + filename: "chezmoi.toml", + contents: chezmoitest.JoinLines( + "encryption = \"gpg\"", + "[gpg]", + "command = \"fakegpg\"", + "symmetric = true", + ), + expectsErr: false, + warns: nil, + expectedEncryptionType: &chezmoi.GPGEncryption{}, + usesBuiltinAge: false, + }, + { + name: "unset_encryption_uses_gpg", + filename: "chezmoi.toml", + contents: chezmoitest.JoinLines( + "[gpg]", + "command = \"fakegpg\"", + "symmetric = true", + ), + expectsErr: false, + warns: []string{"warning: 'encryption' not set, using gpg configuration"}, + expectedEncryptionType: &chezmoi.GPGEncryption{}, + usesBuiltinAge: false, + }, + { + name: "unset_encryption_uses_age", + filename: "chezmoi.toml", + contents: chezmoitest.JoinLines( + "[age]", + "command = \"fakeage\"", + "identity = \"key.txt\"", + ), + expectsErr: false, + warns: []string{"warning: 'encryption' not set, using age configuration"}, + expectedEncryptionType: &chezmoi.AgeEncryption{}, + usesBuiltinAge: true, + }, + { + name: "unset_encryption_uses_age_no_builtin", + filename: "chezmoi.toml", + contents: chezmoitest.JoinLines( + "useBuiltinAge = \"off\"", + "[age]", + "command = \"fakeage\"", + "identity = \"key.txt\"", + ), + expectsErr: false, + warns: []string{"warning: 'encryption' not set, using age configuration"}, + expectedEncryptionType: &chezmoi.AgeEncryption{}, + usesBuiltinAge: false, + }, + { + name: "unset_encryption_gpg_priority", + filename: "chezmoi.toml", + contents: chezmoitest.JoinLines( + "[age]", + "command = \"fakeage\"", + "identity = \"key.txt\"", + "[gpg]", + "command = \"fakegpg\"", + "symmetric = true", + ), + expectsErr: false, + warns: []string{"warning: 'encryption' not set, using gpg configuration"}, + expectedEncryptionType: &chezmoi.GPGEncryption{}, + usesBuiltinAge: false, + }, + { + name: "unknown_encryption", + filename: "chezmoi.toml", + contents: chezmoitest.JoinLines( + "encryption = \"unknown\"", + ), + expectsErr: true, + warns: nil, + usesBuiltinAge: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + chezmoitest.WithTestFS(t, map[string]any{ + "/home/user/.config/chezmoi/" + tt.filename: tt.contents, + }, func(fileSystem vfs.FS) { + c := newTestConfig(t, fileSystem) + + // Create a buffer to capture stderr. + var stderr bytes.Buffer + c.stderr = &stderr + + err := c.execute([]string{"init"}) + + if tt.expectsErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + assert.Equal(t, reflect.TypeOf(tt.expectedEncryptionType), reflect.TypeOf(c.encryption)) + assert.Equal(t, tt.usesBuiltinAge, c.Age.UseBuiltin) + + for _, warn := range tt.warns { + assert.Contains(t, stderr.String(), warn) + } + }) + }) + } +} + func newTestConfig(t *testing.T, fileSystem vfs.FS, options ...configOption) *Config { t.Helper() system := chezmoi.NewRealSystem(fileSystem)