Skip to content

Commit

Permalink
backup: fix working directory change (#20)
Browse files Browse the repository at this point in the history
* restore working dir after backup

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.

* goimports

* fix golangcilint

* comment unused code

* lint 📿

* lint
  • Loading branch information
endorama authored Jun 25, 2024
1 parent 8e8ac45 commit 2d25fae
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 20 deletions.
12 changes: 5 additions & 7 deletions .golangci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
issues:
exclude-dirs:
- internal/tmp
exclude-rules:
# disable funlen for all _test.go files
- path: _test.go
linters:
- funlen

output:
unique-by-line: false
Expand Down Expand Up @@ -107,10 +112,3 @@ linters-settings:
allow-unused: true
require-explanation: true
require-specific: true

issues:
exclude-rules:
# disable funlen for all _test.go files
- path: _test.go
linters:
- funlen
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
}
51 changes: 38 additions & 13 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)
}
// paths for files to be added to the archive.
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)
}

return nil
})
}

// 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)
}

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)
}

return nil
err = f()

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

import (
"bytes"
"os"
"testing"

"github.com/stretchr/testify/assert"

"github.com/endorama/devid/internal/backup"
)

func TestPerform(t *testing.T) {
// 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)
// }
// })
// }
t.Skip("not implemented")
}

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 2d25fae

Please sign in to comment.