mirror of
https://github.com/cupcakearmy/autorestic.git
synced 2025-09-06 10:30:39 +00:00
Compare commits
44 Commits
Author | SHA1 | Date | |
---|---|---|---|
86ae70672a | |||
5c0788900f | |||
20334a7e83 | |||
7bebd04482 | |||
b9b8857bf4 | |||
dd6e618161 | |||
a4b54f9f64 | |||
c2e88193cd | |||
a2ef69d96d | |||
|
d45949b028 | ||
77d47cc697 | |||
a3239c0f3b | |||
90cd3171e5 | |||
61673bd88b | |||
41736ea3c4 | |||
a7779e04bd | |||
|
1326e7e53c | ||
478e193d78 | |||
11d4c67dce | |||
1643309957 | |||
e05386b0b5 | |||
aebaf0a225 | |||
c090013bf5 | |||
88c6949208 | |||
9256cdc38c | |||
a8c611e8ce | |||
d4522c7ffe | |||
21185d894e | |||
b119fc7ea5 | |||
|
73f4bcfd48 | ||
|
3567319314 | ||
|
bf19c983d1 | ||
|
12e4de110b | ||
c9f425ef64 | |||
974f555dff | |||
1688c1f3c3 | |||
29e46d3b5c | |||
0335abb669 | |||
b2f9b9a54e | |||
2b13a6e13d | |||
cc293ea256 | |||
a4ddd5bbcb | |||
7cbf43b75d | |||
bae77c4673 |
5
.github/workflows/build.yml
vendored
5
.github/workflows/build.yml
vendored
@@ -15,6 +15,11 @@ jobs:
|
|||||||
go-version: '^1.16.3'
|
go-version: '^1.16.3'
|
||||||
- name: Build
|
- name: Build
|
||||||
run: go run build/build.go
|
run: go run build/build.go
|
||||||
|
|
||||||
|
- name: Sign
|
||||||
|
uses: tristan-weil/ghaction-checksum-sign-artifact@v1.0.1
|
||||||
|
with:
|
||||||
|
path: dist/*
|
||||||
- name: Release
|
- name: Release
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v1
|
||||||
with:
|
with:
|
||||||
|
41
CHANGELOG.md
41
CHANGELOG.md
@@ -5,6 +5,47 @@ 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/),
|
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).
|
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
|
||||||
|
|
||||||
|
- Validation for docker volumes
|
||||||
|
|
||||||
## [1.0.8] - 2021-04-28
|
## [1.0.8] - 2021-04-28
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
@@ -19,5 +19,12 @@ Releases are automatically built by the github workflow and uploaded to the rele
|
|||||||
1. Bump `VERSION` in `internal/config.go`.
|
1. Bump `VERSION` in `internal/config.go`.
|
||||||
2. Update `CHANGELOG.md`
|
2. Update `CHANGELOG.md`
|
||||||
3. Commit to master
|
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.
|
5. The Github action will build the binaries, upload and mark the release as ready when done.
|
||||||
|
|
||||||
|
### Brew
|
||||||
|
|
||||||
|
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
|
||||||
|
@@ -10,6 +10,13 @@
|
|||||||
Config driven, easy backup cli for <a href="https://restic.net/">restic</a>.
|
Config driven, easy backup cli for <a href="https://restic.net/">restic</a>.
|
||||||
<br>
|
<br>
|
||||||
<strong><a href="https://autorestic.vercel.app/">»»» Docs & Getting Started »»»</a></strong>
|
<strong><a href="https://autorestic.vercel.app/">»»» Docs & Getting Started »»»</a></strong>
|
||||||
|
<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>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@@ -35,7 +42,7 @@ Autorestic is a wrapper around the amazing [restic](https://restic.net/). While
|
|||||||
|
|
||||||
### ❓ Questions / Support
|
### ❓ Questions / Support
|
||||||
|
|
||||||
Check the [discussions page](https://github.com/cupcakearmy/autorestic/discussions)
|
Check the [discussions page](https://github.com/cupcakearmy/autorestic/discussions) or [join on discord](https://discord.gg/wS7RpYTYd2)
|
||||||
|
|
||||||
## Contributing / Developing
|
## Contributing / Developing
|
||||||
|
|
||||||
|
@@ -13,19 +13,18 @@ var backupCmd = &cobra.Command{
|
|||||||
Use: "backup",
|
Use: "backup",
|
||||||
Short: "Create backups for given locations",
|
Short: "Create backups for given locations",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
internal.GetConfig()
|
||||||
err := lock.Lock()
|
err := lock.Lock()
|
||||||
CheckErr(err)
|
CheckErr(err)
|
||||||
defer lock.Unlock()
|
defer lock.Unlock()
|
||||||
|
|
||||||
CheckErr(internal.CheckConfig())
|
|
||||||
|
|
||||||
selected, err := internal.GetAllOrSelected(cmd, false)
|
selected, err := internal.GetAllOrSelected(cmd, false)
|
||||||
CheckErr(err)
|
CheckErr(err)
|
||||||
errors := 0
|
errors := 0
|
||||||
for _, name := range selected {
|
for _, name := range selected {
|
||||||
location, _ := internal.GetLocation(name)
|
location, _ := internal.GetLocation(name)
|
||||||
err := location.Backup(false)
|
errs := location.Backup(false)
|
||||||
if err != nil {
|
for err := range errs {
|
||||||
colors.Error.Println(err)
|
colors.Error.Println(err)
|
||||||
errors++
|
errors++
|
||||||
}
|
}
|
||||||
|
@@ -11,12 +11,11 @@ var execCmd = &cobra.Command{
|
|||||||
Use: "exec",
|
Use: "exec",
|
||||||
Short: "Execute arbitrary native restic commands for given backends",
|
Short: "Execute arbitrary native restic commands for given backends",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
internal.GetConfig()
|
||||||
err := lock.Lock()
|
err := lock.Lock()
|
||||||
CheckErr(err)
|
CheckErr(err)
|
||||||
defer lock.Unlock()
|
defer lock.Unlock()
|
||||||
|
|
||||||
CheckErr(internal.CheckConfig())
|
|
||||||
|
|
||||||
selected, err := internal.GetAllOrSelected(cmd, true)
|
selected, err := internal.GetAllOrSelected(cmd, true)
|
||||||
CheckErr(err)
|
CheckErr(err)
|
||||||
for _, name := range selected {
|
for _, name := range selected {
|
||||||
|
@@ -10,12 +10,11 @@ var forgetCmd = &cobra.Command{
|
|||||||
Use: "forget",
|
Use: "forget",
|
||||||
Short: "Forget and optionally prune snapshots according the specified policies",
|
Short: "Forget and optionally prune snapshots according the specified policies",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
internal.GetConfig()
|
||||||
err := lock.Lock()
|
err := lock.Lock()
|
||||||
CheckErr(err)
|
CheckErr(err)
|
||||||
defer lock.Unlock()
|
defer lock.Unlock()
|
||||||
|
|
||||||
CheckErr(internal.CheckConfig())
|
|
||||||
|
|
||||||
selected, err := internal.GetAllOrSelected(cmd, false)
|
selected, err := internal.GetAllOrSelected(cmd, false)
|
||||||
CheckErr(err)
|
CheckErr(err)
|
||||||
prune, _ := cmd.Flags().GetBool("prune")
|
prune, _ := cmd.Flags().GetBool("prune")
|
||||||
|
@@ -37,6 +37,7 @@ func init() {
|
|||||||
rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "", "config file (default is $HOME/.autorestic.yml or ./.autorestic.yml)")
|
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().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().BoolVarP(&internal.VERBOSE, "verbose", "v", false, "verbose mode")
|
||||||
|
rootCmd.PersistentFlags().StringVar(&internal.RESTIC_BIN, "restic-bin", "restic", "specify custom restic binary")
|
||||||
cobra.OnInitialize(initConfig)
|
cobra.OnInitialize(initConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
[Home](/)
|
[Home](/)
|
||||||
[Quick Start](/quick)
|
[Quick Start](/quick)
|
||||||
|
[Installation](/installation)
|
||||||
[Configuration](/config)
|
[Configuration](/config)
|
||||||
[Upgrade](/upgrade)
|
[Upgrade](/upgrade)
|
||||||
|
|
||||||
@@ -7,8 +8,13 @@
|
|||||||
>
|
>
|
||||||
> [Overview](/location/overview)
|
> [Overview](/location/overview)
|
||||||
> [Hooks](/location/hooks)
|
> [Hooks](/location/hooks)
|
||||||
> [Excluding Files](/location/exclude)
|
>
|
||||||
> [Forget Policy](/location/forget)
|
> > :Collapse label=Options
|
||||||
|
> >
|
||||||
|
> > [Overview](/location/options)
|
||||||
|
> > [Excluding Files](/location/exclude)
|
||||||
|
> > [Forget Policy](/location/forget)
|
||||||
|
>
|
||||||
> [Cron](/location/cron)
|
> [Cron](/location/cron)
|
||||||
> [Docker Volumes](/location/docker)
|
> [Docker Volumes](/location/docker)
|
||||||
|
|
||||||
@@ -16,6 +22,7 @@
|
|||||||
>
|
>
|
||||||
> [Overview](/backend/overview)
|
> [Overview](/backend/overview)
|
||||||
> [Available Backends](/backend/available)
|
> [Available Backends](/backend/available)
|
||||||
|
> [Options](/backend/options)
|
||||||
|
|
||||||
> :Collapse label=CLI
|
> :Collapse label=CLI
|
||||||
>
|
>
|
||||||
@@ -30,10 +37,10 @@
|
|||||||
> [Exec](/cli/exec)
|
> [Exec](/cli/exec)
|
||||||
> [Install](/cli/install)
|
> [Install](/cli/install)
|
||||||
> [Uninstall](/cli/uninstall)
|
> [Uninstall](/cli/uninstall)
|
||||||
> [Update](/cli/update)
|
> [Upgrade](/cli/upgrade)
|
||||||
|
|
||||||
[Examples](/examples)
|
[Examples](/examples)
|
||||||
|
|
||||||
[QA](/qa)
|
[QA](/qa)
|
||||||
|
[Community](/community)
|
||||||
[Contributors](/contrib)
|
[Contributors](/contrib)
|
||||||
|
|
||||||
|
23
docs/markdown/backend/options.md
Normal file
23
docs/markdown/backend/options.md
Normal 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
|
@@ -4,7 +4,7 @@
|
|||||||
autorestic exec [-b, --backend] [-a, --all] <command> -- [native options]
|
autorestic exec [-b, --backend] [-a, --all] <command> -- [native options]
|
||||||
```
|
```
|
||||||
|
|
||||||
This is avery handy command which enables you to run any native restic command on desired backends. Generally will want to include the verbose flag `-v, --verbose` to see the output. An example would be listing all the snapshots of all your backends:
|
This is a very handy command which enables you to run any native restic command on desired backends. Generally you will want to include the verbose flag `-v, --verbose` to see the output. An example would be listing all the snapshots of all your backends:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
autorestic exec -av -- snapshots
|
autorestic exec -av -- snapshots
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
autorestic forget [-l, --location] [-a, --all] [--dry-run] [--prune]
|
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.
|
The `--dry-run` flag will do a dry run showing what would have been deleted, but won't touch the actual data.
|
||||||
|
|
||||||
|
@@ -27,4 +27,12 @@ Verbose mode will show the output of the native restic commands that are otherwi
|
|||||||
autorestic --verbose backup -a
|
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
|
> :ToCPrevNext
|
||||||
|
@@ -6,7 +6,7 @@ autorestic restore [-l, --location] [--from backend] [--to <out dir>] [-f, --for
|
|||||||
|
|
||||||
This will restore all the locations to the selected target. If for one location there are more than one backends specified autorestic will take the first one.
|
This will restore all the locations to the selected target. If for one location there are more than one backends specified autorestic will take the first one.
|
||||||
|
|
||||||
The `--to` path das to be empty as no data will be overwritten by default. If you are sure you can pass the `-f, --force` flag and the data will be overwritten in the destination. However note that this will overwrite all the data existent in the backup, not only the 1 file that is missing e.g.
|
The `--to` path has to be empty as no data will be overwritten by default. If you are sure you can pass the `-f, --force` flag and the data will be overwritten in the destination. However note that this will overwrite all the data existent in the backup, not only the 1 file that is missing e.g.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
# Uninstall
|
# Uninstall
|
||||||
|
|
||||||
Installs both restic and autorestic from `/usr/local/bin`.
|
Uninstalls both restic and autorestic from `/usr/local/bin`.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
autorestic uninstall
|
autorestic uninstall
|
||||||
|
@@ -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
|
|
11
docs/markdown/cli/upgrade.md
Normal file
11
docs/markdown/cli/upgrade.md
Normal 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
|
9
docs/markdown/community.md
Normal file
9
docs/markdown/community.md
Normal 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
|
@@ -31,12 +31,53 @@ backends:
|
|||||||
remote:
|
remote:
|
||||||
type: b2
|
type: b2
|
||||||
path: 'myBucket:backup/home'
|
path: 'myBucket:backup/home'
|
||||||
B2_ACCOUNT_ID: account_id
|
env:
|
||||||
B2_ACCOUNT_KEY: account_key
|
B2_ACCOUNT_ID: account_id
|
||||||
|
B2_ACCOUNT_KEY: account_key
|
||||||
|
|
||||||
hdd:
|
hdd:
|
||||||
type: local
|
type: local
|
||||||
path: /mnt/my_external_storage
|
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
|
> :ToCPrevNext
|
||||||
|
@@ -4,10 +4,14 @@ This amazing people helped the project!
|
|||||||
|
|
||||||
- @agateblue - Docs, Pruning, S3
|
- @agateblue - Docs, Pruning, S3
|
||||||
- @david-boles - Docs
|
- @david-boles - Docs
|
||||||
|
- @SebDanielsson - Brew
|
||||||
|
- @n194 - AUR Package
|
||||||
- @jin-park-dev - Typos
|
- @jin-park-dev - Typos
|
||||||
- @sumnerboy12 - Typos
|
- @sumnerboy12 - Typos
|
||||||
- @FuzzyMistborn - Typos
|
- @FuzzyMistborn - Typos
|
||||||
- @ChanceM - Typos
|
- @ChanceM - Typos
|
||||||
- @TheForcer - Typos
|
- @TheForcer - Typos
|
||||||
|
- @themorlan - Typos
|
||||||
|
- @somebox - Typos
|
||||||
|
|
||||||
> :ToCPrevNext
|
> :ToCPrevNext
|
||||||
|
@@ -8,7 +8,7 @@ autorestic exec -av -- snapshots
|
|||||||
|
|
||||||
## Unlock a locked repository
|
## Unlock a locked repository
|
||||||
|
|
||||||
This can come in handy if a backup process crashed or if it was accidentally cancelled. Then the repository would still be locked without an actual process using it. Only do this if you know what you are sure no other process is actually reading/writing to the repository of course.
|
This can come in handy if a backup process crashed or if it was accidentally cancelled. Then the repository would still be locked without an actual process using it. Only do this if you know what you are doing and are sure no other process is actually reading/writing to the repository of course.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
autorestic exec -b my-backend -- unlock
|
autorestic exec -b my-backend -- unlock
|
||||||
|
@@ -8,4 +8,18 @@ Autorestic requires `curl`, `wget` and `bzip2` to be installed. For most systems
|
|||||||
curl -s https://raw.githubusercontent.com/CupCakeArmy/autorestic/master/install.sh | bash
|
curl -s https://raw.githubusercontent.com/CupCakeArmy/autorestic/master/install.sh | bash
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Alternatives
|
||||||
|
|
||||||
|
### Manual
|
||||||
|
|
||||||
|
You can download the right binary from the release page and simply copy it to `/usr/local/bin` or whatever path you prefer. Autoupdates will still work.
|
||||||
|
|
||||||
|
### Brew
|
||||||
|
|
||||||
|
If you are on macOS you can install through brew: `brew install autorestic`.
|
||||||
|
|
||||||
|
### AUR
|
||||||
|
|
||||||
|
If you are on Arch there is an [AUR Package](https://aur.archlinux.org/packages/autorestic-bin/) by @n194.
|
||||||
|
|
||||||
> :ToCPrevNext
|
> :ToCPrevNext
|
||||||
|
@@ -2,7 +2,14 @@
|
|||||||
|
|
||||||
If you want to perform some commands before and/or after a backup, you can use hooks.
|
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
|
```yml | .autorestic.yml
|
||||||
locations:
|
locations:
|
||||||
@@ -11,10 +18,25 @@ locations:
|
|||||||
to: my-backend
|
to: my-backend
|
||||||
hooks:
|
hooks:
|
||||||
before:
|
before:
|
||||||
- echo "Hello"
|
- echo "One"
|
||||||
- echo "Human"
|
- echo "Two"
|
||||||
|
- echo "Three"
|
||||||
after:
|
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
|
> :ToCPrevNext
|
||||||
|
21
docs/markdown/location/options.md
Normal file
21
docs/markdown/location/options.md
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
# Options
|
||||||
|
|
||||||
|
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:
|
||||||
|
path: ...
|
||||||
|
to: ...
|
||||||
|
options:
|
||||||
|
backup:
|
||||||
|
tag:
|
||||||
|
- foo
|
||||||
|
- bar
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example, whenever `autorestic` runs `restic backup` it will append a `--tag abc --tag` to the native command.
|
||||||
|
|
||||||
|
> :ToCPrevNext
|
@@ -6,6 +6,8 @@
|
|||||||
curl -s https://raw.githubusercontent.com/CupCakeArmy/autorestic/master/install.sh | bash
|
curl -s https://raw.githubusercontent.com/CupCakeArmy/autorestic/master/install.sh | bash
|
||||||
```
|
```
|
||||||
|
|
||||||
|
See [installation](/installation) for alternative options.
|
||||||
|
|
||||||
## Write a simple config file
|
## Write a simple config file
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
# Update
|
# Upgrade
|
||||||
|
|
||||||
## From `0.x` to `1.0`
|
## From `0.x` to `1.0`
|
||||||
|
|
||||||
|
854
docs/package-lock.json
generated
854
docs/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -23,6 +23,8 @@ elif [[ $NATIVE_ARCH == *"arm64"* || $NATIVE_ARCH == *"aarch64"* ]]; then
|
|||||||
ARCH=arm64
|
ARCH=arm64
|
||||||
elif [[ $NATIVE_ARCH == *"x86"* ]]; then
|
elif [[ $NATIVE_ARCH == *"x86"* ]]; then
|
||||||
ARCH=386
|
ARCH=386
|
||||||
|
elif [[ $NATIVE_ARCH == *"armv7"* ]]; then
|
||||||
|
ARCH=arm
|
||||||
else
|
else
|
||||||
echo "Could not determine Architecure automatically, please check the release page manually: https://github.com/cupcakearmy/autorestic/releases"
|
echo "Could not determine Architecure automatically, please check the release page manually: https://github.com/cupcakearmy/autorestic/releases"
|
||||||
exit 1
|
exit 1
|
||||||
|
@@ -17,12 +17,13 @@ type BackendRest struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Backend struct {
|
type Backend struct {
|
||||||
name string
|
name string
|
||||||
Type string `yaml:"type,omitempty"`
|
Type string `yaml:"type,omitempty"`
|
||||||
Path string `yaml:"path,omitempty"`
|
Path string `yaml:"path,omitempty"`
|
||||||
Key string `yaml:"key,omitempty"`
|
Key string `yaml:"key,omitempty"`
|
||||||
Env map[string]string `yaml:"env,omitempty"`
|
Env map[string]string `yaml:"env,omitempty"`
|
||||||
Rest BackendRest `yaml:"rest,omitempty"`
|
Rest BackendRest `yaml:"rest,omitempty"`
|
||||||
|
Options Options `yaml:"options,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetBackend(name string) (Backend, bool) {
|
func GetBackend(name string) (Backend, bool) {
|
||||||
|
@@ -2,23 +2,27 @@ package internal
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/cupcakearmy/autorestic/internal/colors"
|
"github.com/cupcakearmy/autorestic/internal/colors"
|
||||||
|
"github.com/cupcakearmy/autorestic/internal/lock"
|
||||||
"github.com/mitchellh/go-homedir"
|
"github.com/mitchellh/go-homedir"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
const VERSION = "1.0.8"
|
const VERSION = "1.2.0"
|
||||||
|
|
||||||
var CI bool = false
|
var CI bool = false
|
||||||
var VERBOSE bool = false
|
var VERBOSE bool = false
|
||||||
var CRON_LEAN bool = false
|
var CRON_LEAN bool = false
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
|
Extras interface{} `yaml:"extras"`
|
||||||
Locations map[string]Location `yaml:"locations"`
|
Locations map[string]Location `yaml:"locations"`
|
||||||
Backends map[string]Backend `yaml:"backends"`
|
Backends map[string]Backend `yaml:"backends"`
|
||||||
}
|
}
|
||||||
@@ -31,7 +35,8 @@ func GetConfig() *Config {
|
|||||||
once.Do(func() {
|
once.Do(func() {
|
||||||
if err := viper.ReadInConfig(); err == nil {
|
if err := viper.ReadInConfig(); err == nil {
|
||||||
if !CRON_LEAN {
|
if !CRON_LEAN {
|
||||||
colors.Faint.Println("Using config file:", viper.ConfigFileUsed())
|
absConfig, _ := filepath.Abs(viper.ConfigFileUsed())
|
||||||
|
colors.Faint.Println("Using config file:", absConfig)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return
|
return
|
||||||
@@ -39,7 +44,9 @@ func GetConfig() *Config {
|
|||||||
|
|
||||||
config = &Config{}
|
config = &Config{}
|
||||||
if err := viper.UnmarshalExact(config); err != nil {
|
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)
|
colors.PrintDescription("Cron", l.Cron)
|
||||||
}
|
}
|
||||||
|
|
||||||
after, before := len(l.Hooks.After), len(l.Hooks.Before)
|
tmp = ""
|
||||||
if after+before > 0 {
|
hooks := map[string][]string{
|
||||||
tmp = ""
|
"Before": l.Hooks.Before,
|
||||||
if before > 0 {
|
"After": l.Hooks.After,
|
||||||
tmp += "\tBefore"
|
"Failure": l.Hooks.Failure,
|
||||||
for _, cmd := range l.Hooks.Before {
|
"Success": l.Hooks.Success,
|
||||||
tmp += colors.Faint.Sprintf("\n\t ▶ %s", cmd)
|
}
|
||||||
}
|
for hook, commands := range hooks {
|
||||||
}
|
if len(commands) > 0 {
|
||||||
if after > 0 {
|
tmp += "\n\t" + hook
|
||||||
tmp += "\n\tAfter"
|
for _, cmd := range commands {
|
||||||
for _, cmd := range l.Hooks.After {
|
|
||||||
tmp += colors.Faint.Sprintf("\n\t ▶ %s", cmd)
|
tmp += colors.Faint.Sprintf("\n\t ▶ %s", cmd)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if tmp != "" {
|
||||||
colors.PrintDescription("Hooks", tmp)
|
colors.PrintDescription("Hooks", tmp)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,7 +137,7 @@ func CheckConfig() error {
|
|||||||
return fmt.Errorf("config could not be loaded/found")
|
return fmt.Errorf("config could not be loaded/found")
|
||||||
}
|
}
|
||||||
if !CheckIfResticIsCallable() {
|
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 {
|
for name, backend := range c.Backends {
|
||||||
backend.name = name
|
backend.name = name
|
||||||
@@ -139,7 +147,7 @@ func CheckConfig() error {
|
|||||||
}
|
}
|
||||||
for name, location := range c.Locations {
|
for name, location := range c.Locations {
|
||||||
location.name = name
|
location.name = name
|
||||||
if err := location.validate(c); err != nil {
|
if err := location.validate(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -219,3 +227,13 @@ func (c *Config) SaveConfig() error {
|
|||||||
|
|
||||||
return viper.WriteConfig()
|
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
|
||||||
|
}
|
||||||
|
@@ -24,8 +24,10 @@ const (
|
|||||||
type HookArray = []string
|
type HookArray = []string
|
||||||
|
|
||||||
type Hooks struct {
|
type Hooks struct {
|
||||||
Before HookArray `yaml:"before"`
|
Before HookArray `yaml:"before,omitempty"`
|
||||||
After HookArray `yaml:"after"`
|
After HookArray `yaml:"after,omitempty"`
|
||||||
|
Success HookArray `yaml:"success,omitempty"`
|
||||||
|
Failure HookArray `yaml:"failure,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Options map[string]map[string][]string
|
type Options map[string]map[string][]string
|
||||||
@@ -45,18 +47,20 @@ func GetLocation(name string) (Location, bool) {
|
|||||||
return l, ok
|
return l, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l Location) validate(c *Config) error {
|
func (l Location) validate() error {
|
||||||
if l.From == "" {
|
if l.From == "" {
|
||||||
return fmt.Errorf(`Location "%s" is missing "from" key`, l.name)
|
return fmt.Errorf(`Location "%s" is missing "from" key`, l.name)
|
||||||
}
|
}
|
||||||
if from, err := GetPathRelativeToConfig(l.From); err != nil {
|
if l.getType() == TypeLocal {
|
||||||
return err
|
if from, err := GetPathRelativeToConfig(l.From); err != nil {
|
||||||
} else {
|
|
||||||
if stat, err := os.Stat(from); err != nil {
|
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
if !stat.IsDir() {
|
if stat, err := os.Stat(from); err != nil {
|
||||||
return fmt.Errorf("\"%s\" is not valid directory for location \"%s\"", from, l.name)
|
return err
|
||||||
|
} else {
|
||||||
|
if !stat.IsDir() {
|
||||||
|
return fmt.Errorf("\"%s\" is not valid directory for location \"%s\"", from, l.name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -74,17 +78,6 @@ func (l Location) validate(c *Config) error {
|
|||||||
return nil
|
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 {
|
func ExecuteHooks(commands []string, options ExecuteOptions) error {
|
||||||
if len(commands) == 0 {
|
if len(commands) == 0 {
|
||||||
return nil
|
return nil
|
||||||
@@ -131,21 +124,30 @@ func (l Location) getPath() (string, error) {
|
|||||||
return "", fmt.Errorf("could not get path for location \"%s\"", l.name)
|
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)
|
colors.PrimaryPrint(" Backing up location \"%s\" ", l.name)
|
||||||
t := l.getType()
|
t := l.getType()
|
||||||
options := ExecuteOptions{
|
options := ExecuteOptions{
|
||||||
Command: "bash",
|
Command: "bash",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := l.validate(); err != nil {
|
||||||
|
errors = append(errors, err)
|
||||||
|
colors.Error.Print(err)
|
||||||
|
goto after
|
||||||
|
}
|
||||||
|
|
||||||
if t == TypeLocal {
|
if t == TypeLocal {
|
||||||
dir, _ := GetPathRelativeToConfig(l.From)
|
dir, _ := GetPathRelativeToConfig(l.From)
|
||||||
|
colors.Faint.Printf("Executing under: \"%s\"\n", dir)
|
||||||
options.Dir = dir
|
options.Dir = dir
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hooks
|
// Hooks
|
||||||
if err := ExecuteHooks(l.Hooks.Before, options); err != nil {
|
if err := ExecuteHooks(l.Hooks.Before, options); err != nil {
|
||||||
return err
|
errors = append(errors, err)
|
||||||
|
goto after
|
||||||
}
|
}
|
||||||
|
|
||||||
// Backup
|
// Backup
|
||||||
@@ -154,12 +156,15 @@ func (l Location) Backup(cron bool) error {
|
|||||||
colors.Secondary.Printf("Backend: %s\n", backend.name)
|
colors.Secondary.Printf("Backend: %s\n", backend.name)
|
||||||
env, err := backend.getEnv()
|
env, err := backend.getEnv()
|
||||||
if err != nil {
|
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 := []string{"backup"}
|
||||||
cmd = append(cmd, flags...)
|
cmd = append(cmd, lFlags...)
|
||||||
|
cmd = append(cmd, bFlags...)
|
||||||
if cron {
|
if cron {
|
||||||
cmd = append(cmd, "--tag", "cron")
|
cmd = append(cmd, "--tag", "cron")
|
||||||
}
|
}
|
||||||
@@ -179,7 +184,8 @@ func (l Location) Backup(cron bool) error {
|
|||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
colors.Error.Println(out)
|
colors.Error.Println(out)
|
||||||
return err
|
errors = append(errors, err)
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
if VERBOSE {
|
if VERBOSE {
|
||||||
colors.Faint.Println(out)
|
colors.Faint.Println(out)
|
||||||
@@ -188,10 +194,22 @@ func (l Location) Backup(cron bool) error {
|
|||||||
|
|
||||||
// After hooks
|
// After hooks
|
||||||
if err := ExecuteHooks(l.Hooks.After, options); err != nil {
|
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")
|
colors.Success.Println("Done")
|
||||||
return nil
|
return errors
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l Location) Forget(prune bool, dry bool) error {
|
func (l Location) Forget(prune bool, dry bool) error {
|
||||||
@@ -212,7 +230,8 @@ func (l Location) Forget(prune bool, dry bool) error {
|
|||||||
options := ExecuteOptions{
|
options := ExecuteOptions{
|
||||||
Envs: env,
|
Envs: env,
|
||||||
}
|
}
|
||||||
flags := l.getOptions("forget")
|
lFlags := getOptions(l.Options, "forget")
|
||||||
|
bFlags := getOptions(backend.Options, "forget")
|
||||||
cmd := []string{"forget", "--path", path}
|
cmd := []string{"forget", "--path", path}
|
||||||
if prune {
|
if prune {
|
||||||
cmd = append(cmd, "--prune")
|
cmd = append(cmd, "--prune")
|
||||||
@@ -220,7 +239,8 @@ func (l Location) Forget(prune bool, dry bool) error {
|
|||||||
if dry {
|
if dry {
|
||||||
cmd = append(cmd, "--dry-run")
|
cmd = append(cmd, "--dry-run")
|
||||||
}
|
}
|
||||||
cmd = append(cmd, flags...)
|
cmd = append(cmd, lFlags...)
|
||||||
|
cmd = append(cmd, bFlags...)
|
||||||
out, err := ExecuteResticCommand(options, cmd...)
|
out, err := ExecuteResticCommand(options, cmd...)
|
||||||
if VERBOSE {
|
if VERBOSE {
|
||||||
colors.Faint.Println(out)
|
colors.Faint.Println(out)
|
||||||
|
@@ -10,13 +10,15 @@ import (
|
|||||||
"github.com/cupcakearmy/autorestic/internal/colors"
|
"github.com/cupcakearmy/autorestic/internal/colors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var RESTIC_BIN string
|
||||||
|
|
||||||
func CheckIfCommandIsCallable(cmd string) bool {
|
func CheckIfCommandIsCallable(cmd string) bool {
|
||||||
_, err := exec.LookPath(cmd)
|
_, err := exec.LookPath(cmd)
|
||||||
return err == nil
|
return err == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func CheckIfResticIsCallable() bool {
|
func CheckIfResticIsCallable() bool {
|
||||||
return CheckIfCommandIsCallable("restic")
|
return CheckIfCommandIsCallable(RESTIC_BIN)
|
||||||
}
|
}
|
||||||
|
|
||||||
type ExecuteOptions struct {
|
type ExecuteOptions struct {
|
||||||
@@ -50,7 +52,7 @@ func ExecuteCommand(options ExecuteOptions, args ...string) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ExecuteResticCommand(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...)
|
return ExecuteCommand(options, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user