mirror of
https://github.com/balena-io/balena-cli.git
synced 2025-01-31 00:24:15 +00:00
Merge pull request #2251 from balena-io/1003-config-inject-umount
config inject/read/write: Fix umount errors with OS image files
This commit is contained in:
commit
f6e6d9ce8b
@ -2330,7 +2330,7 @@ device type (Check available types with `balena devices supported`)
|
|||||||
|
|
||||||
#### -d, --drive DRIVE
|
#### -d, --drive DRIVE
|
||||||
|
|
||||||
device filesystem or OS image location
|
path to OS image file (e.g. balena.img) or block device (e.g. /dev/disk2)
|
||||||
|
|
||||||
## config read
|
## config read
|
||||||
|
|
||||||
@ -2350,7 +2350,7 @@ device type (Check available types with `balena devices supported`)
|
|||||||
|
|
||||||
#### -d, --drive DRIVE
|
#### -d, --drive DRIVE
|
||||||
|
|
||||||
device filesystem or OS image location
|
path to OS image file (e.g. balena.img) or block device (e.g. /dev/disk2)
|
||||||
|
|
||||||
## config reconfigure
|
## config reconfigure
|
||||||
|
|
||||||
@ -2370,7 +2370,7 @@ device type (Check available types with `balena devices supported`)
|
|||||||
|
|
||||||
#### -d, --drive DRIVE
|
#### -d, --drive DRIVE
|
||||||
|
|
||||||
device filesystem or OS image location
|
path to OS image file (e.g. balena.img) or block device (e.g. /dev/disk2)
|
||||||
|
|
||||||
#### -v, --advanced
|
#### -v, --advanced
|
||||||
|
|
||||||
@ -2405,7 +2405,7 @@ device type (Check available types with `balena devices supported`)
|
|||||||
|
|
||||||
#### -d, --drive DRIVE
|
#### -d, --drive DRIVE
|
||||||
|
|
||||||
device filesystem or OS image location
|
path to OS image file (e.g. balena.img) or block device (e.g. /dev/disk2)
|
||||||
|
|
||||||
# Preload
|
# Preload
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* @license
|
* @license
|
||||||
* Copyright 2016-2020 Balena Ltd.
|
* Copyright 2016-2021 Balena Ltd.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -54,16 +54,8 @@ export default class ConfigInjectCmd extends Command {
|
|||||||
public static usage = 'config inject <file>';
|
public static usage = 'config inject <file>';
|
||||||
|
|
||||||
public static flags: flags.Input<FlagsDef> = {
|
public static flags: flags.Input<FlagsDef> = {
|
||||||
type: flags.string({
|
type: cf.deviceType,
|
||||||
description:
|
drive: cf.driveOrImg,
|
||||||
'device type (Check available types with `balena devices supported`)',
|
|
||||||
char: 't',
|
|
||||||
required: true,
|
|
||||||
}),
|
|
||||||
drive: flags.string({
|
|
||||||
description: 'device filesystem or OS image location',
|
|
||||||
char: 'd',
|
|
||||||
}),
|
|
||||||
help: cf.help,
|
help: cf.help,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -76,12 +68,11 @@ export default class ConfigInjectCmd extends Command {
|
|||||||
ConfigInjectCmd,
|
ConfigInjectCmd,
|
||||||
);
|
);
|
||||||
|
|
||||||
const { promisify } = await import('util');
|
const { safeUmount } = await import('../../utils/helpers');
|
||||||
const umountAsync = promisify((await import('umount')).umount);
|
|
||||||
|
|
||||||
const drive =
|
const drive =
|
||||||
options.drive || (await getVisuals().drive('Select the device/OS drive'));
|
options.drive || (await getVisuals().drive('Select the device/OS drive'));
|
||||||
await umountAsync(drive);
|
await safeUmount(drive);
|
||||||
|
|
||||||
const fs = await import('fs');
|
const fs = await import('fs');
|
||||||
const configJSON = JSON.parse(
|
const configJSON = JSON.parse(
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* @license
|
* @license
|
||||||
* Copyright 2016-2020 Balena Ltd.
|
* Copyright 2016-2021 Balena Ltd.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -42,16 +42,8 @@ export default class ConfigReadCmd extends Command {
|
|||||||
public static usage = 'config read';
|
public static usage = 'config read';
|
||||||
|
|
||||||
public static flags: flags.Input<FlagsDef> = {
|
public static flags: flags.Input<FlagsDef> = {
|
||||||
type: flags.string({
|
type: cf.deviceType,
|
||||||
description:
|
drive: cf.driveOrImg,
|
||||||
'device type (Check available types with `balena devices supported`)',
|
|
||||||
char: 't',
|
|
||||||
required: true,
|
|
||||||
}),
|
|
||||||
drive: flags.string({
|
|
||||||
description: 'device filesystem or OS image location',
|
|
||||||
char: 'd',
|
|
||||||
}),
|
|
||||||
help: cf.help,
|
help: cf.help,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -62,12 +54,11 @@ export default class ConfigReadCmd extends Command {
|
|||||||
public async run() {
|
public async run() {
|
||||||
const { flags: options } = this.parse<FlagsDef, {}>(ConfigReadCmd);
|
const { flags: options } = this.parse<FlagsDef, {}>(ConfigReadCmd);
|
||||||
|
|
||||||
const { promisify } = await import('util');
|
const { safeUmount } = await import('../../utils/helpers');
|
||||||
const umountAsync = promisify((await import('umount')).umount);
|
|
||||||
|
|
||||||
const drive =
|
const drive =
|
||||||
options.drive || (await getVisuals().drive('Select the device drive'));
|
options.drive || (await getVisuals().drive('Select the device drive'));
|
||||||
await umountAsync(drive);
|
await safeUmount(drive);
|
||||||
|
|
||||||
const config = await import('balena-config-json');
|
const config = await import('balena-config-json');
|
||||||
const configJSON = await config.read(drive, options.type);
|
const configJSON = await config.read(drive, options.type);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* @license
|
* @license
|
||||||
* Copyright 2016-2020 Balena Ltd.
|
* Copyright 2016-2021 Balena Ltd.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -42,16 +42,8 @@ export default class ConfigReconfigureCmd extends Command {
|
|||||||
public static usage = 'config reconfigure';
|
public static usage = 'config reconfigure';
|
||||||
|
|
||||||
public static flags: flags.Input<FlagsDef> = {
|
public static flags: flags.Input<FlagsDef> = {
|
||||||
type: flags.string({
|
type: cf.deviceType,
|
||||||
description:
|
drive: cf.driveOrImg,
|
||||||
'device type (Check available types with `balena devices supported`)',
|
|
||||||
char: 't',
|
|
||||||
required: true,
|
|
||||||
}),
|
|
||||||
drive: flags.string({
|
|
||||||
description: 'device filesystem or OS image location',
|
|
||||||
char: 'd',
|
|
||||||
}),
|
|
||||||
advanced: flags.boolean({
|
advanced: flags.boolean({
|
||||||
description: 'show advanced commands',
|
description: 'show advanced commands',
|
||||||
char: 'v',
|
char: 'v',
|
||||||
@ -66,16 +58,15 @@ export default class ConfigReconfigureCmd extends Command {
|
|||||||
public async run() {
|
public async run() {
|
||||||
const { flags: options } = this.parse<FlagsDef, {}>(ConfigReconfigureCmd);
|
const { flags: options } = this.parse<FlagsDef, {}>(ConfigReconfigureCmd);
|
||||||
|
|
||||||
const { promisify } = await import('util');
|
const { safeUmount } = await import('../../utils/helpers');
|
||||||
const umountAsync = promisify((await import('umount')).umount);
|
|
||||||
|
|
||||||
const drive =
|
const drive =
|
||||||
options.drive || (await getVisuals().drive('Select the device drive'));
|
options.drive || (await getVisuals().drive('Select the device drive'));
|
||||||
await umountAsync(drive);
|
await safeUmount(drive);
|
||||||
|
|
||||||
const config = await import('balena-config-json');
|
const config = await import('balena-config-json');
|
||||||
const { uuid } = await config.read(drive, options.type);
|
const { uuid } = await config.read(drive, options.type);
|
||||||
await umountAsync(drive);
|
await safeUmount(drive);
|
||||||
|
|
||||||
const configureCommand = ['os', 'configure', drive, '--device', uuid];
|
const configureCommand = ['os', 'configure', drive, '--device', uuid];
|
||||||
if (options.advanced) {
|
if (options.advanced) {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* @license
|
* @license
|
||||||
* Copyright 2016-2020 Balena Ltd.
|
* Copyright 2016-2021 Balena Ltd.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -61,16 +61,8 @@ export default class ConfigWriteCmd extends Command {
|
|||||||
public static usage = 'config write <key> <value>';
|
public static usage = 'config write <key> <value>';
|
||||||
|
|
||||||
public static flags: flags.Input<FlagsDef> = {
|
public static flags: flags.Input<FlagsDef> = {
|
||||||
type: flags.string({
|
type: cf.deviceType,
|
||||||
description:
|
drive: cf.driveOrImg,
|
||||||
'device type (Check available types with `balena devices supported`)',
|
|
||||||
char: 't',
|
|
||||||
required: true,
|
|
||||||
}),
|
|
||||||
drive: flags.string({
|
|
||||||
description: 'device filesystem or OS image location',
|
|
||||||
char: 'd',
|
|
||||||
}),
|
|
||||||
help: cf.help,
|
help: cf.help,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -83,12 +75,11 @@ export default class ConfigWriteCmd extends Command {
|
|||||||
ConfigWriteCmd,
|
ConfigWriteCmd,
|
||||||
);
|
);
|
||||||
|
|
||||||
const { promisify } = await import('util');
|
const { safeUmount } = await import('../../utils/helpers');
|
||||||
const umountAsync = promisify((await import('umount')).umount);
|
|
||||||
|
|
||||||
const drive =
|
const drive =
|
||||||
options.drive || (await getVisuals().drive('Select the device drive'));
|
options.drive || (await getVisuals().drive('Select the device drive'));
|
||||||
await umountAsync(drive);
|
await safeUmount(drive);
|
||||||
|
|
||||||
const config = await import('balena-config-json');
|
const config = await import('balena-config-json');
|
||||||
const configJSON = await config.read(drive, options.type);
|
const configJSON = await config.read(drive, options.type);
|
||||||
@ -97,7 +88,7 @@ export default class ConfigWriteCmd extends Command {
|
|||||||
const _ = await import('lodash');
|
const _ = await import('lodash');
|
||||||
_.set(configJSON, params.key, params.value);
|
_.set(configJSON, params.key, params.value);
|
||||||
|
|
||||||
await umountAsync(drive);
|
await safeUmount(drive);
|
||||||
|
|
||||||
await config.write(drive, options.type, configJSON);
|
await config.write(drive, options.type, configJSON);
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* @license
|
* @license
|
||||||
* Copyright 2016-2020 Balena Ltd.
|
* Copyright 2016-2021 Balena Ltd.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -61,20 +61,16 @@ export default class LocalConfigureCmd extends Command {
|
|||||||
const { args: params } = this.parse<FlagsDef, ArgsDef>(LocalConfigureCmd);
|
const { args: params } = this.parse<FlagsDef, ArgsDef>(LocalConfigureCmd);
|
||||||
|
|
||||||
const path = await import('path');
|
const path = await import('path');
|
||||||
const umount = await import('umount');
|
|
||||||
const umountAsync = promisify(umount.umount);
|
|
||||||
const isMountedAsync = promisify(umount.isMounted);
|
|
||||||
const reconfix = await import('reconfix');
|
const reconfix = await import('reconfix');
|
||||||
const denymount = promisify(await import('denymount'));
|
const denymount = promisify(await import('denymount'));
|
||||||
|
const { safeUmount } = await import('../../utils/helpers');
|
||||||
const Logger = await import('../../utils/logger');
|
const Logger = await import('../../utils/logger');
|
||||||
|
|
||||||
const logger = Logger.getLogger();
|
const logger = Logger.getLogger();
|
||||||
|
|
||||||
const configurationSchema = await this.prepareConnectionFile(params.target);
|
const configurationSchema = await this.prepareConnectionFile(params.target);
|
||||||
|
|
||||||
if (await isMountedAsync(params.target)) {
|
await safeUmount(params.target);
|
||||||
await umountAsync(params.target);
|
|
||||||
}
|
|
||||||
|
|
||||||
const dmOpts: any = {};
|
const dmOpts: any = {};
|
||||||
if (process.pkg) {
|
if (process.pkg) {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* @license
|
* @license
|
||||||
* Copyright 2016-2020 Balena Ltd.
|
* Copyright 2016-2021 Balena Ltd.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -61,12 +61,7 @@ export default class OsInitializeCmd extends Command {
|
|||||||
public static usage = 'os initialize <image>';
|
public static usage = 'os initialize <image>';
|
||||||
|
|
||||||
public static flags: flags.Input<FlagsDef> = {
|
public static flags: flags.Input<FlagsDef> = {
|
||||||
type: flags.string({
|
type: cf.deviceType,
|
||||||
description:
|
|
||||||
'device type (Check available types with `balena devices supported`)',
|
|
||||||
char: 't',
|
|
||||||
required: true,
|
|
||||||
}),
|
|
||||||
drive: cf.drive,
|
drive: cf.drive,
|
||||||
yes: cf.yes,
|
yes: cf.yes,
|
||||||
help: cf.help,
|
help: cf.help,
|
||||||
@ -79,9 +74,9 @@ export default class OsInitializeCmd extends Command {
|
|||||||
OsInitializeCmd,
|
OsInitializeCmd,
|
||||||
);
|
);
|
||||||
|
|
||||||
const { promisify } = await import('util');
|
const { getManifest, safeUmount, sudo } = await import(
|
||||||
const umountAsync = promisify((await import('umount')).umount);
|
'../../utils/helpers'
|
||||||
const { getManifest, sudo } = await import('../../utils/helpers');
|
);
|
||||||
|
|
||||||
console.info(`Initializing device ${INIT_WARNING_MESSAGE}`);
|
console.info(`Initializing device ${INIT_WARNING_MESSAGE}`);
|
||||||
|
|
||||||
@ -101,7 +96,7 @@ export default class OsInitializeCmd extends Command {
|
|||||||
`Going to erase ${answers.drive}.`,
|
`Going to erase ${answers.drive}.`,
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
await umountAsync(answers.drive);
|
await safeUmount(answers.drive);
|
||||||
}
|
}
|
||||||
|
|
||||||
await sudo([
|
await sudo([
|
||||||
@ -113,22 +108,7 @@ export default class OsInitializeCmd extends Command {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
if (answers.drive != null) {
|
if (answers.drive != null) {
|
||||||
// TODO: balena local makes use of ejectAsync, see below
|
await safeUmount(answers.drive);
|
||||||
// DO we need this / should we do that here?
|
|
||||||
|
|
||||||
// getDrive = (drive) ->
|
|
||||||
// driveListAsync().then (drives) ->
|
|
||||||
// selectedDrive = _.find(drives, device: drive)
|
|
||||||
|
|
||||||
// if not selectedDrive?
|
|
||||||
// throw new Error("Drive not found: #{drive}")
|
|
||||||
|
|
||||||
// return selectedDrive
|
|
||||||
// if (os.platform() is 'win32') and selectedDrive.mountpoint?
|
|
||||||
// ejectAsync = Promise.promisify(require('removedrive').eject)
|
|
||||||
// return ejectAsync(selectedDrive.mountpoint)
|
|
||||||
|
|
||||||
await umountAsync(answers.drive);
|
|
||||||
console.info(`You can safely remove ${answers.drive} now`);
|
console.info(`You can safely remove ${answers.drive} now`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* @license
|
* @license
|
||||||
* Copyright 2019 Balena Ltd.
|
* Copyright 2019-2021 Balena Ltd.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -82,6 +82,19 @@ export const drive = flags.string({
|
|||||||
`,
|
`,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const driveOrImg = flags.string({
|
||||||
|
char: 'd',
|
||||||
|
description:
|
||||||
|
'path to OS image file (e.g. balena.img) or block device (e.g. /dev/disk2)',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const deviceType = flags.string({
|
||||||
|
description:
|
||||||
|
'device type (Check available types with `balena devices supported`)',
|
||||||
|
char: 't',
|
||||||
|
required: true,
|
||||||
|
});
|
||||||
|
|
||||||
export const json: IBooleanFlag<boolean> = flags.boolean({
|
export const json: IBooleanFlag<boolean> = flags.boolean({
|
||||||
char: 'j',
|
char: 'j',
|
||||||
description: 'produce JSON output instead of tabular output',
|
description: 'produce JSON output instead of tabular output',
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2016-2020 Balena
|
Copyright 2016-2021 Balena Ltd.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@ -16,16 +16,11 @@ limitations under the License.
|
|||||||
|
|
||||||
import type { InitializeEmitter, OperationState } from 'balena-device-init';
|
import type { InitializeEmitter, OperationState } from 'balena-device-init';
|
||||||
import type * as BalenaSdk from 'balena-sdk';
|
import type * as BalenaSdk from 'balena-sdk';
|
||||||
import { spawn, SpawnOptions } from 'child_process';
|
|
||||||
import * as _ from 'lodash';
|
|
||||||
import * as os from 'os';
|
|
||||||
import type * as ShellEscape from 'shell-escape';
|
|
||||||
|
|
||||||
import type { Device, PineOptions } from 'balena-sdk';
|
import * as _ from 'lodash';
|
||||||
import { ExpectedError, SIGINTError } from '../errors';
|
|
||||||
import { getBalenaSdk, getChalk, getVisuals } from './lazy';
|
|
||||||
import { promisify } from 'util';
|
import { promisify } from 'util';
|
||||||
import { isSubcommand } from '../preparser';
|
|
||||||
|
import { getBalenaSdk, getChalk, getVisuals } from './lazy';
|
||||||
|
|
||||||
export function getGroupDefaults(group: {
|
export function getGroupDefaults(group: {
|
||||||
options: Array<{ name: string; default: string | number }>;
|
options: Array<{ name: string; default: string | number }>;
|
||||||
@ -84,7 +79,7 @@ export async function sudo(
|
|||||||
) {
|
) {
|
||||||
const { executeWithPrivileges } = await import('./sudo');
|
const { executeWithPrivileges } = await import('./sudo');
|
||||||
|
|
||||||
if (os.platform() !== 'win32') {
|
if (process.platform !== 'win32') {
|
||||||
console.log(
|
console.log(
|
||||||
msg ||
|
msg ||
|
||||||
'Admin privileges required: you may be asked for your computer password to continue.',
|
'Admin privileges required: you may be asked for your computer password to continue.',
|
||||||
@ -95,6 +90,9 @@ export async function sudo(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function runCommand<T>(commandArgs: string[]): Promise<T> {
|
export function runCommand<T>(commandArgs: string[]): Promise<T> {
|
||||||
|
const {
|
||||||
|
isSubcommand,
|
||||||
|
} = require('../preparser') as typeof import('../preparser');
|
||||||
if (isSubcommand(commandArgs)) {
|
if (isSubcommand(commandArgs)) {
|
||||||
commandArgs = [
|
commandArgs = [
|
||||||
commandArgs[0] + ':' + commandArgs[1],
|
commandArgs[0] + ':' + commandArgs[1],
|
||||||
@ -238,6 +236,7 @@ export async function retry<T>({
|
|||||||
backoffScaler?: number;
|
backoffScaler?: number;
|
||||||
maxSingleDelayMs?: number;
|
maxSingleDelayMs?: number;
|
||||||
}): Promise<T> {
|
}): Promise<T> {
|
||||||
|
const { SIGINTError } = await import('../errors');
|
||||||
let delayMs = initialDelayMs;
|
let delayMs = initialDelayMs;
|
||||||
for (let count = 0; count < maxAttempts - 1; count++) {
|
for (let count = 0; count < maxAttempts - 1; count++) {
|
||||||
const lastAttemptMs = Date.now();
|
const lastAttemptMs = Date.now();
|
||||||
@ -348,7 +347,7 @@ export function shellEscape(args: string[], detectShell = false): string[] {
|
|||||||
if (isCmdExe) {
|
if (isCmdExe) {
|
||||||
return args.map((v) => windowsCmdExeEscapeArg(v));
|
return args.map((v) => windowsCmdExeEscapeArg(v));
|
||||||
} else {
|
} else {
|
||||||
const shellEscapeFunc: typeof ShellEscape = require('shell-escape');
|
const shellEscapeFunc: typeof import('shell-escape') = require('shell-escape');
|
||||||
return args.map((v) => shellEscapeFunc([v]));
|
return args.map((v) => shellEscapeFunc([v]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -392,6 +391,7 @@ export async function which(
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err.code === 'ENOENT') {
|
if (err.code === 'ENOENT') {
|
||||||
if (rejectOnMissing) {
|
if (rejectOnMissing) {
|
||||||
|
const { ExpectedError } = await import('../errors');
|
||||||
throw new ExpectedError(
|
throw new ExpectedError(
|
||||||
`'${program}' program not found. Is it installed?`,
|
`'${program}' program not found. Is it installed?`,
|
||||||
);
|
);
|
||||||
@ -422,9 +422,10 @@ export async function which(
|
|||||||
export async function whichSpawn(
|
export async function whichSpawn(
|
||||||
programName: string,
|
programName: string,
|
||||||
args: string[],
|
args: string[],
|
||||||
options: SpawnOptions = { stdio: 'inherit' },
|
options: import('child_process').SpawnOptions = { stdio: 'inherit' },
|
||||||
returnExitCodeOrSignal = false,
|
returnExitCodeOrSignal = false,
|
||||||
): Promise<[number | undefined, string | undefined]> {
|
): Promise<[number | undefined, string | undefined]> {
|
||||||
|
const { spawn } = await import('child_process');
|
||||||
const program = await which(programName);
|
const program = await which(programName);
|
||||||
if (process.env.DEBUG) {
|
if (process.env.DEBUG) {
|
||||||
console.error(`[debug] [${program}, ${args.join(', ')}]`);
|
console.error(`[debug] [${program}, ${args.join(', ')}]`);
|
||||||
@ -510,7 +511,7 @@ export function getProxyConfig(): ProxyConfig | undefined {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const expandForAppName: PineOptions<Device> = {
|
export const expandForAppName: BalenaSdk.PineOptions<BalenaSdk.Device> = {
|
||||||
$expand: {
|
$expand: {
|
||||||
belongs_to__application: { $select: 'app_name' },
|
belongs_to__application: { $select: 'app_name' },
|
||||||
is_of__device_type: { $select: 'slug' },
|
is_of__device_type: { $select: 'slug' },
|
||||||
@ -565,6 +566,9 @@ export async function awaitInterruptibleTask<
|
|||||||
let sigintHandler: () => void = () => undefined;
|
let sigintHandler: () => void = () => undefined;
|
||||||
const sigintPromise = new Promise<T>((_resolve, reject) => {
|
const sigintPromise = new Promise<T>((_resolve, reject) => {
|
||||||
sigintHandler = () => {
|
sigintHandler = () => {
|
||||||
|
const {
|
||||||
|
SIGINTError,
|
||||||
|
} = require('../errors') as typeof import('../errors');
|
||||||
reject(new SIGINTError('Task aborted on SIGINT signal'));
|
reject(new SIGINTError('Task aborted on SIGINT signal'));
|
||||||
};
|
};
|
||||||
addSIGINTHandler(sigintHandler);
|
addSIGINTHandler(sigintHandler);
|
||||||
@ -575,3 +579,16 @@ export async function awaitInterruptibleTask<
|
|||||||
process.removeListener('SIGINT', sigintHandler);
|
process.removeListener('SIGINT', sigintHandler);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Check if `drive` is mounted, and if so umount it. No-op on Windows. */
|
||||||
|
export async function safeUmount(drive: string) {
|
||||||
|
if (!drive) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const { isMounted, umount } = await import('umount');
|
||||||
|
const isMountedAsync = promisify(isMounted);
|
||||||
|
if (await isMountedAsync(drive)) {
|
||||||
|
const umountAsync = promisify(umount);
|
||||||
|
await umountAsync(drive);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user