Skip to content

Commit b6e0a0e

Browse files
committed
refactor(output): relocate tarball creation logic to output
1 parent ea6121a commit b6e0a0e

File tree

5 files changed

+127
-54
lines changed

5 files changed

+127
-54
lines changed

.gitattributes

Lines changed: 0 additions & 1 deletion
This file was deleted.

system/backup/dotfile_scanner.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ var DotfilePaths = []string{
2020

2121
type Dotfile struct {
2222
Path string
23-
RelPath string
23+
RealPath string
2424
IsDir bool
2525
IsBinary bool
2626
Mode os.FileMode
@@ -61,10 +61,10 @@ func ScanDotfiles() ([]Dotfile, error) {
6161
continue
6262
}
6363

64-
relPath, _ := filepath.Rel(home, full)
64+
realPath, _ := filepath.Rel(home, full)
6565
entry := Dotfile{
6666
Path: full,
67-
RelPath: relPath,
67+
RealPath: realPath,
6868
IsDir: info.IsDir(),
6969
Mode: info.Mode(),
7070
}

system/backup/dotfiles_backup.go

Lines changed: 8 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
11
package backup
22

33
import (
4-
"archive/tar"
5-
"compress/gzip"
6-
"encoding/json"
74
"fmt"
8-
"io"
95
"os"
106
"time"
7+
"github.com/mdgspace/sysreplicate/system/output"
118
)
129

1310
type BackupMetadata struct {
@@ -23,55 +20,25 @@ func NewDotfileBackupManager() *DotfileBackupManager {
2320
}
2421

2522
func (db *DotfileBackupManager) CreateDotfileBackup(outputTar string) error {
23+
// Scan dotfiles
2624
files, err := ScanDotfiles()
2725
if err != nil {
2826
return fmt.Errorf("error scanning dotfiles: %w", err)
2927
}
3028

29+
// Get hostname
3130
hostname, _ := os.Hostname()
3231

33-
meta := BackupMetadata{
32+
// Create backup metadata
33+
meta := &BackupMetadata{
3434
Timestamp: time.Now(),
3535
Hostname: hostname,
3636
Files: files,
3737
}
3838

39-
tarFile, err := os.Create(outputTar)
40-
if err != nil {
41-
return fmt.Errorf("failed to create tar file: %w", err)
42-
}
43-
defer tarFile.Close()
44-
45-
gzipWriter := gzip.NewWriter(tarFile)
46-
defer gzipWriter.Close()
47-
tarWriter := tar.NewWriter(gzipWriter)
48-
defer tarWriter.Close()
49-
50-
// Write metadata JSON
51-
metaBytes, _ := json.MarshalIndent(meta, "", " ")
52-
tarWriter.WriteHeader(&tar.Header{
53-
Name: "backup.json",
54-
Mode: 0644,
55-
Size: int64(len(metaBytes)),
56-
})
57-
tarWriter.Write(metaBytes)
58-
59-
// Add dotfiles
60-
for _, f := range files {
61-
if f.IsDir {
62-
continue
63-
}
64-
file, err := os.Open(f.Path)
65-
if err != nil {
66-
continue
67-
}
68-
defer file.Close()
69-
70-
info, _ := file.Stat()
71-
hdr, _ := tar.FileInfoHeader(info, "")
72-
hdr.Name = f.RelPath
73-
tarWriter.WriteHeader(hdr)
74-
io.Copy(tarWriter, file)
39+
// Delegate tarball creation to utility
40+
if err := CreateDotfilesBackupTarball(meta, outputTar); err != nil {
41+
return fmt.Errorf("failed to create backup tarball: %w", err)
7542
}
7643

7744
fmt.Println("Backup complete:", outputTar)

system/backup_integration.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package system
33
import (
44
"fmt"
55
"log"
6-
6+
"os"
77
"github.com/mdgspace/sysreplicate/system/backup"
88
)
99

@@ -33,9 +33,15 @@ func RunDotfileBackup() {
3333
// Create a backup manager
3434
manager := backup.NewDotfileBackupManager()
3535

36-
//Output to dist directory
36+
// Output path
3737
outputPath := "dist/dotfile-backup.tar.gz"
3838

39+
// Ensure "dist" directory exists
40+
if err := os.MkdirAll("dist", os.ModePerm); err != nil {
41+
fmt.Printf("Failed to create output directory: %v\n", err)
42+
return
43+
}
44+
3945
// Run the backup
4046
err := manager.CreateDotfileBackup(outputPath)
4147
if err != nil {

system/output/tarball.go

Lines changed: 108 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@ import (
44
"archive/tar"
55
"compress/gzip"
66
"encoding/json"
7+
"fmt"
8+
"io"
79
"os"
810
"time"
911
)
1012

11-
//backupData structure for tarball creation
13+
// backupData structure for tarball creation
1214
type BackupData struct {
13-
Timestamp time.Time `json:"timestamp"`
15+
Timestamp time.Time `json:"timestamp"`
1416
SystemInfo SystemInfo `json:"system_info"`
1517
EncryptedKeys map[string]EncryptedKey `json:"encrypted_keys"`
1618
EncryptionKey []byte `json:"encryption_key"`
@@ -30,12 +32,20 @@ type EncryptedKey struct {
3032
}
3133

3234
type Dotfile struct {
33-
Path string `json:"path"`
34-
Content string `json:"content"`
35-
Mode uint32 `json:"mode"`
36-
IsBinary bool `json:"is_binary"`
35+
Path string
36+
RealPath string
37+
IsDir bool
38+
IsBinary bool
39+
Mode os.FileMode
40+
Content string // ignore for the binary files
3741
}
38-
//create a compressed tarball with the backup data
42+
type BackupMetadata struct {
43+
Timestamp time.Time `json:"timestamp"`
44+
Hostname string `json:"hostname"`
45+
Files []Dotfile `json:"files"`
46+
}
47+
48+
// create a compressed tarball with the backup data
3949
func CreateBackupTarball(backupData *BackupData, tarballPath string) error {
4050
//create tarball file
4151
file, err := os.Create(tarballPath)
@@ -75,3 +85,94 @@ func CreateBackupTarball(backupData *BackupData, tarballPath string) error {
7585

7686
return nil
7787
}
88+
89+
func CreateDotfilesBackupTarball(meta *BackupMetadata, tarballPath string) error {
90+
// Create the tarball file
91+
file, err := os.Create(tarballPath)
92+
if err != nil {
93+
return fmt.Errorf("failed to create tarball: %w", err)
94+
}
95+
defer file.Close()
96+
97+
98+
gzipWriter := gzip.NewWriter(file)
99+
defer gzipWriter.Close()
100+
tarWriter := tar.NewWriter(gzipWriter)
101+
defer tarWriter.Close()
102+
103+
jsonData, err := json.MarshalIndent(meta, "", " ")
104+
if err != nil {
105+
return fmt.Errorf("failed to marshal metadata: %w", err)
106+
}
107+
108+
// Add metadata as backup.json
109+
header := &tar.Header{
110+
Name: "backup.json",
111+
Mode: 0644,
112+
Size: int64(len(jsonData)),
113+
}
114+
if err := tarWriter.WriteHeader(header); err != nil {
115+
return fmt.Errorf("failed to write header for metadata: %w", err)
116+
}
117+
if _, err := tarWriter.Write(jsonData); err != nil {
118+
return fmt.Errorf("failed to write metadata to tar: %w", err)
119+
}
120+
121+
// Add dotfiles
122+
for _, f := range meta.Files {
123+
if f.IsDir {
124+
continue
125+
}
126+
127+
if f.IsBinary {
128+
// Read file from disk
129+
fileHandle, err := os.Open(f.Path)
130+
if err != nil {
131+
fmt.Fprintf(os.Stderr, "warning: failed to open binary file %s: %v\n", f.Path, err)
132+
continue
133+
}
134+
defer fileHandle.Close()
135+
136+
info, err := fileHandle.Stat()
137+
if err != nil {
138+
fmt.Fprintf(os.Stderr, "warning: failed to stat %s: %v\n", f.Path, err)
139+
continue
140+
}
141+
142+
hdr, err := tar.FileInfoHeader(info, "")
143+
if err != nil {
144+
fmt.Fprintf(os.Stderr, "warning: failed to get tar header for %s: %v\n", f.Path, err)
145+
continue
146+
}
147+
hdr.Name = f.RealPath
148+
hdr.Mode = int64(f.Mode)
149+
150+
if err := tarWriter.WriteHeader(hdr); err != nil {
151+
fmt.Fprintf(os.Stderr, "warning: failed to write tar header for %s: %v\n", f.Path, err)
152+
continue
153+
}
154+
if _, err := io.Copy(tarWriter, fileHandle); err != nil {
155+
fmt.Fprintf(os.Stderr, "warning: failed to copy binary file %s: %v\n", f.Path, err)
156+
continue
157+
}
158+
} else {
159+
// Use in-memory content
160+
contentBytes := []byte(f.Content)
161+
hdr := &tar.Header{
162+
Name: f.RealPath,
163+
Mode: int64(f.Mode),
164+
Size: int64(len(contentBytes)),
165+
}
166+
if err := tarWriter.WriteHeader(hdr); err != nil {
167+
fmt.Fprintf(os.Stderr, "warning: failed to write header for text file %s: %v\n", f.Path, err)
168+
continue
169+
}
170+
if _, err := tarWriter.Write(contentBytes); err != nil {
171+
fmt.Fprintf(os.Stderr, "warning: failed to write text file %s: %v\n", f.Path, err)
172+
continue
173+
}
174+
}
175+
}
176+
177+
return nil
178+
}

0 commit comments

Comments
 (0)