mirror of
https://github.com/cupcakearmy/autorestic.git
synced 2024-12-22 08:16:25 +00:00
progress
This commit is contained in:
parent
43244302be
commit
335724cce7
1
.gitignore
vendored
1
.gitignore
vendored
@ -12,3 +12,4 @@ node_modules
|
||||
test
|
||||
autorestic
|
||||
data
|
||||
dist
|
@ -39,25 +39,18 @@ var backupCmd = &cobra.Command{
|
||||
}
|
||||
defer lock.Unlock()
|
||||
{
|
||||
backup(internal.GetAllOrLocation(cmd, false), config)
|
||||
selected, err := internal.GetAllOrSelected(cmd, false)
|
||||
cobra.CheckErr(err)
|
||||
for _, name := range selected {
|
||||
location := config.Locations[name]
|
||||
fmt.Printf("Backing up: `%s`", name)
|
||||
location.Backup()
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(backupCmd)
|
||||
backupCmd.PersistentFlags().StringSliceP("location", "l", []string{}, "Locations")
|
||||
backupCmd.PersistentFlags().BoolP("all", "a", false, "Backup all locations")
|
||||
}
|
||||
|
||||
func backup(locations []string, config *internal.Config) {
|
||||
for _, name := range locations {
|
||||
location, ok := config.Locations[name]
|
||||
if !ok {
|
||||
fmt.Println(fmt.Errorf("location `%s` does not exist", name))
|
||||
} else {
|
||||
fmt.Printf("Backing up: `%s`", name)
|
||||
location.Backup()
|
||||
}
|
||||
}
|
||||
internal.AddFlagsToCommand(backupCmd, false)
|
||||
}
|
||||
|
21
cmd/exec.go
21
cmd/exec.go
@ -31,20 +31,19 @@ var execCmd = &cobra.Command{
|
||||
if err := config.CheckConfig(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
exec(internal.GetAllOrLocation(cmd, true), config, args)
|
||||
{
|
||||
selected, err := internal.GetAllOrSelected(cmd, true)
|
||||
cobra.CheckErr(err)
|
||||
for _, name := range selected {
|
||||
fmt.Println(name)
|
||||
backend := config.Backends[name]
|
||||
backend.Exec(args)
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(execCmd)
|
||||
execCmd.PersistentFlags().StringSliceP("backend", "b", []string{}, "backends")
|
||||
execCmd.PersistentFlags().BoolP("all", "a", false, "Exec in all backends")
|
||||
}
|
||||
|
||||
func exec(backends []string, config *internal.Config, args []string) {
|
||||
for _, name := range backends {
|
||||
fmt.Println(name)
|
||||
backend := config.Backends[name]
|
||||
backend.Exec(args)
|
||||
}
|
||||
internal.AddFlagsToCommand(execCmd, true)
|
||||
}
|
||||
|
49
cmd/forget.go
Normal file
49
cmd/forget.go
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
Copyright © 2021 NAME HERE <EMAIL ADDRESS>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/cupcakearmy/autorestic/internal"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// forgetCmd represents the forget command
|
||||
var forgetCmd = &cobra.Command{
|
||||
Use: "forget",
|
||||
Short: "Forget and optionally prune snapshots according the specified policies",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
config := internal.GetConfig()
|
||||
if err := config.CheckConfig(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
{
|
||||
selected, err := internal.GetAllOrSelected(cmd, false)
|
||||
cobra.CheckErr(err)
|
||||
prune, _ := cmd.Flags().GetBool("prune")
|
||||
for _, name := range selected {
|
||||
location := config.Locations[name]
|
||||
err := location.Forget(prune)
|
||||
cobra.CheckErr(err)
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(forgetCmd)
|
||||
internal.AddFlagsToCommand(forgetCmd, false)
|
||||
forgetCmd.Flags().Bool("prune", false, "Also prune repository")
|
||||
}
|
34
cmd/install.go
Normal file
34
cmd/install.go
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
Copyright © 2021 NAME HERE <EMAIL ADDRESS>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/cupcakearmy/autorestic/internal/bins"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var installCmd = &cobra.Command{
|
||||
Use: "install",
|
||||
Short: "A brief description of your command",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := bins.InstallRestic()
|
||||
cobra.CheckErr(err)
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(installCmd)
|
||||
}
|
@ -30,6 +30,7 @@ var cfgFile string
|
||||
|
||||
// rootCmd represents the base command when called without any subcommands
|
||||
var rootCmd = &cobra.Command{
|
||||
Version: internal.VERSION,
|
||||
Use: "autorestic",
|
||||
Short: "CLI Wrapper for restic",
|
||||
}
|
||||
|
36
cmd/upgrade.go
Normal file
36
cmd/upgrade.go
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
Copyright © 2021 NAME HERE <EMAIL ADDRESS>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/cupcakearmy/autorestic/internal/bins"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var upgradeCmd = &cobra.Command{
|
||||
Use: "upgrade",
|
||||
Short: "A brief description of your command",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
noRestic, _ := cmd.Flags().GetBool("no-restic")
|
||||
err := bins.Upgrade(!noRestic)
|
||||
cobra.CheckErr(err)
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(upgradeCmd)
|
||||
upgradeCmd.Flags().Bool("no-restic", false, "Also update restic. Default: true")
|
||||
}
|
1
go.mod
1
go.mod
@ -3,6 +3,7 @@ module github.com/cupcakearmy/autorestic
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
github.com/blang/semver/v4 v4.0.0
|
||||
github.com/buger/goterm v1.0.0
|
||||
github.com/mitchellh/go-homedir v1.1.0
|
||||
github.com/spf13/cobra v1.1.3
|
||||
|
3
go.sum
3
go.sum
@ -24,6 +24,8 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
|
||||
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
|
||||
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
|
||||
github.com/buger/goterm v1.0.0 h1:ZB6uUlY8+sjJyFGzz2WpRqX2XYPeXVgtZAOJMwOsTWM=
|
||||
github.com/buger/goterm v1.0.0/go.mod h1:16STi3LquiscTIHA8SXUNKEa/Cnu4ZHBH8NsCaWgso0=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
@ -248,7 +250,6 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0 h1:HyfiK1WMnHj5FXFXatD+Qs1A/xC2Run6RzeW1SyHxpc=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210331175145-43e1dd70ce54 h1:rF3Ohx8DRyl8h2zw9qojyLHLhrJpEMgyPOImREEryf0=
|
||||
golang.org/x/sys v0.0.0-20210331175145-43e1dd70ce54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
127
internal/bins/bins.go
Normal file
127
internal/bins/bins.go
Normal file
@ -0,0 +1,127 @@
|
||||
package bins
|
||||
|
||||
import (
|
||||
"compress/bzip2"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/blang/semver/v4"
|
||||
"github.com/cupcakearmy/autorestic/internal"
|
||||
)
|
||||
|
||||
const INSTALL_PATH = "/usr/local/bin"
|
||||
|
||||
type GithubReleaseAsset struct {
|
||||
Name string `json:"name"`
|
||||
Link string `json:"browser_download_url"`
|
||||
}
|
||||
type GithubRelease struct {
|
||||
Tag string `json:"tag_name"`
|
||||
Assets []GithubReleaseAsset `json:"assets"`
|
||||
}
|
||||
|
||||
func dlJSON(url string) (GithubRelease, error) {
|
||||
var parsed GithubRelease
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return parsed, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return parsed, err
|
||||
|
||||
}
|
||||
json.Unmarshal(body, &parsed)
|
||||
return parsed, nil
|
||||
}
|
||||
|
||||
func InstallRestic() error {
|
||||
installed := internal.CheckIfCommandIsCallable("restic")
|
||||
if installed {
|
||||
fmt.Println("restic already installed")
|
||||
return nil
|
||||
} else {
|
||||
body, err := dlJSON("https://api.github.com/repos/restic/restic/releases/latest")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ending := fmt.Sprintf("_%s_%s.bz2", runtime.GOOS, runtime.GOARCH)
|
||||
for _, asset := range body.Assets {
|
||||
if strings.HasSuffix(asset.Name, ending) {
|
||||
// Found
|
||||
fmt.Println(asset.Link)
|
||||
|
||||
// Download archive
|
||||
resp, err := http.Get(asset.Link)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Uncompress
|
||||
bz := bzip2.NewReader(resp.Body)
|
||||
|
||||
// Save binary
|
||||
file, err := os.Create(path.Join(INSTALL_PATH, "restic"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
file.Chmod(0755)
|
||||
defer file.Close()
|
||||
io.Copy(file, bz)
|
||||
|
||||
fmt.Printf("Successfully installed restic under %s\n", INSTALL_PATH)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return errors.New("could not find right binary for your system, please install restic manually. https://bit.ly/2Y1Rzai")
|
||||
}
|
||||
}
|
||||
|
||||
func upgradeRestic() error {
|
||||
out, err := internal.ExecuteCommand(internal.ExecuteOptions{
|
||||
Command: "restic",
|
||||
}, "self-update")
|
||||
fmt.Println(out)
|
||||
return err
|
||||
}
|
||||
|
||||
func Upgrade(restic bool) error {
|
||||
// Upgrade restic
|
||||
if restic {
|
||||
InstallRestic()
|
||||
upgradeRestic()
|
||||
}
|
||||
|
||||
// Upgrade self
|
||||
current, err := semver.ParseTolerant(internal.VERSION)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(current)
|
||||
|
||||
body, err := dlJSON("https://api.github.com/repos/cupcakearmy/autorestic/releases/latest")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
latest, err := semver.ParseTolerant(body.Tag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if current.GT(latest) {
|
||||
|
||||
fmt.Println("Updated autorestic")
|
||||
} else {
|
||||
fmt.Println("Already up to date")
|
||||
}
|
||||
return nil
|
||||
}
|
@ -12,6 +12,8 @@ import (
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
const VERSION = "1.0.0"
|
||||
|
||||
type Config struct {
|
||||
Locations map[string]Location `mapstructure:"locations"`
|
||||
Backends map[string]Backend `mapstructure:"backends"`
|
||||
@ -60,7 +62,7 @@ func (c Config) CheckConfig() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetAllOrLocation(cmd *cobra.Command, backends bool) []string {
|
||||
func GetAllOrSelected(cmd *cobra.Command, backends bool) ([]string, error) {
|
||||
var list []string
|
||||
if backends {
|
||||
for key := range config.Backends {
|
||||
@ -73,7 +75,7 @@ func GetAllOrLocation(cmd *cobra.Command, backends bool) []string {
|
||||
}
|
||||
all, _ := cmd.Flags().GetBool("all")
|
||||
if all {
|
||||
return list
|
||||
return list, nil
|
||||
} else {
|
||||
var selected []string
|
||||
if backends {
|
||||
@ -92,9 +94,22 @@ func GetAllOrLocation(cmd *cobra.Command, backends bool) []string {
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
panic("invalid key")
|
||||
if backends {
|
||||
return nil, fmt.Errorf("invalid backend \"%s\"", s)
|
||||
} else {
|
||||
return nil, fmt.Errorf("invalid location \"%s\"", s)
|
||||
}
|
||||
}
|
||||
return selected
|
||||
}
|
||||
return selected, nil
|
||||
}
|
||||
}
|
||||
|
||||
func AddFlagsToCommand(cmd *cobra.Command, backend bool) {
|
||||
cmd.PersistentFlags().BoolP("all", "a", false, "Backup all locations")
|
||||
if backend {
|
||||
cmd.PersistentFlags().StringSliceP("backend", "b", []string{}, "backends")
|
||||
} else {
|
||||
cmd.PersistentFlags().StringSliceP("location", "l", []string{}, "Locations")
|
||||
}
|
||||
}
|
||||
|
@ -83,3 +83,27 @@ func (l Location) Backup() error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l Location) Forget(prune bool) error {
|
||||
c := GetConfig()
|
||||
from := GetPathRelativeToConfig(l.From)
|
||||
for _, to := range l.To {
|
||||
backend := c.Backends[to]
|
||||
options := ExecuteOptions{
|
||||
Envs: backend.getEnv(),
|
||||
Dir: from,
|
||||
}
|
||||
flags := l.getOptions("forget")
|
||||
cmd := []string{"forget", "--path", from}
|
||||
if prune {
|
||||
cmd = append(cmd, "--prune")
|
||||
}
|
||||
cmd = append(cmd, flags...)
|
||||
out, err := ExecuteResticCommand(options, cmd...)
|
||||
fmt.Println(out)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user