Skip to content

Commit

Permalink
also search configuration from ~/.config/resticprofile on macOS (#370)
Browse files Browse the repository at this point in the history
* also search configuration from ~/.config/resticprofile on macOS
  • Loading branch information
creativeprojects authored Jun 18, 2024
1 parent 0598026 commit 6da0317
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 17 deletions.
2 changes: 1 addition & 1 deletion .goreleaser.yml
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ brews:
commit_author:
name: goreleaser
email: [email protected]
folder: Formula
directory: Formula
homepage: https://github.com/creativeprojects/{{ .ProjectName }}
description: Configuration profiles for restic backup
license: "GPL-3.0-only"
Expand Down
2 changes: 1 addition & 1 deletion codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ coverage:
default:
target: auto
threshold: "2%"
patch:
patch:
default:
target: "70%"
threshold: "2%"
43 changes: 37 additions & 6 deletions filesearch/filesearch.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (
)

var (
XDGAppName = "resticprofile"
AppName = "resticprofile"

// configurationExtensions list the possible extensions for the config file
configurationExtensions = []string{
Expand All @@ -40,6 +40,10 @@ var (
"/opt/local/etc/resticprofile/",
}

addConfigurationLocationsDarwin = []string{
".config/" + AppName + "/",
}

defaultConfigurationLocationsWindows = []string{
"c:\\restic\\",
"c:\\resticprofile\\",
Expand All @@ -65,7 +69,7 @@ var (
}
)

var fs afero.Fs
var fs afero.Fs // we could probably change the implementation to use fs.FS instead

func init() {
fs = afero.NewOsFs()
Expand Down Expand Up @@ -187,6 +191,8 @@ func FindResticBinary(configLocation string) (string, error) {
return filename, nil
}
}
clog.Tracef("could not find restic binary %q in any of these locations: %s", binaryFile, strings.Join(paths, ", "))

// Last resort, search from the OS PATH
filename, err := exec.LookPath(binaryFile)
if err != nil {
Expand All @@ -211,9 +217,11 @@ func ShellExpand(filename string) (string, error) {
}

func getSearchConfigurationLocations() []string {
locations := []string{filepath.Join(xdg.ConfigHome, XDGAppName)}
home, _ := os.UserHomeDir()

locations := []string{filepath.Join(xdg.ConfigHome, AppName)}
for _, configDir := range xdg.ConfigDirs {
locations = append(locations, filepath.Join(configDir, XDGAppName))
locations = append(locations, filepath.Join(configDir, AppName))
}

if platform.IsWindows() {
Expand All @@ -222,7 +230,11 @@ func getSearchConfigurationLocations() []string {
locations = append(locations, defaultConfigurationLocationsUnix...)
}

if home, err := os.UserHomeDir(); err == nil {
if platform.IsDarwin() {
locations = append(locations, addRootToRelativePaths(home, addConfigurationLocationsDarwin)...)
}

if home != "" {
locations = append(locations, home)
}

Expand All @@ -244,7 +256,7 @@ func getSearchBinaryLocations() []string {
paths = defaultBinaryLocationsUnix
}
if home, err := os.UserHomeDir(); err == nil {
paths = append(paths, filepath.Join(home, ".local/bin/"), filepath.Join(home, "bin/"))
paths = append(paths, addRootToRelativePaths(home, []string{".local/bin/", "bin/"})...)
}
return paths
}
Expand All @@ -260,3 +272,22 @@ func fileExists(filename string) bool {
_, err := fs.Stat(filename)
return err == nil || errors.Is(err, iofs.ErrExist)
}

func addRootToRelativePaths(home string, paths []string) []string {
if platform.IsWindows() {
return paths
}
if home == "" {
return paths
}
rootedPaths := make([]string, len(paths))
for i, path := range paths {
if filepath.IsAbs(path) {
rootedPaths[i] = path
continue
}
path = strings.TrimPrefix(path, "~/")
rootedPaths[i] = filepath.Join(home, path)
}
return rootedPaths
}
59 changes: 56 additions & 3 deletions filesearch/filesearch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ func TestMain(t *testing.M) {
// ConfigDirs: [C:\ProgramData C:\Users\runneradmin\AppData\Roaming]
// ApplicationDirs: [C:\Users\runneradmin\AppData\Roaming\Microsoft\Windows\Start Menu\Programs C:\ProgramData\Microsoft\Windows\Start Menu\Programs]
func TestDefaultConfigDirs(t *testing.T) {
t.Parallel()

t.Log("ConfigHome:", xdg.ConfigHome)
t.Log("ConfigDirs:", xdg.ConfigDirs)
t.Log("ApplicationDirs:", xdg.ApplicationDirs)
Expand Down Expand Up @@ -192,6 +194,8 @@ func testLocations(t *testing.T) []testLocation {
}

func TestFindConfigurationFile(t *testing.T) {
t.Parallel()

// Work from a temporary directory
err := os.Chdir(os.TempDir())
require.NoError(t, err)
Expand All @@ -206,7 +210,7 @@ func TestFindConfigurationFile(t *testing.T) {
var err error
// Install empty config file
if location.realPath != "" {
err = fs.MkdirAll(location.realPath, 0700)
err = fs.MkdirAll(location.realPath, 0o700)
require.NoError(t, err)
}
file, err := fs.Create(filepath.Join(location.realPath, location.realFile))
Expand All @@ -230,12 +234,16 @@ func TestFindConfigurationFile(t *testing.T) {
}

func TestCannotFindConfigurationFile(t *testing.T) {
t.Parallel()

found, err := FindConfigurationFile("some_config_file")
assert.Empty(t, found)
assert.Error(t, err)
}

func TestFindResticBinary(t *testing.T) {
t.Parallel()

binary, err := FindResticBinary("some_other_name")
if binary != "" {
assert.True(t, strings.HasSuffix(binary, getResticBinaryName()))
Expand All @@ -246,14 +254,16 @@ func TestFindResticBinary(t *testing.T) {
}

func TestFindResticBinaryWithTilde(t *testing.T) {
t.Parallel()

if runtime.GOOS == "windows" {
t.Skip("not supported on Windows")
return
}
home, err := os.UserHomeDir()
require.NoError(t, err)

tempFile, err := afero.TempFile(fs, home, "TestFindResticBinaryWithTilde")
tempFile, err := afero.TempFile(fs, home, t.Name())
require.NoError(t, err)
tempFile.Close()
defer func() {
Expand All @@ -267,6 +277,8 @@ func TestFindResticBinaryWithTilde(t *testing.T) {
}

func TestShellExpand(t *testing.T) {
t.Parallel()

if runtime.GOOS == "windows" {
t.Skip("not supported on Windows")
return
Expand All @@ -290,6 +302,8 @@ func TestShellExpand(t *testing.T) {

for _, testItem := range testData {
t.Run(testItem.source, func(t *testing.T) {
t.Parallel()

result, err := ShellExpand(testItem.source)
require.NoError(t, err)
assert.Equal(t, testItem.expected, result)
Expand All @@ -298,6 +312,8 @@ func TestShellExpand(t *testing.T) {
}

func TestFindConfigurationIncludes(t *testing.T) {
t.Parallel()

testID := fmt.Sprintf("%d", uint32(time.Now().UnixNano()))
tempDir := os.TempDir()
files := []string{
Expand All @@ -309,7 +325,9 @@ func TestFindConfigurationIncludes(t *testing.T) {

for _, file := range files {
require.NoError(t, afero.WriteFile(fs, file, []byte{}, iofs.ModePerm))
defer fs.Remove(file) // defer stack is ok for cleanup
t.Cleanup(func() {
_ = fs.Remove(file)
})
}

testData := []struct {
Expand All @@ -335,6 +353,8 @@ func TestFindConfigurationIncludes(t *testing.T) {

for _, test := range testData {
t.Run(strings.Join(test.includes, ","), func(t *testing.T) {
t.Parallel()

result, err := FindConfigurationIncludes(files[0], test.includes)
if test.expected == nil {
assert.Nil(t, result)
Expand All @@ -350,3 +370,36 @@ func TestFindConfigurationIncludes(t *testing.T) {
})
}
}

func TestAddRootToRelativePaths(t *testing.T) {
t.Parallel()

if platform.IsWindows() {
t.Skip("not supported on Windows")
}

testCases := []struct {
root string
inputPath []string
outputPath []string
}{
{
root: "",
inputPath: []string{"", "dir", "~/user", "/root"},
outputPath: []string{"", "dir", "~/user", "/root"},
},
{
root: "/home",
inputPath: []string{"", "dir", "~/user", "/root"},
outputPath: []string{"/home", "/home/dir", "/home/user", "/root"},
},
}
for _, testCase := range testCases {
t.Run(testCase.root, func(t *testing.T) {
t.Parallel()

result := addRootToRelativePaths(testCase.root, testCase.inputPath)
assert.Equal(t, testCase.outputPath, result)
})
}
}
7 changes: 1 addition & 6 deletions schedule/scheduler_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (

"github.com/creativeprojects/resticprofile/config"
"github.com/creativeprojects/resticprofile/constants"
"github.com/spf13/afero"
)

type SchedulerConfig interface {
Expand Down Expand Up @@ -35,16 +34,13 @@ type SchedulerWindows struct{}
func (s SchedulerWindows) Type() string { return constants.SchedulerWindows }
func (s SchedulerWindows) Convert(_ string) SchedulerConfig { return s }

type SchedulerLaunchd struct {
Fs afero.Fs
}
type SchedulerLaunchd struct{}

func (s SchedulerLaunchd) Type() string { return constants.SchedulerLaunchd }
func (s SchedulerLaunchd) Convert(_ string) SchedulerConfig { return s }

// SchedulerCrond configures crond compatible schedulers, either needs CrontabBinary or CrontabFile
type SchedulerCrond struct {
Fs afero.Fs
CrontabFile string
CrontabBinary string
Username string
Expand All @@ -54,7 +50,6 @@ func (s SchedulerCrond) Type() string { return constants.Sch
func (s SchedulerCrond) Convert(_ string) SchedulerConfig { return s }

type SchedulerSystemd struct {
Fs afero.Fs
UnitTemplate string
TimerTemplate string
}
Expand Down

0 comments on commit 6da0317

Please sign in to comment.