mirror of https://github.com/Hypfer/Valetudo.git
195 lines
6.0 KiB
JavaScript
195 lines
6.0 KiB
JavaScript
const fs = require("fs");
|
|
const GithubValetudoNightlyUpdateProvider = require("./lib/update_provider/GithubValetudoNightlyUpdateProvider");
|
|
const GithubValetudoUpdateProvider = require("./lib/update_provider/GithubValetudoUpdateProvider");
|
|
const Logger = require("../Logger");
|
|
const States = require("../entities/core/updater");
|
|
const Steps = require("./lib/steps");
|
|
const Tools = require("../utils/Tools");
|
|
|
|
|
|
|
|
class Updater {
|
|
/**
|
|
* @param {object} options
|
|
* @param {import("../Configuration")} options.config
|
|
* @param {import("../core/ValetudoRobot")} options.robot
|
|
*/
|
|
constructor(options) {
|
|
this.config = options.config;
|
|
this.robot = options.robot;
|
|
|
|
/** @type {import("../entities/core/updater/ValetudoUpdaterState")} */
|
|
this.state = undefined;
|
|
/** @type {import("./lib/update_provider/ValetudoUpdateProvider")} */
|
|
this.updateProvider = undefined;
|
|
|
|
this.config.onUpdate((key) => {
|
|
if (key === "updater") {
|
|
this.reconfigure();
|
|
}
|
|
});
|
|
|
|
this.cleanupHandler = () => {};
|
|
this.pendingCleanupTimeout = undefined;
|
|
|
|
this.reconfigure();
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
reconfigure() {
|
|
const updaterConfig = this.config.get("updater");
|
|
|
|
clearTimeout(this.pendingCleanupTimeout);
|
|
this.cleanupHandler();
|
|
|
|
if (updaterConfig.enabled === true) {
|
|
this.state = new States.ValetudoUpdaterIdleState({
|
|
currentVersion: Tools.GET_VALETUDO_VERSION()
|
|
});
|
|
} else {
|
|
this.state = new States.ValetudoUpdaterDisabledState({});
|
|
}
|
|
|
|
|
|
switch (updaterConfig.updateProvider.type) {
|
|
case GithubValetudoUpdateProvider.TYPE:
|
|
this.updateProvider = new GithubValetudoUpdateProvider();
|
|
break;
|
|
case GithubValetudoNightlyUpdateProvider.TYPE:
|
|
this.updateProvider = new GithubValetudoNightlyUpdateProvider();
|
|
break;
|
|
default:
|
|
throw new Error(`Invalid UpdateProvider ${updaterConfig.updateProvider.type}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* As everything regarding networking might take a long time, we just accept this request
|
|
* and then asynchronously process it.
|
|
* Updates are reported via the updaters state
|
|
*
|
|
* @return {void}
|
|
*/
|
|
triggerCheck() {
|
|
if (
|
|
!(
|
|
this.state instanceof States.ValetudoUpdaterIdleState ||
|
|
this.state instanceof States.ValetudoUpdaterErrorState ||
|
|
this.state instanceof States.ValetudoUpdaterNoUpdateRequiredState
|
|
)
|
|
) {
|
|
throw new Error("Updates can only be started when the updaters state is idle or error");
|
|
}
|
|
|
|
|
|
this.state.busy = true;
|
|
|
|
const step = new Steps.ValetudoUpdaterCheckStep({
|
|
embedded: this.config.get("embedded"),
|
|
architectures: Updater.ARCHITECTURES,
|
|
spaceRequired: Updater.SPACE_REQUIREMENTS,
|
|
robot: this.robot,
|
|
updateProvider: this.updateProvider
|
|
});
|
|
|
|
step.execute().then((state) => {
|
|
this.state = state;
|
|
}).catch(err => {
|
|
this.state = new States.ValetudoUpdaterErrorState({
|
|
type: err.type,
|
|
message: err.message
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @return {void}
|
|
*/
|
|
triggerDownload() {
|
|
if (!(this.state instanceof States.ValetudoUpdaterApprovalPendingState)) {
|
|
throw new Error("Downloads can only be started when there's pending approval");
|
|
}
|
|
const downloadPath = this.state.downloadPath;
|
|
this.state.busy = true;
|
|
|
|
const step = new Steps.ValetudoUpdaterDownloadStep({
|
|
downloadUrl: this.state.downloadUrl,
|
|
downloadPath: this.state.downloadPath,
|
|
expectedHash: this.state.expectedHash,
|
|
version: this.state.version,
|
|
releaseTimestamp: this.state.releaseTimestamp
|
|
});
|
|
|
|
const state = new States.ValetudoUpdaterDownloadingState({
|
|
downloadUrl: this.state.downloadUrl,
|
|
downloadPath: this.state.downloadPath,
|
|
expectedHash: this.state.expectedHash,
|
|
version: this.state.version,
|
|
releaseTimestamp: this.state.releaseTimestamp
|
|
});
|
|
this.state = state;
|
|
this.state.busy = true;
|
|
|
|
step.onProgressUpdate = (progressPercent) => {
|
|
state.metaData.progress = progressPercent;
|
|
};
|
|
|
|
step.execute().then((state) => {
|
|
this.state = state;
|
|
|
|
this.cleanupHandler = () => {
|
|
fs.unlinkSync(downloadPath);
|
|
|
|
this.state = new States.ValetudoUpdaterIdleState({
|
|
currentVersion: Tools.GET_VALETUDO_VERSION()
|
|
});
|
|
|
|
this.cleanupHandler = () => {};
|
|
};
|
|
|
|
this.pendingCleanupTimeout = setTimeout(() => {
|
|
Logger.warn("Updater: User confirmation timeout.");
|
|
|
|
this.cleanupHandler();
|
|
}, 10 * 60 * 1000); // 10 minutes
|
|
}).catch(err => {
|
|
this.state = new States.ValetudoUpdaterErrorState({
|
|
type: err.type,
|
|
message: err.message
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @return {void}
|
|
*/
|
|
triggerApply() {
|
|
if (!(this.state instanceof States.ValetudoUpdaterApplyPendingState)) {
|
|
throw new Error("Can only apply if there's finalization pending");
|
|
}
|
|
|
|
this.state.busy = true;
|
|
|
|
const step = new Steps.ValetudoUpdaterApplyStep({
|
|
downloadPath: this.state.downloadPath
|
|
});
|
|
|
|
step.execute().catch(err => { //no .then() required as the system will reboot
|
|
this.state = new States.ValetudoUpdaterErrorState({
|
|
type: err.type,
|
|
message: err.message
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
Updater.SPACE_REQUIREMENTS = 40 * 1024 * 1024;
|
|
Updater.ARCHITECTURES = {
|
|
"arm": "armv7",
|
|
"arm64": "aarch64"
|
|
};
|
|
|
|
module.exports = Updater;
|