Compare commits

...

24 Commits

Author SHA1 Message Date
86ae70672a right version 2021-08-05 21:58:48 +02:00
5c0788900f docs 2021-08-05 21:54:40 +02:00
20334a7e83 better config handling 2021-08-05 21:48:02 +02:00
7bebd04482 validate and show cwd 2021-07-12 19:10:36 +02:00
b9b8857bf4 aliases 2021-07-12 19:10:25 +02:00
dd6e618161 typo 2021-07-11 14:03:04 +02:00
a4b54f9f64 check config bugs 2021-07-11 13:51:04 +02:00
c2e88193cd Update contrib.md 2021-06-06 15:51:50 +02:00
a2ef69d96d Merge pull request #87 from somebox/patch-1
correction to B2 config in the example
2021-06-06 15:48:02 +02:00
Jeremy Seitz
d45949b028 correction to B2 config in the example
This example was missing the `env:` block in YAML, which if missing, caused `autorestic check` to compain:

``` * 'Backends[remote]' has invalid keys: b2_account_id, b2_account_key```
2021-06-06 14:27:26 +02:00
77d47cc697 add arm in install script 2021-05-25 22:57:42 +02:00
a3239c0f3b changelog 2021-05-17 22:40:27 +02:00
90cd3171e5 docs 2021-05-17 22:40:23 +02:00
61673bd88b allow options for backend 2021-05-17 22:34:14 +02:00
41736ea3c4 typos 2021-05-07 11:34:46 +02:00
a7779e04bd Merge pull request #75 from FuzzyMistborn/patch-2
Update update command in docs
2021-05-07 11:31:26 +02:00
Fuzzy
1326e7e53c Update update command in docs 2021-05-06 23:05:34 -04:00
478e193d78 forgot version bump 2021-05-06 16:17:45 +02:00
11d4c67dce docs 2021-05-06 16:14:29 +02:00
1643309957 changelog 2021-05-06 15:57:28 +02:00
e05386b0b5 add failure and success hooks 2021-05-06 15:55:32 +02:00
aebaf0a225 Merge branch 'master' of https://github.com/cupcakearmy/autorestic 2021-05-06 15:12:37 +02:00
c090013bf5 Update README.md 2021-05-06 15:12:11 +02:00
88c6949208 custom restic binary 2021-05-06 15:04:35 +02:00
25 changed files with 342 additions and 863 deletions

View File

@@ -5,6 +5,41 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.2.0] - 2021-08-05
### Added
- Community page
- Support for yaml references and aliases
### Fixed
- Better verbose output for hooks
- Better error message for bad formatted configs
## [1.1.2] - 2021-07-11
### Fixes
Don't check all backend when running `forget` or `exec` commands.
## [1.1.1] - 2021-05-17
### Added
- Options for backends
## [1.1.0] - 2021-05-06
### Added
- use custom restic binary
- success & failure hooks
### Fixed
- don't skip other locations on failure
## [1.0.9] - 2021-05-01
### Fixed

View File

@@ -19,11 +19,12 @@ Releases are automatically built by the github workflow and uploaded to the rele
1. Bump `VERSION` in `internal/config.go`.
2. Update `CHANGELOG.md`
3. Commit to master
4. Create a new release with the `v1.2.3` tag and mark as draft.
4. Create a new release with the `v1.2.3` tag and mark as pre-release.
5. The Github action will build the binaries, upload and mark the release as ready when done.
### Brew
1. Check the checksum with `shasum -a 256 autorestic-1.2.3.tar.gz`
2. Update `url` and `sha256` in the brew repo.
3. Submit PR
1. Download the latest release.
2. Check the checksum with `shasum -a 256 autorestic-1.2.3.tar.gz`
3. Update `url` and `sha256` in the brew repo.
4. Submit PR

View File

@@ -13,6 +13,9 @@
<br><br>
<a target="_blank" href="https://discord.gg/wS7RpYTYd2">
<img src="https://img.shields.io/discord/252403122348097536" alt="discord badge" />
<img src="https://img.shields.io/github/contributors/cupcakearmy/autorestic" alt="contributor badge" />
<img src="https://img.shields.io/github/downloads/cupcakearmy/autorestic/total" alt="downloads badge" />
<img src="https://img.shields.io/github/v/release/cupcakearmy/autorestic" alt="version badge" />
</a>
</p>
</p>

View File

@@ -13,19 +13,18 @@ var backupCmd = &cobra.Command{
Use: "backup",
Short: "Create backups for given locations",
Run: func(cmd *cobra.Command, args []string) {
internal.GetConfig()
err := lock.Lock()
CheckErr(err)
defer lock.Unlock()
CheckErr(internal.CheckConfig())
selected, err := internal.GetAllOrSelected(cmd, false)
CheckErr(err)
errors := 0
for _, name := range selected {
location, _ := internal.GetLocation(name)
err := location.Backup(false)
if err != nil {
errs := location.Backup(false)
for err := range errs {
colors.Error.Println(err)
errors++
}

View File

@@ -11,12 +11,11 @@ var execCmd = &cobra.Command{
Use: "exec",
Short: "Execute arbitrary native restic commands for given backends",
Run: func(cmd *cobra.Command, args []string) {
internal.GetConfig()
err := lock.Lock()
CheckErr(err)
defer lock.Unlock()
CheckErr(internal.CheckConfig())
selected, err := internal.GetAllOrSelected(cmd, true)
CheckErr(err)
for _, name := range selected {

View File

@@ -10,12 +10,11 @@ var forgetCmd = &cobra.Command{
Use: "forget",
Short: "Forget and optionally prune snapshots according the specified policies",
Run: func(cmd *cobra.Command, args []string) {
internal.GetConfig()
err := lock.Lock()
CheckErr(err)
defer lock.Unlock()
CheckErr(internal.CheckConfig())
selected, err := internal.GetAllOrSelected(cmd, false)
CheckErr(err)
prune, _ := cmd.Flags().GetBool("prune")

View File

@@ -37,6 +37,7 @@ func init() {
rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "", "config file (default is $HOME/.autorestic.yml or ./.autorestic.yml)")
rootCmd.PersistentFlags().BoolVar(&internal.CI, "ci", false, "CI mode disabled interactive mode and colors and enables verbosity")
rootCmd.PersistentFlags().BoolVarP(&internal.VERBOSE, "verbose", "v", false, "verbose mode")
rootCmd.PersistentFlags().StringVar(&internal.RESTIC_BIN, "restic-bin", "restic", "specify custom restic binary")
cobra.OnInitialize(initConfig)
}

View File

@@ -22,6 +22,7 @@
>
> [Overview](/backend/overview)
> [Available Backends](/backend/available)
> [Options](/backend/options)
> :Collapse label=CLI
>
@@ -36,10 +37,10 @@
> [Exec](/cli/exec)
> [Install](/cli/install)
> [Uninstall](/cli/uninstall)
> [Update](/cli/update)
> [Upgrade](/cli/upgrade)
[Examples](/examples)
[QA](/qa)
[Community](/community)
[Contributors](/contrib)

View File

@@ -0,0 +1,23 @@
# Options
For the `backup` and `forget` commands you can pass any native flags to `restic`.
> It is also possible to set options for an [a specific location](/location/options).
```yaml
backend:
foo:
type: ...
path: ...
options:
backup:
tag:
- foo
- bar
```
In this example, whenever `autorestic` runs `restic backup` it will append a `--tag abc --tag` to the native command.
For more detail see the [location docs](/location/options) for options, as they are the same
> :ToCPrevNext

View File

@@ -4,7 +4,7 @@
autorestic forget [-l, --location] [-a, --all] [--dry-run] [--prune]
```
This will prune and remove old data form the backends according to the [keep policy you have specified for the location](/location/forget)
This will prune and remove old data form the backends according to the [keep policy you have specified for the location](/location/forget).
The `--dry-run` flag will do a dry run showing what would have been deleted, but won't touch the actual data.

View File

@@ -27,4 +27,12 @@ Verbose mode will show the output of the native restic commands that are otherwi
autorestic --verbose backup -a
```
## `--restic-bin`
With `--restic-bin` you can specify to run a specific restic binary. This can be useful if you want to [create a custom binary with root access that can be executed by any user](https://restic.readthedocs.io/en/stable/080_examples.html#full-backup-without-root).
```bash
autorestic --restic-bin /some/path/to/my/custom/restic/binary
```
> :ToCPrevNext

View File

@@ -1,11 +0,0 @@
# Update
Autorestic can update itself! Super handy right? Simply run autorestic update and we will check for you if there are updates for restic and autorestic and install them if necessary.
```bash
autorestic update
```
Updates both restic and autorestic automagically.
> :ToCPrevNext

View File

@@ -0,0 +1,11 @@
# Upgrade
Autorestic can upgrade itself! Super handy right? Simply run autorestic upgrade and we will check for you if there are updates for restic and autorestic and install them if necessary.
```bash
autorestic upgrade
```
Updates both restic and autorestic automagically.
> :ToCPrevNext

View File

@@ -0,0 +1,9 @@
# 🏘 Community
A list of community driven projects. (No official affiliation)
- Docker image: https://github.com/pascaliske/docker-autorestic
- Ansible Role: https://github.com/adsanz/ansible-restic-role
- Ansible Role: https://github.com/ItsNotGoodName/ansible-role-autorestic
> :ToCPrevNext

View File

@@ -31,12 +31,53 @@ backends:
remote:
type: b2
path: 'myBucket:backup/home'
B2_ACCOUNT_ID: account_id
B2_ACCOUNT_KEY: account_key
env:
B2_ACCOUNT_ID: account_id
B2_ACCOUNT_KEY: account_key
hdd:
type: local
path: /mnt/my_external_storage
```
## Aliases
A handy tool for more advanced configurations is to use yaml aliases.
These must be specified under the global `extras` key in the `.autorestic.yml` config file.
Aliases allow to reuse snippets of config throughout the same file.
The following example shows how the locations `a` and `b` share the same hooks and forget policies.
```yaml | .autorestic.yml
extras:
hooks: &foo
before:
- echo "Hello"
after:
- echo "kthxbye"
policies: &bar
keep-daily: 14
keep-weekly: 52
backends:
# ...
locations:
a:
from: /data/a
to: some
hooks:
<<: *foo
options:
forget:
<<: *bar
b:
from: data/b
to: some
hooks:
<<: *foo
options:
forget:
<<: *bar
```
> :ToCPrevNext

View File

@@ -12,5 +12,6 @@ This amazing people helped the project!
- @ChanceM - Typos
- @TheForcer - Typos
- @themorlan - Typos
- @somebox - Typos
> :ToCPrevNext

View File

@@ -2,7 +2,14 @@
If you want to perform some commands before and/or after a backup, you can use hooks.
They consist of a list of `before`/`after` commands that will be executed in the same directory as the target `from`.
They consist of a list of commands that will be executed in the same directory as the target `from`.
The following hooks groups are supported, none are required:
- `before`
- `after`
- `failure`
- `success`
```yml | .autorestic.yml
locations:
@@ -11,10 +18,25 @@ locations:
to: my-backend
hooks:
before:
- echo "Hello"
- echo "Human"
- echo "One"
- echo "Two"
- echo "Three"
after:
- echo "kthxbye"
- echo "Byte"
failure:
- echo "Something went wrong"
success:
- echo "Well done!"
```
## Flowchart
1. `before` hook
2. Run backup
3. `after` hook
4. - `success` hook if no errors were found
- `failure` hook if at least error was encountered
If the `before` hook encounters errors the backup and `after` hooks will be skipped and only the `failed` hooks will run.
> :ToCPrevNext

View File

@@ -2,6 +2,8 @@
For the `backup` and `forget` commands you can pass any native flags to `restic`.
> It is also possible to set options for an [entire backend](/backend/options).
```yaml
locations:
foo:

View File

@@ -1,4 +1,4 @@
# Update
# Upgrade
## From `0.x` to `1.0`

854
docs/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -23,6 +23,8 @@ elif [[ $NATIVE_ARCH == *"arm64"* || $NATIVE_ARCH == *"aarch64"* ]]; then
ARCH=arm64
elif [[ $NATIVE_ARCH == *"x86"* ]]; then
ARCH=386
elif [[ $NATIVE_ARCH == *"armv7"* ]]; then
ARCH=arm
else
echo "Could not determine Architecure automatically, please check the release page manually: https://github.com/cupcakearmy/autorestic/releases"
exit 1

View File

@@ -17,12 +17,13 @@ type BackendRest struct {
}
type Backend struct {
name string
Type string `yaml:"type,omitempty"`
Path string `yaml:"path,omitempty"`
Key string `yaml:"key,omitempty"`
Env map[string]string `yaml:"env,omitempty"`
Rest BackendRest `yaml:"rest,omitempty"`
name string
Type string `yaml:"type,omitempty"`
Path string `yaml:"path,omitempty"`
Key string `yaml:"key,omitempty"`
Env map[string]string `yaml:"env,omitempty"`
Rest BackendRest `yaml:"rest,omitempty"`
Options Options `yaml:"options,omitempty"`
}
func GetBackend(name string) (Backend, bool) {

View File

@@ -2,23 +2,27 @@ package internal
import (
"fmt"
"os"
"path"
"path/filepath"
"strings"
"sync"
"github.com/cupcakearmy/autorestic/internal/colors"
"github.com/cupcakearmy/autorestic/internal/lock"
"github.com/mitchellh/go-homedir"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
const VERSION = "1.0.9"
const VERSION = "1.2.0"
var CI bool = false
var VERBOSE bool = false
var CRON_LEAN bool = false
type Config struct {
Extras interface{} `yaml:"extras"`
Locations map[string]Location `yaml:"locations"`
Backends map[string]Backend `yaml:"backends"`
}
@@ -31,7 +35,8 @@ func GetConfig() *Config {
once.Do(func() {
if err := viper.ReadInConfig(); err == nil {
if !CRON_LEAN {
colors.Faint.Println("Using config file:", viper.ConfigFileUsed())
absConfig, _ := filepath.Abs(viper.ConfigFileUsed())
colors.Faint.Println("Using config file:", absConfig)
}
} else {
return
@@ -39,7 +44,9 @@ func GetConfig() *Config {
config = &Config{}
if err := viper.UnmarshalExact(config); err != nil {
panic(err)
colors.Error.Println("Could not parse config file!")
lock.Unlock()
os.Exit(1)
}
})
}
@@ -75,21 +82,22 @@ func (c *Config) Describe() {
colors.PrintDescription("Cron", l.Cron)
}
after, before := len(l.Hooks.After), len(l.Hooks.Before)
if after+before > 0 {
tmp = ""
if before > 0 {
tmp += "\tBefore"
for _, cmd := range l.Hooks.Before {
tmp += colors.Faint.Sprintf("\n\t ▶ %s", cmd)
}
}
if after > 0 {
tmp += "\n\tAfter"
for _, cmd := range l.Hooks.After {
tmp = ""
hooks := map[string][]string{
"Before": l.Hooks.Before,
"After": l.Hooks.After,
"Failure": l.Hooks.Failure,
"Success": l.Hooks.Success,
}
for hook, commands := range hooks {
if len(commands) > 0 {
tmp += "\n\t" + hook
for _, cmd := range commands {
tmp += colors.Faint.Sprintf("\n\t ▶ %s", cmd)
}
}
}
if tmp != "" {
colors.PrintDescription("Hooks", tmp)
}
@@ -129,7 +137,7 @@ func CheckConfig() error {
return fmt.Errorf("config could not be loaded/found")
}
if !CheckIfResticIsCallable() {
return fmt.Errorf(`restic was not found. Install either with "autorestic install" or manually`)
return fmt.Errorf(`%s was not found. Install either with "autorestic install" or manually`, RESTIC_BIN)
}
for name, backend := range c.Backends {
backend.name = name
@@ -139,7 +147,7 @@ func CheckConfig() error {
}
for name, location := range c.Locations {
location.name = name
if err := location.validate(c); err != nil {
if err := location.validate(); err != nil {
return err
}
}
@@ -219,3 +227,13 @@ func (c *Config) SaveConfig() error {
return viper.WriteConfig()
}
func getOptions(options Options, key string) []string {
var selected []string
for k, values := range options[key] {
for _, value := range values {
selected = append(selected, fmt.Sprintf("--%s", k), value)
}
}
return selected
}

View File

@@ -24,8 +24,10 @@ const (
type HookArray = []string
type Hooks struct {
Before HookArray `yaml:"before"`
After HookArray `yaml:"after"`
Before HookArray `yaml:"before,omitempty"`
After HookArray `yaml:"after,omitempty"`
Success HookArray `yaml:"success,omitempty"`
Failure HookArray `yaml:"failure,omitempty"`
}
type Options map[string]map[string][]string
@@ -45,7 +47,7 @@ func GetLocation(name string) (Location, bool) {
return l, ok
}
func (l Location) validate(c *Config) error {
func (l Location) validate() error {
if l.From == "" {
return fmt.Errorf(`Location "%s" is missing "from" key`, l.name)
}
@@ -76,17 +78,6 @@ func (l Location) validate(c *Config) error {
return nil
}
func (l Location) getOptions(key string) []string {
var options []string
saved := l.Options[key]
for k, values := range saved {
for _, value := range values {
options = append(options, fmt.Sprintf("--%s", k), value)
}
}
return options
}
func ExecuteHooks(commands []string, options ExecuteOptions) error {
if len(commands) == 0 {
return nil
@@ -133,21 +124,30 @@ func (l Location) getPath() (string, error) {
return "", fmt.Errorf("could not get path for location \"%s\"", l.name)
}
func (l Location) Backup(cron bool) error {
func (l Location) Backup(cron bool) []error {
var errors []error
colors.PrimaryPrint(" Backing up location \"%s\" ", l.name)
t := l.getType()
options := ExecuteOptions{
Command: "bash",
}
if err := l.validate(); err != nil {
errors = append(errors, err)
colors.Error.Print(err)
goto after
}
if t == TypeLocal {
dir, _ := GetPathRelativeToConfig(l.From)
colors.Faint.Printf("Executing under: \"%s\"\n", dir)
options.Dir = dir
}
// Hooks
if err := ExecuteHooks(l.Hooks.Before, options); err != nil {
return err
errors = append(errors, err)
goto after
}
// Backup
@@ -156,12 +156,15 @@ func (l Location) Backup(cron bool) error {
colors.Secondary.Printf("Backend: %s\n", backend.name)
env, err := backend.getEnv()
if err != nil {
return nil
errors = append(errors, err)
continue
}
flags := l.getOptions("backup")
lFlags := getOptions(l.Options, "backup")
bFlags := getOptions(backend.Options, "backup")
cmd := []string{"backup"}
cmd = append(cmd, flags...)
cmd = append(cmd, lFlags...)
cmd = append(cmd, bFlags...)
if cron {
cmd = append(cmd, "--tag", "cron")
}
@@ -181,7 +184,8 @@ func (l Location) Backup(cron bool) error {
}
if err != nil {
colors.Error.Println(out)
return err
errors = append(errors, err)
continue
}
if VERBOSE {
colors.Faint.Println(out)
@@ -190,10 +194,22 @@ func (l Location) Backup(cron bool) error {
// After hooks
if err := ExecuteHooks(l.Hooks.After, options); err != nil {
return err
errors = append(errors, err)
}
after:
var commands []string
if len(errors) > 0 {
commands = l.Hooks.Failure
} else {
commands = l.Hooks.Success
}
if err := ExecuteHooks(commands, options); err != nil {
errors = append(errors, err)
}
colors.Success.Println("Done")
return nil
return errors
}
func (l Location) Forget(prune bool, dry bool) error {
@@ -214,7 +230,8 @@ func (l Location) Forget(prune bool, dry bool) error {
options := ExecuteOptions{
Envs: env,
}
flags := l.getOptions("forget")
lFlags := getOptions(l.Options, "forget")
bFlags := getOptions(backend.Options, "forget")
cmd := []string{"forget", "--path", path}
if prune {
cmd = append(cmd, "--prune")
@@ -222,7 +239,8 @@ func (l Location) Forget(prune bool, dry bool) error {
if dry {
cmd = append(cmd, "--dry-run")
}
cmd = append(cmd, flags...)
cmd = append(cmd, lFlags...)
cmd = append(cmd, bFlags...)
out, err := ExecuteResticCommand(options, cmd...)
if VERBOSE {
colors.Faint.Println(out)

View File

@@ -10,13 +10,15 @@ import (
"github.com/cupcakearmy/autorestic/internal/colors"
)
var RESTIC_BIN string
func CheckIfCommandIsCallable(cmd string) bool {
_, err := exec.LookPath(cmd)
return err == nil
}
func CheckIfResticIsCallable() bool {
return CheckIfCommandIsCallable("restic")
return CheckIfCommandIsCallable(RESTIC_BIN)
}
type ExecuteOptions struct {
@@ -50,7 +52,7 @@ func ExecuteCommand(options ExecuteOptions, args ...string) (string, error) {
}
func ExecuteResticCommand(options ExecuteOptions, args ...string) (string, error) {
options.Command = "restic"
options.Command = RESTIC_BIN
return ExecuteCommand(options, args...)
}