Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion cmd/init.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package cmd

import (
"eko/internal/db"
"fmt"
"os"
"eko/internal/db"

"github.com/spf13/cobra"
)
Expand All @@ -14,6 +14,7 @@ var initCmd = &cobra.Command{
Run: func(cmd *cobra.Command, args []string) {
os.MkdirAll(".eko/snapshots", 0755)
database := db.InitDB()
defer database.Close()
database.Exec(`
CREATE TABLE IF NOT EXISTS snapshots (
id TEXT PRIMARY KEY,
Expand Down
1 change: 1 addition & 0 deletions cmd/restore.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ var restoreCmd = &cobra.Command{
Run: func(cmd *cobra.Command, args []string) {
id := args[0]
database := db.InitDB()
defer database.Close()
var path string
database.QueryRow("SELECT path FROM snapshots WHERE id=?", id).Scan(&path)
err := snapshot.RestoreSnapshot(path)
Expand Down
1 change: 1 addition & 0 deletions cmd/save.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ used with the restore command to revert to this state.`,
panic(err)
}
database := db.InitDB()
defer database.Close()
database.Exec(
"INSERT INTO snapshots(id, message, path) VALUES (?, ?, ?)",
id,
Expand Down
18 changes: 13 additions & 5 deletions internal/snapshot/snapshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,13 @@ const ekoDir = ".eko"
// It generates a random 8-hex-char ID, copies the project tree (excluding .eko itself)
// into .eko/snapshots/<id>/, and returns the snapshot ID and its storage path.
func CreateSnapshot() (string, string, error) {
id := generateID()
id, err := generateID()
if err != nil {
return "", "", err
}

base := ekoDir + "/snapshots/" + id
err := util.CopyDir(".", base)
err = util.CopyDir(".", base)
if err != nil {
return "", "", err
}
Expand All @@ -38,10 +42,14 @@ func CreateSnapshot() (string, string, error) {
}

// generateID returns a random 8-character hexadecimal string used as a snapshot identifier.
func generateID() string {
func generateID() (string, error) {
b := make([]byte, 4)
rand.Read(b)
return hex.EncodeToString(b)
_, err := rand.Read(b)
if err != nil {
return "", err
}

return hex.EncodeToString(b), nil
}

// RestoreSnapshot reverts the working directory to the state captured in path.
Expand Down
23 changes: 20 additions & 3 deletions internal/snapshot/snapshot_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,23 @@ func setupProject(t *testing.T) string {

// TestGenerateID_length checks the hex string is 8 characters (4 bytes → 8 hex).
func TestGenerateID_length(t *testing.T) {
id := generateID()
id, err := generateID()
if err != nil {
t.Fatalf("generateID error: %v", err)
}

if len(id) != 8 {
t.Errorf("expected 8-char hex id, got %q (len=%d)", id, len(id))
}
}

// TestGenerateID_hex checks the result is valid hex.
func TestGenerateID_hex(t *testing.T) {
id := generateID()
id, err := generateID()
if err != nil {
t.Fatalf("generateID error: %v", err)
}

for _, c := range id {
if !((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f')) {
t.Errorf("non-hex character %q in id %q", c, id)
Expand All @@ -53,7 +61,16 @@ func TestGenerateID_hex(t *testing.T) {

// TestGenerateID_unique checks two consecutive calls produce different IDs.
func TestGenerateID_unique(t *testing.T) {
a, b := generateID(), generateID()
a, err := generateID()
if err != nil {
t.Fatalf("generateID error for a: %v", err)
}

b, err := generateID()
if err != nil {
t.Fatalf("generateID error for b: %v", err)
}

if a == b {
t.Errorf("generateID produced the same value twice: %q", a)
}
Expand Down
Loading