mirror of
https://github.com/sbrow/envr.git
synced 2025-12-29 15:47:38 -05:00
refactor: Restore and Sync now share code through the sync function.
This commit is contained in:
125
app/env_file.go
125
app/env_file.go
@@ -2,6 +2,7 @@ package app
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
@@ -24,7 +25,7 @@ type EnvFileSyncResult int
|
|||||||
const (
|
const (
|
||||||
// The struct has been updated from the filesystem
|
// The struct has been updated from the filesystem
|
||||||
// and should be updated in the database.
|
// and should be updated in the database.
|
||||||
Updated EnvFileSyncResult = iota
|
BackedUp EnvFileSyncResult = iota
|
||||||
// The filesystem has been restored to match the struct
|
// The filesystem has been restored to match the struct
|
||||||
// no further action is required.
|
// no further action is required.
|
||||||
Restored
|
Restored
|
||||||
@@ -34,6 +35,13 @@ const (
|
|||||||
Noop
|
Noop
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type syncDirection int
|
||||||
|
|
||||||
|
const (
|
||||||
|
TrustDatabase syncDirection = iota
|
||||||
|
TrustFilesystem
|
||||||
|
)
|
||||||
|
|
||||||
func NewEnvFile(path string) EnvFile {
|
func NewEnvFile(path string) EnvFile {
|
||||||
// Get absolute path and directory
|
// Get absolute path and directory
|
||||||
absPath, err := filepath.Abs(path)
|
absPath, err := filepath.Abs(path)
|
||||||
@@ -96,85 +104,78 @@ func getGitRemotes(dir string) []string {
|
|||||||
return remotes
|
return remotes
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to reconcile the EnvFile with the filesystem.
|
// Reconcile the state of the database with the state of the filesystem, using
|
||||||
//
|
// dir to determine which side to use a the source of truth
|
||||||
// If Updated is returned, [Db.Insert] should be called on file.
|
func (f *EnvFile) sync(dir syncDirection) (result EnvFileSyncResult, err error) {
|
||||||
func (file *EnvFile) Sync() (result EnvFileSyncResult, err error) {
|
// How Sync should work
|
||||||
// TODO: If the directory doesn't exist, look for other directories with the same remote(s)
|
//
|
||||||
// TODO: If one is found, update file.Dir and File.Path
|
// If the directory doesn't exist, look for other directories with the same remote(s)
|
||||||
// TODO: If nothing if found, return an error
|
// -> If one is found, update file.Dir and File.Path, then continue with "changed" flag
|
||||||
// TODO: If more than one is found, return a different error
|
// -> If multiple are found, return an error
|
||||||
|
// -> If none are found, return a different error
|
||||||
|
|
||||||
// Check if the path exists in the file system
|
// Ensure the directory exists
|
||||||
_, err = os.Stat(file.Path)
|
if _, err := os.Stat(f.Dir); err != nil {
|
||||||
if err == nil {
|
return Error, fmt.Errorf("directory missing")
|
||||||
contents, err := os.ReadFile(file.Path)
|
}
|
||||||
|
|
||||||
|
if _, err := os.Stat(f.Path); err != nil {
|
||||||
|
if errors.Is(err, os.ErrNotExist) {
|
||||||
|
if err := os.WriteFile(f.Path, []byte(f.contents), 0644); err != nil {
|
||||||
|
return Error, fmt.Errorf("failed to write file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Restored, err
|
||||||
|
} else {
|
||||||
|
return Error, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// File exists, check its hash
|
||||||
|
contents, err := os.ReadFile(f.Path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Error, fmt.Errorf("failed to read file for SHA comparison: %w", err)
|
return Error, fmt.Errorf("failed to read file for SHA comparison: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if sha matches by reading the current file and calculating its hash
|
|
||||||
hash := sha256.Sum256(contents)
|
hash := sha256.Sum256(contents)
|
||||||
currentSha := fmt.Sprintf("%x", hash)
|
currentSha := fmt.Sprintf("%x", hash)
|
||||||
if file.Sha256 == currentSha {
|
|
||||||
// Nothing to do
|
// Compare the hashes
|
||||||
|
if currentSha == f.Sha256 {
|
||||||
return Noop, nil
|
return Noop, nil
|
||||||
} else {
|
} else {
|
||||||
if err = file.Backup(); err != nil {
|
switch dir {
|
||||||
return Error, err
|
case TrustDatabase:
|
||||||
} else {
|
if err := os.WriteFile(f.Path, []byte(f.contents), 0644); err != nil {
|
||||||
return Updated, nil
|
return Error, fmt.Errorf("failed to write file: %w", err)
|
||||||
|
}
|
||||||
|
return Restored, nil
|
||||||
|
case TrustFilesystem:
|
||||||
|
// Overwrite the database
|
||||||
|
if err = f.Backup(); err != nil {
|
||||||
|
return Error, err
|
||||||
|
} else {
|
||||||
|
return BackedUp, nil
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
panic("unknown sync direction")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if err = file.Restore(); err != nil {
|
|
||||||
return Error, err
|
|
||||||
} else {
|
|
||||||
return Restored, nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Try to reconcile the EnvFile with the filesystem.
|
||||||
|
//
|
||||||
|
// If Updated is returned, [Db.Insert] should be called on file.
|
||||||
|
func (file *EnvFile) Sync() (result EnvFileSyncResult, err error) {
|
||||||
|
return file.sync(TrustFilesystem)
|
||||||
|
}
|
||||||
|
|
||||||
// Install the file into the file system. If the file already exists,
|
// Install the file into the file system. If the file already exists,
|
||||||
// it will be overwritten.
|
// it will be overwritten.
|
||||||
func (file EnvFile) Restore() error {
|
func (file EnvFile) Restore() error {
|
||||||
// TODO: Duplicate work is being done when called from the Sync function.
|
_, err := file.sync(TrustDatabase)
|
||||||
if _, err := os.Stat(file.Path); err == nil {
|
|
||||||
// file already exists
|
|
||||||
|
|
||||||
// Read existing file and calculate its hash
|
return err
|
||||||
existingContents, err := os.ReadFile(file.Path)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to read existing file for hash comparison: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
hash := sha256.Sum256(existingContents)
|
|
||||||
existingSha := fmt.Sprintf("%x", hash)
|
|
||||||
|
|
||||||
if existingSha == file.Sha256 {
|
|
||||||
return fmt.Errorf("file already exists: %s", file.Path)
|
|
||||||
} else {
|
|
||||||
if err := os.WriteFile(file.Path, []byte(file.contents), 0644); err != nil {
|
|
||||||
return fmt.Errorf("failed to write file: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// file doesn't exist
|
|
||||||
|
|
||||||
// Ensure the directory exists
|
|
||||||
if _, err := os.Stat(file.Dir); err != nil {
|
|
||||||
return fmt.Errorf("directory missing")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write the contents to the file
|
|
||||||
if err := os.WriteFile(file.Path, []byte(file.contents), 0644); err != nil {
|
|
||||||
return fmt.Errorf("failed to write file: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the EnvFile using the file system.
|
// Update the EnvFile using the file system.
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ var syncCmd = &cobra.Command{
|
|||||||
|
|
||||||
var status string
|
var status string
|
||||||
switch changed {
|
switch changed {
|
||||||
case app.Updated:
|
case app.BackedUp:
|
||||||
status = "Backed Up"
|
status = "Backed Up"
|
||||||
if err := db.Insert(file); err != nil {
|
if err := db.Insert(file); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
Reference in New Issue
Block a user