mirror of
https://github.com/cupcakearmy/autorestic.git
synced 2025-09-06 10:30:39 +00:00
Compare commits
11 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
6a17444c4c | ||
|
9538881f9f | ||
|
edddcebcea | ||
|
793ab1c6fe | ||
|
9832eeab22 | ||
|
b1f2678dc1 | ||
|
64f2eaf16b | ||
|
b1b12f4592 | ||
|
24a364ce08 | ||
|
d59362e82c | ||
|
0802dedb47 |
@@ -1,2 +1,9 @@
|
||||
# autorestic
|
||||
High level CLI utility for restic
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
```
|
||||
curl -s https://raw.githubusercontent.com/CupCakeArmy/autorestic/master/install.sh | sh
|
||||
```
|
||||
|
22
install.sh
Executable file
22
install.sh
Executable file
@@ -0,0 +1,22 @@
|
||||
#!/bin/sh
|
||||
|
||||
OUT_FILE=/usr/local/bin/autorestic
|
||||
|
||||
if [[ "$OSTYPE" == "linux-gnu" ]]; then
|
||||
TYPE=linux
|
||||
elif [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
TYPE=macos
|
||||
else
|
||||
echo "Unsupported OS"
|
||||
exit
|
||||
fi
|
||||
|
||||
curl -s https://api.github.com/repos/cupcakearmy/autorestic/releases/latest \
|
||||
| grep "browser_download_url.*_${TYPE}" \
|
||||
| cut -d : -f 2,3 \
|
||||
| tr -d \" \
|
||||
| wget -O ${OUT_FILE} -i -
|
||||
chmod +x ${OUT_FILE}
|
||||
|
||||
autorestic install
|
||||
autorestic
|
@@ -1,5 +1,6 @@
|
||||
import 'colors'
|
||||
import minimist from 'minimist'
|
||||
import { homedir } from 'os'
|
||||
import { resolve } from 'path'
|
||||
|
||||
import { init } from './config'
|
||||
@@ -25,17 +26,25 @@ export const { _: commands, ...flags } = minimist(process.argv.slice(2), {
|
||||
string: ['l', 'b'],
|
||||
})
|
||||
|
||||
export const DEFAULT_CONFIG = 'config.yml'
|
||||
export const VERSION = '0.4'
|
||||
export const DEFAULT_CONFIG = '/.autorestic.yml'
|
||||
export const INSTALL_DIR = '/usr/local/bin'
|
||||
export const CONFIG_FILE: string = resolve(flags.config || DEFAULT_CONFIG)
|
||||
export const CONFIG_FILE: string = resolve(flags.config || homedir() + DEFAULT_CONFIG)
|
||||
export const VERBOSE = flags.verbose
|
||||
|
||||
export const config: Config = init()
|
||||
|
||||
if (commands.length < 1)
|
||||
help()
|
||||
else {
|
||||
function main() {
|
||||
if (flags.version)
|
||||
return console.log('version'.grey, VERSION)
|
||||
|
||||
if (commands.length < 1)
|
||||
return help()
|
||||
|
||||
|
||||
const command: string = commands[0]
|
||||
const args: string[] = commands.slice(1)
|
||||
;(handlers[command] || error)(args, flags)
|
||||
}
|
||||
|
||||
main()
|
@@ -1,14 +1,21 @@
|
||||
import axios from 'axios'
|
||||
import { Writer } from 'clitastic'
|
||||
import { createWriteStream, unlinkSync } from 'fs'
|
||||
import { arch, platform, tmpdir } from 'os'
|
||||
import { unlinkSync } from 'fs'
|
||||
import { tmpdir } from 'os'
|
||||
import { join, resolve } from 'path'
|
||||
|
||||
import { config, INSTALL_DIR } from './autorestic'
|
||||
import { config, CONFIG_FILE, INSTALL_DIR, VERSION } from './autorestic'
|
||||
import { checkAndConfigureBackends, getEnvFromBackend } from './backend'
|
||||
import { backupAll } from './backup'
|
||||
import { Backends, Flags, Locations } from './types'
|
||||
import { checkIfCommandIsAvailable, checkIfResticIsAvailable, exec, filterObjectByKey, singleToArray } from './utils'
|
||||
import {
|
||||
checkIfCommandIsAvailable,
|
||||
checkIfResticIsAvailable,
|
||||
downloadFile,
|
||||
exec,
|
||||
filterObjectByKey,
|
||||
singleToArray,
|
||||
} from './utils'
|
||||
|
||||
export type Handlers = { [command: string]: (args: string[], flags: Flags) => void }
|
||||
|
||||
@@ -114,63 +121,76 @@ const handlers: Handlers = {
|
||||
}
|
||||
|
||||
w.replaceLn('Downloading binary... 🌎')
|
||||
const name = `${json.name.replace(' ', '_')}_${platform()}_${archMap[arch()]}.bz2`
|
||||
const name = `${json.name.replace(' ', '_')}_${process.platform}_${archMap[process.arch]}.bz2`
|
||||
const dl = json.assets.find((asset: any) => asset.name === name)
|
||||
if (!dl) return console.log(
|
||||
'Cannot get the right binary.'.red,
|
||||
'Please see https://bit.ly/2Y1Rzai',
|
||||
)
|
||||
|
||||
const { data: file } = await axios({
|
||||
method: 'get',
|
||||
url: dl.browser_download_url,
|
||||
responseType: 'stream',
|
||||
})
|
||||
const tmp = join(tmpdir(), name)
|
||||
const extracted = tmp.slice(0, -4) //without the .bz2
|
||||
|
||||
const from = join(tmpdir(), name)
|
||||
const to = from.slice(0, -4)
|
||||
await downloadFile(dl.browser_download_url, tmp)
|
||||
|
||||
w.replaceLn('Decompressing binary... 📦')
|
||||
const stream = createWriteStream(from)
|
||||
await new Promise(res => {
|
||||
const writer = file.pipe(stream)
|
||||
writer.on('close', res)
|
||||
})
|
||||
stream.close()
|
||||
|
||||
w.replaceLn(`Moving to ${INSTALL_DIR} 🚙`)
|
||||
// TODO: Native bz2
|
||||
// Decompress
|
||||
exec('bzip2', ['-dk', from])
|
||||
// Remove .bz2
|
||||
exec('chmod', ['+x', to])
|
||||
exec('mv', [to, INSTALL_DIR + '/restic'])
|
||||
w.replaceLn('Decompressing binary... 📦')
|
||||
exec('bzip2', ['-dk', tmp])
|
||||
unlinkSync(tmp)
|
||||
|
||||
unlinkSync(from)
|
||||
w.replaceLn(`Moving to ${INSTALL_DIR} 🚙`)
|
||||
exec('chmod', ['+x', extracted])
|
||||
exec('mv', [extracted, INSTALL_DIR + '/restic'])
|
||||
|
||||
w.done(`\nFinished! restic is installed under: ${INSTALL_DIR}`.underline + ' 🎉')
|
||||
},
|
||||
uninstall() {
|
||||
try {
|
||||
unlinkSync(INSTALL_DIR + '/restic')
|
||||
console.log(`Finished! restic was uninstalled`)
|
||||
} catch (e) {
|
||||
console.log('restic is already uninstalled'.red)
|
||||
}
|
||||
for (const bin of ['restic', 'autorestic'])
|
||||
try {
|
||||
unlinkSync(INSTALL_DIR + '/' + bin)
|
||||
console.log(`Finished! ${bin} was uninstalled`)
|
||||
} catch (e) {
|
||||
console.log(`${bin} is already uninstalled`.red)
|
||||
}
|
||||
},
|
||||
update() {
|
||||
async update() {
|
||||
checkIfResticIsAvailable()
|
||||
const w = new Writer('Checking for new restic version... ⏳')
|
||||
const w = new Writer('Checking for latest restic version... ⏳')
|
||||
exec('restic', ['self-update'])
|
||||
|
||||
|
||||
w.replaceLn('Checking for latest autorestic version... ⏳')
|
||||
const { data: json } = await axios({
|
||||
method: 'get',
|
||||
url: 'https://api.github.com/repos/cupcakearmy/autorestic/releases/latest',
|
||||
responseType: 'json',
|
||||
})
|
||||
|
||||
if (json.tag_name != VERSION) {
|
||||
const platformMap: { [key: string]: string } = {
|
||||
'darwin': 'macos',
|
||||
}
|
||||
|
||||
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'
|
||||
w.replaceLn('Downloading binary... 🌎')
|
||||
await downloadFile(dl.browser_download_url, to)
|
||||
|
||||
exec('chmod', ['+x', to])
|
||||
}
|
||||
|
||||
w.done('All up to date! 🚀')
|
||||
},
|
||||
}
|
||||
|
||||
export const help = () => {
|
||||
console.log('\nAutorestic'.blue + ' - Easy Restic CLI Utility'
|
||||
console.log('\nAutorestic'.blue + ` - ${VERSION} - Easy Restic CLI Utility`
|
||||
+ '\n'
|
||||
+ '\nOptions:'.yellow
|
||||
+ '\n -c, --config [default=config.yml] Specify config file'
|
||||
+ `\n -c, --config Specify config file. Default: ${CONFIG_FILE}`
|
||||
+ '\n'
|
||||
+ '\nCommands:'.yellow
|
||||
+ '\n check [-b, --backend] [-a, --all] Check backends'
|
||||
|
20
src/utils.ts
20
src/utils.ts
@@ -1,5 +1,7 @@
|
||||
import axios from 'axios'
|
||||
import { spawnSync, SpawnSyncOptions } from 'child_process'
|
||||
import { randomBytes } from 'crypto'
|
||||
import { createWriteStream } from 'fs'
|
||||
|
||||
export const exec = (command: string, args: string[], { env, ...rest }: SpawnSyncOptions = {}) => {
|
||||
|
||||
@@ -42,4 +44,20 @@ export const singleToArray = <T>(singleOrArray: T | T[]): T[] => Array.isArray(s
|
||||
|
||||
export const filterObject = <T>(obj: { [key: string]: T }, filter: (item: [string, T]) => boolean): { [key: string]: T } => Object.fromEntries(Object.entries(obj).filter(filter))
|
||||
|
||||
export const filterObjectByKey = <T>(obj: { [key: string]: T }, keys: string[]) => filterObject(obj, ([key]) => keys.includes(key))
|
||||
export const filterObjectByKey = <T>(obj: { [key: string]: T }, keys: string[]) => filterObject(obj, ([key]) => keys.includes(key))
|
||||
|
||||
export const downloadFile = async (url: string, to: string) => new Promise<void>(async res => {
|
||||
const { data: file } = await axios({
|
||||
method: 'get',
|
||||
url: url,
|
||||
responseType: 'stream',
|
||||
})
|
||||
|
||||
const stream = createWriteStream(to)
|
||||
|
||||
const writer = file.pipe(stream)
|
||||
writer.on('close', () => {
|
||||
stream.close()
|
||||
res()
|
||||
})
|
||||
})
|
Reference in New Issue
Block a user