Skip to content

Commit

Permalink
restore working dir after backup
Browse files Browse the repository at this point in the history
backup.Perform was using os.Chdir, which changes working
directory for the entire process. This in practice did not
have any effect, but is the potential source of future bugs
as it's an unexpected occurrence.

Solve this by restoring working directory after executing
the backup.
A dedicated test case is provided.
  • Loading branch information
endorama committed Jun 23, 2024
1 parent 9a14b07 commit e961763
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 12 deletions.
1 change: 1 addition & 0 deletions internal/backup/backup.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package backup
import "io"

// NewTask initialize a Task.
// TODO: maybe rename to Prepare() or New()?
func NewTask(name, source string, out io.Writer) (Task, error) {
return Task{name, source, out}, nil
}
49 changes: 37 additions & 12 deletions internal/backup/perform.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,51 @@ import (
)

// Perform create an encrypted backup archive from the specified set of files, using the
// specified passphrase. To support relative paths, it allows a cwd parameter to change directory
// before creating the file.
// specified passphrase. To support relative paths, it changes cwd before creating the file.
// Archive will be created in the current folder.
func Perform(b Task, passphrase string) error {
// TODO: consider removing and use Task.Perform()
func Perform(b Task, passphrase string) (err error) {
// NOTE: change folder to source location, as b.Files() return relative
// paths for files to be added to the archive
err := os.Chdir(b.Source)
if err != nil {
return fmt.Errorf("cannot change dir: %w", err)
}
return wrapChdirRestore(b.Source, func() error {
files, err := b.Files()
if err != nil {
return fmt.Errorf("failed retrieving files to backup: %w", err)
}

err = encryptedarchive.Create(b.Destination, files, passphrase)
if err != nil {
return fmt.Errorf("failed creating encrypted backup archive: %w", err)
}

Check warning on line 26 in internal/backup/perform.go

View check run for this annotation

Codecov / codecov/patch

internal/backup/perform.go#L23-L26

Added lines #L23 - L26 were not covered by tests

return nil

Check warning on line 28 in internal/backup/perform.go

View check run for this annotation

Codecov / codecov/patch

internal/backup/perform.go#L28

Added line #L28 was not covered by tests
})
}

// wrapChdirRestore change folder to dir, execute the specified function
// and restore the working directory as set before the change.
func wrapChdirRestore(target string, f func() error) (err error) {
var cwd string

files, err := b.Files()
cwd, err = os.Getwd()
if err != nil {
return fmt.Errorf("failed retrieving files to backup: %w", err)
return fmt.Errorf("cannot get current working directory: %w", err)

Check warning on line 39 in internal/backup/perform.go

View check run for this annotation

Codecov / codecov/patch

internal/backup/perform.go#L39

Added line #L39 was not covered by tests
}

err = encryptedarchive.Create(b.Destination, files, passphrase)
defer func() {
// NOTE: In case changing back to the previous working directory
// fails is possible to shadow the error of the passed in function.
// This is acceptable as it is not considered common to not be able
// to chdir to a previously working directory.
err = os.Chdir(cwd)
}()

err = os.Chdir(target)
if err != nil {
return fmt.Errorf("failed creating encrypted backup archive: %w", err)
return fmt.Errorf("cannot change dir: %w", err)

Check warning on line 52 in internal/backup/perform.go

View check run for this annotation

Codecov / codecov/patch

internal/backup/perform.go#L52

Added line #L52 was not covered by tests
}

return nil
err = f()

return err
}
49 changes: 49 additions & 0 deletions internal/backup/perform_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package backup_test

import (
"bytes"
"os"
"testing"

"github.com/endorama/devid/internal/backup"
"github.com/stretchr/testify/assert"
)

func TestPerform(t *testing.T) {
t.Skip("not implemented")
type args struct {
b backup.Task
passphrase string
}
tests := []struct {
name string
args args
wantErr bool
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := backup.Perform(tt.args.b, tt.args.passphrase); (err != nil) != tt.wantErr {
t.Errorf("Perform() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}

func TestPerform_Chdir(t *testing.T) {
var out bytes.Buffer
tk, err := backup.NewTask("test", "testdata", &out)
assert.NoError(t, err)

want, err := os.Getwd()
assert.NoError(t, err)

err = backup.Perform(tk, "test")
assert.NoError(t, err)

got, err := os.Getwd()
assert.NoError(t, err)

assert.Equal(t, want, got)
}
2 changes: 2 additions & 0 deletions internal/backup/task.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
)

// Task contains required information for backup operation.
// TODO: add Perform() method to run backups
// TODO: move New() here
type Task struct {
// Name is the name of the backup task
Name string
Expand Down
1 change: 1 addition & 0 deletions internal/backup/testdata/file.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Foo Bar

0 comments on commit e961763

Please sign in to comment.