mirror of
https://github.com/cupcakearmy/autorestic.git
synced 2024-12-22 08:16:25 +00:00
rewrite with commander
This commit is contained in:
parent
e9a7e03af7
commit
60d7e0b561
6
.gitignore
vendored
6
.gitignore
vendored
@ -12,11 +12,9 @@ data
|
|||||||
restore
|
restore
|
||||||
docker
|
docker
|
||||||
Dockerfile
|
Dockerfile
|
||||||
|
build
|
||||||
|
|
||||||
# Config
|
# Config
|
||||||
.autorestic.yml
|
.autorestic.yml
|
||||||
.autorestic.lock
|
.autorestic.lock
|
||||||
.docker.yml
|
.docker.yml
|
||||||
|
|
||||||
# Docs
|
|
||||||
.codedoc
|
|
@ -2,10 +2,9 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsc",
|
"build": "tsc",
|
||||||
"build:watch": "tsc -w",
|
"dev": "tsc -w",
|
||||||
"dev": "tsnd --no-notify --respawn ./src/autorestic.ts",
|
"move": "mv bin/index-linux bin/autorestic_linux_x64 && mv bin/index-macos bin/autorestic_macos_x64",
|
||||||
"move": "mv bin/autorestic-linux bin/autorestic_linux_x64 && mv bin/autorestic-macos bin/autorestic_macos_x64",
|
"bin": "yarn run build && pkg lib/index.js --targets latest-macos-x64,latest-linux-x64 --out-path bin && yarn run move",
|
||||||
"bin": "yarn run build && pkg lib/autorestic.js --targets latest-macos-x64,latest-linux-x64 --out-path bin && yarn run move",
|
|
||||||
"docs:build": "codedoc install && codedoc build",
|
"docs:build": "codedoc install && codedoc build",
|
||||||
"docs:dev": "codedoc serve"
|
"docs:dev": "codedoc serve"
|
||||||
},
|
},
|
||||||
@ -25,7 +24,6 @@
|
|||||||
"commander": "^6.2.0",
|
"commander": "^6.2.0",
|
||||||
"cron-parser": "2.x.x",
|
"cron-parser": "2.x.x",
|
||||||
"js-yaml": "3.x.x",
|
"js-yaml": "3.x.x",
|
||||||
"minimist": "1.x.x",
|
|
||||||
"uhrwerk": "1.x.x"
|
"uhrwerk": "1.x.x"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,63 +0,0 @@
|
|||||||
import 'colors'
|
|
||||||
import minimist from 'minimist'
|
|
||||||
|
|
||||||
import { init } from './config'
|
|
||||||
import handlers, { error, help } from './handlers'
|
|
||||||
import { Config } from './types'
|
|
||||||
import { readLock, writeLock, unlock } from './lock'
|
|
||||||
|
|
||||||
process.on('uncaughtException', (err) => {
|
|
||||||
console.log(err.message)
|
|
||||||
unlock()
|
|
||||||
process.exit(1)
|
|
||||||
})
|
|
||||||
|
|
||||||
export const { _: commands, ...flags } = minimist(process.argv.slice(2), {
|
|
||||||
alias: {
|
|
||||||
c: 'config',
|
|
||||||
v: 'version',
|
|
||||||
h: 'help',
|
|
||||||
a: 'all',
|
|
||||||
l: 'location',
|
|
||||||
b: 'backend',
|
|
||||||
d: 'dry-run',
|
|
||||||
},
|
|
||||||
boolean: ['a', 'd'],
|
|
||||||
string: ['l', 'b'],
|
|
||||||
})
|
|
||||||
|
|
||||||
export const VERSION = '0.20'
|
|
||||||
export const INSTALL_DIR = '/usr/local/bin'
|
|
||||||
export const VERBOSE = flags.verbose
|
|
||||||
|
|
||||||
export let config: Config
|
|
||||||
|
|
||||||
async function main() {
|
|
||||||
config = init()
|
|
||||||
|
|
||||||
// Don't let 2 instances run on the same config
|
|
||||||
const lock = readLock()
|
|
||||||
if (lock.running) {
|
|
||||||
console.log('An instance of autorestic is already running for this config file'.red)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
writeLock({
|
|
||||||
...lock,
|
|
||||||
running: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
// For dev
|
|
||||||
// return await handlers['cron']([], { ...flags, all: true })
|
|
||||||
|
|
||||||
if (commands.length < 1 || commands[0] === 'help') return help()
|
|
||||||
|
|
||||||
const command: string = commands[0]
|
|
||||||
const args: string[] = commands.slice(1)
|
|
||||||
|
|
||||||
const fn = handlers[command] || error
|
|
||||||
await fn(args, flags)
|
|
||||||
}
|
|
||||||
|
|
||||||
main()
|
|
||||||
.catch((e: Error) => console.error(e.message))
|
|
||||||
.finally(unlock)
|
|
@ -1,74 +1,66 @@
|
|||||||
import { Writer } from 'clitastic'
|
import { Writer } from 'clitastic'
|
||||||
|
|
||||||
import { config, VERBOSE } from './autorestic'
|
import { config, VERBOSE } from './'
|
||||||
import { Backend, Backends, Locations } from './types'
|
import { Backend, Backends, Locations } from './types'
|
||||||
import { exec, pathRelativeToConfigFile, filterObjectByKey } from './utils'
|
import { exec, pathRelativeToConfigFile, filterObjectByKey } from './utils'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const ALREADY_EXISTS = /(?=.*already)(?=.*config).*/
|
const ALREADY_EXISTS = /(?=.*already)(?=.*config).*/
|
||||||
|
|
||||||
export const getPathFromBackend = (backend: Backend): string => {
|
export const getPathFromBackend = (backend: Backend): string => {
|
||||||
switch (backend.type) {
|
switch (backend.type) {
|
||||||
case 'local':
|
case 'local':
|
||||||
return pathRelativeToConfigFile(backend.path)
|
return pathRelativeToConfigFile(backend.path)
|
||||||
case 'b2':
|
case 'b2':
|
||||||
case 'azure':
|
case 'azure':
|
||||||
case 'gs':
|
case 'gs':
|
||||||
case 's3':
|
case 's3':
|
||||||
case 'sftp':
|
case 'sftp':
|
||||||
case 'rest':
|
case 'rest':
|
||||||
return `${backend.type}:${backend.path}`
|
return `${backend.type}:${backend.path}`
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unknown backend type.`)
|
throw new Error(`Unknown backend type.`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getEnvFromBackend = (backend: Backend) => {
|
export const getEnvFromBackend = (backend: Backend) => {
|
||||||
const { type, path, key, ...rest } = backend
|
const { type, path, key, ...rest } = backend
|
||||||
return {
|
return {
|
||||||
RESTIC_PASSWORD: key,
|
RESTIC_PASSWORD: key,
|
||||||
RESTIC_REPOSITORY: getPathFromBackend(backend),
|
RESTIC_REPOSITORY: getPathFromBackend(backend),
|
||||||
...rest,
|
...rest,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getBackendsFromLocations = (locations: Locations): string[] => {
|
export const getBackendsFromLocations = (locations: Locations): string[] => {
|
||||||
const backends = new Set<string>()
|
const backends = new Set<string>()
|
||||||
for (const to of Object.values(locations).map(location => location.to))
|
for (const to of Object.values(locations).map((location) => location.to)) Array.isArray(to) ? to.forEach((t) => backends.add(t)) : backends.add(to)
|
||||||
Array.isArray(to) ? to.forEach(t => backends.add(t)) : backends.add(to)
|
return Array.from(backends)
|
||||||
return Array.from(backends)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const checkAndConfigureBackend = (name: string, backend: Backend) => {
|
export const checkAndConfigureBackend = (name: string, backend: Backend) => {
|
||||||
const writer = new Writer(name.blue + ' : ' + 'Configuring... ⏳')
|
const writer = new Writer(name.blue + ' : ' + 'Configuring... ⏳')
|
||||||
try {
|
try {
|
||||||
const env = getEnvFromBackend(backend)
|
const env = getEnvFromBackend(backend)
|
||||||
|
|
||||||
const { out, err } = exec('restic', ['init'], { env })
|
const { out, err } = exec('restic', ['init'], { env })
|
||||||
|
|
||||||
if (err.length > 0 && !ALREADY_EXISTS.test(err))
|
if (err.length > 0 && !ALREADY_EXISTS.test(err)) throw new Error(`Could not load the backend "${name}": ${err}`)
|
||||||
throw new Error(`Could not load the backend "${name}": ${err}`)
|
|
||||||
|
|
||||||
if (VERBOSE && out.length > 0) console.log(out)
|
if (VERBOSE && out.length > 0) console.log(out)
|
||||||
|
|
||||||
writer.done(name.blue + ' : ' + 'Done ✓'.green)
|
writer.done(name.blue + ' : ' + 'Done ✓'.green)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
writer.done(name.blue + ' : ' + 'Error ⚠️ ' + e.message.red)
|
writer.done(name.blue + ' : ' + 'Error ⚠️ ' + e.message.red)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const checkAndConfigureBackends = (backends?: Backends) => {
|
export const checkAndConfigureBackends = (backends?: Backends) => {
|
||||||
if (!backends)
|
if (!backends) backends = config.backends
|
||||||
backends = config.backends
|
|
||||||
|
|
||||||
console.log('\nConfiguring Backends'.grey.underline)
|
console.log('\nConfiguring Backends'.grey.underline)
|
||||||
for (const [name, backend] of Object.entries(backends))
|
for (const [name, backend] of Object.entries(backends)) checkAndConfigureBackend(name, backend)
|
||||||
checkAndConfigureBackend(name, backend)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const checkAndConfigureBackendsForLocations = (locations: Locations) => {
|
export const checkAndConfigureBackendsForLocations = (locations: Locations) => {
|
||||||
checkAndConfigureBackends(
|
checkAndConfigureBackends(filterObjectByKey(config.backends, getBackendsFromLocations(locations)))
|
||||||
filterObjectByKey(config.backends, getBackendsFromLocations(locations)),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
144
src/backup.ts
144
src/backup.ts
@ -1,111 +1,103 @@
|
|||||||
import { Writer } from 'clitastic'
|
import { Writer } from 'clitastic'
|
||||||
import { mkdirSync } from 'fs'
|
import { mkdirSync } from 'fs'
|
||||||
|
|
||||||
import { config, VERBOSE } from './autorestic'
|
import { config, VERBOSE } from './'
|
||||||
import { getEnvFromBackend } from './backend'
|
import { getEnvFromBackend } from './backend'
|
||||||
import { LocationFromPrefixes } from './config'
|
import { LocationFromPrefixes } from './config'
|
||||||
import { Locations, Location, Backend } from './types'
|
import { Locations, Location, Backend } from './types'
|
||||||
import {
|
import {
|
||||||
exec,
|
exec,
|
||||||
pathRelativeToConfigFile,
|
pathRelativeToConfigFile,
|
||||||
getFlagsFromLocation,
|
getFlagsFromLocation,
|
||||||
makeArrayIfIsNot,
|
makeArrayIfIsNot,
|
||||||
execPlain,
|
execPlain,
|
||||||
MeasureDuration,
|
MeasureDuration,
|
||||||
fill,
|
fill,
|
||||||
decodeLocationFromPrefix,
|
decodeLocationFromPrefix,
|
||||||
checkIfDockerVolumeExistsOrFail,
|
checkIfDockerVolumeExistsOrFail,
|
||||||
getPathFromVolume,
|
getPathFromVolume,
|
||||||
} from './utils'
|
} from './utils'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export const backupFromFilesystem = (from: string, location: Location, backend: Backend, tags?: string[]) => {
|
export const backupFromFilesystem = (from: string, location: Location, backend: Backend, tags?: string[]) => {
|
||||||
const path = pathRelativeToConfigFile(from)
|
const path = pathRelativeToConfigFile(from)
|
||||||
|
|
||||||
const { out, err, status } = exec(
|
const { out, err, status } = exec('restic', ['backup', '.', ...getFlagsFromLocation(location, 'backup')], {
|
||||||
'restic',
|
env: getEnvFromBackend(backend),
|
||||||
['backup', '.', ...getFlagsFromLocation(location, 'backup')],
|
cwd: path,
|
||||||
{ env: getEnvFromBackend(backend), cwd: path },
|
})
|
||||||
)
|
|
||||||
|
|
||||||
if (VERBOSE) console.log(out, err)
|
if (VERBOSE) console.log(out, err)
|
||||||
if (status != 0 || err.length > 0)
|
if (status != 0 || err.length > 0) throw new Error(err)
|
||||||
throw new Error(err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const backupFromVolume = (volume: string, location: Location, backend: Backend) => {
|
export const backupFromVolume = (volume: string, location: Location, backend: Backend) => {
|
||||||
const tmp = getPathFromVolume(volume)
|
const tmp = getPathFromVolume(volume)
|
||||||
try {
|
try {
|
||||||
mkdirSync(tmp)
|
mkdirSync(tmp)
|
||||||
checkIfDockerVolumeExistsOrFail(volume)
|
checkIfDockerVolumeExistsOrFail(volume)
|
||||||
|
|
||||||
// For incremental backups. Unfortunately due to how the docker mounts work the permissions get lost.
|
// For incremental backups. Unfortunately due to how the docker mounts work the permissions get lost.
|
||||||
// execPlain(`docker run --rm -v ${volume}:/data -v ${tmp}:/backup alpine cp -aT /data /backup`)
|
// execPlain(`docker run --rm -v ${volume}:/data -v ${tmp}:/backup alpine cp -aT /data /backup`)
|
||||||
execPlain(`docker run --rm -v ${volume}:/data -v ${tmp}:/backup alpine tar cf /backup/archive.tar -C /data .`)
|
execPlain(`docker run --rm -v ${volume}:/data -v ${tmp}:/backup alpine tar cf /backup/archive.tar -C /data .`)
|
||||||
|
|
||||||
backupFromFilesystem(tmp, location, backend)
|
backupFromFilesystem(tmp, location, backend)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw e
|
throw e
|
||||||
} finally {
|
} finally {
|
||||||
execPlain(`rm -rf ${tmp}`)
|
execPlain(`rm -rf ${tmp}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const backupSingle = (name: string, to: string, location: Location) => {
|
export const backupSingle = (name: string, to: string, location: Location) => {
|
||||||
const delta = new MeasureDuration()
|
const delta = new MeasureDuration()
|
||||||
const writer = new Writer(name + to.blue + ' : ' + 'Backing up... ⏳')
|
const writer = new Writer(name + to.blue + ' : ' + 'Backing up... ⏳')
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const backend = config.backends[to]
|
const backend = config.backends[to]
|
||||||
const [type, value] = decodeLocationFromPrefix(location.from)
|
const [type, value] = decodeLocationFromPrefix(location.from)
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
case LocationFromPrefixes.Filesystem:
|
||||||
|
backupFromFilesystem(value, location, backend)
|
||||||
|
break
|
||||||
|
|
||||||
case LocationFromPrefixes.Filesystem:
|
case LocationFromPrefixes.DockerVolume:
|
||||||
backupFromFilesystem(value, location, backend)
|
backupFromVolume(value, location, backend)
|
||||||
break
|
break
|
||||||
|
}
|
||||||
|
|
||||||
case LocationFromPrefixes.DockerVolume:
|
writer.done(`${name}${to.blue} : ${'Done ✓'.green} (${delta.finished(true)})`)
|
||||||
backupFromVolume(value, location, backend)
|
} catch (e) {
|
||||||
break
|
writer.done(`${name}${to.blue} : ${'Failed!'.red} (${delta.finished(true)}) ${e.message}`)
|
||||||
|
}
|
||||||
}
|
|
||||||
|
|
||||||
writer.done(`${name}${to.blue} : ${'Done ✓'.green} (${delta.finished(true)})`)
|
|
||||||
} catch (e) {
|
|
||||||
writer.done(`${name}${to.blue} : ${'Failed!'.red} (${delta.finished(true)}) ${e.message}`)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const backupLocation = (name: string, location: Location) => {
|
export const backupLocation = (name: string, location: Location) => {
|
||||||
const display = name.yellow + ' ▶ '
|
const display = name.yellow + ' ▶ '
|
||||||
const filler = fill(name.length + 3)
|
const filler = fill(name.length + 3)
|
||||||
let first = true
|
let first = true
|
||||||
|
|
||||||
if (location.hooks && location.hooks.before)
|
if (location.hooks && location.hooks.before)
|
||||||
for (const command of makeArrayIfIsNot(location.hooks.before)) {
|
for (const command of makeArrayIfIsNot(location.hooks.before)) {
|
||||||
const cmd = execPlain(command, {})
|
const cmd = execPlain(command, {})
|
||||||
console.log(cmd.out, cmd.err)
|
console.log(cmd.out, cmd.err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const t of makeArrayIfIsNot(location.to)) {
|
for (const t of makeArrayIfIsNot(location.to)) {
|
||||||
backupSingle(first ? display : filler, t, location)
|
backupSingle(first ? display : filler, t, location)
|
||||||
if (first) first = false
|
if (first) first = false
|
||||||
}
|
}
|
||||||
|
|
||||||
if (location.hooks && location.hooks.after)
|
if (location.hooks && location.hooks.after)
|
||||||
for (const command of makeArrayIfIsNot(location.hooks.after)) {
|
for (const command of makeArrayIfIsNot(location.hooks.after)) {
|
||||||
const cmd = execPlain(command)
|
const cmd = execPlain(command)
|
||||||
console.log(cmd.out, cmd.err)
|
console.log(cmd.out, cmd.err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const backupAll = (locations?: Locations) => {
|
export const backupAll = (locations?: Locations) => {
|
||||||
if (!locations)
|
if (!locations) locations = config.locations
|
||||||
locations = config.locations
|
|
||||||
|
|
||||||
console.log('\nBacking Up'.underline.grey)
|
console.log('\nBacking Up'.underline.grey)
|
||||||
for (const [name, location] of Object.entries(locations))
|
for (const [name, location] of Object.entries(locations)) backupLocation(name, location)
|
||||||
backupLocation(name, location)
|
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,6 @@ import { homedir } from 'os'
|
|||||||
import yaml from 'js-yaml'
|
import yaml from 'js-yaml'
|
||||||
import CronParser from 'cron-parser'
|
import CronParser from 'cron-parser'
|
||||||
|
|
||||||
import { flags } from './autorestic'
|
|
||||||
import { Backend, Config } from './types'
|
import { Backend, Config } from './types'
|
||||||
import { makeArrayIfIsNot, makeObjectKeysLowercase, rand } from './utils'
|
import { makeArrayIfIsNot, makeObjectKeysLowercase, rand } from './utils'
|
||||||
|
|
||||||
@ -56,9 +55,9 @@ export const normalizeAndCheckLocations = (config: Config) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const findConfigFile = (): string => {
|
const findConfigFile = (custom: string): string => {
|
||||||
const config = '.autorestic.yml'
|
const config = '.autorestic.yml'
|
||||||
const paths = [resolve(flags.config || ''), resolve('./' + config), homedir() + '/' + config]
|
const paths = [resolve(custom || ''), resolve('./' + config), homedir() + '/' + config]
|
||||||
for (const path of paths) {
|
for (const path of paths) {
|
||||||
try {
|
try {
|
||||||
const file = statSync(path)
|
const file = statSync(path)
|
||||||
@ -70,8 +69,8 @@ const findConfigFile = (): string => {
|
|||||||
|
|
||||||
export let CONFIG_FILE: string = ''
|
export let CONFIG_FILE: string = ''
|
||||||
|
|
||||||
export const init = (): Config => {
|
export const init = (custom: string): Config => {
|
||||||
const file = findConfigFile()
|
const file = findConfigFile(custom)
|
||||||
CONFIG_FILE = file
|
CONFIG_FILE = file
|
||||||
|
|
||||||
const parsed = yaml.safeLoad(readFileSync(CONFIG_FILE).toString())
|
const parsed = yaml.safeLoad(readFileSync(CONFIG_FILE).toString())
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
import CronParser from 'cron-parser'
|
import CronParser from 'cron-parser'
|
||||||
|
|
||||||
import { config } from './autorestic'
|
import { config } from './'
|
||||||
import { checkAndConfigureBackendsForLocations } from './backend'
|
import { checkAndConfigureBackendsForLocations } from './backend'
|
||||||
import { Location } from './types'
|
import { Location } from './types'
|
||||||
import { backupLocation } from './backup'
|
import { backupLocation } from './backup'
|
||||||
import { readLock, writeLock } from './lock'
|
import { readLock, writeLock } from './lock'
|
||||||
|
|
||||||
|
|
||||||
const runCronForLocation = (name: string, location: Location) => {
|
const runCronForLocation = (name: string, location: Location) => {
|
||||||
const lock = readLock()
|
const lock = readLock()
|
||||||
const parsed = CronParser.parseExpression(location.cron || '')
|
const parsed = CronParser.parseExpression(location.cron || '')
|
||||||
@ -26,8 +25,7 @@ export const runCron = () => {
|
|||||||
checkAndConfigureBackendsForLocations(Object.fromEntries(locationsWithCron))
|
checkAndConfigureBackendsForLocations(Object.fromEntries(locationsWithCron))
|
||||||
|
|
||||||
console.log('\nRunning cron jobs'.underline.gray)
|
console.log('\nRunning cron jobs'.underline.gray)
|
||||||
for (const [name, location] of locationsWithCron)
|
for (const [name, location] of locationsWithCron) runCronForLocation(name, location)
|
||||||
runCronForLocation(name, location)
|
|
||||||
|
|
||||||
console.log('\nFinished!'.underline + ' 🎉')
|
console.log('\nFinished!'.underline + ' 🎉')
|
||||||
}
|
}
|
||||||
|
@ -1,75 +1,61 @@
|
|||||||
import { Writer } from 'clitastic'
|
import { Writer } from 'clitastic'
|
||||||
|
|
||||||
import { config, VERBOSE } from './autorestic'
|
import { config, VERBOSE } from './'
|
||||||
import { getEnvFromBackend } from './backend'
|
import { getEnvFromBackend } from './backend'
|
||||||
import { LocationFromPrefixes } from './config'
|
import { LocationFromPrefixes } from './config'
|
||||||
import { Locations, Location, Flags } from './types'
|
import { Locations, Location, Flags } from './types'
|
||||||
import {
|
import { exec, pathRelativeToConfigFile, getFlagsFromLocation, makeArrayIfIsNot, fill, decodeLocationFromPrefix, getPathFromVolume } from './utils'
|
||||||
exec,
|
|
||||||
pathRelativeToConfigFile,
|
|
||||||
getFlagsFromLocation,
|
|
||||||
makeArrayIfIsNot,
|
|
||||||
fill, decodeLocationFromPrefix, getPathFromVolume,
|
|
||||||
} from './utils'
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export const forgetSingle = (name: string, to: string, location: Location, dryRun: boolean) => {
|
export const forgetSingle = (name: string, to: string, location: Location, dryRun: boolean) => {
|
||||||
const base = name + to.blue + ' : '
|
const base = name + to.blue + ' : '
|
||||||
const writer = new Writer(base + 'Removing old snapshots… ⏳')
|
const writer = new Writer(base + 'Removing old snapshots… ⏳')
|
||||||
|
|
||||||
const backend = config.backends[to]
|
const backend = config.backends[to]
|
||||||
const flags = getFlagsFromLocation(location, 'forget')
|
const flags = getFlagsFromLocation(location, 'forget')
|
||||||
|
|
||||||
const [type, value] = decodeLocationFromPrefix(location.from)
|
const [type, value] = decodeLocationFromPrefix(location.from)
|
||||||
let path: string
|
let path: string
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case LocationFromPrefixes.Filesystem:
|
case LocationFromPrefixes.Filesystem:
|
||||||
path = pathRelativeToConfigFile(value)
|
path = pathRelativeToConfigFile(value)
|
||||||
break
|
break
|
||||||
case LocationFromPrefixes.DockerVolume:
|
case LocationFromPrefixes.DockerVolume:
|
||||||
path = getPathFromVolume(value)
|
path = getPathFromVolume(value)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flags.length == 0) {
|
if (flags.length == 0) {
|
||||||
writer.done(base + 'Skipping, no policy declared')
|
writer.done(base + 'Skipping, no policy declared')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (dryRun) flags.push('--dry-run')
|
if (dryRun) flags.push('--dry-run')
|
||||||
|
|
||||||
writer.replaceLn(base + 'Forgetting old snapshots… ⏳')
|
writer.replaceLn(base + 'Forgetting old snapshots… ⏳')
|
||||||
const cmd = exec(
|
const cmd = exec('restic', ['forget', '--path', path, '--prune', ...flags], { env: getEnvFromBackend(backend) })
|
||||||
'restic',
|
|
||||||
['forget', '--path', path, '--prune', ...flags],
|
|
||||||
{ env: getEnvFromBackend(backend) },
|
|
||||||
)
|
|
||||||
|
|
||||||
if (VERBOSE) console.log(cmd.out, cmd.err)
|
if (VERBOSE) console.log(cmd.out, cmd.err)
|
||||||
writer.done(base + 'Done ✓'.green)
|
writer.done(base + 'Done ✓'.green)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const forgetLocation = (name: string, backup: Location, dryRun: boolean) => {
|
export const forgetLocation = (name: string, backup: Location, dryRun: boolean) => {
|
||||||
const display = name.yellow + ' ▶ '
|
const display = name.yellow + ' ▶ '
|
||||||
const filler = fill(name.length + 3)
|
const filler = fill(name.length + 3)
|
||||||
let first = true
|
let first = true
|
||||||
|
|
||||||
for (const t of makeArrayIfIsNot(backup.to)) {
|
for (const t of makeArrayIfIsNot(backup.to)) {
|
||||||
const nameOrBlankSpaces: string = first ? display : filler
|
const nameOrBlankSpaces: string = first ? display : filler
|
||||||
forgetSingle(nameOrBlankSpaces, t, backup, dryRun)
|
forgetSingle(nameOrBlankSpaces, t, backup, dryRun)
|
||||||
if (first) first = false
|
if (first) first = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const forgetAll = (backups?: Locations, flags?: Flags) => {
|
export const forgetAll = (backups?: Locations, dryRun = false) => {
|
||||||
if (!backups) {
|
if (!backups) {
|
||||||
backups = config.locations
|
backups = config.locations
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('\nRemoving old snapshots 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)
|
||||||
if (dryRun) console.log('Running in dry-run mode, not touching data\n'.yellow)
|
|
||||||
|
|
||||||
for (const [name, backup] of Object.entries(backups))
|
for (const [name, backup] of Object.entries(backups)) forgetLocation(name, backup, dryRun)
|
||||||
forgetLocation(name, backup, dryRun)
|
|
||||||
}
|
}
|
||||||
|
244
src/handlers.ts
244
src/handlers.ts
@ -1,244 +0,0 @@
|
|||||||
import { chmodSync, renameSync, unlinkSync } from 'fs'
|
|
||||||
import { tmpdir } from 'os'
|
|
||||||
import { join, resolve } from 'path'
|
|
||||||
|
|
||||||
import axios from 'axios'
|
|
||||||
import { Writer } from 'clitastic'
|
|
||||||
|
|
||||||
import { config, INSTALL_DIR, VERSION } from './autorestic'
|
|
||||||
import { checkAndConfigureBackends, getEnvFromBackend, checkAndConfigureBackendsForLocations } from './backend'
|
|
||||||
import { backupAll } from './backup'
|
|
||||||
import { runCron } from './cron'
|
|
||||||
import { forgetAll } from './forget'
|
|
||||||
import showAll from './info'
|
|
||||||
import { restoreSingle } from './restore'
|
|
||||||
import { Backends, Flags, Locations } from './types'
|
|
||||||
import {
|
|
||||||
checkIfCommandIsAvailable,
|
|
||||||
checkIfResticIsAvailable,
|
|
||||||
downloadFile,
|
|
||||||
exec,
|
|
||||||
filterObjectByKey,
|
|
||||||
makeArrayIfIsNot,
|
|
||||||
} from './utils'
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export type Handlers = {
|
|
||||||
[command: string]: (args: string[], flags: Flags) => void
|
|
||||||
}
|
|
||||||
|
|
||||||
const parseBackend = (flags: Flags): Backends => {
|
|
||||||
if (!flags.all && !flags.backend)
|
|
||||||
throw new Error(
|
|
||||||
'No backends specified.'.red +
|
|
||||||
'\n--all [-a]\t\t\t\tCheck all.' +
|
|
||||||
'\n--backend [-b] myBackend\t\tSpecify one or more backend',
|
|
||||||
)
|
|
||||||
if (flags.all) return config.backends
|
|
||||||
else {
|
|
||||||
const backends = makeArrayIfIsNot<string>(flags.backend)
|
|
||||||
for (const backend of backends)
|
|
||||||
if (!config.backends[backend])
|
|
||||||
throw new Error('Invalid backend: '.red + backend)
|
|
||||||
return filterObjectByKey(config.backends, backends)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const parseLocations = (flags: Flags): Locations => {
|
|
||||||
if (!flags.all && !flags.location)
|
|
||||||
throw new Error(
|
|
||||||
'No locations specified.'.red +
|
|
||||||
'\n--all [-a]\t\t\t\tBackup all.' +
|
|
||||||
'\n--location [-l] site1\t\t\tSpecify one or more locations',
|
|
||||||
)
|
|
||||||
|
|
||||||
if (flags.all) {
|
|
||||||
return config.locations
|
|
||||||
} else {
|
|
||||||
const locations = makeArrayIfIsNot<string>(flags.location)
|
|
||||||
for (const location of locations)
|
|
||||||
if (!config.locations[location])
|
|
||||||
throw new Error('Invalid location: '.red + location)
|
|
||||||
return filterObjectByKey(config.locations, locations)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handlers: Handlers = {
|
|
||||||
check(args, flags) {
|
|
||||||
checkIfResticIsAvailable()
|
|
||||||
const backends = parseBackend(flags)
|
|
||||||
checkAndConfigureBackends(backends)
|
|
||||||
},
|
|
||||||
backup(args, flags) {
|
|
||||||
checkIfResticIsAvailable()
|
|
||||||
const locations: Locations = parseLocations(flags)
|
|
||||||
checkAndConfigureBackendsForLocations(locations)
|
|
||||||
backupAll(locations)
|
|
||||||
|
|
||||||
console.log('\nFinished!'.underline + ' 🎉')
|
|
||||||
},
|
|
||||||
cron(args, flags) {
|
|
||||||
checkIfResticIsAvailable()
|
|
||||||
runCron()
|
|
||||||
},
|
|
||||||
restore(args, flags) {
|
|
||||||
checkIfResticIsAvailable()
|
|
||||||
|
|
||||||
const locations = parseLocations(flags)
|
|
||||||
const keys = Object.keys(locations)
|
|
||||||
if (keys.length < 1) throw new Error(`You need to specify the location to restore with --location`.red)
|
|
||||||
if (keys.length > 2) throw new Error(`Only one location is supported at a time when restoring`.red)
|
|
||||||
|
|
||||||
restoreSingle(keys[0], flags.from, flags.to)
|
|
||||||
},
|
|
||||||
forget(args, flags) {
|
|
||||||
checkIfResticIsAvailable()
|
|
||||||
const locations: Locations = parseLocations(flags)
|
|
||||||
checkAndConfigureBackendsForLocations(locations)
|
|
||||||
forgetAll(locations, flags)
|
|
||||||
|
|
||||||
console.log('\nFinished!'.underline + ' 🎉')
|
|
||||||
},
|
|
||||||
exec(args, flags) {
|
|
||||||
checkIfResticIsAvailable()
|
|
||||||
const backends = parseBackend(flags)
|
|
||||||
for (const [name, backend] of Object.entries(backends)) {
|
|
||||||
console.log(`\n${name}:\n`.grey.underline)
|
|
||||||
const env = getEnvFromBackend(backend)
|
|
||||||
|
|
||||||
const { out, err } = exec('restic', args, { env })
|
|
||||||
console.log(out, err)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
info() {
|
|
||||||
showAll()
|
|
||||||
},
|
|
||||||
async install() {
|
|
||||||
try {
|
|
||||||
checkIfResticIsAvailable()
|
|
||||||
console.log('Restic is already installed')
|
|
||||||
return
|
|
||||||
} catch {
|
|
||||||
}
|
|
||||||
|
|
||||||
const w = new Writer('Checking latest version... ⏳')
|
|
||||||
checkIfCommandIsAvailable('bzip2')
|
|
||||||
const { data: json } = await axios({
|
|
||||||
method: 'get',
|
|
||||||
url: 'https://api.github.com/repos/restic/restic/releases/latest',
|
|
||||||
responseType: 'json',
|
|
||||||
})
|
|
||||||
|
|
||||||
const archMap: { [a: string]: string } = {
|
|
||||||
x32: '386',
|
|
||||||
x64: 'amd64',
|
|
||||||
}
|
|
||||||
|
|
||||||
w.replaceLn('Downloading binary... 🌎')
|
|
||||||
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 tmp = join(tmpdir(), name)
|
|
||||||
const extracted = tmp.slice(0, -4) //without the .bz2
|
|
||||||
|
|
||||||
await downloadFile(dl.browser_download_url, tmp)
|
|
||||||
|
|
||||||
w.replaceLn('Decompressing binary... 📦')
|
|
||||||
exec('bzip2', ['-dk', tmp])
|
|
||||||
unlinkSync(tmp)
|
|
||||||
|
|
||||||
w.replaceLn(`Moving to ${INSTALL_DIR} 🚙`)
|
|
||||||
chmodSync(extracted, 0o755)
|
|
||||||
renameSync(extracted, INSTALL_DIR + '/restic')
|
|
||||||
|
|
||||||
w.done(
|
|
||||||
`\nFinished! restic is installed under: ${INSTALL_DIR}`.underline + ' 🎉',
|
|
||||||
)
|
|
||||||
},
|
|
||||||
uninstall() {
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async update() {
|
|
||||||
checkIfResticIsAvailable()
|
|
||||||
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)
|
|
||||||
|
|
||||||
chmodSync(to, 0o755)
|
|
||||||
}
|
|
||||||
|
|
||||||
w.done('All up to date! 🚀')
|
|
||||||
},
|
|
||||||
version() {
|
|
||||||
console.log('version'.grey, VERSION)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
export const help = () => {
|
|
||||||
console.log(
|
|
||||||
'\nAutorestic'.blue +
|
|
||||||
` - ${VERSION} - Easy Restic CLI Utility` +
|
|
||||||
'\n' +
|
|
||||||
'\nOptions:'.yellow +
|
|
||||||
`\n -c, --config Specify config file. Default: .autorestic.yml` +
|
|
||||||
'\n' +
|
|
||||||
'\nCommands:'.yellow +
|
|
||||||
'\n info Show all locations and backends' +
|
|
||||||
'\n check [-b, --backend] [-a, --all] Check backends' +
|
|
||||||
'\n backup [-l, --location] [-a, --all] Backup all or specified locations' +
|
|
||||||
'\n forget [-l, --location] [-a, --all] [--dry-run] Forget old snapshots according to declared policies' +
|
|
||||||
'\n restore [-l, --location] [--from backend] [--to <out dir>] Restore all or specified locations' +
|
|
||||||
'\n' +
|
|
||||||
'\n exec [-b, --backend] [-a, --all] <command> -- [native options] Execute native restic command' +
|
|
||||||
'\n' +
|
|
||||||
'\n install install restic' +
|
|
||||||
'\n uninstall uninstall restic' +
|
|
||||||
'\n update update restic' +
|
|
||||||
'\n help Show help' +
|
|
||||||
'\n' +
|
|
||||||
'\nExamples: '.yellow +
|
|
||||||
'https://git.io/Jf0x6' +
|
|
||||||
'\n',
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const error = () => {
|
|
||||||
help()
|
|
||||||
console.log(
|
|
||||||
`Invalid Command:`.red.underline,
|
|
||||||
`${process.argv.slice(2).join(' ')}`,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default handlers
|
|
13
src/handlers/backup.ts
Normal file
13
src/handlers/backup.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { checkAndConfigureBackendsForLocations } from '../backend'
|
||||||
|
import { backupAll } from '../backup'
|
||||||
|
import { Flags, Locations } from '../types'
|
||||||
|
import { checkIfResticIsAvailable, parseLocations } from '../utils'
|
||||||
|
|
||||||
|
export default function backup({ location, all }: Flags) {
|
||||||
|
checkIfResticIsAvailable()
|
||||||
|
const locations: Locations = parseLocations(location, all)
|
||||||
|
checkAndConfigureBackendsForLocations(locations)
|
||||||
|
backupAll(locations)
|
||||||
|
|
||||||
|
console.log('\nFinished!'.underline + ' 🎉')
|
||||||
|
}
|
9
src/handlers/check.ts
Normal file
9
src/handlers/check.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { checkAndConfigureBackends } from '../backend'
|
||||||
|
import { Flags } from '../types'
|
||||||
|
import { checkIfResticIsAvailable, parseBackend } from '../utils'
|
||||||
|
|
||||||
|
export default function check({ backend, all }: Flags) {
|
||||||
|
checkIfResticIsAvailable()
|
||||||
|
const backends = parseBackend(backend, all)
|
||||||
|
checkAndConfigureBackends(backends)
|
||||||
|
}
|
7
src/handlers/cron.ts
Normal file
7
src/handlers/cron.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { runCron } from '../cron'
|
||||||
|
import { checkIfResticIsAvailable } from '../utils'
|
||||||
|
|
||||||
|
export function cron() {
|
||||||
|
checkIfResticIsAvailable()
|
||||||
|
runCron()
|
||||||
|
}
|
14
src/handlers/exec.ts
Normal file
14
src/handlers/exec.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { getEnvFromBackend } from '../backend'
|
||||||
|
import { Flags } from '../types'
|
||||||
|
import { checkIfResticIsAvailable, exec as execCLI, parseBackend } from '../utils'
|
||||||
|
|
||||||
|
export default function exec({ backend, all }: Flags, args: string[]) {
|
||||||
|
checkIfResticIsAvailable()
|
||||||
|
const backends = parseBackend(backend, all)
|
||||||
|
for (const [name, backend] of Object.entries(backends)) {
|
||||||
|
console.log(`\n${name}:\n`.grey.underline)
|
||||||
|
const env = getEnvFromBackend(backend)
|
||||||
|
const { out, err } = execCLI('restic', args, { env })
|
||||||
|
console.log(out, err)
|
||||||
|
}
|
||||||
|
}
|
13
src/handlers/forget.ts
Normal file
13
src/handlers/forget.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { checkAndConfigureBackendsForLocations } from '../backend'
|
||||||
|
import { forgetAll } from '../forget'
|
||||||
|
import { Flags, Locations } from '../types'
|
||||||
|
import { checkIfResticIsAvailable, parseLocations } from '../utils'
|
||||||
|
|
||||||
|
export default function forget({ location, all, dryRun }: Flags) {
|
||||||
|
checkIfResticIsAvailable()
|
||||||
|
const locations: Locations = parseLocations(location, all)
|
||||||
|
checkAndConfigureBackendsForLocations(locations)
|
||||||
|
forgetAll(locations, dryRun)
|
||||||
|
|
||||||
|
console.log('\nFinished!'.underline + ' 🎉')
|
||||||
|
}
|
18
src/handlers/info.ts
Normal file
18
src/handlers/info.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { config } from '../'
|
||||||
|
import { fill, treeToString } from '../utils'
|
||||||
|
|
||||||
|
const showAll = () => {
|
||||||
|
console.log('\n\n' + fill(32, '_') + 'LOCATIONS:'.underline)
|
||||||
|
for (const [key, data] of Object.entries(config.locations)) {
|
||||||
|
console.log(`\n${key.blue.underline}:`)
|
||||||
|
console.log(treeToString(data, ['to:', 'from:', 'hooks:', 'options:', 'cron:']))
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('\n\n' + fill(32, '_') + 'BACKENDS:'.underline)
|
||||||
|
for (const [key, data] of Object.entries(config.backends)) {
|
||||||
|
console.log(`\n${key.blue.underline}:`)
|
||||||
|
console.log(treeToString(data, ['type:', 'path:', 'key:']))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default showAll
|
50
src/handlers/install.ts
Normal file
50
src/handlers/install.ts
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import { join } from 'path'
|
||||||
|
import { chmodSync, renameSync, unlinkSync } from 'fs'
|
||||||
|
import { tmpdir } from 'os'
|
||||||
|
|
||||||
|
import axios from 'axios'
|
||||||
|
import { Writer } from 'clitastic'
|
||||||
|
|
||||||
|
import { INSTALL_DIR } from '..'
|
||||||
|
import { checkIfCommandIsAvailable, checkIfResticIsAvailable, downloadFile, exec } from '../utils'
|
||||||
|
|
||||||
|
export default async function install() {
|
||||||
|
try {
|
||||||
|
checkIfResticIsAvailable()
|
||||||
|
console.log('Restic is already installed')
|
||||||
|
return
|
||||||
|
} catch {}
|
||||||
|
|
||||||
|
const w = new Writer('Checking latest version... ⏳')
|
||||||
|
checkIfCommandIsAvailable('bzip2')
|
||||||
|
const { data: json } = await axios({
|
||||||
|
method: 'get',
|
||||||
|
url: 'https://api.github.com/repos/restic/restic/releases/latest',
|
||||||
|
responseType: 'json',
|
||||||
|
})
|
||||||
|
|
||||||
|
const archMap: { [a: string]: string } = {
|
||||||
|
x32: '386',
|
||||||
|
x64: 'amd64',
|
||||||
|
}
|
||||||
|
|
||||||
|
w.replaceLn('Downloading binary... 🌎')
|
||||||
|
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 tmp = join(tmpdir(), name)
|
||||||
|
const extracted = tmp.slice(0, -4) //without the .bz2
|
||||||
|
|
||||||
|
await downloadFile(dl.browser_download_url, tmp)
|
||||||
|
|
||||||
|
w.replaceLn('Decompressing binary... 📦')
|
||||||
|
exec('bzip2', ['-dk', tmp])
|
||||||
|
unlinkSync(tmp)
|
||||||
|
|
||||||
|
w.replaceLn(`Moving to ${INSTALL_DIR} 🚙`)
|
||||||
|
chmodSync(extracted, 0o755)
|
||||||
|
renameSync(extracted, INSTALL_DIR + '/restic')
|
||||||
|
|
||||||
|
w.done(`\nFinished! restic is installed under: ${INSTALL_DIR}`.underline + ' 🎉')
|
||||||
|
}
|
9
src/handlers/restore.ts
Normal file
9
src/handlers/restore.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { restoreSingle } from '../restore'
|
||||||
|
import { Flags } from '../types'
|
||||||
|
import { checkIfResticIsAvailable, checkIfValidLocation } from '../utils'
|
||||||
|
|
||||||
|
export default function restore({ location, to, from }: Flags) {
|
||||||
|
checkIfResticIsAvailable()
|
||||||
|
checkIfValidLocation(location)
|
||||||
|
restoreSingle(location, from, to)
|
||||||
|
}
|
13
src/handlers/uninstall.ts
Normal file
13
src/handlers/uninstall.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { unlinkSync } from 'fs'
|
||||||
|
|
||||||
|
import { INSTALL_DIR } from '..'
|
||||||
|
|
||||||
|
export function uninstall() {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
37
src/handlers/upgrade.ts
Normal file
37
src/handlers/upgrade.ts
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import { chmodSync } from 'fs'
|
||||||
|
|
||||||
|
import axios from 'axios'
|
||||||
|
import { Writer } from 'clitastic'
|
||||||
|
|
||||||
|
import { INSTALL_DIR, VERSION } from '..'
|
||||||
|
import { checkIfResticIsAvailable, downloadFile, exec } from '../utils'
|
||||||
|
|
||||||
|
export async function upgrade() {
|
||||||
|
checkIfResticIsAvailable()
|
||||||
|
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)
|
||||||
|
|
||||||
|
chmodSync(to, 0o755)
|
||||||
|
}
|
||||||
|
|
||||||
|
w.done('All up to date! 🚀')
|
||||||
|
}
|
104
src/index.ts
Normal file
104
src/index.ts
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
import 'colors'
|
||||||
|
import { program } from 'commander'
|
||||||
|
|
||||||
|
import { unlock, readLock, writeLock } from './lock'
|
||||||
|
import { Config } from './types'
|
||||||
|
import { init } from './config'
|
||||||
|
|
||||||
|
import info from './handlers/info'
|
||||||
|
import check from './handlers/check'
|
||||||
|
import backup from './handlers/backup'
|
||||||
|
import restore from './handlers/restore'
|
||||||
|
import forget from './handlers/forget'
|
||||||
|
import { cron } from './handlers/cron'
|
||||||
|
import exec from './handlers/exec'
|
||||||
|
import install from './handlers/install'
|
||||||
|
import { uninstall } from './handlers/uninstall'
|
||||||
|
import { upgrade } from './handlers/upgrade'
|
||||||
|
|
||||||
|
export const VERSION = '0.20'
|
||||||
|
export const INSTALL_DIR = '/usr/local/bin'
|
||||||
|
|
||||||
|
process.on('uncaughtException', (err) => {
|
||||||
|
console.log(err.message)
|
||||||
|
unlock()
|
||||||
|
process.exit(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
let queue: Function = () => {}
|
||||||
|
const enqueue = (fn: Function) => (cmd: any) => {
|
||||||
|
queue = () => fn(cmd.opts())
|
||||||
|
}
|
||||||
|
|
||||||
|
program.storeOptionsAsProperties()
|
||||||
|
program.name('autorestic').version(VERSION)
|
||||||
|
|
||||||
|
program.option('-c, --config <path>', 'Config file').option('-v, --verbose', 'Verbosity', false)
|
||||||
|
|
||||||
|
program.command('info').action(enqueue(info))
|
||||||
|
|
||||||
|
program
|
||||||
|
.command('check')
|
||||||
|
.description('Checks and initializes backend as needed')
|
||||||
|
.option('-b, --backend <backends...>')
|
||||||
|
.option('-a, --all')
|
||||||
|
.action(enqueue(check))
|
||||||
|
|
||||||
|
program.command('backup').description('Performs a backup').option('-b, --backend <backends...>').option('-a, --all').action(enqueue(backup))
|
||||||
|
|
||||||
|
program
|
||||||
|
.command('restore')
|
||||||
|
.description('Restores data to a specified folder from a location')
|
||||||
|
.requiredOption('-l, --location <location>')
|
||||||
|
.option('--from <backend>')
|
||||||
|
.requiredOption('--to <path>', 'Path to save the restored data to')
|
||||||
|
.action(enqueue(restore))
|
||||||
|
|
||||||
|
program
|
||||||
|
.command('forget')
|
||||||
|
.description('This will prune and remove data according to your policies')
|
||||||
|
.option('-l, --location <locations...>')
|
||||||
|
.option('-a, --all')
|
||||||
|
.option('--dry-run')
|
||||||
|
.action(enqueue(forget))
|
||||||
|
|
||||||
|
program
|
||||||
|
.command('cron')
|
||||||
|
.description('Intended to be triggered by an automated system like systemd or crontab.')
|
||||||
|
.option('-a, --all')
|
||||||
|
.action(enqueue(cron))
|
||||||
|
|
||||||
|
program
|
||||||
|
.command('exec')
|
||||||
|
.description('Run any native restic command on desired backends')
|
||||||
|
.option('-b, --backend <backends...>')
|
||||||
|
.option('-a, --all')
|
||||||
|
.action(({ args, all, backend }) => {
|
||||||
|
queue = () => exec({ all, backend }, args)
|
||||||
|
})
|
||||||
|
|
||||||
|
program.command('install').description('Installs both restic and autorestic to /usr/local/bin').action(enqueue(install))
|
||||||
|
|
||||||
|
program.command('uninstall').description('Uninstalls autorestic from the system').action(enqueue(uninstall))
|
||||||
|
|
||||||
|
program.command('upgrade').alias('update').description('Checks and installs new autorestic versions').action(enqueue(upgrade))
|
||||||
|
|
||||||
|
const { verbose, config: configFile } = program.parse(process.argv)
|
||||||
|
|
||||||
|
export const VERBOSE = verbose
|
||||||
|
export let config: Config = init(configFile)
|
||||||
|
|
||||||
|
try {
|
||||||
|
const lock = readLock()
|
||||||
|
if (lock.running) throw new Error('An instance of autorestic is already running for this config file'.red)
|
||||||
|
|
||||||
|
writeLock({
|
||||||
|
...lock,
|
||||||
|
running: true,
|
||||||
|
})
|
||||||
|
queue()
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e.message)
|
||||||
|
} finally {
|
||||||
|
unlock()
|
||||||
|
}
|
32
src/info.ts
32
src/info.ts
@ -1,26 +1,18 @@
|
|||||||
import { config } from './autorestic'
|
import { config } from './'
|
||||||
import { fill, treeToString } from './utils'
|
import { fill, treeToString } from './utils'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const showAll = () => {
|
const showAll = () => {
|
||||||
console.log('\n\n' + fill(32, '_') + 'LOCATIONS:'.underline)
|
console.log('\n\n' + fill(32, '_') + 'LOCATIONS:'.underline)
|
||||||
for (const [key, data] of Object.entries(config.locations)) {
|
for (const [key, data] of Object.entries(config.locations)) {
|
||||||
console.log(`\n${key.blue.underline}:`)
|
console.log(`\n${key.blue.underline}:`)
|
||||||
console.log(treeToString(
|
console.log(treeToString(data, ['to:', 'from:', 'hooks:', 'options:', 'cron:']))
|
||||||
data,
|
}
|
||||||
['to:', 'from:', 'hooks:', 'options:', 'cron:'],
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('\n\n' + fill(32, '_') + 'BACKENDS:'.underline)
|
console.log('\n\n' + fill(32, '_') + 'BACKENDS:'.underline)
|
||||||
for (const [key, data] of Object.entries(config.backends)) {
|
for (const [key, data] of Object.entries(config.backends)) {
|
||||||
console.log(`\n${key.blue.underline}:`)
|
console.log(`\n${key.blue.underline}:`)
|
||||||
console.log(treeToString(
|
console.log(treeToString(data, ['type:', 'path:', 'key:']))
|
||||||
data,
|
}
|
||||||
['type:', 'path:', 'key:'],
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default showAll
|
export default showAll
|
||||||
|
10
src/lock.ts
10
src/lock.ts
@ -1,7 +1,7 @@
|
|||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
|
|
||||||
import { pathRelativeToConfigFile } from "./utils"
|
import { pathRelativeToConfigFile } from './utils'
|
||||||
import { Lockfile } from "./types"
|
import { Lockfile } from './types'
|
||||||
|
|
||||||
export const getLockFileName = () => {
|
export const getLockFileName = () => {
|
||||||
const LOCK_FILE = '.autorestic.lock'
|
const LOCK_FILE = '.autorestic.lock'
|
||||||
@ -12,11 +12,11 @@ export const readLock = (): Lockfile => {
|
|||||||
const name = getLockFileName()
|
const name = getLockFileName()
|
||||||
let lock = {
|
let lock = {
|
||||||
running: false,
|
running: false,
|
||||||
crons: {}
|
crons: {},
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
lock = JSON.parse(fs.readFileSync(name, { encoding: 'utf-8' }))
|
lock = JSON.parse(fs.readFileSync(name, { encoding: 'utf-8' }))
|
||||||
} catch { }
|
} catch {}
|
||||||
return lock
|
return lock
|
||||||
}
|
}
|
||||||
export const writeLock = (lock: Lockfile) => {
|
export const writeLock = (lock: Lockfile) => {
|
||||||
@ -29,4 +29,4 @@ export const unlock = () => {
|
|||||||
...readLock(),
|
...readLock(),
|
||||||
running: false,
|
running: false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
101
src/restore.ts
101
src/restore.ts
@ -1,78 +1,63 @@
|
|||||||
import { Writer } from 'clitastic'
|
import { Writer } from 'clitastic'
|
||||||
import { resolve } from 'path'
|
import { resolve } from 'path'
|
||||||
|
|
||||||
import { config } from './autorestic'
|
import { config } from './'
|
||||||
import { getEnvFromBackend } from './backend'
|
import { getEnvFromBackend } from './backend'
|
||||||
import { LocationFromPrefixes } from './config'
|
import { LocationFromPrefixes } from './config'
|
||||||
import { Backend } from './types'
|
import { Backend } from './types'
|
||||||
import {
|
import { checkIfDockerVolumeExistsOrFail, decodeLocationFromPrefix, exec, execPlain, getPathFromVolume } from './utils'
|
||||||
checkIfDockerVolumeExistsOrFail,
|
|
||||||
decodeLocationFromPrefix,
|
|
||||||
exec,
|
|
||||||
execPlain,
|
|
||||||
getPathFromVolume,
|
|
||||||
} from './utils'
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export const restoreToFilesystem = (from: string, to: string, backend: Backend) => {
|
export const restoreToFilesystem = (from: string, to: string, backend: Backend) => {
|
||||||
exec(
|
exec('restic', ['restore', 'latest', '--path', resolve(from), '--target', to], { env: getEnvFromBackend(backend) })
|
||||||
'restic',
|
|
||||||
['restore', 'latest', '--path', resolve(from), '--target', to],
|
|
||||||
{ env: getEnvFromBackend(backend) },
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const restoreToVolume = (volume: string, backend: Backend) => {
|
export const restoreToVolume = (volume: string, backend: Backend) => {
|
||||||
const tmp = getPathFromVolume(volume)
|
const tmp = getPathFromVolume(volume)
|
||||||
try {
|
try {
|
||||||
restoreToFilesystem(tmp, tmp, backend)
|
restoreToFilesystem(tmp, tmp, backend)
|
||||||
try {
|
try {
|
||||||
checkIfDockerVolumeExistsOrFail(volume)
|
checkIfDockerVolumeExistsOrFail(volume)
|
||||||
} catch {
|
} catch {
|
||||||
execPlain(`docker volume create ${volume}`)
|
execPlain(`docker volume create ${volume}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
// For incremental backups. Unfortunately due to how the docker mounts work the permissions get lost.
|
// For incremental backups. Unfortunately due to how the docker mounts work the permissions get lost.
|
||||||
// execPlain(`docker run --rm -v ${volume}:/data -v ${tmp}:/backup alpine cp -aT /backup /data`)
|
// execPlain(`docker run --rm -v ${volume}:/data -v ${tmp}:/backup alpine cp -aT /backup /data`)
|
||||||
execPlain(`docker run --rm -v ${volume}:/data -v ${tmp}:/backup alpine tar xf /backup/archive.tar -C /data`)
|
execPlain(`docker run --rm -v ${volume}:/data -v ${tmp}:/backup alpine tar xf /backup/archive.tar -C /data`)
|
||||||
} finally {
|
} finally {
|
||||||
execPlain(`rm -rf ${tmp}`)
|
execPlain(`rm -rf ${tmp}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const restoreSingle = (locationName: string, from: string, to?: string) => {
|
export const restoreSingle = (locationName: string, from: string, to?: string) => {
|
||||||
const location = config.locations[locationName]
|
const location = config.locations[locationName]
|
||||||
|
|
||||||
const baseText = locationName.green + '\t\t'
|
const baseText = locationName.green + '\t\t'
|
||||||
const w = new Writer(baseText + `Restoring...`)
|
const w = new Writer(baseText + `Restoring...`)
|
||||||
|
|
||||||
let backendName: string = Array.isArray(location.to) ? location.to[0] : location.to
|
let backendName: string = Array.isArray(location.to) ? location.to[0] : location.to
|
||||||
if (from) {
|
if (from) {
|
||||||
if (!location.to.includes(from)) {
|
if (!location.to.includes(from)) {
|
||||||
w.done(baseText + `Backend ${from} is not a valid location for ${locationName}`.red)
|
w.done(baseText + `Backend ${from} is not a valid location for ${locationName}`.red)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
backendName = from
|
backendName = from
|
||||||
w.replaceLn(baseText + `Restoring from ${backendName.blue}...`)
|
w.replaceLn(baseText + `Restoring from ${backendName.blue}...`)
|
||||||
} else if (Array.isArray(location.to) && location.to.length > 1) {
|
} else if (Array.isArray(location.to) && location.to.length > 1) {
|
||||||
w.replaceLn(baseText + `Restoring from ${backendName.blue}...\tTo select a specific backend pass the ${'--from'.blue} flag`)
|
w.replaceLn(baseText + `Restoring from ${backendName.blue}...\tTo select a specific backend pass the ${'--from'.blue} flag`)
|
||||||
}
|
}
|
||||||
const backend = config.backends[backendName]
|
const backend = config.backends[backendName]
|
||||||
|
|
||||||
const [type, value] = decodeLocationFromPrefix(location.from)
|
const [type, value] = decodeLocationFromPrefix(location.from)
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
case LocationFromPrefixes.Filesystem:
|
||||||
|
if (!to) throw new Error(`You need to specify the restore path with --to`.red)
|
||||||
|
restoreToFilesystem(value, to, backend)
|
||||||
|
break
|
||||||
|
|
||||||
case LocationFromPrefixes.Filesystem:
|
case LocationFromPrefixes.DockerVolume:
|
||||||
if (!to) throw new Error(`You need to specify the restore path with --to`.red)
|
restoreToVolume(value, backend)
|
||||||
restoreToFilesystem(value, to, backend)
|
break
|
||||||
break
|
}
|
||||||
|
w.done(locationName.green + '\t\tDone 🎉')
|
||||||
case LocationFromPrefixes.DockerVolume:
|
|
||||||
restoreToVolume(value, backend)
|
|
||||||
break
|
|
||||||
|
|
||||||
}
|
|
||||||
w.done(locationName.green + '\t\tDone 🎉')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
113
src/types.ts
113
src/types.ts
@ -3,84 +3,77 @@ export type StringOrArray = string | string[]
|
|||||||
// BACKENDS
|
// BACKENDS
|
||||||
|
|
||||||
type BackendLocal = {
|
type BackendLocal = {
|
||||||
type: 'local'
|
type: 'local'
|
||||||
key: string
|
key: string
|
||||||
path: string
|
path: string
|
||||||
}
|
}
|
||||||
|
|
||||||
type BackendSFTP = {
|
type BackendSFTP = {
|
||||||
type: 'sftp'
|
type: 'sftp'
|
||||||
key: string
|
key: string
|
||||||
path: string
|
path: string
|
||||||
password?: string
|
password?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
type BackendREST = {
|
type BackendREST = {
|
||||||
type: 'rest'
|
type: 'rest'
|
||||||
key: string
|
key: string
|
||||||
path: string
|
path: string
|
||||||
user?: string
|
user?: string
|
||||||
password?: string
|
password?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
type BackendS3 = {
|
type BackendS3 = {
|
||||||
type: 's3'
|
type: 's3'
|
||||||
key: string
|
key: string
|
||||||
path: string
|
path: string
|
||||||
aws_access_key_id: string
|
aws_access_key_id: string
|
||||||
aws_secret_access_key: string
|
aws_secret_access_key: string
|
||||||
}
|
}
|
||||||
|
|
||||||
type BackendB2 = {
|
type BackendB2 = {
|
||||||
type: 'b2'
|
type: 'b2'
|
||||||
key: string
|
key: string
|
||||||
path: string
|
path: string
|
||||||
b2_account_id: string
|
b2_account_id: string
|
||||||
b2_account_key: string
|
b2_account_key: string
|
||||||
}
|
}
|
||||||
|
|
||||||
type BackendAzure = {
|
type BackendAzure = {
|
||||||
type: 'azure'
|
type: 'azure'
|
||||||
key: string
|
key: string
|
||||||
path: string
|
path: string
|
||||||
azure_account_name: string
|
azure_account_name: string
|
||||||
azure_account_key: string
|
azure_account_key: string
|
||||||
}
|
}
|
||||||
|
|
||||||
type BackendGS = {
|
type BackendGS = {
|
||||||
type: 'gs'
|
type: 'gs'
|
||||||
key: string
|
key: string
|
||||||
path: string
|
path: string
|
||||||
google_project_id: string
|
google_project_id: string
|
||||||
google_application_credentials: string
|
google_application_credentials: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Backend =
|
export type Backend = BackendAzure | BackendB2 | BackendGS | BackendLocal | BackendREST | BackendS3 | BackendSFTP
|
||||||
| BackendAzure
|
|
||||||
| BackendB2
|
|
||||||
| BackendGS
|
|
||||||
| BackendLocal
|
|
||||||
| BackendREST
|
|
||||||
| BackendS3
|
|
||||||
| BackendSFTP
|
|
||||||
|
|
||||||
export type Backends = { [name: string]: Backend }
|
export type Backends = { [name: string]: Backend }
|
||||||
|
|
||||||
// LOCATIONS
|
// LOCATIONS
|
||||||
|
|
||||||
export type Location = {
|
export type Location = {
|
||||||
from: string
|
from: string
|
||||||
to: StringOrArray
|
to: StringOrArray
|
||||||
cron?: string
|
cron?: string
|
||||||
hooks?: {
|
hooks?: {
|
||||||
before?: StringOrArray
|
before?: StringOrArray
|
||||||
after?: StringOrArray
|
after?: StringOrArray
|
||||||
}
|
}
|
||||||
options?: {
|
options?: {
|
||||||
[key: string]: {
|
[key: string]: {
|
||||||
[key: string]: StringOrArray
|
[key: string]: StringOrArray
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Locations = { [name: string]: Location }
|
export type Locations = { [name: string]: Location }
|
||||||
@ -88,17 +81,17 @@ export type Locations = { [name: string]: Location }
|
|||||||
// OTHER
|
// OTHER
|
||||||
|
|
||||||
export type Config = {
|
export type Config = {
|
||||||
locations: Locations
|
locations: Locations
|
||||||
backends: Backends
|
backends: Backends
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Lockfile = {
|
export type Lockfile = {
|
||||||
running: boolean
|
running: boolean
|
||||||
crons: {
|
crons: {
|
||||||
[name: string]: {
|
[name: string]: {
|
||||||
lastRun: number
|
lastRun: number
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Flags = { [arg: string]: any }
|
export type Flags = { [arg: string]: any }
|
||||||
|
35
src/utils.ts
35
src/utils.ts
@ -1,13 +1,15 @@
|
|||||||
import axios from 'axios'
|
|
||||||
import { spawnSync, SpawnSyncOptions } from 'child_process'
|
import { spawnSync, SpawnSyncOptions } from 'child_process'
|
||||||
import { createHash, randomBytes } from 'crypto'
|
import { createHash, randomBytes } from 'crypto'
|
||||||
import { createWriteStream, renameSync, unlinkSync } from 'fs'
|
import { createWriteStream, renameSync, unlinkSync } from 'fs'
|
||||||
import { homedir, tmpdir } from 'os'
|
import { homedir, tmpdir } from 'os'
|
||||||
import { dirname, isAbsolute, join, resolve } from 'path'
|
import { dirname, isAbsolute, join, resolve } from 'path'
|
||||||
|
|
||||||
|
import axios from 'axios'
|
||||||
import { Duration, Humanizer } from 'uhrwerk'
|
import { Duration, Humanizer } from 'uhrwerk'
|
||||||
|
|
||||||
import { CONFIG_FILE, LocationFromPrefixes } from './config'
|
import { CONFIG_FILE, LocationFromPrefixes } from './config'
|
||||||
import { Location } from './types'
|
import { Backends, Location, Locations } from './types'
|
||||||
|
import { config } from '.'
|
||||||
|
|
||||||
export const exec = (command: string, args: string[], { env, ...rest }: SpawnSyncOptions = {}) => {
|
export const exec = (command: string, args: string[], { env, ...rest }: SpawnSyncOptions = {}) => {
|
||||||
const { stdout, stderr, status } = spawnSync(command, args, {
|
const { stdout, stderr, status } = spawnSync(command, args, {
|
||||||
@ -106,6 +108,35 @@ export const getFlagsFromLocation = (location: Location, command?: string): stri
|
|||||||
return flags
|
return flags
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function parseBackend(backends: string[] = [], all: boolean = false): Backends {
|
||||||
|
if (all) return config.backends
|
||||||
|
if (backends.length) {
|
||||||
|
for (const backend of backends) if (!config.backends[backend]) throw new Error('Invalid backend: '.red + backend)
|
||||||
|
return filterObjectByKey(config.backends, backends)
|
||||||
|
} else {
|
||||||
|
throw new Error(
|
||||||
|
'No backends specified.'.red + '\n-a, --all, -a\t\t\tSelect all.' + '\n-b, --backend <backends...>\t\tSpecify one or more backend'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function checkIfValidLocation(location: string) {
|
||||||
|
if (!config.locations[location]) throw new Error('Invalid location: '.red + location)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parseLocations(locations: string[] = [], all: boolean = false): Locations {
|
||||||
|
if (all) {
|
||||||
|
return config.locations
|
||||||
|
}
|
||||||
|
if (locations.length) {
|
||||||
|
for (const location of locations) checkIfValidLocation(location)
|
||||||
|
return filterObjectByKey(config.locations, locations)
|
||||||
|
}
|
||||||
|
throw new Error(
|
||||||
|
'No locations specified.'.red + '\n-a, --all\t\t\tSelect all.' + '\n-l, --location <locations...>\t\t\tSpecify one or more location'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export const makeArrayIfIsNot = <T>(maybeArray: T | T[]): T[] => (Array.isArray(maybeArray) ? maybeArray : [maybeArray])
|
export const makeArrayIfIsNot = <T>(maybeArray: T | T[]): T[] => (Array.isArray(maybeArray) ? maybeArray : [maybeArray])
|
||||||
|
|
||||||
export const fill = (length: number, filler = ' '): string => new Array(length).fill(filler).join('')
|
export const fill = (length: number, filler = ' '): string => new Array(length).fill(filler).join('')
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
"target": "esnext",
|
"target": "esnext",
|
||||||
"module": "commonjs",
|
"module": "commonjs",
|
||||||
"outDir": "./lib",
|
"outDir": "./lib",
|
||||||
|
"rootDir": "./src",
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"esModuleInterop": true
|
"esModuleInterop": true
|
||||||
}
|
}
|
||||||
|
233
yarn.lock
233
yarn.lock
@ -3,21 +3,21 @@
|
|||||||
|
|
||||||
|
|
||||||
"@babel/parser@^7.9.4":
|
"@babel/parser@^7.9.4":
|
||||||
version "7.10.3"
|
version "7.12.5"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.10.3.tgz#7e71d892b0d6e7d04a1af4c3c79d72c1f10f5315"
|
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.12.5.tgz#b4af32ddd473c0bfa643bd7ff0728b8e71b81ea0"
|
||||||
integrity sha512-oJtNJCMFdIMwXGmx+KxuaD7i3b8uS7TTFYW/FNG2BT8m+fmGHoiPYoH0Pe3gya07WuFmM5FCDIr1x0irkD/hyA==
|
integrity sha512-FVM6RZQ0mn2KCf1VUED7KepYeUWoVShczewOCfm3nzoBybaih51h+sYVVGthW9M6lPByEPTQf+xm27PBdlpwmQ==
|
||||||
|
|
||||||
"@babel/runtime@^7.9.2":
|
"@babel/runtime@^7.9.2":
|
||||||
version "7.10.3"
|
version "7.12.5"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.10.3.tgz#670d002655a7c366540c67f6fd3342cd09500364"
|
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.5.tgz#410e7e487441e1b360c29be715d870d9b985882e"
|
||||||
integrity sha512-RzGO0RLSdokm9Ipe/YD+7ww8X2Ro79qiXZF3HU9ljrM+qnJmH1Vqth+hbiQZy761LnMJTMitHDuKVYTk3k4dLw==
|
integrity sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==
|
||||||
dependencies:
|
dependencies:
|
||||||
regenerator-runtime "^0.13.4"
|
regenerator-runtime "^0.13.4"
|
||||||
|
|
||||||
"@codedoc/cli@0.2.x":
|
"@codedoc/cli@0.2.x":
|
||||||
version "0.2.1"
|
version "0.2.6"
|
||||||
resolved "https://registry.yarnpkg.com/@codedoc/cli/-/cli-0.2.1.tgz#03daa1b1e0cedff25c0b5dc29ab8c823b2dd28ef"
|
resolved "https://registry.yarnpkg.com/@codedoc/cli/-/cli-0.2.6.tgz#5e6e981f8eafefcab2b8c81ee6c14815baf2732c"
|
||||||
integrity sha512-kdxbcKaxM3AUEoD70trMrUFitdbJ8mTS7u5ojJfzZ2uQtTIjO1D0XJLydsPyPXqLQf/TyOZJPEm03Q3d9Smt0A==
|
integrity sha512-zn92PvMamXCteZudz1i606qa4bPXcXveNgb6oQTAiW7Lq+bRMZHUyKvA3Q3jVwlQtV7SidJg3jA4RwkMllktog==
|
||||||
dependencies:
|
dependencies:
|
||||||
chalk "^4.0.0"
|
chalk "^4.0.0"
|
||||||
shelljs "^0.8.3"
|
shelljs "^0.8.3"
|
||||||
@ -46,11 +46,6 @@
|
|||||||
"@nodelib/fs.scandir" "2.1.3"
|
"@nodelib/fs.scandir" "2.1.3"
|
||||||
fastq "^1.6.0"
|
fastq "^1.6.0"
|
||||||
|
|
||||||
"@types/color-name@^1.1.1":
|
|
||||||
version "1.1.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0"
|
|
||||||
integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==
|
|
||||||
|
|
||||||
"@types/js-yaml@3.x.x":
|
"@types/js-yaml@3.x.x":
|
||||||
version "3.12.5"
|
version "3.12.5"
|
||||||
resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-3.12.5.tgz#136d5e6a57a931e1cce6f9d8126aa98a9c92a6bb"
|
resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-3.12.5.tgz#136d5e6a57a931e1cce6f9d8126aa98a9c92a6bb"
|
||||||
@ -62,9 +57,9 @@
|
|||||||
integrity sha1-aaI6OtKcrwCX8G7aWbNh7i8GOfY=
|
integrity sha1-aaI6OtKcrwCX8G7aWbNh7i8GOfY=
|
||||||
|
|
||||||
"@types/node@14.x.x":
|
"@types/node@14.x.x":
|
||||||
version "14.0.14"
|
version "14.14.6"
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.0.14.tgz#24a0b5959f16ac141aeb0c5b3cd7a15b7c64cbce"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.6.tgz#146d3da57b3c636cc0d1769396ce1cfa8991147f"
|
||||||
integrity sha512-syUgf67ZQpaJj01/tRTknkMNoBBLWJOBODF0Zm4NrXmiSuxjymFrxnTu1QVYRubhVkRcZLYZG8STTwJRdVm/WQ==
|
integrity sha512-6QlRuqsQ/Ox/aJEQWBEJG7A9+u7oSYl3mem/K8IzxXG/kAGbV1YPD9Bg9Zw3vyxC/YP+zONKwy8hGkSt1jxFMw==
|
||||||
|
|
||||||
"@types/strip-bom@^3.0.0":
|
"@types/strip-bom@^3.0.0":
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
@ -76,10 +71,10 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz#9aa30c04db212a9a0649d6ae6fd50accc40748a1"
|
resolved "https://registry.yarnpkg.com/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz#9aa30c04db212a9a0649d6ae6fd50accc40748a1"
|
||||||
integrity sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==
|
integrity sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==
|
||||||
|
|
||||||
ajv@^6.5.5:
|
ajv@^6.12.3:
|
||||||
version "6.12.2"
|
version "6.12.6"
|
||||||
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.2.tgz#c629c5eced17baf314437918d2da88c99d5958cd"
|
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
|
||||||
integrity sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==
|
integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
|
||||||
dependencies:
|
dependencies:
|
||||||
fast-deep-equal "^3.1.1"
|
fast-deep-equal "^3.1.1"
|
||||||
fast-json-stable-stringify "^2.0.0"
|
fast-json-stable-stringify "^2.0.0"
|
||||||
@ -87,11 +82,10 @@ ajv@^6.5.5:
|
|||||||
uri-js "^4.2.2"
|
uri-js "^4.2.2"
|
||||||
|
|
||||||
ansi-styles@^4.1.0:
|
ansi-styles@^4.1.0:
|
||||||
version "4.2.1"
|
version "4.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.1.tgz#90ae75c424d008d2624c5bf29ead3177ebfcf359"
|
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
|
||||||
integrity sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==
|
integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/color-name" "^1.1.1"
|
|
||||||
color-convert "^2.0.1"
|
color-convert "^2.0.1"
|
||||||
|
|
||||||
anymatch@~3.1.1:
|
anymatch@~3.1.1:
|
||||||
@ -147,9 +141,9 @@ aws-sign2@~0.7.0:
|
|||||||
integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=
|
integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=
|
||||||
|
|
||||||
aws4@^1.8.0:
|
aws4@^1.8.0:
|
||||||
version "1.10.0"
|
version "1.11.0"
|
||||||
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.10.0.tgz#a17b3a8ea811060e74d47d306122400ad4497ae2"
|
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59"
|
||||||
integrity sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA==
|
integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==
|
||||||
|
|
||||||
axios@0.19.x:
|
axios@0.19.x:
|
||||||
version "0.19.2"
|
version "0.19.2"
|
||||||
@ -235,9 +229,9 @@ chalk@^4.0.0:
|
|||||||
supports-color "^7.1.0"
|
supports-color "^7.1.0"
|
||||||
|
|
||||||
chokidar@^3.4.0:
|
chokidar@^3.4.0:
|
||||||
version "3.4.0"
|
version "3.4.3"
|
||||||
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.0.tgz#b30611423ce376357c765b9b8f904b9fba3c0be8"
|
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.3.tgz#c1df38231448e45ca4ac588e6c79573ba6a57d5b"
|
||||||
integrity sha512-aXAaho2VJtisB/1fg1+3nlLJqGOuewTzQpd/Tz0yTg2R0e4IGtshYvtjowyEumcBv2z+y4+kc75Mz7j5xJskcQ==
|
integrity sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
anymatch "~3.1.1"
|
anymatch "~3.1.1"
|
||||||
braces "~3.0.2"
|
braces "~3.0.2"
|
||||||
@ -245,7 +239,7 @@ chokidar@^3.4.0:
|
|||||||
is-binary-path "~2.1.0"
|
is-binary-path "~2.1.0"
|
||||||
is-glob "~4.0.1"
|
is-glob "~4.0.1"
|
||||||
normalize-path "~3.0.0"
|
normalize-path "~3.0.0"
|
||||||
readdirp "~3.4.0"
|
readdirp "~3.5.0"
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
fsevents "~2.1.2"
|
fsevents "~2.1.2"
|
||||||
|
|
||||||
@ -280,6 +274,11 @@ combined-stream@^1.0.6, combined-stream@~1.0.6:
|
|||||||
dependencies:
|
dependencies:
|
||||||
delayed-stream "~1.0.0"
|
delayed-stream "~1.0.0"
|
||||||
|
|
||||||
|
commander@^6.2.0:
|
||||||
|
version "6.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.0.tgz#b990bfb8ac030aedc6d11bc04d1488ffef56db75"
|
||||||
|
integrity sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q==
|
||||||
|
|
||||||
concat-map@0.0.1:
|
concat-map@0.0.1:
|
||||||
version "0.0.1"
|
version "0.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
||||||
@ -291,9 +290,9 @@ core-util-is@1.0.2, core-util-is@~1.0.0:
|
|||||||
integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
|
integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
|
||||||
|
|
||||||
cron-parser@2.x.x:
|
cron-parser@2.x.x:
|
||||||
version "2.15.0"
|
version "2.17.0"
|
||||||
resolved "https://registry.yarnpkg.com/cron-parser/-/cron-parser-2.15.0.tgz#04803cd51d8efcfcc6f83ac08e60f3f8c40c7ec5"
|
resolved "https://registry.yarnpkg.com/cron-parser/-/cron-parser-2.17.0.tgz#5707421a7e0a73ee74675d1c032a2f14123f2cf8"
|
||||||
integrity sha512-rMFkrQw8+oG5OuwjiXesup4KeIlEG/IU82YtG4xyAHbO5jhKmYaHPp/ZNhq9+7TjSJ65E3zV3kQPUbmXSff2/g==
|
integrity sha512-oTmzVEwlurRe51HqTm4afshVr8Rkxy9kFiWxh5e6SmrY2o9NDYU4S6SduanBZYXLgkLy0skA98y7/tztW/DmjQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
is-nan "^1.3.0"
|
is-nan "^1.3.0"
|
||||||
moment-timezone "^0.5.31"
|
moment-timezone "^0.5.31"
|
||||||
@ -458,9 +457,9 @@ fast-levenshtein@~2.0.6:
|
|||||||
integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=
|
integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=
|
||||||
|
|
||||||
fastq@^1.6.0:
|
fastq@^1.6.0:
|
||||||
version "1.8.0"
|
version "1.9.0"
|
||||||
resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.8.0.tgz#550e1f9f59bbc65fe185cb6a9b4d95357107f481"
|
resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.9.0.tgz#e16a72f338eaca48e91b5c23593bcc2ef66b7947"
|
||||||
integrity sha512-SMIZoZdLh/fgofivvIkmknUXyPnvxRE3DhtZ5Me3Mrsk5gyPL42F0xr51TdRXskBxHfMp+07bcYzfsYEsSQA9Q==
|
integrity sha512-i7FVWL8HhVY+CTkwFxkN2mk3h+787ixS5S63eb78diVRc1MCssarHq3W5cj0av7YDSwmaV928RNag+U1etRQ7w==
|
||||||
dependencies:
|
dependencies:
|
||||||
reusify "^1.0.4"
|
reusify "^1.0.4"
|
||||||
|
|
||||||
@ -527,6 +526,11 @@ fsevents@~2.1.2:
|
|||||||
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e"
|
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e"
|
||||||
integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==
|
integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==
|
||||||
|
|
||||||
|
function-bind@^1.1.1:
|
||||||
|
version "1.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
|
||||||
|
integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
|
||||||
|
|
||||||
get-stdin@^4.0.1:
|
get-stdin@^4.0.1:
|
||||||
version "4.0.1"
|
version "4.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe"
|
resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe"
|
||||||
@ -575,22 +579,17 @@ graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0:
|
|||||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb"
|
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb"
|
||||||
integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==
|
integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==
|
||||||
|
|
||||||
growly@^1.3.0:
|
|
||||||
version "1.3.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081"
|
|
||||||
integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=
|
|
||||||
|
|
||||||
har-schema@^2.0.0:
|
har-schema@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92"
|
resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92"
|
||||||
integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=
|
integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=
|
||||||
|
|
||||||
har-validator@~5.1.3:
|
har-validator@~5.1.3:
|
||||||
version "5.1.3"
|
version "5.1.5"
|
||||||
resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080"
|
resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd"
|
||||||
integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==
|
integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==
|
||||||
dependencies:
|
dependencies:
|
||||||
ajv "^6.5.5"
|
ajv "^6.12.3"
|
||||||
har-schema "^2.0.0"
|
har-schema "^2.0.0"
|
||||||
|
|
||||||
has-flag@^4.0.0:
|
has-flag@^4.0.0:
|
||||||
@ -598,6 +597,13 @@ has-flag@^4.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
|
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
|
||||||
integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
|
integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
|
||||||
|
|
||||||
|
has@^1.0.3:
|
||||||
|
version "1.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
|
||||||
|
integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
|
||||||
|
dependencies:
|
||||||
|
function-bind "^1.1.1"
|
||||||
|
|
||||||
hosted-git-info@^2.1.4:
|
hosted-git-info@^2.1.4:
|
||||||
version "2.8.8"
|
version "2.8.8"
|
||||||
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488"
|
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488"
|
||||||
@ -662,6 +668,13 @@ is-binary-path@~2.1.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
binary-extensions "^2.0.0"
|
binary-extensions "^2.0.0"
|
||||||
|
|
||||||
|
is-core-module@^2.0.0:
|
||||||
|
version "2.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.1.0.tgz#a4cc031d9b1aca63eecbd18a650e13cb4eeab946"
|
||||||
|
integrity sha512-YcV7BgVMRFRua2FqQzKtTDMz8iCuLEyGKjr70q8Zm1yy2qKcurbFEd79PAdHV77oL3NrAaOVQIbMmiHQCHB7ZA==
|
||||||
|
dependencies:
|
||||||
|
has "^1.0.3"
|
||||||
|
|
||||||
is-extglob@^2.1.1:
|
is-extglob@^2.1.1:
|
||||||
version "2.1.1"
|
version "2.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
|
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
|
||||||
@ -701,21 +714,11 @@ is-utf8@^0.2.0:
|
|||||||
resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72"
|
resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72"
|
||||||
integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=
|
integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=
|
||||||
|
|
||||||
is-wsl@^1.1.0:
|
|
||||||
version "1.1.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d"
|
|
||||||
integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=
|
|
||||||
|
|
||||||
isarray@~1.0.0:
|
isarray@~1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
|
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
|
||||||
integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
|
integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
|
||||||
|
|
||||||
isexe@^2.0.0:
|
|
||||||
version "2.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
|
|
||||||
integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
|
|
||||||
|
|
||||||
isstream@~0.1.2:
|
isstream@~0.1.2:
|
||||||
version "0.1.2"
|
version "0.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
|
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
|
||||||
@ -851,7 +854,7 @@ minimatch@^3.0.4:
|
|||||||
dependencies:
|
dependencies:
|
||||||
brace-expansion "^1.1.7"
|
brace-expansion "^1.1.7"
|
||||||
|
|
||||||
minimist@1.x.x, minimist@^1.1.3, minimist@^1.2.5:
|
minimist@^1.1.3, minimist@^1.2.5:
|
||||||
version "1.2.5"
|
version "1.2.5"
|
||||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
|
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
|
||||||
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
|
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
|
||||||
@ -876,9 +879,9 @@ moment-timezone@^0.5.31:
|
|||||||
moment ">= 2.9.0"
|
moment ">= 2.9.0"
|
||||||
|
|
||||||
"moment@>= 2.9.0":
|
"moment@>= 2.9.0":
|
||||||
version "2.27.0"
|
version "2.29.1"
|
||||||
resolved "https://registry.yarnpkg.com/moment/-/moment-2.27.0.tgz#8bff4e3e26a236220dfe3e36de756b6ebaa0105d"
|
resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3"
|
||||||
integrity sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ==
|
integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==
|
||||||
|
|
||||||
ms@2.0.0:
|
ms@2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
@ -893,17 +896,6 @@ multistream@^2.1.1:
|
|||||||
inherits "^2.0.1"
|
inherits "^2.0.1"
|
||||||
readable-stream "^2.0.5"
|
readable-stream "^2.0.5"
|
||||||
|
|
||||||
node-notifier@^5.4.0:
|
|
||||||
version "5.4.3"
|
|
||||||
resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-5.4.3.tgz#cb72daf94c93904098e28b9c590fd866e464bd50"
|
|
||||||
integrity sha512-M4UBGcs4jeOK9CjTsYwkvH6/MzuUmGCyTW+kCY7uO+1ZVr0+FHGdPdIf5CCLqAaxnRrWidyoQlNkMIIVwbKB8Q==
|
|
||||||
dependencies:
|
|
||||||
growly "^1.3.0"
|
|
||||||
is-wsl "^1.1.0"
|
|
||||||
semver "^5.5.0"
|
|
||||||
shellwords "^0.1.1"
|
|
||||||
which "^1.3.0"
|
|
||||||
|
|
||||||
normalize-package-data@^2.3.2, normalize-package-data@^2.3.4:
|
normalize-package-data@^2.3.2, normalize-package-data@^2.3.4:
|
||||||
version "2.5.0"
|
version "2.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8"
|
resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8"
|
||||||
@ -1028,7 +1020,7 @@ pinkie@^2.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870"
|
resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870"
|
||||||
integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA=
|
integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA=
|
||||||
|
|
||||||
pkg-fetch@^2.6.7:
|
pkg-fetch@^2.6.9:
|
||||||
version "2.6.9"
|
version "2.6.9"
|
||||||
resolved "https://registry.yarnpkg.com/pkg-fetch/-/pkg-fetch-2.6.9.tgz#c18c5fa9604c57a3df3d9630afb64e176bc8732d"
|
resolved "https://registry.yarnpkg.com/pkg-fetch/-/pkg-fetch-2.6.9.tgz#c18c5fa9604c57a3df3d9630afb64e176bc8732d"
|
||||||
integrity sha512-EnVR8LRILXBvaNP+wJOSY02c3+qDDfyEyR+aqAHLhcc9PBnbxFT9UZ1+If49goPQzQPn26TzF//fc6KXZ0aXEg==
|
integrity sha512-EnVR8LRILXBvaNP+wJOSY02c3+qDDfyEyR+aqAHLhcc9PBnbxFT9UZ1+If49goPQzQPn26TzF//fc6KXZ0aXEg==
|
||||||
@ -1046,9 +1038,9 @@ pkg-fetch@^2.6.7:
|
|||||||
unique-temp-dir "^1.0.0"
|
unique-temp-dir "^1.0.0"
|
||||||
|
|
||||||
pkg@4.4.x:
|
pkg@4.4.x:
|
||||||
version "4.4.8"
|
version "4.4.9"
|
||||||
resolved "https://registry.yarnpkg.com/pkg/-/pkg-4.4.8.tgz#145fb81f31eebfb90d2010dd2c4b663ca0db4009"
|
resolved "https://registry.yarnpkg.com/pkg/-/pkg-4.4.9.tgz#be04f8d03795772b7c4394724ae7252d7c2a4519"
|
||||||
integrity sha512-Fqqv0iaX48U3CFZxd6Dq6JKe7BrAWbgRAqMJkz/m8W3H5cqJ6suvsUWe5AJPRlN/AhbBYXBJ0XG9QlYPTXcVFA==
|
integrity sha512-FK4GqHtcCY2PPPVaKViU0NyRzpo6gCS7tPKN5b7AkElqjAOCH1bsRKgohEnxThr6DWfTGByGqba2YHGR/BqbmA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/parser" "^7.9.4"
|
"@babel/parser" "^7.9.4"
|
||||||
"@babel/runtime" "^7.9.2"
|
"@babel/runtime" "^7.9.2"
|
||||||
@ -1059,7 +1051,7 @@ pkg@4.4.x:
|
|||||||
into-stream "^5.1.1"
|
into-stream "^5.1.1"
|
||||||
minimist "^1.2.5"
|
minimist "^1.2.5"
|
||||||
multistream "^2.1.1"
|
multistream "^2.1.1"
|
||||||
pkg-fetch "^2.6.7"
|
pkg-fetch "^2.6.9"
|
||||||
progress "^2.0.3"
|
progress "^2.0.3"
|
||||||
resolve "^1.15.1"
|
resolve "^1.15.1"
|
||||||
stream-meter "^1.0.4"
|
stream-meter "^1.0.4"
|
||||||
@ -1124,10 +1116,10 @@ readable-stream@^2.0.0, readable-stream@^2.0.5, readable-stream@^2.1.4:
|
|||||||
string_decoder "~1.1.1"
|
string_decoder "~1.1.1"
|
||||||
util-deprecate "~1.0.1"
|
util-deprecate "~1.0.1"
|
||||||
|
|
||||||
readdirp@~3.4.0:
|
readdirp@~3.5.0:
|
||||||
version "3.4.0"
|
version "3.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.4.0.tgz#9fdccdf9e9155805449221ac645e8303ab5b9ada"
|
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.5.0.tgz#9ba74c019b15d365278d2e91bb8c48d7b4d42c9e"
|
||||||
integrity sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==
|
integrity sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
picomatch "^2.2.1"
|
picomatch "^2.2.1"
|
||||||
|
|
||||||
@ -1147,9 +1139,9 @@ redent@^1.0.0:
|
|||||||
strip-indent "^1.0.1"
|
strip-indent "^1.0.1"
|
||||||
|
|
||||||
regenerator-runtime@^0.13.4:
|
regenerator-runtime@^0.13.4:
|
||||||
version "0.13.5"
|
version "0.13.7"
|
||||||
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz#d878a1d094b4306d10b9096484b33ebd55e26697"
|
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55"
|
||||||
integrity sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==
|
integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==
|
||||||
|
|
||||||
repeating@^2.0.0:
|
repeating@^2.0.0:
|
||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
@ -1192,10 +1184,11 @@ request@^2.88.0:
|
|||||||
uuid "^3.3.2"
|
uuid "^3.3.2"
|
||||||
|
|
||||||
resolve@^1.0.0, resolve@^1.1.6, resolve@^1.10.0, resolve@^1.15.1:
|
resolve@^1.0.0, resolve@^1.1.6, resolve@^1.10.0, resolve@^1.15.1:
|
||||||
version "1.17.0"
|
version "1.18.1"
|
||||||
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444"
|
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.18.1.tgz#018fcb2c5b207d2a6424aee361c5a266da8f4130"
|
||||||
integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==
|
integrity sha512-lDfCPaMKfOJXjy0dPayzPdF1phampNWr3qFCjAu+rw/qbQmr5jWH5xN2hwh9QKfw9E5v4hwV7A+jrCmL8yjjqA==
|
||||||
dependencies:
|
dependencies:
|
||||||
|
is-core-module "^2.0.0"
|
||||||
path-parse "^1.0.6"
|
path-parse "^1.0.6"
|
||||||
|
|
||||||
reusify@^1.0.4:
|
reusify@^1.0.4:
|
||||||
@ -1211,9 +1204,9 @@ rimraf@^2.6.1:
|
|||||||
glob "^7.1.3"
|
glob "^7.1.3"
|
||||||
|
|
||||||
run-parallel@^1.1.9:
|
run-parallel@^1.1.9:
|
||||||
version "1.1.9"
|
version "1.1.10"
|
||||||
resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.9.tgz#c9dd3a7cf9f4b2c4b6244e173a6ed866e61dd679"
|
resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.10.tgz#60a51b2ae836636c81377df16cb107351bcd13ef"
|
||||||
integrity sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==
|
integrity sha512-zb/1OuZ6flOlH6tQyMPUrE3x3Ulxjlo9WIVXR4yVYi4H9UXQaeIsPbLn2R3O3vQCnDKkAl2qHiuocKKX4Tz/Sw==
|
||||||
|
|
||||||
safe-buffer@^5.0.1, safe-buffer@^5.1.2:
|
safe-buffer@^5.0.1, safe-buffer@^5.1.2:
|
||||||
version "5.2.1"
|
version "5.2.1"
|
||||||
@ -1230,7 +1223,7 @@ safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
|
|||||||
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
||||||
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
|
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
|
||||||
|
|
||||||
"semver@2 || 3 || 4 || 5", semver@^5.5.0:
|
"semver@2 || 3 || 4 || 5":
|
||||||
version "5.7.1"
|
version "5.7.1"
|
||||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
|
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
|
||||||
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
|
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
|
||||||
@ -1249,11 +1242,6 @@ shelljs@^0.8.3:
|
|||||||
interpret "^1.0.0"
|
interpret "^1.0.0"
|
||||||
rechoir "^0.6.2"
|
rechoir "^0.6.2"
|
||||||
|
|
||||||
shellwords@^0.1.1:
|
|
||||||
version "0.1.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b"
|
|
||||||
integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==
|
|
||||||
|
|
||||||
signal-exit@^3.0.0:
|
signal-exit@^3.0.0:
|
||||||
version "3.0.3"
|
version "3.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c"
|
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c"
|
||||||
@ -1299,9 +1287,9 @@ spdx-expression-parse@^3.0.0:
|
|||||||
spdx-license-ids "^3.0.0"
|
spdx-license-ids "^3.0.0"
|
||||||
|
|
||||||
spdx-license-ids@^3.0.0:
|
spdx-license-ids@^3.0.0:
|
||||||
version "3.0.5"
|
version "3.0.6"
|
||||||
resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz#3694b5804567a458d3c8045842a6358632f62654"
|
resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.6.tgz#c80757383c28abf7296744998cbc106ae8b854ce"
|
||||||
integrity sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==
|
integrity sha512-+orQK83kyMva3WyPf59k1+Y525csj5JejicWut55zeTWANuN17qSiSLUXWtzHeNWORSvT7GLDJ/E/XiIWoXBTw==
|
||||||
|
|
||||||
sprintf-js@~1.0.2:
|
sprintf-js@~1.0.2:
|
||||||
version "1.0.3"
|
version "1.0.3"
|
||||||
@ -1362,9 +1350,9 @@ strip-json-comments@^2.0.0:
|
|||||||
integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo=
|
integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo=
|
||||||
|
|
||||||
supports-color@^7.1.0:
|
supports-color@^7.1.0:
|
||||||
version "7.1.0"
|
version "7.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.1.0.tgz#68e32591df73e25ad1c4b49108a2ec507962bfd1"
|
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
|
||||||
integrity sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==
|
integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
|
||||||
dependencies:
|
dependencies:
|
||||||
has-flag "^4.0.0"
|
has-flag "^4.0.0"
|
||||||
|
|
||||||
@ -1399,24 +1387,23 @@ trim-newlines@^1.0.0:
|
|||||||
integrity sha1-WIeWa7WCpFA6QetST301ARgVphM=
|
integrity sha1-WIeWa7WCpFA6QetST301ARgVphM=
|
||||||
|
|
||||||
ts-node-dev@^1.0.0-pre.40, ts-node-dev@^1.0.0-pre.44:
|
ts-node-dev@^1.0.0-pre.40, ts-node-dev@^1.0.0-pre.44:
|
||||||
version "1.0.0-pre.49"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/ts-node-dev/-/ts-node-dev-1.0.0-pre.49.tgz#28836f73fe9513f339ccb1c0bebdb2e2e25c52f0"
|
resolved "https://registry.yarnpkg.com/ts-node-dev/-/ts-node-dev-1.0.0.tgz#24a2270d225c29ce269de2a31f88b1b259fc84cb"
|
||||||
integrity sha512-iJd4QPPOaCAByl/WuEdmDX8xDR2GmoWYu6aKvGudGUcfP1sJRjpaHb7oFDuvBspQF1xhxUdbjfHuvEtZPwKZFQ==
|
integrity sha512-leA/3TgGtnVU77fGngBwVZztqyDRXirytR7dMtMWZS5b2hGpLl+VDnB0F/gf3A+HEPSzS/KwxgXFP7/LtgX4MQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
chokidar "^3.4.0"
|
chokidar "^3.4.0"
|
||||||
dateformat "~1.0.4-1.2.3"
|
dateformat "~1.0.4-1.2.3"
|
||||||
dynamic-dedupe "^0.3.0"
|
dynamic-dedupe "^0.3.0"
|
||||||
minimist "^1.2.5"
|
minimist "^1.2.5"
|
||||||
mkdirp "^1.0.4"
|
mkdirp "^1.0.4"
|
||||||
node-notifier "^5.4.0"
|
|
||||||
resolve "^1.0.0"
|
resolve "^1.0.0"
|
||||||
rimraf "^2.6.1"
|
rimraf "^2.6.1"
|
||||||
source-map-support "^0.5.12"
|
source-map-support "^0.5.12"
|
||||||
tree-kill "^1.2.2"
|
tree-kill "^1.2.2"
|
||||||
ts-node "^8.10.2"
|
ts-node "^9.0.0"
|
||||||
tsconfig "^7.0.0"
|
tsconfig "^7.0.0"
|
||||||
|
|
||||||
ts-node@^8.10.2, ts-node@^8.8.2:
|
ts-node@^8.8.2:
|
||||||
version "8.10.2"
|
version "8.10.2"
|
||||||
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.10.2.tgz#eee03764633b1234ddd37f8db9ec10b75ec7fb8d"
|
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.10.2.tgz#eee03764633b1234ddd37f8db9ec10b75ec7fb8d"
|
||||||
integrity sha512-ISJJGgkIpDdBhWVu3jufsWpK3Rzo7bdiIXJjQc0ynKxVOVcg2oIrf2H2cejminGrptVc6q6/uynAHNCuWGbpVA==
|
integrity sha512-ISJJGgkIpDdBhWVu3jufsWpK3Rzo7bdiIXJjQc0ynKxVOVcg2oIrf2H2cejminGrptVc6q6/uynAHNCuWGbpVA==
|
||||||
@ -1427,6 +1414,17 @@ ts-node@^8.10.2, ts-node@^8.8.2:
|
|||||||
source-map-support "^0.5.17"
|
source-map-support "^0.5.17"
|
||||||
yn "3.1.1"
|
yn "3.1.1"
|
||||||
|
|
||||||
|
ts-node@^9.0.0:
|
||||||
|
version "9.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-9.0.0.tgz#e7699d2a110cc8c0d3b831715e417688683460b3"
|
||||||
|
integrity sha512-/TqB4SnererCDR/vb4S/QvSZvzQMJN8daAslg7MeaiHvD8rDZsSfXmNeNumyZZzMned72Xoq/isQljYSt8Ynfg==
|
||||||
|
dependencies:
|
||||||
|
arg "^4.1.0"
|
||||||
|
diff "^4.0.1"
|
||||||
|
make-error "^1.1.1"
|
||||||
|
source-map-support "^0.5.17"
|
||||||
|
yn "3.1.1"
|
||||||
|
|
||||||
tsconfig@^7.0.0:
|
tsconfig@^7.0.0:
|
||||||
version "7.0.0"
|
version "7.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/tsconfig/-/tsconfig-7.0.0.tgz#84538875a4dc216e5c4a5432b3a4dec3d54e91b7"
|
resolved "https://registry.yarnpkg.com/tsconfig/-/tsconfig-7.0.0.tgz#84538875a4dc216e5c4a5432b3a4dec3d54e91b7"
|
||||||
@ -1457,9 +1455,9 @@ type-check@~0.3.2:
|
|||||||
prelude-ls "~1.1.2"
|
prelude-ls "~1.1.2"
|
||||||
|
|
||||||
typescript@3.9.x, typescript@^3.8.3:
|
typescript@3.9.x, typescript@^3.8.3:
|
||||||
version "3.9.5"
|
version "3.9.7"
|
||||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.5.tgz#586f0dba300cde8be52dd1ac4f7e1009c1b13f36"
|
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.7.tgz#98d600a5ebdc38f40cb277522f12dc800e9e25fa"
|
||||||
integrity sha512-hSAifV3k+i6lEoCJ2k6R2Z/rp/H3+8sdmcn5NrS3/3kE7+RyZXm9aqvxWqjEXHAd8b0pShatpcdMTvEdvAJltQ==
|
integrity sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==
|
||||||
|
|
||||||
uhrwerk@1.x.x:
|
uhrwerk@1.x.x:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
@ -1486,9 +1484,9 @@ universalify@^0.1.0:
|
|||||||
integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
|
integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
|
||||||
|
|
||||||
uri-js@^4.2.2:
|
uri-js@^4.2.2:
|
||||||
version "4.2.2"
|
version "4.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0"
|
resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.0.tgz#aa714261de793e8a82347a7bcc9ce74e86f28602"
|
||||||
integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==
|
integrity sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==
|
||||||
dependencies:
|
dependencies:
|
||||||
punycode "^2.1.0"
|
punycode "^2.1.0"
|
||||||
|
|
||||||
@ -1519,13 +1517,6 @@ verror@1.10.0:
|
|||||||
core-util-is "1.0.2"
|
core-util-is "1.0.2"
|
||||||
extsprintf "^1.2.0"
|
extsprintf "^1.2.0"
|
||||||
|
|
||||||
which@^1.3.0:
|
|
||||||
version "1.3.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
|
|
||||||
integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
|
|
||||||
dependencies:
|
|
||||||
isexe "^2.0.0"
|
|
||||||
|
|
||||||
word-wrap@~1.2.3:
|
word-wrap@~1.2.3:
|
||||||
version "1.2.3"
|
version "1.2.3"
|
||||||
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
|
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
|
||||||
|
Loading…
Reference in New Issue
Block a user