5

Some of our users seem to run the app from the dmg file and never move it to their /Applications folder:

enter image description here

Which may explain why we're seeing this message in our internal logs:

Cannot update while running on a read-only volume. The application is on a read-only volume. Please move the application and try again. If you're on macOS Sierra or later, you'll need to move the application out of the Downloads directory. See https://github.com/Squirrel/Squirrel.Mac/issues/182 for more information.

(I visited the GitHub issue but couldn't find anything useful.)

I can definitely reproduce this error message by mounting the dmg, cd into it and run the app from the command line:

# I mounted the dmg and then cd into it, see:
$ pwd
/Volumes/**REDACTED**2.42.0-beta.5

# Then I launched the app from the command line, see:
$ ./**REDACTED**.app/Contents/MacOS/**REDACTED**
Found version 2.42.1-beta.0 (url: **REDACTED**-2.42.1-beta.0.zip, **REDACTED**-2.42.1-beta.0.dmg, **REDACTED**-2.42.1-beta.0.dmg)
Downloading update from **REDACTED**-2.42.1-beta.0.zip, **REDACTED**-2.42.1-beta.0.dmg, **REDACTED**-2.42.1-beta.0.dmg
updater cache dir: /Users/**REDACTED**/Library/Application Support/Caches/**REDACTED**-updater
Update has already been downloaded to /Users/**REDACTED**/Library/Application Support/Caches/**REDACTED**-updater/pending/**REDACTED**-2.42.1-beta.0.zip).
/ requested
[Error: Cannot update while running on a read-only volume. The application is on a read-only volume. Please move the application and try again. If you're on macOS Sierra or later, you'll need to move the application out of the Downloads directory. See https://github.com/Squirrel/Squirrel.Mac/issues/182 for more information.] {
  code: 8,
  domain: 'SQRLUpdaterErrorDomain'
}

While the app is running, a background process checks for updates. We can see that it has found one and downloaded it. However it couldn't apply it.

We ship regular updates but these users can't get them. I suspect some of them don't even realise that they haven't "properly" installed the app.

Is there a way we can detect that with Electron or Node.js?

customcommander
  • 17,580
  • 5
  • 58
  • 84
  • Same happens, at least for some apps, if they are extracted in Downloads instead of Applications: update does not work. Seems like VSCode is able to detect it and not auto-update. – ghybs Mar 15 '22 at 08:57

1 Answers1

2

This answer comes from my repository of Electron recipes:
https://customcommander.github.io/electron-survival-guide/


TL; DR

You can use the app.isInApplicationsFolder() method.


Main Process

The renderer process can ask if the app is running from the Applications folder by asking in the in-app-folder IPC channel. The main process will respond in that same channel.

const {app, BrowserWindow, ipcMain} = require('electron');
const path = require('path');

app.whenReady().then(async () => {
  const bwin = new BrowserWindow({
    width: 300,
    height: 300,
    webPreferences: {
      nodeIntegration: false,
      contextIsolation: true,
      preload: path.resolve(__dirname, 'preload.js')
    }
  });

  // Must be registered **before** the renderer process starts.
  ipcMain.handle('in-app-folder', () => app.isInApplicationsFolder());

  await bwin.loadFile('renderer.html');
  bwin.show();
});

Preload Script

The preload script exposes a method under the MY_APP namespace that the renderer process can use to know if the app is run from the Applications folder.

const {contextBridge, ipcRenderer} = require('electron');

contextBridge.exposeInMainWorld('MY_APP', {
  async isInApplicationsFolder() {
    return ipcRenderer.invoke('in-app-folder');
  }
});

Renderer

The MY_APP namespace is defined in the preload script and contains a method to enquire about the app location.

<html>
  <head>
    <style>
      body {background-color:black;color:limegreen}
    </style>
  </head>
  <body>
    <h1>Are you running this app from the Applications folder?</h1>
    <div id="response"></div>
    <script>
      MY_APP.isInApplicationsFolder().then(yes => {
        if (yes) {
          document.querySelector('#response').innerHTML = 'Yes';
        } else {
          document.querySelector('#response').innerHTML = 'No';
        }
      });
    </script>
  </body>
</html>

enter image description here

customcommander
  • 17,580
  • 5
  • 58
  • 84