mirror of
https://github.com/balena-io/balena-cli.git
synced 2024-12-19 13:47:52 +00:00
Merge pull request #979 from resin-io/978-oss-flow
Add support for the Opensource provisioning flow
This commit is contained in:
commit
e71f622453
@ -142,7 +142,7 @@ environment variable (in the same standard URL format).
|
||||
- [os versions <type>](#os-versions-type-)
|
||||
- [os download <type>](#os-download-type-)
|
||||
- [os build-config <image> <device-type>](#os-build-config-image-device-type-)
|
||||
- [os configure <image> [uuid] [deviceApiKey]](#os-configure-image-uuid-deviceapikey-)
|
||||
- [os configure <image>](#os-configure-image-)
|
||||
- [os initialize <image>](#os-initialize-image-)
|
||||
|
||||
- Config
|
||||
@ -965,13 +965,12 @@ show advanced configuration options
|
||||
|
||||
the path to the output JSON file
|
||||
|
||||
## os configure <image> [uuid] [deviceApiKey]
|
||||
## os configure <image>
|
||||
|
||||
Use this command to configure a previously downloaded operating system image for
|
||||
the specific device or for an application generally.
|
||||
|
||||
Calling this command without --version is not recommended, and may fail in
|
||||
future releases if the OS version cannot be inferred.
|
||||
Calling this command with the exact version number of the targeted image is required.
|
||||
|
||||
Note that device api keys are only supported on ResinOS 2.0.3+.
|
||||
|
||||
@ -1125,8 +1124,7 @@ show advanced commands
|
||||
|
||||
Use this command to generate a config.json for a device or application.
|
||||
|
||||
Calling this command without --version is not recommended, and may fail in
|
||||
future releases if the OS version cannot be inferred.
|
||||
Calling this command with the exact version number of the targeted image is required.
|
||||
|
||||
This is interactive by default, but you can do this automatically without interactivity
|
||||
by specifying an option for each question on the command line, if you know the questions
|
||||
|
@ -49,13 +49,17 @@ exports.optionalOsVersion =
|
||||
description: 'a resinOS version'
|
||||
parameter: 'version'
|
||||
|
||||
exports.osVersion = _.defaults
|
||||
required: 'You have to specify an exact os version'
|
||||
, exports.optionalOsVersion
|
||||
|
||||
exports.booleanDevice =
|
||||
signature: 'device'
|
||||
description: 'device'
|
||||
boolean: true
|
||||
alias: 'd'
|
||||
|
||||
exports.osVersion =
|
||||
exports.osVersionOrSemver =
|
||||
signature: 'version'
|
||||
description: """
|
||||
exact version number, or a valid semver range,
|
||||
|
@ -223,8 +223,7 @@ exports.generate =
|
||||
help: '''
|
||||
Use this command to generate a config.json for a device or application.
|
||||
|
||||
Calling this command without --version is not recommended, and may fail in
|
||||
future releases if the OS version cannot be inferred.
|
||||
Calling this command with the exact version number of the targeted image is required.
|
||||
|
||||
This is interactive by default, but you can do this automatically without interactivity
|
||||
by specifying an option for each question on the command line, if you know the questions
|
||||
@ -242,7 +241,7 @@ exports.generate =
|
||||
--network wifi --wifiSsid mySsid --wifiKey abcdefgh --appUpdatePollInterval 1
|
||||
'''
|
||||
options: [
|
||||
commandOptions.optionalOsVersion
|
||||
commandOptions.osVersion
|
||||
commandOptions.optionalApplication
|
||||
commandOptions.optionalDevice
|
||||
commandOptions.optionalDeviceApiKey
|
||||
|
@ -395,7 +395,7 @@ exports.init =
|
||||
commandOptions.optionalApplication
|
||||
commandOptions.yes
|
||||
commandOptions.advancedConfig
|
||||
_.assign({}, commandOptions.osVersion, { signature: 'os-version', parameter: 'os-version' })
|
||||
_.assign({}, commandOptions.osVersionOrSemver, { signature: 'os-version', parameter: 'os-version' })
|
||||
commandOptions.drive
|
||||
{
|
||||
signature: 'config'
|
||||
|
@ -96,7 +96,7 @@ exports.download =
|
||||
alias: 'o'
|
||||
required: 'You have to specify the output location'
|
||||
}
|
||||
commandOptions.osVersion
|
||||
commandOptions.osVersionOrSemver
|
||||
]
|
||||
action: (params, options, done) ->
|
||||
Promise = require('bluebird')
|
||||
@ -197,14 +197,13 @@ exports.buildConfig =
|
||||
.nodeify(done)
|
||||
|
||||
exports.configure =
|
||||
signature: 'os configure <image> [uuid] [deviceApiKey]'
|
||||
signature: 'os configure <image>'
|
||||
description: 'configure an os image'
|
||||
help: '''
|
||||
Use this command to configure a previously downloaded operating system image for
|
||||
the specific device or for an application generally.
|
||||
|
||||
Calling this command without --version is not recommended, and may fail in
|
||||
future releases if the OS version cannot be inferred.
|
||||
Calling this command with the exact version number of the targeted image is required.
|
||||
|
||||
Note that device api keys are only supported on ResinOS 2.0.3+.
|
||||
|
||||
@ -224,7 +223,7 @@ exports.configure =
|
||||
commandOptions.optionalApplication
|
||||
commandOptions.optionalDevice
|
||||
commandOptions.optionalDeviceApiKey
|
||||
commandOptions.optionalOsVersion
|
||||
commandOptions.osVersion
|
||||
{
|
||||
signature: 'config'
|
||||
description: 'path to the config JSON file, see `resin os build-config`'
|
||||
@ -232,7 +231,6 @@ exports.configure =
|
||||
}
|
||||
]
|
||||
action: (params, options, done) ->
|
||||
normalizeUuidProp(params)
|
||||
normalizeUuidProp(options, 'device')
|
||||
fs = require('fs')
|
||||
Promise = require('bluebird')
|
||||
@ -246,30 +244,20 @@ exports.configure =
|
||||
if _.filter([
|
||||
options.device
|
||||
options.application
|
||||
params.uuid
|
||||
]).length != 1
|
||||
patterns.exitWithExpectedError '''
|
||||
To configure an image, you must provide exactly one of:
|
||||
|
||||
* A device, with --device <uuid>
|
||||
* An application, with --app <appname>
|
||||
* [Deprecated] A device, passing its uuid directly on the command line
|
||||
|
||||
See the help page for examples:
|
||||
|
||||
$ resin help os configure
|
||||
'''
|
||||
if params.uuid
|
||||
console.warn(
|
||||
'Directly passing a UUID to `resin os configure` is deprecated, and will stop working in future.\n' +
|
||||
'Pass your device UUID with --device <uuid> instead.' +
|
||||
if params.deviceApiKey
|
||||
' Device api keys can be passed with --deviceApiKey.\n'
|
||||
else '\n'
|
||||
)
|
||||
|
||||
uuid = options.device || params.uuid
|
||||
deviceApiKey = options.deviceApiKey || params.deviceApiKey
|
||||
uuid = options.device
|
||||
deviceApiKey = options.deviceApiKey
|
||||
|
||||
console.info('Configuring operating system image')
|
||||
|
||||
|
@ -22,10 +22,13 @@ allDeviceTypes = undefined
|
||||
|
||||
getDeviceTypes = ->
|
||||
Bluebird = require('bluebird')
|
||||
_ = require('lodash')
|
||||
if allDeviceTypes != undefined
|
||||
return Bluebird.resolve(allDeviceTypes)
|
||||
resin = require('resin-sdk').fromSharedOptions()
|
||||
resin.models.config.getDeviceTypes()
|
||||
.then (deviceTypes) ->
|
||||
_.sortBy(deviceTypes, 'name')
|
||||
.tap (dt) ->
|
||||
allDeviceTypes = dt
|
||||
|
||||
|
@ -304,8 +304,8 @@ createRelease = (apiEndpoint, auth, userId, appId, composition) ->
|
||||
'is_created_by__user'
|
||||
'__metadata'
|
||||
])
|
||||
_.keys serviceImages, (serviceName) ->
|
||||
serviceImages[serviceName] = _.omit(serviceImages[serviceName], [
|
||||
serviceImages = _.mapValues serviceImages, (serviceImage) ->
|
||||
_.omit(serviceImage, [
|
||||
'created_at'
|
||||
'is_a_build_of__service'
|
||||
'__metadata'
|
||||
@ -418,7 +418,7 @@ exports.deployProject = (
|
||||
localImage.remove()
|
||||
.then ->
|
||||
release.status = 'success'
|
||||
.tapCatch (e) ->
|
||||
.tapCatch ->
|
||||
release.status = 'failed'
|
||||
.finally ->
|
||||
runloop = runSpinner(tty, spinner, "#{prefix}Saving release...")
|
||||
|
@ -13,77 +13,65 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
import * as fs from 'fs';
|
||||
|
||||
import Promise = require('bluebird');
|
||||
import ResinSdk = require('resin-sdk');
|
||||
import deviceConfig = require('resin-device-config');
|
||||
import * as semver from 'resin-semver';
|
||||
|
||||
const resin = ResinSdk.fromSharedOptions();
|
||||
|
||||
function readRootCa(): Promise<string | void> {
|
||||
const caFile = process.env.NODE_EXTRA_CA_CERTS;
|
||||
if (!caFile) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
return Promise.fromCallback(cb =>
|
||||
fs.readFile(caFile, { encoding: 'utf8' }, cb),
|
||||
)
|
||||
.then(pem => Buffer.from(pem).toString('base64'))
|
||||
.catch({ code: 'ENOENT' }, () => {});
|
||||
}
|
||||
type ImgConfig = {
|
||||
applicationName: string;
|
||||
applicationId: number;
|
||||
deviceType: string;
|
||||
userId: number;
|
||||
username: string;
|
||||
appUpdatePollInterval: number;
|
||||
listenPort: number;
|
||||
vpnPort: number;
|
||||
apiEndpoint: string;
|
||||
vpnEndpoint: string;
|
||||
registryEndpoint: string;
|
||||
deltaEndpoint: string;
|
||||
pubnubSubscribeKey: string;
|
||||
pubnubPublishKey: string;
|
||||
mixpanelToken: string;
|
||||
wifiSsid?: string;
|
||||
wifiKey?: string;
|
||||
|
||||
// props for older OS versions
|
||||
connectivity?: string;
|
||||
files?: {
|
||||
[filepath: string]: string;
|
||||
};
|
||||
|
||||
// device specific config props
|
||||
deviceId?: number;
|
||||
uuid?: string;
|
||||
registered_at?: number;
|
||||
};
|
||||
|
||||
export function generateBaseConfig(
|
||||
application: ResinSdk.Application,
|
||||
options: { version?: string; appUpdatePollInterval?: number },
|
||||
) {
|
||||
if (options.appUpdatePollInterval) {
|
||||
options = {
|
||||
...options,
|
||||
appUpdatePollInterval: options.appUpdatePollInterval * 60 * 1000,
|
||||
};
|
||||
}
|
||||
options: { version: string; appUpdatePollInterval?: number },
|
||||
): Promise<ImgConfig> {
|
||||
options = {
|
||||
...options,
|
||||
appUpdatePollInterval: options.appUpdatePollInterval || 10,
|
||||
};
|
||||
|
||||
return Promise.props({
|
||||
userId: resin.auth.getUserId(),
|
||||
username: resin.auth.whoami(),
|
||||
apiUrl: resin.settings.get('apiUrl'),
|
||||
vpnUrl: resin.settings.get('vpnUrl'),
|
||||
registryUrl: resin.settings.get('registryUrl'),
|
||||
deltaUrl: resin.settings.get('deltaUrl'),
|
||||
apiConfig: resin.models.config.getAll(),
|
||||
rootCA: readRootCa().catch(() => {
|
||||
console.warn('Could not read root CA');
|
||||
}),
|
||||
}).then(results => {
|
||||
return deviceConfig.generate(
|
||||
{
|
||||
application,
|
||||
user: {
|
||||
id: results.userId,
|
||||
username: results.username,
|
||||
},
|
||||
endpoints: {
|
||||
api: results.apiUrl,
|
||||
vpn: results.vpnUrl,
|
||||
registry: results.registryUrl,
|
||||
delta: results.deltaUrl,
|
||||
},
|
||||
pubnub: results.apiConfig.pubnub,
|
||||
mixpanel: {
|
||||
token: results.apiConfig.mixpanelToken,
|
||||
},
|
||||
balenaRootCA: results.rootCA,
|
||||
},
|
||||
options,
|
||||
);
|
||||
const promise = resin.models.os.getConfig(
|
||||
application.app_name,
|
||||
options,
|
||||
) as Promise<ImgConfig & { apiKey?: string }>;
|
||||
return promise.tap(config => {
|
||||
// os.getConfig always returns a config for an app
|
||||
delete config.apiKey;
|
||||
});
|
||||
}
|
||||
|
||||
export function generateApplicationConfig(
|
||||
application: ResinSdk.Application,
|
||||
options: { version?: string },
|
||||
options: { version: string },
|
||||
) {
|
||||
return generateBaseConfig(application, options).tap(config => {
|
||||
if (semver.satisfies(options.version, '>=2.7.8')) {
|
||||
@ -97,7 +85,7 @@ export function generateApplicationConfig(
|
||||
export function generateDeviceConfig(
|
||||
device: ResinSdk.Device & { belongs_to__application: ResinSdk.PineDeferred },
|
||||
deviceApiKey: string | true | null,
|
||||
options: { version?: string },
|
||||
options: { version: string },
|
||||
) {
|
||||
return resin.models.application
|
||||
.get(device.belongs_to__application.__id)
|
||||
|
@ -99,6 +99,7 @@ export function askLoginType() {
|
||||
|
||||
export function selectDeviceType() {
|
||||
return resin.models.config.getDeviceTypes().then(deviceTypes => {
|
||||
deviceTypes = _.sortBy(deviceTypes, 'name');
|
||||
return form.ask({
|
||||
message: 'Device Type',
|
||||
type: 'list',
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { stripIndent } from 'common-tags';
|
||||
import { ResinSDK, Application } from 'resin-sdk';
|
||||
import * as ResinSdk from 'resin-sdk';
|
||||
|
||||
import Logger = require('./logger');
|
||||
|
||||
@ -10,7 +10,7 @@ const MIN_RESINOS_VERSION = 'v2.14.0';
|
||||
|
||||
export async function join(
|
||||
logger: Logger,
|
||||
sdk: ResinSDK,
|
||||
sdk: ResinSdk.ResinSDK,
|
||||
deviceHostnameOrIp?: string,
|
||||
appName?: string,
|
||||
): Promise<void> {
|
||||
@ -39,8 +39,14 @@ export async function join(
|
||||
app.device_type = deviceType;
|
||||
}
|
||||
|
||||
logger.logDebug('Determining device OS version...');
|
||||
const deviceOsVersion = await getOsVersion(deviceIp);
|
||||
logger.logDebug(`Device OS version: ${deviceOsVersion}`);
|
||||
|
||||
logger.logDebug('Generating application config...');
|
||||
const config = await generateApplicationConfig(sdk, app);
|
||||
const config = await generateApplicationConfig(sdk, app, {
|
||||
version: deviceOsVersion,
|
||||
});
|
||||
logger.logDebug(`Using config: ${JSON.stringify(config, null, 2)}`);
|
||||
|
||||
logger.logDebug('Configuring...');
|
||||
@ -53,7 +59,7 @@ export async function join(
|
||||
|
||||
export async function leave(
|
||||
logger: Logger,
|
||||
_sdk: ResinSDK,
|
||||
_sdk: ResinSdk.ResinSDK,
|
||||
deviceHostnameOrIp?: string,
|
||||
): Promise<void> {
|
||||
logger.logDebug('Determining device...');
|
||||
@ -123,6 +129,15 @@ async function getDeviceType(deviceIp: string): Promise<string> {
|
||||
return match[1];
|
||||
}
|
||||
|
||||
async function getOsVersion(deviceIp: string): Promise<string> {
|
||||
const output = await execBuffered(deviceIp, 'cat /etc/os-release');
|
||||
const match = /^VERSION_ID="([^"]+)"$/m.exec(output);
|
||||
if (!match) {
|
||||
throw new Error('Failed to determine OS version ID');
|
||||
}
|
||||
return match[1];
|
||||
}
|
||||
|
||||
async function getOrSelectLocalDevice(deviceIp?: string): Promise<string> {
|
||||
if (deviceIp) {
|
||||
return deviceIp;
|
||||
@ -158,14 +173,58 @@ async function getOrSelectLocalDevice(deviceIp?: string): Promise<string> {
|
||||
return ip;
|
||||
}
|
||||
|
||||
async function getApplicationsWithOptionalUsers(
|
||||
sdk: ResinSdk.ResinSDK,
|
||||
options: ResinSdk.PineOptionsFor<ResinSdk.Application>,
|
||||
) {
|
||||
const _ = await import('lodash');
|
||||
|
||||
let applications = await sdk.models.application.getAll(options);
|
||||
// If we got more than one application with the same name it means that the
|
||||
// user has access to a collab app with the same name as a personal app.
|
||||
if (applications.length !== _.uniqBy(applications, 'app_name').length) {
|
||||
options = _.merge(_.cloneDeep(options), {
|
||||
$expand: { user: { $select: ['username'] } },
|
||||
});
|
||||
applications = await sdk.models.application.getAll(options);
|
||||
}
|
||||
|
||||
return applications;
|
||||
}
|
||||
|
||||
async function selectAppFromList(applications: ResinSdk.Application[]) {
|
||||
const _ = await import('lodash');
|
||||
const { selectFromList } = await import('../utils/patterns');
|
||||
|
||||
// If we got more than one application with the same name it means that the
|
||||
// user has access to a collab app with the same name as a personal app. We
|
||||
// present a list to the user which shows the fully qualified application
|
||||
// name (user/appname) and allows them to select.
|
||||
const hasSameNameApps =
|
||||
applications.length !== _.uniqBy(applications, 'app_name').length;
|
||||
|
||||
return selectFromList(
|
||||
hasSameNameApps
|
||||
? 'Found multiple applications with that name; please select the one to use'
|
||||
: 'Select application',
|
||||
_.map(applications, app => {
|
||||
let name = app.app_name;
|
||||
if (hasSameNameApps) {
|
||||
const owner = _.get(app, 'user[0].username');
|
||||
name = `${owner}/${app.app_name}`;
|
||||
}
|
||||
return _.merge({ name }, app);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
async function getOrSelectApplication(
|
||||
sdk: ResinSDK,
|
||||
sdk: ResinSdk.ResinSDK,
|
||||
deviceType: string,
|
||||
appName?: string,
|
||||
): Promise<Application> {
|
||||
): Promise<ResinSdk.Application> {
|
||||
const _ = await import('lodash');
|
||||
const form = await import('resin-cli-form');
|
||||
const { selectFromList } = await import('../utils/patterns');
|
||||
|
||||
const allDeviceTypes = await sdk.models.config.getDeviceTypes();
|
||||
const deviceTypeManifest = _.find(allDeviceTypes, { slug: deviceType });
|
||||
@ -177,14 +236,14 @@ async function getOrSelectApplication(
|
||||
.map(type => type.slug)
|
||||
.value();
|
||||
|
||||
const options: any = {
|
||||
$expand: { user: { $select: ['username'] } },
|
||||
$filter: { device_type: { $in: compatibleDeviceTypes } },
|
||||
};
|
||||
|
||||
if (!appName) {
|
||||
const options = {
|
||||
$filter: { device_type: { $in: compatibleDeviceTypes } },
|
||||
};
|
||||
|
||||
// No application specified, show a list to select one.
|
||||
const applications = await sdk.models.application.getAll(options);
|
||||
const applications = await getApplicationsWithOptionalUsers(sdk, options);
|
||||
|
||||
if (applications.length === 0) {
|
||||
const shouldCreateApp = await form.ask({
|
||||
message:
|
||||
@ -198,29 +257,31 @@ async function getOrSelectApplication(
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
return selectFromList(
|
||||
'Select application',
|
||||
_.map(applications, app => _.merge({ name: app.app_name }, app)),
|
||||
);
|
||||
|
||||
return selectAppFromList(applications);
|
||||
}
|
||||
|
||||
// We're given an application; resolve it if it's ambiguous and also validate
|
||||
// it's of appropriate device type.
|
||||
options.$filter = { app_name: appName };
|
||||
const options: ResinSdk.PineOptionsFor<ResinSdk.Application> = {};
|
||||
|
||||
// Check for an app of the form `user/application` and update the API query.
|
||||
const match = appName.split('/');
|
||||
if (match.length > 1) {
|
||||
// These will match at most one app, so we'll return early.
|
||||
options.$expand.user.$filter = { username: match[0] };
|
||||
options.$filter.app_name = match[1];
|
||||
// These will match at most one app
|
||||
options.$expand = {
|
||||
user: {
|
||||
$select: ['username'],
|
||||
$filter: { username: match[0] },
|
||||
},
|
||||
};
|
||||
|
||||
options.$filter = { app_name: match[1] };
|
||||
} else {
|
||||
// We're given an application; resolve it if it's ambiguous and also validate
|
||||
// it's of appropriate device type.
|
||||
options.$filter = { app_name: appName };
|
||||
}
|
||||
|
||||
// Fetch all applications with the given name that are accessible to the user
|
||||
const applications = await sdk.pine.get<Application>({
|
||||
resource: 'application',
|
||||
options,
|
||||
});
|
||||
const applications = await getApplicationsWithOptionalUsers(sdk, options);
|
||||
|
||||
if (applications.length === 0) {
|
||||
const shouldCreateApp = await form.ask({
|
||||
@ -231,7 +292,8 @@ async function getOrSelectApplication(
|
||||
default: true,
|
||||
});
|
||||
if (shouldCreateApp) {
|
||||
return createApplication(sdk, deviceType, options.$filter.app_name);
|
||||
return createApplication(sdk, deviceType, options.$filter
|
||||
.app_name as string);
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
@ -242,36 +304,27 @@ async function getOrSelectApplication(
|
||||
_.includes(compatibleDeviceTypes, app.device_type),
|
||||
);
|
||||
|
||||
if (validApplications.length === 0) {
|
||||
throw new Error('No application found with a matching device type');
|
||||
}
|
||||
|
||||
if (validApplications.length === 1) {
|
||||
return validApplications[0];
|
||||
}
|
||||
|
||||
// If we got more than one application with the same name it means that the
|
||||
// user has access to a collab app with the same name as a personal app. We
|
||||
// present a list to the user which shows the fully qualified application
|
||||
// name (user/appname) and allows them to select
|
||||
return selectFromList(
|
||||
'Found multiple applications with that name; please select the one to use',
|
||||
_.map(validApplications, app => {
|
||||
const owner = _.get(app, 'user[0].username');
|
||||
return _.merge({ name: `${owner}/${app.app_name}` }, app);
|
||||
}),
|
||||
);
|
||||
return selectAppFromList(applications);
|
||||
}
|
||||
|
||||
async function createApplication(
|
||||
sdk: ResinSDK,
|
||||
sdk: ResinSdk.ResinSDK,
|
||||
deviceType: string,
|
||||
name?: string,
|
||||
): Promise<Application> {
|
||||
): Promise<ResinSdk.Application> {
|
||||
const form = await import('resin-cli-form');
|
||||
const validation = await import('./validation');
|
||||
const patterns = await import('./patterns');
|
||||
|
||||
const user = await sdk.auth.getUserId();
|
||||
const queryOptions = {
|
||||
$filter: { user },
|
||||
};
|
||||
|
||||
const appName = await new Promise<string>(async (resolve, reject) => {
|
||||
while (true) {
|
||||
@ -284,7 +337,9 @@ async function createApplication(
|
||||
});
|
||||
|
||||
try {
|
||||
await sdk.models.application.get(appName, queryOptions);
|
||||
await sdk.models.application.get(appName, {
|
||||
$filter: { user },
|
||||
});
|
||||
patterns.printErrorMessage(
|
||||
'You already have an application with that name; please choose another.',
|
||||
);
|
||||
@ -304,14 +359,21 @@ async function createApplication(
|
||||
});
|
||||
}
|
||||
|
||||
async function generateApplicationConfig(sdk: ResinSDK, app: Application) {
|
||||
async function generateApplicationConfig(
|
||||
sdk: ResinSdk.ResinSDK,
|
||||
app: ResinSdk.Application,
|
||||
options: { version: string },
|
||||
) {
|
||||
const form = await import('resin-cli-form');
|
||||
const { generateApplicationConfig: configGen } = await import('./config');
|
||||
|
||||
const manifest = await sdk.models.device.getManifestBySlug(app.device_type);
|
||||
const opts =
|
||||
manifest.options && manifest.options.filter(opt => opt.name !== 'network');
|
||||
const values = await form.run(opts);
|
||||
const values = {
|
||||
...(await form.run(opts)),
|
||||
...options,
|
||||
};
|
||||
|
||||
const config = await configGen(app, values);
|
||||
if (config.connectivity === 'connman') {
|
||||
|
@ -153,7 +153,7 @@
|
||||
"resin-multibuild": "^0.9.0",
|
||||
"resin-preload": "^7.0.0",
|
||||
"resin-release": "^1.2.0",
|
||||
"resin-sdk": "10.0.0-beta2",
|
||||
"resin-sdk": "11.0.0-balena-sdk-78e343d605d323cff37b039e8e40cae05b52fb36",
|
||||
"resin-sdk-preconfigured": "^6.9.0",
|
||||
"resin-semver": "^1.3.0",
|
||||
"resin-settings-client": "^3.6.1",
|
||||
|
Loading…
Reference in New Issue
Block a user