diff --git a/.travis.yml b/.travis.yml index 571b2325..8e9b59dd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,27 @@ -before_install: - - npm install -g npm@3.10.8 +sudo: required +dist: trusty -sudo: false language: node_js -node_js: - - "6" + +matrix: + include: + - os: linux + node_js: 6 + env: CC=clang CXX=clang++ npm_config_clang=1 + compiler: clang + +addons: + apt: + packages: + - libgnome-keyring-dev + - libicns + - graphicsmagick + - xz-utils + - rpm + - bsdtar + +before_install: + - npm install -g npm@3.10.8 cache: directories: @@ -30,4 +47,4 @@ before_script: script: - npm run lint - npm run test -- npm run build +- npm run package diff --git a/app/AppUpdater.js b/app/AppUpdater.js new file mode 100644 index 00000000..539442ce --- /dev/null +++ b/app/AppUpdater.js @@ -0,0 +1,22 @@ +import * as os from "os"; +import { dialog, } from 'electron' +import { autoUpdater } from "electron-updater"; + +const event = 'update-downloaded' + +export default class AppUpdater { + constructor(w) { + if (process.env.NODE_ENV === 'development' || os.platform() === "linux") { + return + } + + autoUpdater.on(event, (payload) => { + w.webContents.send('message', { + message: event, + payload + }) + }) + + autoUpdater.checkForUpdates() + } +} diff --git a/app/main.development.js b/app/main.development.js index fdafaf36..a73a58d3 100644 --- a/app/main.development.js +++ b/app/main.development.js @@ -4,6 +4,7 @@ import createMainWindow from './main/createWindow' import createBackgroundWindow from './background/createWindow' import config from './lib/config' import AppTray from './main/createWindow/AppTray' +import AppUpdater from './AppUpdater' const trayIconSrc = `${__dirname}/tray_iconTemplate@2x.png` const isDev = () => ( @@ -12,6 +13,7 @@ const isDev = () => ( let mainWindow let backgroundWindow let tray +let appUpdater if (process.env.NODE_ENV !== 'development') { // Set up crash reporter before creating windows in production builds @@ -38,7 +40,7 @@ app.on('ready', () => { src: trayIconSrc, isDev: isDev(), mainWindow, - backgroundWindow + backgroundWindow, }) // Show tray icon if it is set in configuration @@ -46,6 +48,8 @@ app.on('ready', () => { tray.show() } + appUpdater = new AppUpdater(mainWindow) + app.dock && app.dock.hide() }) diff --git a/app/main/createWindow/AppTray.js b/app/main/createWindow/AppTray.js index 36660994..f5e8e508 100644 --- a/app/main/createWindow/AppTray.js +++ b/app/main/createWindow/AppTray.js @@ -1,6 +1,7 @@ import { Menu, Tray, app } from 'electron' import showWindowWithTerm from './showWindowWithTerm' import toggleWindow from './toggleWindow' +import checkForUpdates from './checkForUpdates' /** * Class that controls state of icon in menu bar @@ -44,10 +45,14 @@ export default class AppTray { label: 'Plugins', click: () => showWindowWithTerm(mainWindow, 'plugins'), }, - separator, { label: 'Preferences...', click: () => showWindowWithTerm(mainWindow, 'settings'), + }, + separator, + { + label: 'Check for updates', + click: () => checkForUpdates(), } ] diff --git a/app/main/createWindow/checkForUpdates.js b/app/main/createWindow/checkForUpdates.js new file mode 100644 index 00000000..7badb089 --- /dev/null +++ b/app/main/createWindow/checkForUpdates.js @@ -0,0 +1,80 @@ +import { dialog, app, shell } from 'electron' +import os from 'os' +import semver from 'semver' +import https from 'https' + +const currentVersion = app.getVersion() +const DEFAULT_DOWNLOAD_URL = 'https://github.com/KELiON/cerebro/releases' + +const TITLE = 'Cerebro Updates' + +const PLATFORM_EXTENSIONS = { + darwin: 'dmg', + linux: 'AppImage', + win32: 'exe' +} +const platform = os.platform() +const installerExtension = PLATFORM_EXTENSIONS[platform] + +const findInstaller = (assets) => { + if (!installerExtension) { + return DEFAULT_DOWNLOAD_URL + } + const regexp = new RegExp(`\.${installerExtension}$`) + const downloadUrl = assets + .map(a => a.browser_download_url) + .find(url => url.match(regexp)) + return downloadUrl || DEFAULT_DOWNLOAD_URL +} + +const getLatestRelease = () => ( + new Promise((resolve, reject) => { + const opts = { + host: 'api.github.com', + path: '/repos/KELiON/cerebro/releases', + headers: { + 'User-Agent': `CerebroApp v${currentVersion}` + } + } + https.get(opts, res => { + let json = '' + res.on('data', (chunk) => { + json += chunk + }) + res.on('end', () => resolve(JSON.parse(json)[0])) + }).on('error', () => reject()) + }) +) + +export default () => { + getLatestRelease().then(release => { + const version = semver.valid(release.tag_name) + if (version && semver.gt(version, currentVersion)) { + dialog.showMessageBox({ + buttons: ['Skip', 'Download'], + defaultId: 1, + cancelId: 0, + title: TITLE, + message: `New version available: ${version}`, + detail: 'Click download to get it now', + }, (response) => { + if (response === 1) { + const url = findInstaller(release.assets) + shell.openExternal(url) + } + }) + } else { + dialog.showMessageBox({ + title: TITLE, + message: `Your are using latest version of Cerebro (${currentVersion})`, + buttons: [] + }) + } + }).catch(err => { + console.log('Catch error!', err) + dialog.showErrorBox( + TITLE, + 'Error fetching latest version', + ) + }) +} diff --git a/app/main/main.js b/app/main/main.js index 6201bd81..5b9e086b 100644 --- a/app/main/main.js +++ b/app/main/main.js @@ -38,6 +38,13 @@ on('showTerm', (term) => { store.dispatch(updateTerm(term)); }); +on('update-downloaded', (payload) => { + new Notification('Cerebro: update is ready to install', { + body: 'New version is downloaded and will be automatically installed on quit' + }) +}); + + // Handle `updateTheme` rpc event and change current theme on('updateTheme', changeTheme); diff --git a/app/main/plugins/core/cerebro/plugins/Preview/index.js b/app/main/plugins/core/cerebro/plugins/Preview/index.js index cce7a1bb..1161402c 100644 --- a/app/main/plugins/core/cerebro/plugins/Preview/index.js +++ b/app/main/plugins/core/cerebro/plugins/Preview/index.js @@ -81,7 +81,7 @@ export default class Preview extends Component { installedVersion, isUpdateAvailable } = this.props - const match = repo.match(/^.+github.com\/([^\/]+\/[^\/]+).*?/) + const match = repo && repo.match(/^.+github.com\/([^\/]+\/[^\/]+).*?/) return (