Skip to content

Commit 193c269

Browse files
authored
Merge pull request #1929 from felixfontein/ini-bug
INI: fix converting integers to strings; improve float and time.Time formatting
2 parents c95f6a8 + 277805a commit 193c269

File tree

4 files changed

+104
-18
lines changed

4 files changed

+104
-18
lines changed

functional-tests/src/lib.rs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,57 @@ bar: baz
265265
}
266266
}
267267

268+
#[test]
269+
fn test_ini_values_as_strings() {
270+
let file_path = prepare_temp_file(
271+
"test_ini_values_as_strings.yaml",
272+
b"the_section:
273+
int: 123
274+
float: 1.23
275+
bool: true
276+
date: 2025-01-02
277+
timestamp: 2025-01-02 03:04:05
278+
utc_timestamp: 2025-01-02T03:04:05Z
279+
string: this is a string",
280+
);
281+
assert!(
282+
Command::new(SOPS_BINARY_PATH)
283+
.arg("encrypt")
284+
.arg("-i")
285+
.arg(file_path.clone())
286+
.output()
287+
.expect("Error running sops")
288+
.status
289+
.success(),
290+
"sops didn't exit successfully"
291+
);
292+
let output = Command::new(SOPS_BINARY_PATH)
293+
.arg("decrypt")
294+
.arg("--output-type")
295+
.arg("ini")
296+
.arg(file_path.clone())
297+
.output()
298+
.expect("Error running sops");
299+
println!(
300+
"stdout: {}, stderr: {}",
301+
String::from_utf8_lossy(&output.stdout),
302+
String::from_utf8_lossy(&output.stderr)
303+
);
304+
assert!(output.status.success(), "sops didn't exit successfully");
305+
let data = &String::from_utf8_lossy(&output.stdout);
306+
assert!(
307+
data == "[the_section]
308+
int = 123
309+
float = 1.23
310+
bool = true
311+
date = 2025-01-02T00:00:00Z
312+
timestamp = 2025-01-02T03:04:05Z
313+
utc_timestamp = 2025-01-02T03:04:05Z
314+
string = this is a string
315+
"
316+
);
317+
}
318+
268319
#[test]
269320
fn encrypt_yaml_file() {
270321
let file_path = prepare_temp_file(

stores/ini/store.go

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ import (
44
"bytes"
55
"encoding/json"
66
"fmt"
7-
8-
"strconv"
97
"strings"
108

119
"github.com/getsops/sops/v3"
@@ -56,7 +54,7 @@ func (store Store) encodeTree(branches sops.TreeBranches) ([]byte, error) {
5654
lastItem.Comment = comment.Value
5755
}
5856
} else {
59-
lastItem, err = section.NewKey(keyVal.Key.(string), store.valToString(keyVal.Value))
57+
lastItem, err = section.NewKey(keyVal.Key.(string), stores.ValToString(keyVal.Value))
6058
if err != nil {
6159
return nil, fmt.Errorf("Error encoding key: %s", err)
6260
}
@@ -78,19 +76,6 @@ func (store Store) stripCommentChar(comment string) string {
7876
return comment
7977
}
8078

81-
func (store Store) valToString(v interface{}) string {
82-
switch v := v.(type) {
83-
case fmt.Stringer:
84-
return v.String()
85-
case float64:
86-
return strconv.FormatFloat(v, 'f', 6, 64)
87-
case bool:
88-
return strconv.FormatBool(v)
89-
default:
90-
return fmt.Sprintf("%s", v)
91-
}
92-
}
93-
9479
func (store Store) iniFromTreeBranches(branches sops.TreeBranches) ([]byte, error) {
9580
return store.encodeTree(branches)
9681
}

stores/stores.go

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@ of the purpose of this package is to make it easy to change the SOPS file format
1010
package stores
1111

1212
import (
13-
"time"
14-
1513
"fmt"
14+
"strconv"
15+
"strings"
16+
"time"
1617

1718
"github.com/getsops/sops/v3"
1819
"github.com/getsops/sops/v3/age"
@@ -533,3 +534,25 @@ func HasSopsTopLevelKey(branch sops.TreeBranch) bool {
533534
}
534535
return false
535536
}
537+
538+
// ValToString converts a simple value to a string.
539+
// It does not handle complex values (arrays and mappings).
540+
func ValToString(v interface{}) string {
541+
switch v := v.(type) {
542+
case float64:
543+
result := strconv.FormatFloat(v, 'G', -1, 64)
544+
// If the result can be confused with an integer, make sure we have at least one decimal digit
545+
if !strings.ContainsRune(result, '.') && !strings.ContainsRune(result, 'E') {
546+
result = strconv.FormatFloat(v, 'f', 1, 64)
547+
}
548+
return result
549+
case bool:
550+
return strconv.FormatBool(v)
551+
case time.Time:
552+
return v.Format(time.RFC3339)
553+
case fmt.Stringer:
554+
return v.String()
555+
default:
556+
return fmt.Sprintf("%v", v)
557+
}
558+
}

stores/stores_test.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package stores
2+
3+
import (
4+
"testing"
5+
"time"
6+
7+
"github.com/stretchr/testify/assert"
8+
)
9+
10+
11+
func TestValToString(t *testing.T) {
12+
assert.Equal(t, "1", ValToString(1))
13+
assert.Equal(t, "1.0", ValToString(1.0))
14+
assert.Equal(t, "1.1", ValToString(1.10))
15+
assert.Equal(t, "1.23", ValToString(1.23))
16+
assert.Equal(t, "1.2345678901234567", ValToString(1.234567890123456789))
17+
assert.Equal(t, "200000.0", ValToString(2E5))
18+
assert.Equal(t, "-2E+10", ValToString(-2E10))
19+
assert.Equal(t, "2E-10", ValToString(2E-10))
20+
assert.Equal(t, "1.2345E+100", ValToString(1.2345E100))
21+
assert.Equal(t, "1.2345E-100", ValToString(1.2345E-100))
22+
assert.Equal(t, "true", ValToString(true))
23+
assert.Equal(t, "false", ValToString(false))
24+
ts, _ := time.Parse(time.RFC3339, "2025-01-02T03:04:05Z")
25+
assert.Equal(t, "2025-01-02T03:04:05Z", ValToString(ts))
26+
assert.Equal(t, "a string", ValToString("a string"))
27+
}

0 commit comments

Comments
 (0)