mirror of
https://github.com/cupcakearmy/autorestic.git
synced 2025-09-06 02:20:39 +00:00
Compare commits
19 Commits
Author | SHA1 | Date | |
---|---|---|---|
fd2fd91635 | |||
9c09ce1d79 | |||
c2f6f51789 | |||
|
f09cf90653 | ||
|
d352aced37 | ||
|
563d4ffb96 | ||
|
1c6a061dd1 | ||
|
504ad639ab | ||
f7a15c6d86 | |||
|
2f0092befe | ||
|
1026e68b68 | ||
2389c59aa9 | |||
087aeaf578 | |||
3b7062f733 | |||
|
96b63c744b | ||
|
9669b70e20 | ||
|
bcb081234c | ||
|
336f44e9dc | ||
|
d0cda7f1d5 |
170
README.md
170
README.md
@@ -14,7 +14,17 @@ Autorestic is a wrapper around the amazing [restic](https://restic.net/). While
|
||||
- Simple interface
|
||||
- Fully encrypted
|
||||
|
||||
## Installation
|
||||
###### 📒 Docs
|
||||
|
||||
- [Locations](#-locations)
|
||||
- [Pruning & Deleting old files](#pruning-and-snapshot-policies)
|
||||
- [Excluding files](#excluding-filesfolders)
|
||||
- [Hooks](#before--after-hooks)
|
||||
- [Backends](#-backends)
|
||||
|
||||
## 🛳 Installation
|
||||
|
||||
Linux & macOS. Windows is not supported.
|
||||
|
||||
```
|
||||
curl -s https://raw.githubusercontent.com/CupCakeArmy/autorestic/master/install.sh | bash
|
||||
@@ -54,21 +64,19 @@ backends:
|
||||
|
||||
Then we check if everything is correct by running the `check` command. We will pass the `-a` (or `--all`) to tell autorestic to check all the locations.
|
||||
|
||||
Lets see a more realistic example (from the config above)
|
||||
If we would check only one location we could run the following: `autorestic check -l home`. Otherwise simpply check all locations with `autorestic check -a`
|
||||
|
||||
```
|
||||
autorestic check -l important
|
||||
```
|
||||
##### Note
|
||||
|
||||
If we would check only one location we could run the following: `autorestic -l home check`.
|
||||
Note that the data is automatically encrypted on the server. The key will be generated and added to your config file. Every backend will have a separate key. You should keep a copy of the keys somewhere in case your server dies. Otherwise DATA IS LOST!
|
||||
|
||||
### Backup
|
||||
### 📦 Backup
|
||||
|
||||
```
|
||||
autorestic backup -a
|
||||
```
|
||||
|
||||
### Restore
|
||||
### 📼 Restore
|
||||
|
||||
```
|
||||
autorestic restore -a --to /path/where/to/restore
|
||||
@@ -76,12 +84,16 @@ autorestic restore -a --to /path/where/to/restore
|
||||
|
||||
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.
|
||||
|
||||
Lets see a more realistic example (from the config above)
|
||||
```
|
||||
autorestic restore -l home --from hdd --to /path/where/to/restore
|
||||
```
|
||||
|
||||
This will restore the location `home` to the `/path/where/to/restore` folder and taking the data from the backend `hdd`
|
||||
|
||||
### 📲 Updates
|
||||
|
||||
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.
|
||||
|
||||
## 🗂 Locations
|
||||
|
||||
@@ -96,6 +108,95 @@ locations:
|
||||
- also-backup-to-this-backend
|
||||
```
|
||||
|
||||
#### Pruning and snapshot policies
|
||||
|
||||
Autorestic supports declaring snapshot policies for location to avoid keeping old snapshot around if you don't need them.
|
||||
|
||||
This is based on [Restic's snapshots policies](https://restic.readthedocs.io/en/latest/060_forget.html#removing-snapshots-according-to-a-policy), and can be enabled for each location as shown below:
|
||||
|
||||
```yaml
|
||||
locations:
|
||||
etc:
|
||||
from: /etc
|
||||
to: local
|
||||
options:
|
||||
forget:
|
||||
keep-last: 5 # always keep at least 5 snapshots
|
||||
keep-hourly: 3 # keep 3 last hourly shapshots
|
||||
keep-daily: 4 # keep 4 last daily shapshots
|
||||
keep-weekly: 1 # keep 1 last weekly shapshots
|
||||
keep-monthly: 12 # keep 12 last monthly shapshots
|
||||
keep-yearly: 7 # keep 7 last yearly shapshots
|
||||
keep-within: "2w" # keep snapshots from the last 2 weeks
|
||||
```
|
||||
|
||||
Pruning can be triggered using `autorestic forget -a`, for all locations, or selectively with `autorestic forget -l <location>`. **please note that contrary to the restic CLI, `restic forget` will call `restic prune` internally.**
|
||||
|
||||
Run with the `--dry-run` flag to only print information about the process without actually pruning the snapshots. This is especially useful for debugging or testing policies:
|
||||
```
|
||||
$ autorestic forget -a --dry-run --verbose
|
||||
|
||||
Configuring Backends
|
||||
local : Done ✓
|
||||
|
||||
Removing old shapshots according to policy
|
||||
etc ▶ local : Removing old spnapshots… ⏳
|
||||
etc ▶ local : Running in dry-run mode, not touching data
|
||||
etc ▶ local : Forgeting old snapshots… ⏳Applying Policy: all snapshots within 2d of the newest
|
||||
keep 3 snapshots:
|
||||
ID Time Host Tags Reasons Paths
|
||||
-----------------------------------------------------------------------------
|
||||
531b692a 2019-12-02 12:07:28 computer within 2w /etc
|
||||
51659674 2019-12-02 12:08:46 computer within 2w /etc
|
||||
f8f8f976 2019-12-02 12:11:08 computer within 2w /etc
|
||||
-----------------------------------------------------------------------------
|
||||
3 snapshots
|
||||
```
|
||||
|
||||
#### Excluding files/folders
|
||||
|
||||
If you want to exclude certain files or folders it done easily by specifiyng the right flags in the location you desire to filter. The flags are taken straight from the [restic cli exclude rules](https://restic.readthedocs.io/en/latest/040_backup.html#excluding-files).
|
||||
|
||||
```yaml
|
||||
locations:
|
||||
my-location:
|
||||
from: /data
|
||||
to:
|
||||
- local
|
||||
- remote
|
||||
options:
|
||||
backup:
|
||||
exclude:
|
||||
- '*.nope'
|
||||
- '*.abc'
|
||||
exclude-file: .gitignore
|
||||
|
||||
backends:
|
||||
local:
|
||||
...
|
||||
remote:
|
||||
...
|
||||
```
|
||||
|
||||
#### Before / After hooks
|
||||
|
||||
Sometimes you might want to stop an app/db before backing up data and start the service again after the backup has completed. This is what the hooks are made for. Simply add them to your location config. You can have as many commands as you wish.
|
||||
|
||||
```yaml
|
||||
locations:
|
||||
my-location:
|
||||
from: /data
|
||||
to:
|
||||
- local
|
||||
- remote
|
||||
hooks:
|
||||
before:
|
||||
- echo "Hello"
|
||||
- echo "Human"
|
||||
after:
|
||||
- echo "kthxbye"
|
||||
```
|
||||
|
||||
## 💽 Backends
|
||||
|
||||
Backends are the place where you data will be saved. Backups are incremental and encrypted.
|
||||
@@ -132,59 +233,6 @@ backends:
|
||||
B2_ACCOUNT_KEY: backblaze_account_key
|
||||
```
|
||||
|
||||
## Pruning and snapshot policies
|
||||
|
||||
Autorestic supports declaring snapshot policies for location to avoid keeping old snapshot around if you don't need them.
|
||||
|
||||
This is based on [Restic's snapshots policies](https://restic.readthedocs.io/en/latest/060_forget.html#removing-snapshots-according-to-a-policy), and can be enabled for each location as shown below:
|
||||
|
||||
```yaml
|
||||
locations:
|
||||
etc:
|
||||
from: /etc
|
||||
to: local
|
||||
keep:
|
||||
# options matches the --keep-* options used in the restic forget CLI
|
||||
# cf https://restic.readthedocs.io/en/latest/060_forget.html#removing-snapshots-according-to-a-policy
|
||||
last: 5 # always keep at least 5 snapshots
|
||||
hourly: 3 # keep 3 last hourly shapshots
|
||||
daily: 4 # keep 4 last daily shapshots
|
||||
weekly: 1 # keep 1 last weekly shapshots
|
||||
monthly: 12 # keep 12 last monthly shapshots
|
||||
yearly: 7 # keep 7 last yearly shapshots
|
||||
within: "2w" # keep snapshots from the last 2 weeks
|
||||
```
|
||||
|
||||
Pruning can be triggered using `autorestic forget -a`, for all locations, or selectively with `autorestic forget -l <location>`. **please note that contrary to the restic CLI, `restic forget` will call `restic prune` internally.**
|
||||
|
||||
|
||||
|
||||
Run with the `--dry-run` flag to only print information about the process without actually pruning the snapshots. This is especially useful for debugging or testing policies:
|
||||
|
||||
```
|
||||
$ autorestic forget -a --dry-run --verbose
|
||||
|
||||
Configuring Backends
|
||||
local : Done ✓
|
||||
|
||||
Removing old shapshots according to policy
|
||||
etc ▶ local : Removing old spnapshots… ⏳
|
||||
etc ▶ local : Running in dry-run mode, not touching data
|
||||
etc ▶ local : Forgeting old snapshots… ⏳Applying Policy: all snapshots within 2d of the newest
|
||||
keep 3 snapshots:
|
||||
ID Time Host Tags Reasons Paths
|
||||
-----------------------------------------------------------------------------
|
||||
531b692a 2019-12-02 12:07:28 computer within 2w /etc
|
||||
51659674 2019-12-02 12:08:46 computer within 2w /etc
|
||||
f8f8f976 2019-12-02 12:11:08 computer within 2w /etc
|
||||
-----------------------------------------------------------------------------
|
||||
3 snapshots
|
||||
```
|
||||
|
||||
##### Note
|
||||
|
||||
Note that the data is automatically encrypted on the server. The key will be generated and added to your config file. Every backend will have a separate key. You should keep a copy of the keys somewhere in case your server dies. Otherwise DATA IS LOST!
|
||||
|
||||
## Contributors
|
||||
|
||||
This amazing people helped the project!
|
||||
|
@@ -4,7 +4,7 @@
|
||||
"build": "tsc",
|
||||
"build:watch": "tsc -w",
|
||||
"dev": "tsnd --no-notify --respawn ./src/autorestic.ts",
|
||||
"bin": "npm run build && pkg lib/autorestic.js --targets latest-macos-x64,latest-linux-x64 --out-path bin"
|
||||
"bin": "yarn run build && pkg lib/autorestic.js --targets latest-macos-x64,latest-linux-x64 --out-path bin"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/js-yaml": "^3.12.1",
|
||||
@@ -12,7 +12,7 @@
|
||||
"@types/node": "^12.11.7",
|
||||
"pkg": "^4.4.0",
|
||||
"ts-node-dev": "^1.0.0-pre.40",
|
||||
"typescript": "^3.5.1"
|
||||
"typescript": "^3.7"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^0.19.0",
|
||||
|
@@ -3,7 +3,6 @@ import minimist from 'minimist'
|
||||
|
||||
import { init } from './config'
|
||||
import handlers, { error, help } from './handlers'
|
||||
import { Config } from './types'
|
||||
|
||||
|
||||
|
||||
@@ -26,7 +25,7 @@ export const { _: commands, ...flags } = minimist(process.argv.slice(2), {
|
||||
string: ['l', 'b'],
|
||||
})
|
||||
|
||||
export const VERSION = '0.7'
|
||||
export const VERSION = '0.9'
|
||||
export const INSTALL_DIR = '/usr/local/bin'
|
||||
export const VERBOSE = flags.verbose
|
||||
|
||||
|
@@ -3,15 +3,15 @@ import { Writer } from 'clitastic'
|
||||
import { config, VERBOSE } from './autorestic'
|
||||
import { getEnvFromBackend } from './backend'
|
||||
import { Locations, Location } from './types'
|
||||
import { exec, ConfigError, pathRelativeToConfigFile, getFlagsFromLocation } from './utils'
|
||||
import { exec, ConfigError, pathRelativeToConfigFile, getFlagsFromLocation, makeArrayIfIsNot, execPlain } from './utils'
|
||||
|
||||
|
||||
|
||||
export const backupSingle = (name: string, to: string, location: Location) => {
|
||||
if (!config) throw ConfigError
|
||||
const writer = new Writer(name + to.blue + ' : ' + 'Backing up... ⏳')
|
||||
const backend = config.backends[to]
|
||||
|
||||
const backend = config.backends[to]
|
||||
const path = pathRelativeToConfigFile(location.from)
|
||||
|
||||
const cmd = exec(
|
||||
@@ -27,13 +27,24 @@ export const backupSingle = (name: string, to: string, location: Location) => {
|
||||
export const backupLocation = (name: string, location: Location) => {
|
||||
const display = name.yellow + ' ▶ '
|
||||
const filler = new Array(name.length + 3).fill(' ').join('')
|
||||
if (Array.isArray(location.to)) {
|
||||
let first = true
|
||||
for (const t of location.to) {
|
||||
backupSingle(first ? display : filler, t, location)
|
||||
if (first) first = false
|
||||
let first = true
|
||||
|
||||
if (location.hooks && location.hooks.before)
|
||||
for (const command of makeArrayIfIsNot(location.hooks.before)) {
|
||||
const cmd = execPlain(command)
|
||||
if (cmd) console.log(cmd.out, cmd.err)
|
||||
}
|
||||
|
||||
for (const t of makeArrayIfIsNot(location.to)) {
|
||||
backupSingle(first ? display : filler, t, location)
|
||||
if (first) first = false
|
||||
}
|
||||
|
||||
if (location.hooks && location.hooks.after)
|
||||
for (const command of makeArrayIfIsNot(location.hooks.after)) {
|
||||
const cmd = execPlain(command)
|
||||
if (cmd) console.log(cmd.out, cmd.err)
|
||||
}
|
||||
} else backupSingle(display, location.from, location)
|
||||
}
|
||||
|
||||
export const backupAll = (locations?: Locations) => {
|
||||
|
@@ -3,7 +3,7 @@ import { resolve } from 'path'
|
||||
import yaml from 'js-yaml'
|
||||
import { flags } from './autorestic'
|
||||
import { Backend, Config } from './types'
|
||||
import { makeObjectKeysLowercase, rand } from './utils'
|
||||
import { makeArrayIfIsNot, makeObjectKeysLowercase, rand } from './utils'
|
||||
import { homedir } from 'os'
|
||||
|
||||
|
||||
@@ -48,8 +48,8 @@ export const normalizeAndCheckBackups = (config: Config) => {
|
||||
`The backup "${name}" is missing some required attributes`,
|
||||
)
|
||||
|
||||
if (Array.isArray(to)) for (const t of to) checkDestination(t, name)
|
||||
else checkDestination(to, name)
|
||||
for (const t of makeArrayIfIsNot(to))
|
||||
checkDestination(t, name)
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -2,46 +2,46 @@ import { Writer } from 'clitastic'
|
||||
|
||||
import { config, VERBOSE } from './autorestic'
|
||||
import { getEnvFromBackend } from './backend'
|
||||
import { Locations, Location, ForgetPolicy, Flags } from './types'
|
||||
import { exec, ConfigError } from './utils'
|
||||
import { Locations, Location, Flags } from './types'
|
||||
import { exec, ConfigError, pathRelativeToConfigFile, getFlagsFromLocation, makeArrayIfIsNot } from './utils'
|
||||
|
||||
|
||||
|
||||
export const forgetSingle = (dryRun: boolean, name: string, from: string, to: string, policy: ForgetPolicy) => {
|
||||
export const forgetSingle = (name: string, to: string, location: Location, dryRun: boolean) => {
|
||||
if (!config) throw ConfigError
|
||||
const writer = new Writer(name + to.blue + ' : ' + 'Removing old spnapshots… ⏳')
|
||||
const base = name + to.blue + ' : '
|
||||
const writer = new Writer(base + 'Removing old snapshots… ⏳')
|
||||
|
||||
const backend = config.backends[to]
|
||||
const flags = [] as any[]
|
||||
for (const [name, value] of Object.entries(policy)) {
|
||||
flags.push(`--keep-${name}`)
|
||||
flags.push(value)
|
||||
const path = pathRelativeToConfigFile(location.from)
|
||||
const flags = getFlagsFromLocation(location, 'forget')
|
||||
|
||||
if (flags.length == 0) {
|
||||
writer.done(base + 'skipping, no policy declared')
|
||||
return
|
||||
}
|
||||
if (dryRun) {
|
||||
flags.push('--dry-run')
|
||||
}
|
||||
const env = getEnvFromBackend(backend)
|
||||
writer.replaceLn(name + to.blue + ' : ' + 'Forgeting old snapshots… ⏳')
|
||||
const cmd = exec('restic', ['forget', '--path', from, '--prune', ...flags], { env })
|
||||
if (dryRun) flags.push('--dry-run')
|
||||
|
||||
writer.replaceLn(base + 'Forgetting old snapshots… ⏳')
|
||||
const cmd = exec(
|
||||
'restic',
|
||||
['forget', '--path', path, '--prune', ...flags],
|
||||
{ env: getEnvFromBackend(backend) },
|
||||
)
|
||||
|
||||
if (VERBOSE) console.log(cmd.out, cmd.err)
|
||||
writer.done(name + to.blue + ' : ' + 'Done ✓'.green)
|
||||
writer.done(base + 'Done ✓'.green)
|
||||
}
|
||||
|
||||
export const forgetLocation = (dryRun: boolean, name: string, backup: Location, policy?: ForgetPolicy) => {
|
||||
export const forgetLocation = (name: string, backup: Location, dryRun: boolean) => {
|
||||
const display = name.yellow + ' ▶ '
|
||||
if (!policy) {
|
||||
console.log(display + 'skipping, no policy declared')
|
||||
} else {
|
||||
if (Array.isArray(backup.to)) {
|
||||
let first = true
|
||||
for (const t of backup.to) {
|
||||
const nameOrBlankSpaces: string = first
|
||||
? display
|
||||
: new Array(name.length + 3).fill(' ').join('')
|
||||
forgetSingle(dryRun, nameOrBlankSpaces, backup.from, t, policy)
|
||||
if (first) first = false
|
||||
}
|
||||
} else forgetSingle(dryRun, display, backup.from, backup.to, policy)
|
||||
const filler = new Array(name.length + 3).fill(' ').join('')
|
||||
let first = true
|
||||
|
||||
for (const t of makeArrayIfIsNot(backup.to)) {
|
||||
const nameOrBlankSpaces: string = first ? display : filler
|
||||
forgetSingle(nameOrBlankSpaces, t, backup, dryRun)
|
||||
if (first) first = false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,12 +51,10 @@ export const forgetAll = (backups?: Locations, flags?: Flags) => {
|
||||
backups = config.locations
|
||||
}
|
||||
|
||||
console.log('\nRemoving old shapshots according to policy'.underline.grey)
|
||||
console.log('\nRemoving old snapshots according to policy'.underline.grey)
|
||||
const dryRun = flags ? flags['dry-run'] : false
|
||||
if (dryRun) console.log('Running in dry-run mode, not touching data\n'.yellow)
|
||||
|
||||
for (const [name, backup] of Object.entries(backups)) {
|
||||
const policy = config.locations[name].keep
|
||||
forgetLocation(dryRun, name, backup, policy)
|
||||
}
|
||||
for (const [name, backup] of Object.entries(backups))
|
||||
forgetLocation(name, backup, dryRun)
|
||||
}
|
||||
|
@@ -8,15 +8,14 @@ import { config, INSTALL_DIR, VERSION } from './autorestic'
|
||||
import { checkAndConfigureBackends, getBackendsFromLocations, getEnvFromBackend } from './backend'
|
||||
import { backupAll } from './backup'
|
||||
import { forgetAll } from './forget'
|
||||
import { Backend, Backends, Flags, Locations } from './types'
|
||||
import { Backends, Flags, Locations } from './types'
|
||||
import {
|
||||
checkIfCommandIsAvailable,
|
||||
checkIfResticIsAvailable,
|
||||
downloadFile,
|
||||
exec,
|
||||
filterObjectByKey,
|
||||
singleToArray,
|
||||
ConfigError,
|
||||
ConfigError, makeArrayIfIsNot,
|
||||
} from './utils'
|
||||
|
||||
|
||||
@@ -35,7 +34,7 @@ const parseBackend = (flags: Flags): Backends => {
|
||||
)
|
||||
if (flags.all) return config.backends
|
||||
else {
|
||||
const backends = singleToArray<string>(flags.backend)
|
||||
const backends = makeArrayIfIsNot<string>(flags.backend)
|
||||
for (const backend of backends)
|
||||
if (!config.backends[backend])
|
||||
throw new Error('Invalid backend: '.red + backend)
|
||||
@@ -55,7 +54,7 @@ const parseLocations = (flags: Flags): Locations => {
|
||||
if (flags.all) {
|
||||
return config.locations
|
||||
} else {
|
||||
const locations = singleToArray<string>(flags.location)
|
||||
const locations = makeArrayIfIsNot<string>(flags.location)
|
||||
for (const location of locations)
|
||||
if (!config.locations[location])
|
||||
throw new Error('Invalid location: '.red + location)
|
||||
@@ -215,8 +214,7 @@ const handlers: Handlers = {
|
||||
darwin: 'macos',
|
||||
}
|
||||
|
||||
const name = `autorestic_${platformMap[process.platform] ||
|
||||
process.platform}_${process.arch}`
|
||||
const name = `autorestic_${platformMap[process.platform] || process.platform}_${process.arch}`
|
||||
const dl = json.assets.find((asset: any) => asset.name === name)
|
||||
|
||||
const to = INSTALL_DIR + '/autorestic'
|
||||
|
26
src/types.ts
26
src/types.ts
@@ -1,3 +1,7 @@
|
||||
export type StringOrArray = string | string[]
|
||||
|
||||
// BACKENDS
|
||||
|
||||
type BackendLocal = {
|
||||
type: 'local'
|
||||
key: string
|
||||
@@ -62,30 +66,26 @@ export type Backend =
|
||||
|
||||
export type Backends = { [name: string]: Backend }
|
||||
|
||||
export type ForgetPolicy = {
|
||||
last?: number,
|
||||
hourly?: number,
|
||||
daily?: number,
|
||||
weekly?: number,
|
||||
monthly?: number,
|
||||
yearly?: number,
|
||||
within?: string,
|
||||
tags?: string[],
|
||||
}
|
||||
// LOCATIONS
|
||||
|
||||
export type Location = {
|
||||
from: string
|
||||
to: string | string[]
|
||||
keep?: ForgetPolicy
|
||||
to: StringOrArray
|
||||
hooks?: {
|
||||
before?: StringOrArray
|
||||
after?: StringOrArray
|
||||
}
|
||||
options?: {
|
||||
[key: string]: {
|
||||
[key: string]: string | string[]
|
||||
[key: string]: StringOrArray
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export type Locations = { [name: string]: Location }
|
||||
|
||||
// OTHER
|
||||
|
||||
export type Config = {
|
||||
locations: Locations
|
||||
backends: Backends
|
||||
|
26
src/utils.ts
26
src/utils.ts
@@ -2,7 +2,7 @@ import axios from 'axios'
|
||||
import { spawnSync, SpawnSyncOptions } from 'child_process'
|
||||
import { randomBytes } from 'crypto'
|
||||
import { createWriteStream } from 'fs'
|
||||
import { isAbsolute, resolve, dirname } from 'path'
|
||||
import { dirname, isAbsolute, resolve } from 'path'
|
||||
import { CONFIG_FILE } from './config'
|
||||
import { Location } from './types'
|
||||
|
||||
@@ -27,6 +27,16 @@ export const exec = (
|
||||
return { out, err }
|
||||
}
|
||||
|
||||
export const execPlain = (command: string, opt: SpawnSyncOptions = {}) => {
|
||||
const split = command.split(' ')
|
||||
if (split.length < 1) {
|
||||
console.log(`The command ${command} is not valid`.red)
|
||||
return
|
||||
}
|
||||
|
||||
return exec(split[0], split.slice(1), opt)
|
||||
}
|
||||
|
||||
export const checkIfResticIsAvailable = () =>
|
||||
checkIfCommandIsAvailable(
|
||||
'restic',
|
||||
@@ -50,9 +60,6 @@ export function rand(length = 32): string {
|
||||
}
|
||||
|
||||
|
||||
export const singleToArray = <T>(singleOrArray: T | T[]): T[] =>
|
||||
Array.isArray(singleOrArray) ? singleOrArray : [singleOrArray]
|
||||
|
||||
export const filterObject = <T>(
|
||||
obj: { [key: string]: T },
|
||||
filter: (item: [string, T]) => boolean,
|
||||
@@ -98,12 +105,11 @@ export const getFlagsFromLocation = (location: Location, command?: string): stri
|
||||
|
||||
let flags: string[] = []
|
||||
// Map the flags to an array for the exec function.
|
||||
for (let [flag, values] of Object.entries(all)) {
|
||||
if (!Array.isArray(values))
|
||||
values = [values]
|
||||
for (let [flag, values] of Object.entries(all))
|
||||
for (const value of makeArrayIfIsNot(values))
|
||||
flags = [...flags, `--${String(flag)}`, String(value)]
|
||||
|
||||
for (const value of values)
|
||||
flags = [...flags, `--${flag}`, value]
|
||||
}
|
||||
return flags
|
||||
}
|
||||
|
||||
export const makeArrayIfIsNot = <T>(maybeArray: T | T[]): T[] => Array.isArray(maybeArray) ? maybeArray : [maybeArray]
|
||||
|
Reference in New Issue
Block a user