From b385c011210087e6d6992a4e4b279fec4b22ab89 Mon Sep 17 00:00:00 2001 From: Gareth George Date: Tue, 28 Nov 2023 00:32:31 -0800 Subject: [PATCH] feat: autoinstall required restic version --- resticui.go => cmd/resticui/resticui.go | 49 +++----- go.mod | 12 +- go.sum | 20 +++ hack/build.sh | 9 +- hack/install-deps.sh | 3 +- hack/run.sh | 4 + internal/api/server.go | 1 + .../envopts.go => config/environment.go} | 22 +++- internal/orchestrator/repo_test.go | 2 +- internal/resticinstaller/resticinstaller.go | 117 ++++++++++++++++++ pkg/restic/restic.go | 4 +- pkg/restic/restic_test.go | 22 ++-- test/helpers/installrestic.go | 16 +++ {internal/test => test}/helpers/testdata.go | 2 +- webui/static.go | 8 -- 15 files changed, 222 insertions(+), 69 deletions(-) rename resticui.go => cmd/resticui/resticui.go (73%) mode change 100644 => 100755 hack/build.sh mode change 100644 => 100755 hack/install-deps.sh create mode 100755 hack/run.sh rename internal/{envopts/envopts.go => config/environment.go} (60%) create mode 100644 internal/resticinstaller/resticinstaller.go create mode 100644 test/helpers/installrestic.go rename {internal/test => test}/helpers/testdata.go (95%) delete mode 100644 webui/static.go diff --git a/resticui.go b/cmd/resticui/resticui.go similarity index 73% rename from resticui.go rename to cmd/resticui/resticui.go index 8fcd34b7..8a02eed5 100644 --- a/resticui.go +++ b/cmd/resticui/resticui.go @@ -3,7 +3,7 @@ package main import ( "context" "errors" - "io/fs" + "flag" "net/http" "os" "os/signal" @@ -11,19 +11,18 @@ import ( "sync" "syscall" + rice "github.com/GeertJohan/go.rice" "github.com/garethgeorge/resticui/internal/api" "github.com/garethgeorge/resticui/internal/config" - "github.com/garethgeorge/resticui/internal/envopts" "github.com/garethgeorge/resticui/internal/oplog" "github.com/garethgeorge/resticui/internal/orchestrator" - static "github.com/garethgeorge/resticui/webui" "github.com/mattn/go-colorable" "go.uber.org/zap" "go.uber.org/zap/zapcore" - - _ "embed" ) +var flagAutoInstallRestic = flag.Bool("auto-install-restic", true, "Automatically install restic if $RESTICUI_") + func main() { ctx := context.Background() ctx, cancel := context.WithCancel(ctx) @@ -40,10 +39,15 @@ func main() { // Configure the HTTP mux mux := http.NewServeMux() - mux.Handle("/", http.FileServer(http.FS(&SubdirFilesystem{FS: static.FS, subdir: "dist"}))) + + if box, err := rice.FindBox("webui/dist"); err == nil { + mux.Handle("/", http.FileServer(box.HTTPBox())) + } else { + zap.S().Warnf("Error loading static assets, not serving UI: %v", err) + } // Create and serve API server - oplogFile := path.Join(dataPath(), "oplog.boltdb") + oplogFile := path.Join(config.DataDir(), "oplog.boltdb") oplog, err := oplog.NewOpLog(oplogFile) if err != nil { zap.S().Warnf("Operation log may be corrupted, if errors recur delete the file %q and restart. Your backups stored in your repos are safe.", oplogFile) @@ -84,7 +88,7 @@ func main() { // Serve the HTTP gateway server := &http.Server{ - Addr: envopts.BindAddress(), + Addr: config.BindAddress(), Handler: mux, } @@ -123,7 +127,7 @@ func init() { func createConfigProvider() config.ConfigStore { return &config.CachingValidatingStore{ - ConfigStore: &config.JsonFileStore{Path: envopts.ConfigFilePath()}, + ConfigStore: &config.JsonFileStore{Path: config.ConfigFilePath()}, } } @@ -134,30 +138,9 @@ func onterm(callback func()) { callback() } -type SubdirFilesystem struct { - fs.FS - subdir string -} - -var _ fs.FS = &SubdirFilesystem{} -var _ fs.ReadDirFS = &SubdirFilesystem{} - -func (s *SubdirFilesystem) Open(name string) (fs.File, error) { - return s.FS.Open(path.Join(s.subdir, name)) -} - -func (s *SubdirFilesystem) ReadDir(name string) ([]fs.DirEntry, error) { - readDirFS := s.FS.(fs.ReadDirFS) - if readDirFS == nil { - return nil, &fs.PathError{Op: "readdir", Path: name, Err: errors.New("not implemented")} - } - return readDirFS.ReadDir(path.Join(s.subdir, name)) -} +func findResticBin() { + resticBin := config.ResticBinPath() + if resticBin != "" { -func dataPath() string { - datahome := os.Getenv("XDG_DATA_HOME") - if datahome == "" { - datahome = path.Join(os.Getenv("HOME") + "/.local/share") } - return path.Join(datahome, "resticui") } diff --git a/go.mod b/go.mod index 918163d6..c9709b0e 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/garethgeorge/resticui go 1.21.3 require ( + github.com/GeertJohan/go.rice v1.0.3 github.com/gitploy-io/cronexpr v0.2.2 github.com/google/renameio v1.0.1 github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.1 @@ -11,19 +12,20 @@ require ( github.com/mattn/go-colorable v0.1.13 go.etcd.io/bbolt v1.3.8 go.uber.org/zap v1.26.0 - google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 + google.golang.org/genproto/googleapis/api v0.0.0-20231127180814-3a041ad873d4 google.golang.org/grpc v1.59.0 google.golang.org/protobuf v1.31.0 ) require ( + github.com/daaku/go.zipexe v1.0.2 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/mattn/go-isatty v0.0.20 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/net v0.18.0 // indirect - golang.org/x/sys v0.14.0 // indirect + golang.org/x/net v0.19.0 // indirect + golang.org/x/sys v0.15.0 // indirect golang.org/x/text v0.14.0 // indirect - google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 // indirect + google.golang.org/genproto v0.0.0-20231127180814-3a041ad873d4 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20231127180814-3a041ad873d4 // indirect ) diff --git a/go.sum b/go.sum index 43ab565c..65264e55 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,9 @@ +github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0= +github.com/GeertJohan/go.rice v1.0.3 h1:k5viR+xGtIhF61125vCE1cmJ5957RQGXG6dmbaWZSmI= +github.com/GeertJohan/go.rice v1.0.3/go.mod h1:XVdrU4pW00M4ikZed5q56tPf1v2KwnIKeIdc9CBYNt4= +github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= +github.com/daaku/go.zipexe v1.0.2 h1:Zg55YLYTr7M9wjKn8SY/WcpuuEi+kR2u4E8RhvpyXmk= +github.com/daaku/go.zipexe v1.0.2/go.mod h1:5xWogtqlYnfBXkSB1o9xysukNP9GTvaNkqzUZbt3Bw8= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/gitploy-io/cronexpr v0.2.2 h1:Au+wK6FqmOLAF7AkW6q4gnrNXTe3rEW97XFZ4chy0xs= @@ -19,15 +25,19 @@ github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/nkovacs/streamquote v1.0.0/go.mod h1:BN+NaZ2CmdKqUuTUXUEm9j95B2TRbpOWpxbJYzzgUsc= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= go.etcd.io/bbolt v1.3.8 h1:xs88BrvEv273UsB79e0hcVrlUWmS0a8upikMFhSyAtA= go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= @@ -38,19 +48,29 @@ go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= +golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= +golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 h1:wpZ8pe2x1Q3f2KyT5f8oP/fa9rHAKgFPr/HZdNuS+PQ= google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:J7XzRzVy1+IPwWHZUzoD0IccYZIrXILAQpc+Qy9CMhY= +google.golang.org/genproto v0.0.0-20231127180814-3a041ad873d4 h1:W12Pwm4urIbRdGhMEg2NM9O3TWKjNcxQhs46V0ypf/k= +google.golang.org/genproto v0.0.0-20231127180814-3a041ad873d4/go.mod h1:5RBcpGRxr25RbDzY5w+dmaqpSEvl8Gwl1x2CICf60ic= google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 h1:JpwMPBpFN3uKhdaekDpiNlImDdkUAyiJ6ez/uxGaUSo= google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:0xJLfVdJqpAPl8tDg1ujOCGzx6LFLttXT5NhllGOXY4= +google.golang.org/genproto/googleapis/api v0.0.0-20231127180814-3a041ad873d4 h1:ZcOkrmX74HbKFYnpPY8Qsw93fC29TbJXspYKaBkSXDQ= +google.golang.org/genproto/googleapis/api v0.0.0-20231127180814-3a041ad873d4/go.mod h1:k2dtGpRrbsSyKcNPKKI5sstZkrNCZwpU/ns96JoHbGg= google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 h1:Jyp0Hsi0bmHXG6k9eATXoYtjd6e2UzZ1SCn/wIupY14= google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:oQ5rr10WTTMvP4A36n8JpR1OrO1BEiV4f78CneXZxkA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231127180814-3a041ad873d4 h1:DC7wcm+i+P1rN3Ff07vL+OndGg5OhNddHyTA+ocPqYE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231127180814-3a041ad873d4/go.mod h1:eJVxU6o+4G1PSczBr85xmyvSNYAKvAYgkub40YGomFM= google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= diff --git a/hack/build.sh b/hack/build.sh old mode 100644 new mode 100755 index 91216965..dbed2921 --- a/hack/build.sh +++ b/hack/build.sh @@ -1,5 +1,8 @@ #! /bin/sh +set -x -(cd proto && ./build.sh) - -go build ./.. +(cd proto && ./update.sh) +(cd webui && npm run build) +rm -f resticui +go build ./cmd/resticui +rice append --exec resticui diff --git a/hack/install-deps.sh b/hack/install-deps.sh old mode 100644 new mode 100755 index 030c1544..99a0a465 --- a/hack/install-deps.sh +++ b/hack/install-deps.sh @@ -1,5 +1,6 @@ #! /bin/sh +set -x go install github.com/GeertJohan/go.rice/rice@latest go install github.com/GeertJohan/go.rice@latest -pip install lastversion +python -m pip install lastversion \ No newline at end of file diff --git a/hack/run.sh b/hack/run.sh new file mode 100755 index 00000000..e5dd1a5a --- /dev/null +++ b/hack/run.sh @@ -0,0 +1,4 @@ +#! /bin/sh +set -x + +DEBUG=1 go run ./cmd/resticui diff --git a/internal/api/server.go b/internal/api/server.go index 68991fdf..9df6c538 100644 --- a/internal/api/server.go +++ b/internal/api/server.go @@ -31,6 +31,7 @@ var _ v1.ResticUIServer = &Server{} func NewServer(config config.ConfigStore, orchestrator *orchestrator.Orchestrator, oplog *oplog.OpLog) *Server { s := &Server{ + config: config, orchestrator: orchestrator, oplog: oplog, } diff --git a/internal/envopts/envopts.go b/internal/config/environment.go similarity index 60% rename from internal/envopts/envopts.go rename to internal/config/environment.go index 99e0deb6..86b7a191 100644 --- a/internal/envopts/envopts.go +++ b/internal/config/environment.go @@ -1,4 +1,4 @@ -package envopts +package config import ( "fmt" @@ -7,8 +7,15 @@ import ( "strings" ) +var ( + EnvVarConfigPath = "RESTICUI_CONFIG_PATH" + EnvVarDataDir = "RESTICUI_DATA_DIR" + EnvVarBindAddress = "RESTICUI_PORT" + EnvVarBinPath = "RESTICUI_RESTIC_BIN_PATH" +) + func ConfigFilePath() string { - if val := os.Getenv("RESTICUI_CONFIG_PATH"); val != "" { + if val := os.Getenv(EnvVarConfigPath); val != "" { return val } if val := os.Getenv("XDG_CONFIG_HOME"); val != "" { @@ -18,7 +25,7 @@ func ConfigFilePath() string { } func DataDir() string { - if val := os.Getenv("RESTICUI_DATA_DIR"); val != "" { + if val := os.Getenv(EnvVarDataDir); val != "" { return val } if val := os.Getenv("XDG_DATA_HOME"); val != "" { @@ -28,7 +35,7 @@ func DataDir() string { } func BindAddress() string { - if val := os.Getenv("RESTICUI_PORT"); val != "" { + if val := os.Getenv(EnvVarBindAddress); val != "" { if !strings.Contains(val, ":") { return ":" + val } @@ -37,6 +44,13 @@ func BindAddress() string { return ":9898" } +func ResticBinPath() string { + if val := os.Getenv("RESTICUI_RESTIC_BIN_PATH"); val != "" { + return val + } + return "" +} + func getHomeDir() string { home, err := os.UserHomeDir() if err != nil { diff --git a/internal/orchestrator/repo_test.go b/internal/orchestrator/repo_test.go index a8fa42a1..21c0a778 100644 --- a/internal/orchestrator/repo_test.go +++ b/internal/orchestrator/repo_test.go @@ -6,8 +6,8 @@ import ( "testing" v1 "github.com/garethgeorge/resticui/gen/go/v1" - test "github.com/garethgeorge/resticui/internal/test/helpers" "github.com/garethgeorge/resticui/pkg/restic" + test "github.com/garethgeorge/resticui/test/helpers" ) func TestBackup(t *testing.T) { diff --git a/internal/resticinstaller/resticinstaller.go b/internal/resticinstaller/resticinstaller.go new file mode 100644 index 00000000..5f51c0df --- /dev/null +++ b/internal/resticinstaller/resticinstaller.go @@ -0,0 +1,117 @@ +package resticinstaller + +import ( + "compress/bzip2" + "errors" + "fmt" + "io" + "net/http" + "os" + "path" + "runtime" + "strings" + "sync" + + "github.com/garethgeorge/resticui/internal/config" +) + +var ( + ErrResticNotFound = errors.New("no restic binary") +) + +var ( + RequiredResticVersion = "0.16.2" + + findResticMu sync.Mutex + didTryInstall bool +) + +func resticDownloadURL(version string) string { + return fmt.Sprintf("https://github.com/restic/restic/releases/download/v%v/restic_%v_%v_%v.bz2", version, version, runtime.GOOS, runtime.GOARCH) +} + +func downloadFile(url string, path string) error { + // Download ur as a file and save it to path + resp, err := http.Get(url) + if err != nil { + return fmt.Errorf("http GET %v: %w", url, err) + } + defer resp.Body.Close() + + var body io.Reader = resp.Body + if strings.HasSuffix(url, ".bz2") { + body = bzip2.NewReader(resp.Body) + } + + out, err := os.Create(path) + if err != nil { + return fmt.Errorf("create file %v: %w", path, err) + } + defer out.Close() + if err != nil { + return fmt.Errorf("create file %v: %w", path, err) + } + _, err = io.Copy(out, body) + if err != nil { + return fmt.Errorf("copy response body to file %v: %w", path, err) + } + + return nil +} + +func downloadExecutable(url string, path string) error { + if err := downloadFile(url, path + ".tmp"); err != nil { + return err + } + + if err := os.Chmod(path + ".tmp", 0755); err != nil { + return fmt.Errorf("chmod executable %v: %w", path, err) + } + + if err := os.Rename(path + ".tmp", path); err != nil { + return fmt.Errorf("rename %v.tmp to %v: %w", path, path, err) + } + + return nil +} + +// FindOrInstallResticBinary first tries to find the restic binary if provided as an environment variable. Otherwise it downloads restic if not already installed. +func FindOrInstallResticBinary() (string, error) { + findResticMu.Lock() + defer findResticMu.Unlock() + + // Check if restic is provided. + resticBin := config.ResticBinPath() + if resticBin != "" { + if _, err := os.Stat(resticBin); err != nil { + if !errors.Is(err, os.ErrNotExist) { + return "", fmt.Errorf("stat(%v): %w", resticBin, err) + } + return "", fmt.Errorf("no binary found at path %v: %w", resticBin, ErrResticNotFound) + } + return resticBin, nil + } + + // Check for restic installation in data directory. + resticInstallPath := path.Join(config.DataDir(), fmt.Sprintf("restic-%v", RequiredResticVersion)) + if _, err := os.Stat(resticInstallPath); err != nil { + if !errors.Is(err, os.ErrNotExist) { + return "", fmt.Errorf("could not stat restic binary at %v: %w", resticBin, err) + } + + if didTryInstall { + return "", fmt.Errorf("already tried to install: %w", ErrResticNotFound) + } + didTryInstall = true + + if err := os.MkdirAll(path.Dir(resticInstallPath), 0755); err != nil { + return "", fmt.Errorf("create restic install directory %v: %w", path.Dir(resticInstallPath), err) + } + + if err := downloadExecutable(resticDownloadURL(RequiredResticVersion), resticInstallPath); err != nil { + return "", fmt.Errorf("download restic version %v: %w", RequiredResticVersion, err) + } + } + + return resticInstallPath, nil +} diff --git a/pkg/restic/restic.go b/pkg/restic/restic.go index 69c53ba7..7d27f60b 100644 --- a/pkg/restic/restic.go +++ b/pkg/restic/restic.go @@ -27,14 +27,14 @@ type Repo struct { } // NewRepo instantiates a new repository. TODO: should not accept a v1.Repo, should instead be configured by parameters. -func NewRepo(repo *v1.Repo, opts ...GenericOption) *Repo { +func NewRepo(resticBin string, repo *v1.Repo, opts ...GenericOption) *Repo { opt := &GenericOpts{} for _, o := range opts { o(opt) } return &Repo{ - cmd: "restic", // TODO: configurable binary path + cmd: resticBin, // TODO: configurable binary path repo: repo, initialized: false, extraArgs: opt.extraArgs, diff --git a/pkg/restic/restic_test.go b/pkg/restic/restic_test.go index 0b5b288b..d097ece9 100644 --- a/pkg/restic/restic_test.go +++ b/pkg/restic/restic_test.go @@ -10,14 +10,14 @@ import ( "testing" v1 "github.com/garethgeorge/resticui/gen/go/v1" - test "github.com/garethgeorge/resticui/internal/test/helpers" + "github.com/garethgeorge/resticui/test/helpers" ) func TestResticInit(t *testing.T) { t.Parallel() repo := t.TempDir() - r := NewRepo(&v1.Repo{ + r := NewRepo(helpers.ResticBinary(t), &v1.Repo{ Id: "test", Uri: repo, Password: "test", @@ -33,7 +33,7 @@ func TestResticBackup(t *testing.T) { repo := t.TempDir() // create a new repo with cache disabled for testing - r := NewRepo(&v1.Repo{ + r := NewRepo(helpers.ResticBinary(t), &v1.Repo{ Id: "test", Uri: repo, Password: "test", @@ -42,8 +42,8 @@ func TestResticBackup(t *testing.T) { t.Fatalf("failed to init repo: %v", err) } - testData := test.CreateTestData(t) - testData2 := test.CreateTestData(t) + testData := helpers.CreateTestData(t) + testData2 := helpers.CreateTestData(t) var tests = []struct { name string @@ -107,7 +107,7 @@ func TestSnapshot(t *testing.T) { repo := t.TempDir() - r := NewRepo(&v1.Repo{ + r := NewRepo(helpers.ResticBinary(t), &v1.Repo{ Id: "test", Uri: repo, Password: "test", @@ -116,7 +116,7 @@ func TestSnapshot(t *testing.T) { t.Fatalf("failed to init repo: %v", err) } - testData := test.CreateTestData(t) + testData := helpers.CreateTestData(t) for i := 0; i < 10; i++ { _, err := r.Backup(context.Background(), nil, WithBackupPaths(testData), WithBackupTags(fmt.Sprintf("tag%d", i))) @@ -167,7 +167,7 @@ func TestLs(t *testing.T) { t.Parallel() repo := t.TempDir() - r := NewRepo(&v1.Repo{ + r := NewRepo(helpers.ResticBinary(t), &v1.Repo{ Id: "test", Uri: repo, Password: "test", @@ -176,7 +176,7 @@ func TestLs(t *testing.T) { t.Fatalf("failed to init repo: %v", err) } - testData := test.CreateTestData(t) + testData := helpers.CreateTestData(t) snapshot, err := r.Backup(context.Background(), nil, WithBackupPaths(testData)) if err != nil { @@ -198,7 +198,7 @@ func TestResticForget(t *testing.T) { t.Parallel() repo := t.TempDir() - r := NewRepo(&v1.Repo{ + r := NewRepo(helpers.ResticBinary(t), &v1.Repo{ Id: "test", Uri: repo, Password: "test", @@ -207,7 +207,7 @@ func TestResticForget(t *testing.T) { t.Fatalf("failed to init repo: %v", err) } - testData := test.CreateTestData(t) + testData := helpers.CreateTestData(t) ids := make([]string, 0) for i := 0; i < 10; i++ { diff --git a/test/helpers/installrestic.go b/test/helpers/installrestic.go new file mode 100644 index 00000000..ea9abd3e --- /dev/null +++ b/test/helpers/installrestic.go @@ -0,0 +1,16 @@ +package helpers + +import ( + "testing" + + "github.com/garethgeorge/resticui/internal/resticinstaller" +) + +func ResticBinary(t *testing.T) string { + binPath, err := resticinstaller.FindOrInstallResticBinary() + if err != nil { + t.Fatalf("find restic binary: %v", err) + } + return binPath +} + diff --git a/internal/test/helpers/testdata.go b/test/helpers/testdata.go similarity index 95% rename from internal/test/helpers/testdata.go rename to test/helpers/testdata.go index dfc79d0a..ab912a90 100644 --- a/internal/test/helpers/testdata.go +++ b/test/helpers/testdata.go @@ -1,4 +1,4 @@ -package test +package helpers import ( "fmt" diff --git a/webui/static.go b/webui/static.go deleted file mode 100644 index 3ddcab10..00000000 --- a/webui/static.go +++ /dev/null @@ -1,8 +0,0 @@ -package static - -import ( - "embed" -) - -//go:embed dist/*.js dist/*.css dist/*.html -var FS embed.FS \ No newline at end of file