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 (

{name} ({version})

diff --git a/app/package.json b/app/package.json index 404640ec..5d3f02ff 100644 --- a/app/package.json +++ b/app/package.json @@ -14,6 +14,7 @@ "rebuild": "npm rebuild --runtime=electron --target=1.4.12 --disturl=https://atom.io/download/electron --abi=50" }, "dependencies": { + "electron-updater": "^1.1.1", "nodobjc": "^2.1.0", "rmdir": "^1.2.0", "semver": "^5.3.0", diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 00000000..c2cc50d1 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,29 @@ +# https://github.com/sindresorhus/appveyor-node/blob/master/appveyor.yml + +environment: + matrix: + - platform: x64 + +image: Visual Studio 2015 + +init: + - npm config set msvs_version 2015 # we need this to build `pty.js` + +install: + - ps: Install-Product node 6 x64 + - set CI=true + - npm install -g npm@3.10.8 + - npm install + +build: off + +shallow_clone: true + +test_script: + - node --version + - npm --version + - npm run lint + - npm run test + +on_success: + - npm run package diff --git a/build/icons/1024x1024.png b/build/icons/1024x1024.png new file mode 100644 index 00000000..5dd8d9ff Binary files /dev/null and b/build/icons/1024x1024.png differ diff --git a/build/icons/128x128.png b/build/icons/128x128.png new file mode 100644 index 00000000..b8939a75 Binary files /dev/null and b/build/icons/128x128.png differ diff --git a/build/icons/16x16.png b/build/icons/16x16.png new file mode 100644 index 00000000..a08cba88 Binary files /dev/null and b/build/icons/16x16.png differ diff --git a/build/icons/256x256.png b/build/icons/256x256.png new file mode 100644 index 00000000..89ff2cf8 Binary files /dev/null and b/build/icons/256x256.png differ diff --git a/build/icons/32x32.png b/build/icons/32x32.png new file mode 100644 index 00000000..53c7788f Binary files /dev/null and b/build/icons/32x32.png differ diff --git a/build/icons/48x48.png b/build/icons/48x48.png new file mode 100644 index 00000000..cfbd5633 Binary files /dev/null and b/build/icons/48x48.png differ diff --git a/build/icons/512x512.png b/build/icons/512x512.png new file mode 100644 index 00000000..b23a9acc Binary files /dev/null and b/build/icons/512x512.png differ diff --git a/package.json b/package.json index 612beefe..bddc0107 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "start": "cross-env NODE_ENV=production electron ./app", "start-hot": "cross-env HOT=1 NODE_ENV=development ./node_modules/.bin/electron -r babel-register ./app/main.development", "package": "npm run build && build --publish never", + "release": "build -mwl --draft", "dev": "concurrently --kill-others \"npm run hot-server\" \"npm run start-hot\"", "postinstall": "concurrently \"node node_modules/fbjs-scripts/node/check-dev-engines.js package.json\"" }, @@ -24,10 +25,16 @@ "homepage": "https://cerebroapp.com", "appId": "com.cerebroapp.Cerebro", "category": "public.app-category.productivity", - "protocols" : { + "protocols": { "name": "Cerebro URLs", "role": "Viewer", - "schemes": ["cerebro"] + "schemes": [ + "cerebro" + ] + }, + "directories": { + "app": "./app", + "output": "release" }, "linux": { "arch": [ @@ -75,11 +82,14 @@ "!**/node_modules/.bin", "!**/*.{o,hprof,orig,pyc,pyo,rbc}", "!**/{.DS_Store,.git,.hg,.svn,CVS,RCS,SCCS,__pycache__,thumbs.db,.gitignore,.gitattributes,.editorconfig,.flowconfig,.yarn-metadata.json,.idea,appveyor.yml,.travis.yml,circle.yml,npm-debug.log,.nyc_output,yarn.lock,.yarn-integrity}" - ] - }, - "directories": { - "app": "./app", - "output": "release" + ], + "squirrelWindows": { + "iconUrl": "https://raw.githubusercontent.com/KELiON/cerebro/master/build/icon.ico" + }, + "publish": { + "provider": "github", + "vPrefixedTagName": false + } }, "bin": { "electron": "./node_modules/.bin/electron" @@ -136,7 +146,7 @@ "css-loader": "0.23.1", "del": "2.2.0", "devtron": "1.2.0", - "electron-builder": "10.8.1", + "electron-builder": "11.5.1", "electron-prebuilt": "1.4.12", "electron-rebuild": "1.4.0", "eslint": "2.10.2",