diff --git a/manager/javascripts/manager.js b/manager/javascripts/manager.js
index 4e9a1493..3fa081cf 100644
--- a/manager/javascripts/manager.js
+++ b/manager/javascripts/manager.js
@@ -20,7 +20,8 @@ class Manager {
IP: undefined,
logLocation: path.join(__dirname, "..", "manager.log"),
mode: 'basic',
- state: 'IDLE'
+ state: 'IDLE',
+ forceBeta: false
};
/* Manager pages */
diff --git a/manager/javascripts/preload.js b/manager/javascripts/preload.js
index 7f29f18b..5856af25 100644
--- a/manager/javascripts/preload.js
+++ b/manager/javascripts/preload.js
@@ -1,303 +1,330 @@
-const contextBridge = require('electron').contextBridge;
-const ipcRenderer = require('electron').ipcRenderer;
+const contextBridge = require("electron").contextBridge;
+const ipcRenderer = require("electron").ipcRenderer;
const { exec, spawn } = require("child_process");
-const { showConfirmPopup, showWaitPopup, showErrorPopup } = require('./popup');
-const path = require('path');
-const os = require('os');
-const https = require('follow-redirects').https;
-const fs = require('fs');
+const { showConfirmPopup, showWaitPopup, showErrorPopup } = require("./popup");
+const path = require("path");
+const os = require("os");
+const https = require("follow-redirects").https;
+const fs = require("fs");
const AdmZip = require("adm-zip");
-const { Octokit } = require('octokit');
+const { Octokit } = require("octokit");
const { logger } = require("./filesystem");
-const { getManager } = require('./managerfactory');
-const { sleep } = require('./utils');
+const { getManager } = require("./managerfactory");
+const { sleep } = require("./utils");
const VERSION = "{{OLYMPUS_VERSION_NUMBER}}";
logger.log(`Running in ${__dirname}`);
-function checkVersion() {
- /* Check if we are running the latest version */
- const request = new Request("https://raw.githubusercontent.com/Pax1601/DCSOlympus/main/version.json");
- fetch(request).then((response) => {
- if (response.status === 200) {
- return response.json();
- } else {
- throw new Error("Error connecting to Github to retrieve latest version");
- }
- }).then((res) => {
- /* If we are running a development version of the script (not from a compiled package), skip version checking */
- if (VERSION.includes("OLYMPUS_VERSION_NUMBER")) {
- logger.log("Development build detected, skipping version checks...")
- } else {
- /* Check if there is a newer version available */
- var reg1 = res["version"].match(/\d+/g).map((str) => { return Number(str) });
- var reg2 = VERSION.match(/\d+/g).map((str) => { return Number(str) });
+function checkVersion(forceBeta) {
+ /* Check if we are running the latest version */
+ const request = new Request("https://raw.githubusercontent.com/Pax1601/DCSOlympus/main/version.json");
+ fetch(request)
+ .then((response) => {
+ if (response.status === 200) {
+ return response.json();
+ } else {
+ throw new Error("Error connecting to Github to retrieve latest version");
+ }
+ })
+ .then((res) => {
+ /* If we are running a development version of the script (not from a compiled package), skip version checking */
+ if (VERSION.includes("OLYMPUS_VERSION_NUMBER")) {
+ logger.log("Development build detected, skipping version checks...");
+ } else {
+ /* Check if there is a newer version available */
+ var reg1 = res["version"].match(/\d+/g).map((str) => {
+ return Number(str);
+ });
+ var reg2 = VERSION.match(/\d+/g).map((str) => {
+ return Number(str);
+ });
- /* If a newer version is available update Olympus in Release mode */
- if (reg1[0] > reg2[0] || (reg1[0] == reg2[0] && reg1[1] > reg2[1]) || (reg1[0] == reg2[0] && reg1[1] == reg2[1] && reg1[2] > reg2[2])) {
- logger.log(`New version available: ${res["version"]}`);
- showConfirmPopup(`
You are currently running DCS Olympus ${VERSION}, but ${res["version"]} is available.
Do you want to update DCS Olympus automatically?
Note: DCS and Olympus MUST be stopped before proceeding.
`,
- async () => {
- /* Nested popup calls need to wait for animation to complete */
- await sleep(300);
+ /* If a newer version is available update Olympus in Release mode */
+ if (
+ reg1[0] > reg2[0] ||
+ (reg1[0] == reg2[0] && reg1[1] > reg2[1]) ||
+ (reg1[0] == reg2[0] && reg1[1] == reg2[1] && reg1[2] > reg2[2])
+ ) {
+ logger.log(`New version available: ${res["version"]}`);
+ showConfirmPopup(
+ `You are currently running DCS Olympus ${VERSION}, but ${res["version"]} is available.
Do you want to update DCS Olympus automatically?
Note: DCS and Olympus MUST be stopped before proceeding.
`,
+ async () => {
+ /* Nested popup calls need to wait for animation to complete */
+ await sleep(300);
- updateOlympusRelease();
- }, () => {
- logger.log("Update canceled");
- })
+ updateOlympusRelease();
+ },
+ () => {
+ logger.log("Update canceled");
}
- /* If the current version is newer than the latest release, the user is probably a developer. Ask for a beta update */
- else if (reg2[0] > reg1[0] || (reg2[0] == reg1[0] && reg2[1] > reg1[1]) || (reg2[0] == reg1[0] && reg2[1] == reg1[1] && reg2[2] > reg1[2])) {
- logger.log(`Beta version detected: ${res["version"]} vs ${VERSION}`);
+ );
+ } else if (
+ /* If the current version is newer than the latest release, the user is probably a developer. Ask for a beta update */
+ reg2[0] > reg1[0] ||
+ (reg2[0] == reg1[0] && reg2[1] > reg1[1]) ||
+ (reg2[0] == reg1[0] && reg2[1] == reg1[1] && reg2[2] > reg1[2])
+ ) {
+ logger.log(`Beta version detected: ${res["version"]} vs ${VERSION}`);
+ updateOlympusBeta();
+ }
+ else {
+ logger.log("Running latest version...");
+
+ if (forceBeta) {
+ logger.log(`Beta version forced: ${res["version"]} vs ${VERSION}`);
updateOlympusBeta();
}
}
- })
+
+ }
+ });
}
-/** Update Olympus in "beta" mode. The user will be provided with the option to update from a build artifact
- *
+/** Update Olympus in "beta" mode. The user will be provided with the option to update from a build artifact
+ *
*/
async function updateOlympusBeta() {
- /* Get a list of build artifacts */
- const octokit = new Octokit({});
- const res = await octokit.request('GET /repos/{owner}/{repo}/actions/artifacts', {
- owner: 'Pax1601',
- repo: 'DCSOlympus',
- headers: {
- 'X-GitHub-Api-Version': '2022-11-28'
- }
- });
- const artifacts = res.data.artifacts;
-
- /* Select the newest artifact */
- var artifact = artifacts.find((artifact) => { return artifact.name === "development_build_not_a_release" });
+ /* Get a list of build artifacts */
+ const octokit = new Octokit({});
+ const res = await octokit.request("GET /repos/{owner}/{repo}/actions/artifacts", {
+ owner: "Pax1601",
+ repo: "DCSOlympus",
+ headers: {
+ "X-GitHub-Api-Version": "2022-11-28",
+ },
+ });
+ const artifacts = res.data.artifacts;
- const date1 = new Date(artifact.updated_at);
- const date2 = fs.statSync(".").mtime;
- if (date1 > date2) {
- showConfirmPopup(` Looks like you are running a beta version of Olympus!
Latest beta artifact timestamp of: ${date1.toLocaleString()}
Your installation timestamp: ${date2.toLocaleString()}
Do you want to update to the newest beta version?
`, async () => {
- /* Nested popup calls need to wait for animation to complete */
- await sleep(300);
+ /* Select the newest artifact */
+ var artifact = artifacts.find((artifact) => {
+ return artifact.name === "development_build_not_a_release";
+ });
- /* Run the browser and download the artifact */ //TODO: try and directly download the file from code rather than using the browser
- exec(`start https://github.com/Pax1601/DCSOlympus/actions/runs/${artifact.workflow_run.id}/artifacts/${artifact.id}`)
- showConfirmPopup(` A browser window was opened to download the beta artifact.
Please wait for the download to complete, then press "Accept" and select the downloaded beta artifact.
`,
- () => {
- /* Ask the user to select the downloaded file */
- var input = document.createElement('input');
- input.type = 'file';
- input.click();
- input.onchange = e => {
- /* Run the update process */
- updateOlympus(e.target.files[0])
- }
- },
- () => {
- logger.log("Update canceled");
- });
- },
- () => {
- logger.log("Update canceled");
- }
- )
- } else {
- logger.log("Build is latest")
- }
+ const date1 = new Date(artifact.updated_at);
+ const date2 = fs.statSync(".").mtime;
+ if (date1 > date2) {
+ showConfirmPopup(
+ ` Looks like you are running a beta version of Olympus!
Latest beta artifact timestamp of: ${date1.toLocaleString()}
Your installation timestamp: ${date2.toLocaleString()}
Do you want to update to the newest beta version?
`,
+ async () => {
+ /* Nested popup calls need to wait for animation to complete */
+ await sleep(300);
+
+ /* Run the update process */
+ updateOlympus(
+ "https://nightly.link/Pax1601/DCSOlympus/workflows/build_package/release-candidate/development_build_not_a_release.zip"
+ );
+ },
+ () => {
+ logger.log("Update canceled");
+ }
+ );
+ } else {
+ logger.log("Build is latest");
+ }
}
/** Update Olympus to the lastest release
- *
+ *
*/
async function updateOlympusRelease() {
- const octokit = new Octokit({})
+ const octokit = new Octokit({});
- const res = await octokit.request('GET /repos/{owner}/{repo}/releases/latest', {
- owner: 'Pax1601',
- repo: 'DCSOlympus',
- headers: {
- 'X-GitHub-Api-Version': '2022-11-28'
- }
- })
+ const res = await octokit.request("GET /repos/{owner}/{repo}/releases/latest", {
+ owner: "Pax1601",
+ repo: "DCSOlympus",
+ headers: {
+ "X-GitHub-Api-Version": "2022-11-28",
+ },
+ });
- /* Select the newest artifact */
- var asset = res.data.assets.find((asset) => { return asset.name.includes("manager") });
+ /* Select the newest artifact */
+ var asset = res.data.assets.find((asset) => {
+ return asset.name.includes("manager");
+ });
- /* Run the update process */
- updateOlympus(asset.browser_download_url)
+ /* Run the update process */
+ updateOlympus(asset.browser_download_url);
}
function updateOlympus(location) {
- showWaitPopup("Please wait while Olympus is being updated.
The Manager will be closed and reopened automatically when updating is completed.
")
+ showWaitPopup(
+ "Please wait while Olympus is being updated.
The Manager will be closed and reopened automatically when updating is completed.
"
+ );
+
+ /* If the location is a string, it is interpreted as a download url. Else, it is interpreted as a File (on disk)*/
+ if (typeof location === "string") {
+ logger.log(`Updating Olympus with package from ${location}`);
+ } else {
+ logger.log(`Updating Olympus with package from ${location.path}`);
+ }
+
+ let tmpDir;
+ const appPrefix = "dcs-olympus-";
+ try {
+ /* Create a temporary folder */
+ const folder = path.join(os.tmpdir(), appPrefix);
+ tmpDir = fs.mkdtempSync(folder);
- /* If the location is a string, it is interpreted as a download url. Else, it is interpreted as a File (on disk)*/
if (typeof location === "string") {
- logger.log(`Updating Olympus with package from ${location}`)
- } else {
- logger.log(`Updating Olympus with package from ${location.path}`)
- }
+ /* Download the file */
+ const file = fs.createWriteStream(path.join(tmpDir, "temp.zip"));
+ logger.log(`Downloading update package in ${path.join(tmpDir, "temp.zip")}`);
+ const request = https.get(location, (response) => {
+ if (response.statusCode === 200) {
+ response.pipe(file);
- let tmpDir;
- const appPrefix = 'dcs-olympus-';
- try {
- /* Create a temporary folder */
- const folder = path.join(os.tmpdir(), appPrefix);
- tmpDir = fs.mkdtempSync(folder);
+ /* Either on success or on error close the file stream */
+ file.on("finish", () => {
+ file.close();
+ logger.log("Download completed");
- if (typeof location === "string") {
- /* Download the file */
- const file = fs.createWriteStream(path.join(tmpDir, "temp.zip"));
- logger.log(`Downloading update package in ${path.join(tmpDir, "temp.zip")}`)
- const request = https.get(location, (response) => {
- if (response.statusCode === 200) {
- response.pipe(file);
-
- /* Either on success or on error close the file stream */
- file.on("finish", () => {
- file.close();
- logger.log("Download completed");
-
- /* Extract and copy the files */
- extractAndCopy(tmpDir);
- });
- file.on("error", (err) => {
- file.close();
- logger.error(err);
- throw Error(err);
- })
- } else {
- failUpdate();
- throw Error("Failed to download resource.")
- }
- });
- } else {
- /* Copy the archive to the temporary folder */
- fs.copyFileSync(location.path, path.join(tmpDir, "temp.zip"));
-
/* Extract and copy the files */
extractAndCopy(tmpDir);
+ });
+ file.on("error", (err) => {
+ file.close();
+ logger.error(err);
+ throw Error(err);
+ });
+ } else {
+ failUpdate();
+ throw Error("Failed to download resource.");
}
+ });
+ } else {
+ /* Copy the archive to the temporary folder */
+ fs.copyFileSync(location.path, path.join(tmpDir, "temp.zip"));
+
+ /* Extract and copy the files */
+ extractAndCopy(tmpDir);
}
- catch (err) {
- /* Show the failed update message */
- failUpdate();
- logger.error(err)
- }
+ } catch (err) {
+ /* Show the failed update message */
+ failUpdate();
+ logger.error(err);
+ }
}
/** Extract the contents of the zip and update Olympus by deleting all files from the current installation and copying the new files over.
- *
+ *
*/
function extractAndCopy(folder) {
- /* Extract all the files */
- const zip = new AdmZip(path.join(folder, "temp.zip"));
- zip.extractAllTo(path.join(folder, "temp"));
+ /* Extract all the files */
+ const zip = new AdmZip(path.join(folder, "temp.zip"));
+ zip.extractAllTo(path.join(folder, "temp"));
- /* Create a batch file that:
+ /* Create a batch file that:
1) waits for 5 seconds to allow the old process to close;
2) deletes the existing installation;
3) copies the new installation;
4) cds into the new installation;
5) runs the installer.bat script */
- fs.writeFileSync(path.join(folder, 'update.bat'),
- `timeout /t 5 \necho D|xcopy /Y /S /E "${path.join(folder, "temp")}" "${path.join(__dirname, "..", "..")}" \ncd "${path.join(__dirname, "..", "..")}" \ncall installer.bat`
- )
+ fs.writeFileSync(
+ path.join(folder, "update.bat"),
+ `timeout /t 5 \necho D|xcopy /Y /S /E "${path.join(folder, "temp")}" "${path.join(
+ __dirname,
+ "..",
+ ".."
+ )}" \ncd "${path.join(__dirname, "..", "..")}" \ncall installer.bat`
+ );
- /* Launch the update script then close gracefully */
- var proc = spawn('cmd.exe', ["/c", path.join(folder, 'update.bat')], { cwd: folder, shell: true, detached: true });
- proc.unref();
- ipcRenderer.send('window:close');
+ /* Launch the update script then close gracefully */
+ var proc = spawn("cmd.exe", ["/c", path.join(folder, "update.bat")], { cwd: folder, shell: true, detached: true });
+ proc.unref();
+ ipcRenderer.send("window:close");
}
/** Something went wrong. Tell the user to update manually.
- *
+ *
*/
function failUpdate() {
- showErrorPopup(`An error has occurred while updating Olympus.
Please delete Olympus and update it manually. A browser window will open automatically on the download page.
You can find more info in ${path.join(__dirname, "..", "manager.log")}
`, () => {
- exec(`start https://github.com/Pax1601/DCSOlympus/releases`, () => {
- ipcRenderer.send('window:close');
- })
- })
+ showErrorPopup(
+ `An error has occurred while updating Olympus.
Please delete Olympus and update it manually. A browser window will open automatically on the download page.
You can find more info in ${path.join(
+ __dirname,
+ "..",
+ "manager.log"
+ )}
`,
+ () => {
+ exec(`start https://github.com/Pax1601/DCSOlympus/releases`, () => {
+ ipcRenderer.send("window:close");
+ });
+ }
+ );
}
/* White-listed channels. */
const ipc = {
- 'render': {
- /* From render to main. */
- 'send': [
- 'window:minimize',
- 'window:maximize',
- 'window:restore',
- 'window:close'
- ],
- /* From main to render. */
- 'receive': [
- 'event:maximized',
- 'event:unmaximized'
- ],
- /* From render to main and back again. */
- 'sendReceive': []
- }
+ render: {
+ /* From render to main. */
+ send: ["window:minimize", "window:maximize", "window:restore", "window:close"],
+ /* From main to render. */
+ receive: ["event:maximized", "event:unmaximized"],
+ /* From render to main and back again. */
+ sendReceive: [],
+ },
};
/* Exposed protected methods in the render process. */
contextBridge.exposeInMainWorld(
- /* Allowed 'ipcRenderer' methods. */
- 'ipcRender', {
+ /* Allowed 'ipcRenderer' methods. */
+ "ipcRender",
+ {
/* From render to main. */
send: (channel, args) => {
- let validChannels = ipc.render.send;
- if (validChannels.includes(channel)) {
- ipcRenderer.send(channel, args);
- }
+ let validChannels = ipc.render.send;
+ if (validChannels.includes(channel)) {
+ ipcRenderer.send(channel, args);
+ }
},
/* From main to render. */
receive: (channel, listener) => {
- let validChannels = ipc.render.receive;
- if (validChannels.includes(channel)) {
- /* Deliberately strip event as it includes `sender`. */
- ipcRenderer.on(channel, (event, ...args) => listener(...args));
- }
+ let validChannels = ipc.render.receive;
+ if (validChannels.includes(channel)) {
+ /* Deliberately strip event as it includes `sender`. */
+ ipcRenderer.on(channel, (event, ...args) => listener(...args));
+ }
},
/* From render to main and back again. */
invoke: (channel, args) => {
- let validChannels = ipc.render.sendReceive;
- if (validChannels.includes(channel)) {
- return ipcRenderer.invoke(channel, args);
- }
- }
-});
+ let validChannels = ipc.render.sendReceive;
+ if (validChannels.includes(channel)) {
+ return ipcRenderer.invoke(channel, args);
+ }
+ },
+ }
+);
/* On content loaded */
-window.addEventListener('DOMContentLoaded', async () => {
- document.getElementById("loader").classList.remove("hide");
- await getManager().start();
- await checkVersion();
-})
+window.addEventListener("DOMContentLoaded", async () => {
+ document.getElementById("loader").classList.remove("hide");
+ await getManager().start();
+
+ await checkVersion(getManager().options.forceBeta);
+});
-window.addEventListener('resize', () => {
- /* Compute the height of the content page */
- computePagesHeight();
-})
+window.addEventListener("resize", () => {
+ /* Compute the height of the content page */
+ computePagesHeight();
+});
-window.addEventListener('DOMContentLoaded', () => {
- /* Compute the height of the content page */
- computePagesHeight();
-})
+window.addEventListener("DOMContentLoaded", () => {
+ /* Compute the height of the content page */
+ computePagesHeight();
+});
-document.addEventListener('managerStarted', () => {
- /* Compute the height of the content page */
- computePagesHeight();
-})
+document.addEventListener("managerStarted", () => {
+ /* Compute the height of the content page */
+ computePagesHeight();
+});
/** Computes the height of the content area
- *
+ *
*/
function computePagesHeight() {
- var pages = document.querySelectorAll(".manager-page");
- var titleBar = document.querySelector("#title-bar");
- var header = document.querySelector("#header");
+ var pages = document.querySelectorAll(".manager-page");
+ var titleBar = document.querySelector("#title-bar");
+ var header = document.querySelector("#header");
- for (let i = 0; i < pages.length; i++) {
- pages[i].style.height = (window.innerHeight - (titleBar.clientHeight + header.clientHeight)) + "px";
- }
+ for (let i = 0; i < pages.length; i++) {
+ pages[i].style.height = window.innerHeight - (titleBar.clientHeight + header.clientHeight) + "px";
+ }
}