fix check for executable

This commit is contained in:
cupcakearmy 2020-06-25 09:33:08 +02:00
parent 7128983581
commit 3498e6da7b
No known key found for this signature in database
GPG Key ID: D28129AE5654D9D9

View File

@ -9,179 +9,155 @@ import { Duration, Humanizer } from 'uhrwerk'
import { CONFIG_FILE, LocationFromPrefixes } from './config' import { CONFIG_FILE, LocationFromPrefixes } from './config'
import { Location } from './types' import { Location } from './types'
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, {
...rest, ...rest,
env: { env: {
...process.env, ...process.env,
...env, ...env,
}, },
}) })
const out = stdout && stdout.toString().trim() const out = stdout && stdout.toString().trim()
const err = stderr && stderr.toString().trim() const err = stderr && stderr.toString().trim()
return { out, err, status } return { out, err, status }
} }
export const execPlain = (command: string, opt: SpawnSyncOptions = {}) => { export const execPlain = (command: string, opt: SpawnSyncOptions = {}) => {
const split = command.split(' ') const split = command.split(' ')
if (split.length < 1) throw new Error(`The command ${command} is not valid`.red) if (split.length < 1) throw new Error(`The command ${command} is not valid`.red)
return exec(split[0], split.slice(1), { shell: true, ...opt }) return exec(split[0], split.slice(1), { shell: true, ...opt })
} }
export const checkIfResticIsAvailable = () => export const checkIfResticIsAvailable = () =>
checkIfCommandIsAvailable( checkIfCommandIsAvailable(
'restic', 'restic',
'restic is not installed'.red + 'restic is not installed'.red +
'\nEither run ' + 'autorestic install'.green + '\nEither run ' +
'\nOr go to https://restic.readthedocs.io/en/latest/020_installation.html#stable-releases', 'autorestic install'.green +
) '\nOr go to https://restic.readthedocs.io/en/latest/020_installation.html#stable-releases'
)
export const checkIfCommandIsAvailable = (cmd: string, errorMsg?: string) => { export const checkIfCommandIsAvailable = (cmd: string, errorMsg?: string) => {
if (spawnSync(cmd, { shell: true }).error) const error = spawnSync(cmd, { shell: true }).stderr
throw new Error(errorMsg ? errorMsg : `"${cmd}" is not installed`.red) if (error.length) throw new Error(errorMsg ? errorMsg : `"${cmd}" is not installed`.red)
} }
export const makeObjectKeysLowercase = (object: Object): any => export const makeObjectKeysLowercase = (object: Object): any =>
Object.fromEntries( Object.fromEntries(Object.entries(object).map(([key, value]) => [key.toLowerCase(), value]))
Object.entries(object).map(([key, value]) => [key.toLowerCase(), value]),
)
export function rand(length = 32): string { export function rand(length = 32): string {
return randomBytes(length / 2).toString('hex') return randomBytes(length / 2).toString('hex')
} }
export const filterObject = <T>(obj: { [key: string]: T }, filter: (item: [string, T]) => boolean): { [key: string]: T } =>
Object.fromEntries(Object.entries(obj).filter(filter))
export const filterObject = <T>( export const filterObjectByKey = <T>(obj: { [key: string]: T }, keys: string[]) => filterObject(obj, ([key]) => keys.includes(key))
obj: { [key: string]: T },
filter: (item: [string, T]) => boolean,
): { [key: string]: T } =>
Object.fromEntries(Object.entries(obj).filter(filter))
export const filterObjectByKey = <T>(
obj: { [key: string]: T },
keys: string[],
) => filterObject(obj, ([key]) => keys.includes(key))
export const downloadFile = async (url: string, to: string) => export const downloadFile = async (url: string, to: string) =>
new Promise<void>(async res => { new Promise<void>(async (res) => {
const { data: file } = await axios({ const { data: file } = await axios({
method: 'get', method: 'get',
url: url, url: url,
responseType: 'stream', responseType: 'stream',
}) })
const tmp = join(tmpdir(), rand(64)) const tmp = join(tmpdir(), rand(64))
const stream = createWriteStream(tmp) const stream = createWriteStream(tmp)
const writer = file.pipe(stream) const writer = file.pipe(stream)
writer.on('close', () => { writer.on('close', () => {
stream.close() stream.close()
try { try {
// Delete file if already exists. Needed if the binary wants to replace itself. // Delete file if already exists. Needed if the binary wants to replace itself.
// Unix does not allow to overwrite a file that is being executed, but you can remove it and save other one at its place // Unix does not allow to overwrite a file that is being executed, but you can remove it and save other one at its place
unlinkSync(to) unlinkSync(to)
} catch { } catch {}
} renameSync(tmp, to)
renameSync(tmp, to) res()
res() })
}) })
})
// Check if is an absolute path, otherwise get the path relative to the config file // Check if is an absolute path, otherwise get the path relative to the config file
export const pathRelativeToConfigFile = (path: string): string => isAbsolute(path) export const pathRelativeToConfigFile = (path: string): string => (isAbsolute(path) ? path : resolve(dirname(CONFIG_FILE), path))
? path
: resolve(dirname(CONFIG_FILE), path)
export const resolveTildePath = (path: string): string | null => export const resolveTildePath = (path: string): string | null => (path.length === 0 || path[0] !== '~' ? null : join(homedir(), path.slice(1)))
(path.length === 0 || path[0] !== '~')
? null
: join(homedir(), path.slice(1))
export const getFlagsFromLocation = (location: Location, command?: string): string[] => { export const getFlagsFromLocation = (location: Location, command?: string): string[] => {
if (!location.options) return [] if (!location.options) return []
const all = { const all = {
...location.options.global, ...location.options.global,
...(location.options[command || ''] || {}), ...(location.options[command || ''] || {}),
} }
let flags: string[] = [] let flags: string[] = []
// Map the flags to an array for the exec function. // Map the flags to an array for the exec function.
for (let [flag, values] of Object.entries(all)) for (let [flag, values] of Object.entries(all))
for (const value of makeArrayIfIsNot(values)) { for (const value of makeArrayIfIsNot(values)) {
const stringValue = String(value) const stringValue = String(value)
const resolvedTilde = resolveTildePath(stringValue) const resolvedTilde = resolveTildePath(stringValue)
flags = [...flags, `--${String(flag)}`, resolvedTilde === null ? stringValue : resolvedTilde] flags = [...flags, `--${String(flag)}`, resolvedTilde === null ? stringValue : resolvedTilde]
} }
return flags return flags
} }
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('')
export const capitalize = (string: string): string => string.charAt(0).toUpperCase() + string.slice(1) export const capitalize = (string: string): string => string.charAt(0).toUpperCase() + string.slice(1)
export const treeToString = (obj: Object, highlight = [] as string[]): string => { export const treeToString = (obj: Object, highlight = [] as string[]): string => {
let cleaned = JSON.stringify(obj, null, 2) let cleaned = JSON.stringify(obj, null, 2)
.replace(/[{}"\[\],]/g, '') .replace(/[{}"\[\],]/g, '')
.replace(/^ {2}/mg, '') .replace(/^ {2}/gm, '')
.replace(/\n\s*\n/g, '\n') .replace(/\n\s*\n/g, '\n')
.trim() .trim()
for (const word of highlight) for (const word of highlight) cleaned = cleaned.replace(word, capitalize(word).green)
cleaned = cleaned.replace(word, capitalize(word).green)
return cleaned return cleaned
} }
export class MeasureDuration { export class MeasureDuration {
private static Humanizer: Humanizer = [ private static Humanizer: Humanizer = [
[d => d.hours() > 0, d => `${d.hours()}h ${d.minutes()}min`], [(d) => d.hours() > 0, (d) => `${d.hours()}h ${d.minutes()}min`],
[d => d.minutes() > 0, d => `${d.minutes()}min ${d.seconds()}s`], [(d) => d.minutes() > 0, (d) => `${d.minutes()}min ${d.seconds()}s`],
[d => d.seconds() > 0, d => `${d.seconds()}s`], [(d) => d.seconds() > 0, (d) => `${d.seconds()}s`],
[() => true, d => `${d.milliseconds()}ms`], [() => true, (d) => `${d.milliseconds()}ms`],
] ]
private start = Date.now() private start = Date.now()
finished(human?: false): number
finished(human?: true): string
finished(human?: boolean): number | string {
const delta = Date.now() - this.start
finished(human?: false): number return human ? new Duration(delta, 'ms').humanize(MeasureDuration.Humanizer) : delta
finished(human?: true): string }
finished(human?: boolean): number | string {
const delta = Date.now() - this.start
return human
? new Duration(delta, 'ms').humanize(MeasureDuration.Humanizer)
: delta
}
} }
export const decodeLocationFromPrefix = (from: string): [LocationFromPrefixes, string] => { export const decodeLocationFromPrefix = (from: string): [LocationFromPrefixes, string] => {
const firstDelimiter = from.indexOf(':') const firstDelimiter = from.indexOf(':')
if (firstDelimiter === -1) return [LocationFromPrefixes.Filesystem, from] if (firstDelimiter === -1) return [LocationFromPrefixes.Filesystem, from]
const type = from.substr(0, firstDelimiter) const type = from.substr(0, firstDelimiter)
const value = from.substr(firstDelimiter + 1) const value = from.substr(firstDelimiter + 1)
switch (type.toLowerCase()) { switch (type.toLowerCase()) {
case 'volume': case 'volume':
return [LocationFromPrefixes.DockerVolume, value] return [LocationFromPrefixes.DockerVolume, value]
case 'path': case 'path':
return [LocationFromPrefixes.Filesystem, value] return [LocationFromPrefixes.Filesystem, value]
default: default:
throw new Error(`Could not decode the location from: ${from}`.red) throw new Error(`Could not decode the location from: ${from}`.red)
} }
} }
export const hash = (plain: string): string => createHash('sha1').update(plain).digest().toString('hex') export const hash = (plain: string): string => createHash('sha1').update(plain).digest().toString('hex')
@ -189,9 +165,6 @@ export const hash = (plain: string): string => createHash('sha1').update(plain).
export const getPathFromVolume = (volume: string) => pathRelativeToConfigFile(hash(volume)) export const getPathFromVolume = (volume: string) => pathRelativeToConfigFile(hash(volume))
export const checkIfDockerVolumeExistsOrFail = (volume: string) => { export const checkIfDockerVolumeExistsOrFail = (volume: string) => {
const cmd = exec('docker', [ const cmd = exec('docker', ['volume', 'inspect', volume])
'volume', 'inspect', volume, if (cmd.err.length > 0) throw new Error('Volume not found')
])
if (cmd.err.length > 0)
throw new Error('Volume not found')
} }