Skip to content

Commit bc6bc8c

Browse files
authored
fix: resolve issue with add overwriting existing files #92 (#101)
* fix: resolve issue with `add` overwriting existing files - Updated `builder.go` to ask for confirmation when overwriting existing vault files (skip overwriting by default) - Updated `add.go` to ignore skipped files - Updated vault files to follow `<location>.<filename>.yaml` convention to avoid conflicts with duplicate files * refactor: Move confirmation dialog to `util` - Added unit test for `ConfirmOverwrite` - Modified `StoreFileManifest` in `builder.go` to use `ConfirmOverwrite`
1 parent 0cd5949 commit bc6bc8c

5 files changed

Lines changed: 102 additions & 3 deletions

File tree

cmd/add.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,11 @@ Examples:
243243
// Save the manifest
244244
err = manifest.StoreFileManifest(vaultRoot, filepath.Base(pair.Source), fileManifest)
245245
if err != nil {
246+
if err.Error() == "skipped" {
247+
errorMsg := fmt.Sprintf("✗ '%s': skipped", fileManifest.Destination+filepath.Base(pair.Source))
248+
fmt.Println(errorMsg)
249+
continue
250+
}
246251
errorMsg := fmt.Sprintf("✗ %s: manifest storage failed - %v", filepath.Base(pair.Source), err)
247252
fmt.Println(errorMsg)
248253
failedFiles = append(failedFiles, errorMsg)

cmd/delete.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,9 @@ Examples:
8383
}
8484

8585
// Step 1: Remove the manifest file
86-
manifestPath := filepath.Join(vaultRoot, ".sietch", "manifests", fileBaseName+".yaml")
86+
destination := strings.ReplaceAll(targetFile.Destination, "/", ".")
87+
uniqueFileIdentifier := destination + fileBaseName + ".yaml"
88+
manifestPath := filepath.Join(vaultRoot, ".sietch", "manifests", uniqueFileIdentifier)
8789
if err := os.Remove(manifestPath); err != nil {
8890
return fmt.Errorf("failed to remove manifest file: %v", err)
8991
}

internal/manifest/builder.go

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ import (
44
"fmt"
55
"os"
66
"path/filepath"
7+
"strings"
78

89
"gopkg.in/yaml.v3"
910

1011
"github.com/substantialcattle5/sietch/internal/config"
12+
"github.com/substantialcattle5/sietch/util"
1113
)
1214

1315
// WriteManifest writes the vault configuration to vault.yaml
@@ -54,9 +56,21 @@ func StoreFileManifest(vaultRoot string, fileName string, manifest *config.FileM
5456
}
5557

5658
// Create manifest file path
57-
manifestPath := filepath.Join(manifestsDir, fileName+".yaml")
59+
destination := strings.ReplaceAll(manifest.Destination, "/", ".")
60+
uniqueFileIdentifier := destination + fileName + ".yaml"
61+
manifestPath := filepath.Join(manifestsDir, uniqueFileIdentifier)
62+
63+
// Check if file exists
64+
_, err := os.Stat(manifestPath)
65+
if err == nil {
66+
message := fmt.Sprintf("'%s' exists. Overwrite? ", manifest.Destination+fileName)
67+
response, err := util.ConfirmOverwrite(message, os.Stdin, os.Stdout)
68+
if err != nil || !response {
69+
return fmt.Errorf("skipped")
70+
}
71+
}
5872

59-
// Create the file
73+
// Create/Overwrite the file
6074
file, err := os.Create(manifestPath)
6175
if err != nil {
6276
return fmt.Errorf("failed to create manifest file: %v", err)

util/ConfirmOverwrite.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package util
2+
3+
import (
4+
"bufio"
5+
"fmt"
6+
"io"
7+
"strings"
8+
)
9+
10+
func ConfirmOverwrite(prompt string, in io.Reader, out io.Writer) (bool, error) {
11+
fmt.Fprintf(out, "%s (y/N): ", prompt)
12+
reader := bufio.NewReader(in)
13+
response, err := reader.ReadString('\n')
14+
if err != nil {
15+
return false, err
16+
}
17+
response = strings.TrimSpace(strings.ToLower(response))
18+
return response == "y" || response == "yes", nil
19+
}

util/ConfirmOverwrite_test.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package util
2+
3+
import (
4+
"bytes"
5+
"io"
6+
"strings"
7+
"testing"
8+
)
9+
10+
func TestConfirmOverwrite(t *testing.T) {
11+
tests := []struct {
12+
name string
13+
input string
14+
expected bool
15+
err error
16+
}{
17+
{
18+
name: "yes input",
19+
input: "y\n",
20+
expected: true,
21+
err: nil,
22+
},
23+
{
24+
name: "yes uppercase",
25+
input: "YES\n",
26+
expected: true,
27+
err: nil,
28+
},
29+
{
30+
name: "no input",
31+
input: "n\n",
32+
expected: false,
33+
err: nil,
34+
},
35+
{
36+
name: "empty input",
37+
input: "\n",
38+
expected: false,
39+
err: nil,
40+
},
41+
{
42+
name: "input not terminating with \\n",
43+
input: "yes",
44+
expected: false,
45+
err: io.EOF,
46+
},
47+
}
48+
49+
for _, tt := range tests {
50+
t.Run(tt.name, func(t *testing.T) {
51+
in := strings.NewReader(tt.input)
52+
out := &bytes.Buffer{}
53+
ok, err := ConfirmOverwrite("testfile", in, out)
54+
if err != tt.err || ok != tt.expected {
55+
t.Errorf("ConfirmOverwrite('testfile', %s, out) = (%t, %s), want = (%t, nil)", tt.input, ok, err.Error(), tt.expected)
56+
}
57+
})
58+
}
59+
}

0 commit comments

Comments
 (0)