-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
af45bc7
commit e7723cd
Showing
3 changed files
with
236 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,6 +11,7 @@ | |
"Cygwin", | ||
"dasherize", | ||
"flect", | ||
"fsutil", | ||
"gobuffalo", | ||
"golangci", | ||
"GOPROXY", | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
package fsutil | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"os" | ||
"path/filepath" | ||
"strings" | ||
"syscall" | ||
) | ||
|
||
const ( | ||
// DefaultDirMode grants `rwx------`. | ||
DefaultDirMode = 0700 | ||
// DefaultFileMode grants `rw-------`. | ||
DefaultFileMode = 0600 | ||
) | ||
|
||
var ( | ||
osUserHomeDir = os.UserHomeDir | ||
filepathAbs = filepath.Abs | ||
osMkdirAll = os.MkdirAll | ||
osWriteFile = os.WriteFile | ||
) | ||
|
||
func NoPathExists(path string) bool { | ||
_, err := os.Stat(path) | ||
// for some reason os.ErrInvalid sometimes != syscall.EINVAL :shrug: | ||
if errors.Is(err, os.ErrNotExist) || | ||
errors.Is(err, os.ErrInvalid) || | ||
errors.Is(err, syscall.EINVAL) { | ||
return true | ||
} | ||
return false | ||
} | ||
|
||
func PathExists(path string) bool { | ||
return !NoPathExists(path) | ||
} | ||
|
||
// NormalizePath ensures that name is an absolute path. | ||
// Environment variables (and the ~ string) are expanded. | ||
func NormalizePath(name string) (string, error) { | ||
normalized := strings.TrimSpace(name) | ||
if normalized == "" { | ||
return "", nil | ||
} | ||
|
||
// Replace ENV vars | ||
normalized = os.ExpandEnv(normalized) | ||
|
||
// Replace ~ | ||
if strings.HasPrefix(normalized, "~") { | ||
home, err := osUserHomeDir() | ||
if err != nil { | ||
return "", fmt.Errorf("unable to normalize %s: %w", name, err) | ||
} | ||
normalized = home + strings.TrimPrefix(normalized, "~") | ||
} | ||
|
||
// Ensure abs path | ||
normalized, err := filepathAbs(normalized) | ||
if err != nil { | ||
return "", fmt.Errorf("unable to normalize %s: %w", name, err) | ||
} | ||
|
||
return normalized, nil | ||
} | ||
|
||
// EnsureDirWritable ensures that path is a writable directory. | ||
// Will attempt to create a new directory if path does not exist. | ||
func EnsureDirWritable(path string) error { | ||
// Ensure dir exists (and IsDir). | ||
err := osMkdirAll(path, DefaultDirMode) | ||
if err != nil { | ||
return fmt.Errorf("ensure dir: %w", err) | ||
} | ||
|
||
f := filepath.Join(path, ".touch") | ||
if err := osWriteFile(f, []byte(""), DefaultFileMode); err != nil { | ||
return fmt.Errorf("ensure writable: %w", err) | ||
} | ||
defer os.Remove(f) | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
package fsutil | ||
|
||
import ( | ||
"errors" | ||
"os" | ||
"path/filepath" | ||
"testing" | ||
|
||
"github.com/prashantv/gostub" | ||
"github.com/stretchr/testify/assert" | ||
|
||
"github.com/twelvelabs/termite/testutil" | ||
) | ||
|
||
func TestNoPathExists(t *testing.T) { | ||
testutil.InTempDir(t, func(dir string) { | ||
assert.NoFileExists(t, "foo.txt") | ||
assert.Equal(t, true, NoPathExists("foo.txt")) | ||
|
||
testutil.WriteFile(t, "foo.txt", []byte(""), 0600) | ||
assert.Equal(t, false, NoPathExists("foo.txt")) | ||
}) | ||
} | ||
|
||
func TestPathExists(t *testing.T) { | ||
testutil.InTempDir(t, func(dir string) { | ||
assert.NoFileExists(t, "foo.txt") | ||
assert.Equal(t, false, PathExists("foo.txt")) | ||
|
||
testutil.WriteFile(t, "foo.txt", []byte(""), 0600) | ||
assert.Equal(t, true, PathExists("foo.txt")) | ||
}) | ||
} | ||
|
||
func TestNormalizePath(t *testing.T) { | ||
homeDir, _ := os.UserHomeDir() | ||
workingDir, _ := filepath.Abs(".") | ||
|
||
tests := []struct { | ||
Desc string | ||
EnvVars map[string]string | ||
Input string | ||
Output string | ||
Err string | ||
}{ | ||
{ | ||
Desc: "is a noop when passed an empty string", | ||
Input: "", | ||
Output: "", | ||
Err: "", | ||
}, | ||
{ | ||
Desc: "expands env vars", | ||
Input: filepath.Join(".", "${FOO}-dir", "$BAR"), | ||
Output: filepath.Join(workingDir, "aaa-dir", "bbb"), | ||
EnvVars: map[string]string{ | ||
"FOO": "aaa", | ||
"BAR": "bbb", | ||
}, | ||
Err: "", | ||
}, | ||
{ | ||
Desc: "expands tilde", | ||
Input: "~", | ||
Output: homeDir, | ||
Err: "", | ||
}, | ||
{ | ||
Desc: "expands tilde when prefix", | ||
Input: filepath.Join("~", "foo"), | ||
Output: filepath.Join(homeDir, "foo"), | ||
Err: "", | ||
}, | ||
{ | ||
Desc: "returns an absolute path", | ||
Input: ".", | ||
Output: workingDir, | ||
Err: "", | ||
}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.Desc, func(t *testing.T) { | ||
if tt.EnvVars != nil { | ||
for k, v := range tt.EnvVars { | ||
t.Setenv(k, v) | ||
} | ||
} | ||
|
||
actual, err := NormalizePath(tt.Input) | ||
|
||
assert.Equal(t, tt.Output, actual) | ||
if tt.Err == "" { | ||
assert.NoError(t, err) | ||
} else { | ||
assert.ErrorContains(t, err, tt.Err) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestNormalizePath_WhenUserHomeDirError(t *testing.T) { | ||
stubs := gostub.StubFunc(&osUserHomeDir, "", errors.New("boom")) | ||
defer stubs.Reset() | ||
|
||
actual, err := NormalizePath("~/foo") | ||
|
||
assert.Error(t, err) | ||
assert.Equal(t, "", actual) | ||
} | ||
|
||
func TestNormalizePath_WhenAbsError(t *testing.T) { | ||
stubs := gostub.StubFunc(&filepathAbs, "foo", errors.New("boom")) | ||
defer stubs.Reset() | ||
|
||
actual, err := NormalizePath("foo") | ||
|
||
assert.Error(t, err) | ||
assert.Equal(t, "", actual) | ||
} | ||
|
||
func TestEnsureDirWritable(t *testing.T) { | ||
testutil.InTempDir(t, func(tmpDir string) { | ||
dir := filepath.Join(tmpDir, "foo") | ||
err := EnsureDirWritable(dir) | ||
assert.NoError(t, err) | ||
assert.DirExists(t, dir, "dir should exist") | ||
|
||
dirEntry := filepath.Join(dir, "bar") | ||
testutil.WriteFile(t, dirEntry, []byte(""), 0600) | ||
assert.FileExists(t, dirEntry, "dir should be writable") | ||
}) | ||
} | ||
|
||
func TestEnsureDirWritable_WhenMkdirAllError(t *testing.T) { | ||
stubs := gostub.StubFunc(&osMkdirAll, errors.New("boom")) | ||
defer stubs.Reset() | ||
|
||
err := EnsureDirWritable("foo") | ||
assert.Error(t, err) | ||
} | ||
|
||
func TestEnsureDirWritable_WhenWriteFileError(t *testing.T) { | ||
stubs := gostub.StubFunc(&osMkdirAll, nil). | ||
StubFunc(&osWriteFile, errors.New("boom")) | ||
defer stubs.Reset() | ||
|
||
err := EnsureDirWritable("foo") | ||
assert.Error(t, err) | ||
} |