Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

quitAndInstall and downloadUpdate don't work properly on LInux #58

Closed
serge1peshcoff opened this issue Oct 26, 2018 · 7 comments
Closed

Comments

@serge1peshcoff
Copy link

I've faced a few issues while using it on Linux:

  1. quitAndInstall doesn't work properly. It spawns the child process and the runs app.quit(), which doesn't quit until the child process will quit. Here is more info on that: Spawning a detached process/unref()ing it doesn't work on Linux electron/electron#2208. In my own implementation I've wrote my own quitAndInstall which, instead of spawning child process, uses app.relaunch() (taken from the last comment of this issue).
  2. when downloading the updates file and replacing the original one with it, it gets corrupted (when I run it I get the error, though if I just download the AppImage file and run it directly, it will work). I've fixed it in the following way: downloaded the update file into different directory, then removed the old file, then use app.relaunch with execPath specified, so it'd run the new file instead of old one.

Can you look into that? Would be lovely to have it integrated in electron-simple-updater instead of writing some workarounds for downloadUpdate and quitAndInstall.
I can also make a PR on that, if you want.

@megahertz
Copy link
Owner

  1. Thanks, I'll replace it.
  2. I didn't see this problem before. Do you think the file was corrupted while downloading?

@serge1peshcoff
Copy link
Author

@megahertz

  1. awesome, thanks!
  2. I don't think it was corrupted while downloading since when I downloaded it it was okay. I think it can be because the running application can somehow access/modify the file after it was replaced, making it corrupted. I've faced this issue every time, it didn't happen once or twice.

Here is my solution to both problems:

const { app } = require('electron');
const path =    require('path');
const fs =      require('fs');
const request = require('httpreq');
const updater = require('electron-simple-updater');

const sleep = delay => new Promise(res => setTimeout(res, delay));

let _downloading = false;

module.exports.isDownloading = () => _downloading;
module.exports.downloadUpdate = downloadUpdate;

function downloadUpdate(meta) {
  const downloadUrl = meta.install;
  const appImagePath = process.env.APPIMAGE;

  _downloading = true;
  let downloadedImage;

  return getTemporaryFilePath(appImagePath, downloadUrl)
    .then((tempFile) => {
      downloadedImage = tempFile;
      return downloadFile(downloadUrl, tempFile);
    })
    .then(() => {
      return setExecFlag(downloadedImage);
    })
    .then(() => {
      return updater.emit('update-downloaded', meta);
    });
}

function quitAndInstall(meta) {
  const saveDir = path.dirname(appImagePath);
  const filename = path.resolve(saveDir, meta.install.substring(downloadUrl.lastIndexOf('/') + 1));

  return sleep(10 * 1000)
    .then(() => relaunchFile(filename));
}

function getTemporaryFilePath(appImagePath, downloadUrl) {
  const saveDir = path.dirname(appImagePath);
  return new Promise((resolve, reject) => {
    fs.access(saveDir, fs.W_OK, (err) => {
      if (err) {
        return reject(`Cannot write to the directory ${saveDir}`);
      }

      const filename = path.resolve(saveDir, downloadUrl.substring(downloadUrl.lastIndexOf('/') + 1));

      resolve(filename);
    });
  });
}

function downloadFile(url, tempPath) {
  return new Promise((resolve, reject) => {
    request.download(url, tempPath, (err, progress) => {
      if (err) {
        return reject(err);
      } else if (progress.statusCode !== 200) {
        return reject(progress.statusCode);
      }

      resolve(tempPath);
    });
  });
}

function setExecFlag(filePath) {
  return new Promise((resolve, reject) => {
    fs.access(filePath, fs.X_OK, (err) => {
      if (!err) {
        return resolve(filePath);
      }

      fs.chmod(filePath, '0755', (err) => {
        if (err) {
          return reject(`Could not make a file ${filePath} executable`);
        }

        resolve(filePath);
      });
    });
  });
}

function relaunchFile(filePath) {
  app.relaunch({ execPath: filePath });
  app.exit(0);
}

and the module that is using it:

const updater = require('electron-simple-updater');
const downloader = require('./updateDownloader');

updater.init({
    url: 'updates-json-url',
    autoDownload: false,
    logger: false
});

setInterval(() => updater.checkForUpdates(), 1000 * 60 * 5);

updater.on('update-available', (meta) => {
    if (process.platform === 'linux') {
        console.log('Downloading update for Linux');
        if (!downloader.isDownloading()) {
            downloader.downloadUpdate(meta);
        }
    } else {
        console.log('Downloading update for Windows');
        updater.downloadUpdate();
    }
});

updater.on('update-downloaded', (meta) => {
    console.log('Update downloaded, installing.');
    if (process.platform === 'linux') {
        downloader.quitAndInstall(meta);
    } else {
        updater.quitAndInstall();
    }
});

updater.on('checking-for-update', () => console.log('Checking for updates...'));
updater.on('update-not-available', () => console.log('Update is not available'));
updater.on('update-downloading', (meta) => console.log('Downloading update:', meta));
updater.on('error', (meta) => console.log('Error:', meta));

updater.checkForUpdates();

@megahertz
Copy link
Owner

@serge1peshcoff

Nice work, thanks! I'm going to apply some of your solutions.

megahertz added a commit that referenced this issue Oct 27, 2018
@serge1peshcoff
Copy link
Author

I've checked the new version and unfortunately it doesn't work properly for me. The application quits but doesn't spawn a new version. Probably this is the cause: electron-userland/electron-builder#1727
My workaround to that is not using the same file, but downloading the update into a different file and then running it using the execFile option of app.relaunch. Can you check if it'll work for you?

Thanks in advance for all of your job.

@megahertz
Copy link
Owner

Thank you for the hint. I want to prevent renaming of an AppImage file, but it requires additional time for investigating. Maybe there is no good solution. Anyway, thank you for your feedback. I'll try to solve these problems, but not very fast.

megahertz added a commit that referenced this issue Nov 25, 2018
@megahertz
Copy link
Owner

megahertz commented Nov 26, 2018

@serge1peshcoff Check out v1.4.0

@megahertz
Copy link
Owner

In my tests v1.4.2 works correctly. Feel free to reopen if you still have some errors.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants