From 4fd06c1e160e19f9bf3a8eee46f9e27b1b56974b Mon Sep 17 00:00:00 2001 From: Juan Tejada Date: Thu, 1 Sep 2016 12:19:08 -0700 Subject: [PATCH] fix(packages): Make sure we can migrate and correctly disable new packages - Add a package migration manager to be able to migrate external packages into N1, and decide if they should be enabled or disabled by default when added. --- dot-nylas/config.json | 5 ++ src/browser/application.es6 | 49 +------------- src/browser/package-migration-manager.es6 | 82 +++++++++++++++++++++++ 3 files changed, 90 insertions(+), 46 deletions(-) create mode 100644 src/browser/package-migration-manager.es6 diff --git a/dot-nylas/config.json b/dot-nylas/config.json index 8997f2ed7..5701cf243 100644 --- a/dot-nylas/config.json +++ b/dot-nylas/config.json @@ -6,6 +6,11 @@ "ui-light" ], "disabledPackages": [ + "message-view-on-github", + "personal-level-indicators", + "phishing-detection", + "github-contact-card", + "keybase" ] } } diff --git a/src/browser/application.es6 b/src/browser/application.es6 index 8503bbb78..0e3c7be2f 100644 --- a/src/browser/application.es6 +++ b/src/browser/application.es6 @@ -7,6 +7,7 @@ import ApplicationMenu from './application-menu'; import AutoUpdateManager from './auto-update-manager'; import PerformanceMonitor from './performance-monitor' import NylasProtocolHandler from './nylas-protocol-handler'; +import PackageMigrationManager from './package-migration-manager'; import ConfigPersistenceManager from './config-persistence-manager'; import {BrowserWindow, Menu, app, ipcMain, dialog} from 'electron'; @@ -44,7 +45,8 @@ export default class Application extends EventEmitter { this.configPersistenceManager = new ConfigPersistenceManager({configDirPath, resourcePath}); config.load(); - this.temporaryInitializeDisabledPackages(); + this.packageMigrationManager = new PackageMigrationManager({config, configDirPath, version}) + this.packageMigrationManager.migrate() let initializeInBackground = options.background; if (initializeInBackground === undefined) { @@ -78,51 +80,6 @@ export default class Application extends EventEmitter { return this.quitting; } - temporaryInitializeDisabledPackages() { - if (this.config.get('core.disabledPackagesInitialized')) { - return; - } - - const exampleNewNames = { - 'N1-Message-View-on-Github': 'message-view-on-github', - 'N1-Personal-Level-Indicators': 'personal-level-indicators', - 'N1-Phishing-Detection': 'phishing-detection', - 'N1-Github-Contact-Card-Section': 'github-contact-card', - 'N1-Keybase': 'keybase', - 'N1-Markdown': 'composer-markdown', - } - const exampleOldNames = Object.keys(exampleNewNames); - let examplesEnabled = []; - - // Temporary: Find the examples that have been manually installed - if (fs.existsSync(path.join(this.configDirPath, 'packages'))) { - const packages = fs.readdirSync(path.join(this.configDirPath, 'packages')); - examplesEnabled = packages.filter((packageName) => - exampleOldNames.includes(packageName) && (packageName[0] !== '.') - ); - - // Move old installed examples to a deprecated folder - const deprecatedPath = path.join(this.configDirPath, 'packages-deprecated') - if (!fs.existsSync(deprecatedPath)) { - fs.mkdirSync(deprecatedPath); - } - examplesEnabled.forEach((dir) => { - const prevPath = path.join(this.configDirPath, 'packages', dir) - const nextPath = path.join(deprecatedPath, dir) - fs.renameSync(prevPath, nextPath); - }); - } - - // Disable examples not specifically enabled - for (const oldName of Object.keys(exampleNewNames)) { - if (examplesEnabled.includes(oldName)) { - continue; - } - this.config.pushAtKeyPath('core.disabledPackages', exampleNewNames[oldName]); - } - this.config.set('core.disabledPackagesInitialized', true); - } - temporaryMigrateConfig() { const oldConfigFilePath = fs.resolve(this.configDirPath, 'config.cson'); const newConfigFilePath = path.join(this.configDirPath, 'config.json'); diff --git a/src/browser/package-migration-manager.es6 b/src/browser/package-migration-manager.es6 new file mode 100644 index 000000000..0063b3abc --- /dev/null +++ b/src/browser/package-migration-manager.es6 @@ -0,0 +1,82 @@ +import fs from 'fs' +import path from 'path' +import semver from 'semver' + + +const PACKAGE_MIGRATIONS = [ + { + "version": "0.4.50", + "package-migrations": [{ + "new-name": "composer-markdown", + "old-name": "N1-Markdown-Composer", + "enabled-by-default": false, + }], + }, +] + +class PackageMigrationManager { + + constructor({config, version, configDirPath} = {}) { + this.config = config + this.configDirPath = configDirPath + this.version = version + this.savedMigrationVersion = this.config.get('core.packageMigrationVersion') + } + + getMigrationsToRun() { + let migrations; + if (this.savedMigrationVersion) { + migrations = PACKAGE_MIGRATIONS + .filter((migration) => semver.gt(migration.version, this.savedMigrationVersion)) + .map(migration => migration['package-migrations']) + } else { + migrations = PACKAGE_MIGRATIONS.map(migration => migration['package-migrations']) + } + return [].concat.apply([], migrations) + } + + migrate() { + if (this.savedMigrationVersion === this.version) { return } + const migrations = this.getMigrationsToRun() + const oldPackNames = migrations.map((mig) => mig['old-name']) + const disabledPackNames = this.config.get('core.disabledPackages') || [] + let oldEnabledPackNames = [] + + if (fs.existsSync(path.join(this.configDirPath, 'packages'))) { + // Find any external packages that have been manually installed + const toMigrate = fs.readdirSync(path.join(this.configDirPath, 'packages')) + .filter((packName) => oldPackNames.includes(packName)) + .filter((packName) => packName[0] !== '.') + + // Move old installed packages to a deprecated folder + const deprecatedPath = path.join(this.configDirPath, 'packages-deprecated') + if (!fs.existsSync(deprecatedPath)) { + fs.mkdirSync(deprecatedPath); + } + toMigrate.forEach((packName) => { + const prevPath = path.join(this.configDirPath, 'packages', packName) + const nextPath = path.join(deprecatedPath, packName) + fs.renameSync(prevPath, nextPath); + }); + + oldEnabledPackNames = toMigrate.filter((packName) => ( + !(disabledPackNames).includes(packName) + )) + } + + // Enable any packages that were migrated from an old install and were + // enabled, or that should be enabled by default + migrations.forEach((migration) => { + // If the old install was enabled, keep it that way + if (oldEnabledPackNames.includes(migration['old-name'])) { return } + // If we want to enable the package by default, + if (migration['enabled-by-default']) { return } + const newName = migration['new-name'] + this.config.pushAtKeyPath('core.disabledPackages', newName); + }) + + this.config.set('core.packageMigrationVersion', this.version) + } +} + +export default PackageMigrationManager