balena-cli/build/actions/preload.js
2017-10-16 19:27:12 +02:00

280 lines
10 KiB
JavaScript

// Generated by CoffeeScript 1.12.7
/*
Copyright 2016-2017 Resin.io
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
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.
*/
var LATEST, dockerUtils, getApplicationsWithSuccessfulBuilds, offerToDisableAutomaticUpdates, selectApplication, selectApplicationCommit,
indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
dockerUtils = require('../utils/docker');
LATEST = 'latest';
getApplicationsWithSuccessfulBuilds = function(deviceType) {
var preload, resin;
preload = require('resin-preload');
resin = require('resin-sdk-preconfigured');
return resin.pine.get({
resource: 'my_application',
options: {
filter: {
device_type: deviceType,
build: {
$any: {
$alias: 'b',
$expr: {
b: {
status: 'success'
}
}
}
}
},
expand: preload.applicationExpandOptions,
select: ['id', 'app_name', 'device_type', 'commit'],
orderby: 'app_name asc'
}
});
};
selectApplication = function(deviceType) {
var applicationInfoSpinner, expectedError, form, visuals;
visuals = require('resin-cli-visuals');
form = require('resin-cli-form');
expectedError = require('../utils/patterns').expectedError;
applicationInfoSpinner = new visuals.Spinner('Downloading list of applications and builds.');
applicationInfoSpinner.start();
return getApplicationsWithSuccessfulBuilds(deviceType).then(function(applications) {
applicationInfoSpinner.stop();
if (applications.length === 0) {
expectedError("You have no apps with successful builds for a '" + deviceType + "' device type.");
}
return form.ask({
message: 'Select an application',
type: 'list',
choices: applications.map(function(app) {
return {
name: app.app_name,
value: app
};
})
});
});
};
selectApplicationCommit = function(builds) {
var DEFAULT_CHOICE, choices, expectedError, form;
form = require('resin-cli-form');
expectedError = require('../utils/patterns').expectedError;
if (builds.length === 0) {
expectedError('This application has no successful builds.');
}
DEFAULT_CHOICE = {
'name': LATEST,
'value': LATEST
};
choices = [DEFAULT_CHOICE].concat(builds.map(function(build) {
return {
name: build.push_timestamp + " - " + build.commit_hash,
value: build.commit_hash
};
}));
return form.ask({
message: 'Select a build',
type: 'list',
"default": LATEST,
choices: choices
});
};
offerToDisableAutomaticUpdates = function(application, commit) {
var Promise, form, message, resin;
Promise = require('bluebird');
resin = require('resin-sdk-preconfigured');
form = require('resin-cli-form');
if (commit === LATEST || !application.should_track_latest_release) {
return Promise.resolve();
}
message = '\nThis application is set to automatically update all devices to the latest available version.\nThis might be unexpected behaviour: with this enabled, the preloaded device will still\ndownload and install the latest build once it is online.\n\nDo you want to disable automatic updates for this application?';
return form.ask({
message: message,
type: 'confirm'
}).then(function(update) {
if (!update) {
return;
}
return resin.pine.patch({
resource: 'application',
id: application.id,
body: {
should_track_latest_release: false
}
});
});
};
module.exports = {
signature: 'preload <image>',
description: '(beta) preload an app on a disk image (or Edison zip archive)',
help: 'Warning: "resin preload" requires Docker to be correctly installed in\nyour shell environment. For more information (including Windows support)\nplease check the README here: https://github.com/resin-io/resin-cli .\n\nUse this command to preload an application to a local disk image (or\nEdison zip archive) with a built commit from Resin.io.\nThis can be used with cloud builds, or images deployed with resin deploy.\n\nExamples:\n $ resin preload resin.img --app 1234 --commit e1f2592fc6ee949e68756d4f4a48e49bff8d72a0 --splash-image some-image.png\n $ resin preload resin.img',
permission: 'user',
primary: true,
options: dockerUtils.appendConnectionOptions([
{
signature: 'app',
parameter: 'appId',
description: 'id of the application to preload',
alias: 'a'
}, {
signature: 'commit',
parameter: 'hash',
description: 'a specific application commit to preload (ignored if no appId is given)',
alias: 'c'
}, {
signature: 'splash-image',
parameter: 'splashImage.png',
description: 'path to a png image to replace the splash screen',
alias: 's'
}, {
signature: 'dont-detect-flasher-type-images',
boolean: true,
description: 'Disables the flasher type images detection: treats all images as non flasher types'
}, {
signature: 'dont-check-device-type',
boolean: true,
description: 'Disables check for matching device types in image and application'
}
]),
action: function(params, options, done) {
var Promise, _, errors, expectedError, form, nodeCleanup, preload, progressBars, progressHandler, resin, spinnerHandler, spinners, streamToPromise, visuals;
_ = require('lodash');
Promise = require('bluebird');
resin = require('resin-sdk-preconfigured');
streamToPromise = require('stream-to-promise');
form = require('resin-cli-form');
preload = require('resin-preload');
errors = require('resin-errors');
visuals = require('resin-cli-visuals');
nodeCleanup = require('node-cleanup');
expectedError = require('../utils/patterns').expectedError;
progressBars = {};
progressHandler = function(event) {
var progressBar;
progressBar = progressBars[event.name];
if (!progressBar) {
progressBar = progressBars[event.name] = new visuals.Progress(event.name);
}
return progressBar.update({
percentage: event.percentage
});
};
spinners = {};
spinnerHandler = function(event) {
var spinner;
spinner = spinners[event.name];
if (!spinner) {
spinner = spinners[event.name] = new visuals.Spinner(event.name);
}
if (event.action === 'start') {
return spinner.start();
} else {
console.log();
return spinner.stop();
}
};
options.image = params.image;
options.appId = options.app;
delete options.app;
options.dontDetectFlasherTypeImages = options['dont-detect-flasher-type-images'];
delete options['dont-detect-flasher-type-images'];
options.splashImage = options['splash-image'];
delete options['splash-image'];
if (options['dont-check-device-type'] && !options.appId) {
expectedError('You need to specify an app id if you disable the device type check.');
}
return dockerUtils.getDocker(options).then(function(docker) {
var gotSignal, preloader;
preloader = new preload.Preloader(resin, docker, options.appId, options.commit, options.image, options.splashImage, options.proxy, options.dontDetectFlasherTypeImages);
gotSignal = false;
nodeCleanup(function(exitCode, signal) {
if (signal) {
gotSignal = true;
nodeCleanup.uninstall();
preloader.cleanup().then(function() {
return process.kill(process.pid, signal);
});
return false;
}
});
if (process.env.DEBUG) {
preloader.stderr.pipe(process.stderr);
}
preloader.on('progress', progressHandler);
preloader.on('spinner', spinnerHandler);
return new Promise(function(resolve, reject) {
preloader.on('error', reject);
return preloader.build().then(function() {
return preloader.prepare();
}).then(function() {
return preloader.getDeviceTypeAndPreloadedBuilds();
}).then(function(info) {
return Promise["try"](function() {
if (options.appId) {
return preloader.fetchApplication()["catch"](errors.ResinApplicationNotFound, expectedError);
}
return selectApplication(info.device_type);
}).then(function(application) {
preloader.setApplication(application);
if (info.device_type !== application.device_type) {
expectedError("Image device type (" + application.device_type + ") and application device type (" + slug + ") do not match");
}
return Promise["try"](function() {
if (options.commit) {
if (!_.find(application.build, {
commit_hash: options.commit
})) {
expectedError('There is no build matching this commit');
}
return options.commit;
}
return selectApplicationCommit(application.build);
}).then(function(commit) {
if (commit === LATEST) {
preloader.commit = application.commit;
} else {
preloader.commit = commit;
}
return offerToDisableAutomaticUpdates(application, commit);
});
}).then(function() {
var builds, ref;
builds = info.preloaded_builds.map(function(build) {
return build.slice(-preload.BUILD_HASH_LENGTH);
});
if (ref = preloader.commit, indexOf.call(builds, ref) >= 0) {
throw new preload.errors.ResinError('This build is already preloaded in this image.');
}
return preloader.preload()["catch"](preload.errors.ResinError, expectedError);
});
}).then(resolve)["catch"](reject);
}).then(done)["finally"](function() {
if (!gotSignal) {
return preloader.cleanup();
}
});
});
}
};