Launcher cycle detection
Summary: Flipper Electron delegates to the Launcher if it is found right on startup to fetch the most recent/compatible version of Flipper. The Launcher then opens the downloaded app with a `--no-launcher` option to avoid bouncing back and forth between the Electron app and the Launcher. This depends on the argument processing working unchanged. In the past this has been somewhat difficult to guarantee as this doesn't happen in one place and dev/prod builds have handled arguments different due to Electron weirdness (requiring a `--` passed in, for instance). If anything here goes wrong, we end up in a very nasty scenario where the launcher and the Electron app rapidly open and close, making it nearly impossible for users to escape that vicious cycle. `pkill -f Flipper` being the best option, if you can focus a terminal for long enough. In order to avoid this from ever happening in the future, this introduces a quick check for the last startup is written with a timestamp and if this is less than 5s in the past, we will skip delegating to the Launcher altogether, keeping the current instance running. Reviewed By: jknoxville Differential Revision: D14598136 fbshipit-source-id: b3335ce7ec7dc3e5e014d459db31df4c8a774fc6
This commit is contained in:
committed by
Facebook Github Bot
parent
ec42fd62ac
commit
fd6cab2a7b
@@ -159,11 +159,11 @@ app.on('will-finish-launching', () => {
|
||||
|
||||
app.on('ready', () => {
|
||||
// If we delegate to the launcher, shut down this instance of the app.
|
||||
if (delegateToLauncher(argv)) {
|
||||
delegateToLauncher(argv).then(hasLauncherInvoked => {
|
||||
if (hasLauncherInvoked) {
|
||||
app.quit();
|
||||
return;
|
||||
}
|
||||
|
||||
appReady = true;
|
||||
app.commandLine.appendSwitch('scroll-bounce');
|
||||
tryCreateWindow();
|
||||
@@ -178,6 +178,7 @@ app.on('ready', () => {
|
||||
installExtension(REDUX_DEVTOOLS.id);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
ipcMain.on('componentDidMount', event => {
|
||||
if (deeplinkURL) {
|
||||
|
||||
@@ -7,7 +7,11 @@
|
||||
|
||||
const os = require('os');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const promisify = require('util').promisify;
|
||||
const {spawn} = require('child_process');
|
||||
const xdg = require('xdg-basedir');
|
||||
const mkdirp = require('mkdirp');
|
||||
|
||||
const isProduction = () =>
|
||||
!/node_modules[\\/]electron[\\/]/.test(process.execPath);
|
||||
@@ -38,12 +42,42 @@ const startLauncher = argv => {
|
||||
}
|
||||
};
|
||||
|
||||
const checkIsCycle = async () => {
|
||||
const dir = path.join(xdg.cache, 'flipper');
|
||||
const filePath = path.join(dir, 'last-launcher-run');
|
||||
// This isn't monotonically increasing, so there's a change we get time drift
|
||||
// between the checks, but the worst case here is that we do two roundtrips
|
||||
// before this check works.
|
||||
const rightNow = Date.now();
|
||||
|
||||
let backThen;
|
||||
try {
|
||||
backThen = parseInt(await promisify(fs.readFile)(filePath), 10);
|
||||
} catch (e) {
|
||||
backThen = 0;
|
||||
}
|
||||
|
||||
const delta = rightNow - backThen;
|
||||
await promisify(mkdirp)(dir);
|
||||
await promisify(fs.writeFile)(filePath, rightNow);
|
||||
|
||||
// If the last startup was less than 5s ago, something's not okay.
|
||||
return Math.abs(delta) < 5000;
|
||||
};
|
||||
|
||||
/**
|
||||
* Runs the launcher if required and returns a boolean based on whether
|
||||
* it has. You should shut down this instance of the app in that case.
|
||||
*/
|
||||
module.exports = function delegateToLauncher(argv) {
|
||||
module.exports = async function delegateToLauncher(argv) {
|
||||
if (argv.launcher && isProduction() && isLauncherInstalled()) {
|
||||
if (await checkIsCycle()) {
|
||||
console.error(
|
||||
'Launcher cycle detected. Not delegating even though I usually would.',
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
console.warn('Delegating to Flipper Launcher ...');
|
||||
console.warn(
|
||||
`You can disable this behavior by passing '--no-launcher' at startup.`,
|
||||
|
||||
@@ -15,7 +15,9 @@
|
||||
"@babel/preset-react": "^7.0.0",
|
||||
"expand-tilde": "^2.0.2",
|
||||
"metro": "^0.49.0",
|
||||
"recursive-readdir": "2.2.2"
|
||||
"mkdirp": "^0.5.1",
|
||||
"recursive-readdir": "2.2.2",
|
||||
"xdg-basedir": "^3.0.0"
|
||||
},
|
||||
"devDependencies": {},
|
||||
"resolutions": {
|
||||
|
||||
@@ -3182,6 +3182,11 @@ ws@^1.1.0:
|
||||
options ">=0.0.5"
|
||||
ultron "1.0.x"
|
||||
|
||||
xdg-basedir@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4"
|
||||
integrity sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=
|
||||
|
||||
xpipe@^1.0.5:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/xpipe/-/xpipe-1.0.5.tgz#8dd8bf45fc3f7f55f0e054b878f43a62614dafdf"
|
||||
|
||||
Reference in New Issue
Block a user