diff --git a/apm/package.json b/apm/package.json index c070e36c3..1dce7e90b 100644 --- a/apm/package.json +++ b/apm/package.json @@ -6,6 +6,6 @@ "url": "https://github.com/atom/atom.git" }, "dependencies": { - "atom-package-manager": "0.135.0" + "atom-package-manager": "0.167.0" } } diff --git a/atom.sh b/atom.sh index 292f63780..57bdb9bcc 100755 --- a/atom.sh +++ b/atom.sh @@ -1,6 +1,6 @@ #!/bin/bash -# This file is sym-linked to the `atom` executable. +# This file is sym-linked to the `electron` executable. # It is used by `apm` when calling commands. if [ "$(uname)" == 'Darwin' ]; then @@ -53,16 +53,16 @@ if [ $OS == 'Mac' ]; then exit 1 fi - ATOM_SHELL_PATH=${ATOM_SHELL_PATH:-$EDGEHILL_PATH/atom-shell} # Set ATOM_SHELL_PATH unless it is already set + ELECTRON_PATH=${ELECTRON_PATH:-$EDGEHILL_PATH/atom-shell} # Set ELECTRON_PATH unless it is already set # Exit if Atom can't be found - if [ -z "$ATOM_SHELL_PATH" ]; then - echo "Cannot locate atom-shell. Be sure you have run script/grunt download-atom-shell first from $EDGEHILL_PATH" + if [ -z "$ELECTRON_PATH" ]; then + echo "Cannot locate electron. Be sure you have run script/grunt download-electron first from $EDGEHILL_PATH" exit 1 fi # We find the atom-shell executable inside of the atom-shell directory. - $ATOM_SHELL_PATH/Atom.app/Contents/MacOS/Atom --executed-from="$(pwd)" --pid=$$ "$@" $EDGEHILL_PATH + $ELECTRON_PATH/Electron.app/Contents/MacOS/Electron --executed-from="$(pwd)" --pid=$$ "$@" $EDGEHILL_PATH elif [ $OS == 'Linux' ]; then DOT_INBOX_DIR="$HOME/.nylas" @@ -74,16 +74,16 @@ elif [ $OS == 'Linux' ]; then exit 1 fi - ATOM_SHELL_PATH=${ATOM_SHELL_PATH:-$EDGEHILL_PATH/atom-shell} # Set ATOM_SHELL_PATH unless it is already set + ELECTRON_PATH=${ELECTRON_PATH:-$EDGEHILL_PATH/electron} # Set ELECTRON_PATH unless it is already set # Exit if Atom can't be found - if [ -z "$ATOM_SHELL_PATH" ]; then + if [ -z "$ELECTRON_PATH" ]; then echo "Cannot locate atom-shell. Be sure you have run script/grunt download-atom-shell first from $EDGEHILL_PATH" exit 1 fi - # We find the atom-shell executable inside of the atom-shell directory. - $ATOM_SHELL_PATH/atom --executed-from="$(pwd)" --pid=$$ "$@" $EDGEHILL_PATH + # We find the electron executable inside of the atom-shell directory. + $ELECTRON_PATH/electron --executed-from="$(pwd)" --pid=$$ "$@" $EDGEHILL_PATH fi diff --git a/build/Gruntfile.coffee b/build/Gruntfile.coffee index d6db750c6..21d691b54 100644 --- a/build/Gruntfile.coffee +++ b/build/Gruntfile.coffee @@ -75,8 +75,8 @@ module.exports = (grunt) -> [major, minor, patch] = packageJson.version.split('.') tmpDir = os.tmpdir() - appName = if process.platform is 'darwin' then 'Edgehill.app' else 'Edgehill' - buildDir = grunt.option('build-dir') ? path.join(tmpDir, 'edgehill-build') + appName = if process.platform is 'darwin' then 'Nylas.app' else 'Nylas' + buildDir = grunt.option('build-dir') ? path.join(tmpDir, 'nylas-build') buildDir = path.resolve(buildDir) installDir = grunt.option('install-dir') @@ -89,17 +89,17 @@ module.exports = (grunt) -> contentsDir = shellAppDir appDir = path.join(shellAppDir, 'resources', 'app') installDir ?= path.join(process.env.ProgramFiles, appName) - killCommand = 'taskkill /F /IM edgehill.exe' + killCommand = 'taskkill /F /IM nylas.exe' else if process.platform is 'darwin' contentsDir = path.join(shellAppDir, 'Contents') appDir = path.join(contentsDir, 'Resources', 'app') installDir ?= path.join('/Applications', appName) - killCommand = 'pkill -9 Edgehill' + killCommand = 'pkill -9 Nylas' else contentsDir = shellAppDir appDir = path.join(shellAppDir, 'resources', 'app') installDir ?= process.env.INSTALL_PREFIX ? '/usr/local' - killCommand ='pkill -9 edgehill' + killCommand ='pkill -9 nylas' installDir = path.resolve(installDir) @@ -284,10 +284,10 @@ module.exports = (grunt) -> 'create-windows-installer': appDirectory: shellAppDir outputDirectory: path.join(buildDir, 'installer') - authors: 'Nylas' + authors: 'Nylas Inc.' loadingGif: path.resolve(__dirname, '..', 'resources', 'win', 'loading.gif') iconUrl: 'https://raw.githubusercontent.com/atom/atom/master/resources/win/atom.ico' - setupIcon: path.resolve(__dirname, '..', 'resources', 'win', 'edgehill.ico') + setupIcon: path.resolve(__dirname, '..', 'resources', 'win', 'nylas.ico') shell: 'kill-atom': diff --git a/build/tasks/build-task.coffee b/build/tasks/build-task.coffee index 2e4c106cf..af4e06cad 100644 --- a/build/tasks/build-task.coffee +++ b/build/tasks/build-task.coffee @@ -20,33 +20,25 @@ module.exports = (grunt) -> rm path.join(buildDir, 'installer') mkdir path.dirname(buildDir) - # Atom-shell IS the executable. We just need to make sure our Edgehill - # app ends up in the correct `app` folder for atom-shell to find. if process.platform is 'darwin' - cp 'atom-shell/Atom.app', shellAppDir, filter: /default_app/ - cp(path.join(shellAppDir, 'Contents', 'MacOS', 'Atom'), - path.join(shellAppDir, 'Contents', 'MacOS', 'Edgehill')) - rm path.join(shellAppDir, 'Contents', 'MacOS', 'Atom') - else if process.platform is 'win32' - cp 'atom-shell', shellAppDir, filter: /default_app/ - # We can rename atom.exe to edgehill.exe, but all of the node libraries with - # native code are hard-linked to a file called atom.exe. For now, let's just - # leave it as atom.exe. https://github.com/atom/atom-shell/issues/713 + cp 'atom-shell/Electron.app', shellAppDir, filter: /default_app/ + cp(path.join(shellAppDir, 'Contents', 'MacOS', 'Electron'), + path.join(shellAppDir, 'Contents', 'MacOS', 'Nylas')) + rm path.join(shellAppDir, 'Contents', 'MacOS', 'Electron') else cp 'atom-shell', shellAppDir, filter: /default_app/ - cp path.join(shellAppDir, 'atom'), path.join(shellAppDir, 'edgehill') + cp path.join(shellAppDir, 'atom'), path.join(shellAppDir, 'nylas') rm path.join(shellAppDir, 'atom') mkdir appDir - # Note that `atom.sh` can't be renamed `edgehill.sh` because `apm` - # is currently hard-coded to call `atom.sh` if process.platform isnt 'win32' - cp 'atom.sh', path.join(appDir, 'atom.sh') + cp 'atom.sh', path.resolve(appDir, '..', 'new-app', 'atom.sh') cp 'package.json', path.join(appDir, 'package.json') - cp path.join('resources', 'edgehill.png'), path.join(appDir, 'edgehill.png') + cp path.join('resources', 'nylas.png'), path.join(appDir, 'nylas.png') + packageNames = [] packageDirectories = [] nonPackageDirectories = [ 'benchmark' @@ -56,26 +48,19 @@ module.exports = (grunt) -> ] {devDependencies} = grunt.file.readJSON('package.json') + for packageFolder in ['node_modules', 'internal_packages'] + for child in fs.readdirSync(packageFolder) + directory = path.join(packageFolder, child) + if isAtomPackage(directory) + packageDirectories.push(directory) + packageNames.push(child) + else + nonPackageDirectories.push(directory) - for child in fs.readdirSync('node_modules') - directory = path.join('node_modules', child) - if isAtomPackage(directory) - packageDirectories.push(directory) - else - nonPackageDirectories.push(directory) - - for child in fs.readdirSync('internal_packages') - directory = path.join('internal_packages', child) - if isAtomPackage(directory) - packageDirectories.push(directory) - else - nonPackageDirectories.push(directory) - - # Put any paths here that shouldn't end up in the built Atom.app + # Put any paths here that shouldn't end up in the built Electron.app # so that it doesn't becomes larger than it needs to be. ignoredPaths = [ path.join('git-utils', 'deps') - path.join('oniguruma', 'deps') path.join('less', 'dist') path.join('npm', 'doc') path.join('npm', 'html') @@ -101,19 +86,32 @@ module.exports = (grunt) -> path.join('resources', 'win') # These are only require in dev mode when the grammar isn't precompiled - path.join('atom-keymap', 'node_modules', 'loophole') - path.join('atom-keymap', 'node_modules', 'pegjs') - path.join('atom-keymap', 'node_modules', '.bin', 'pegjs') path.join('snippets', 'node_modules', 'loophole') path.join('snippets', 'node_modules', 'pegjs') path.join('snippets', 'node_modules', '.bin', 'pegjs') + # These aren't needed since WeakMap is built-in + path.join('emissary', 'node_modules', 'es6-weak-map') + path.join('property-accessors', 'node_modules', 'es6-weak-map') + '.DS_Store' '.jshintrc' '.npmignore' '.pairs' '.travis.yml' + 'appveyor.yml' + '.idea' + '.editorconfig' + '.lint' + '.lintignore' + '.eslintrc' + '.jshintignore' + '.gitattributes' + '.gitkeep' ] + + packageNames.forEach (packageName) -> ignoredPaths.push(path.join(packageName, 'spec')) + ignoredPaths = ignoredPaths.map (ignoredPath) -> escapeRegExp(ignoredPath) # Add .* to avoid matching hunspell_dictionaries. @@ -125,7 +123,6 @@ module.exports = (grunt) -> ignoredPaths.push "#{escapeRegExp(path.join('git-utils', 'src') + path.sep)}.*\\.(cc|h)*" ignoredPaths.push "#{escapeRegExp(path.join('keytar', 'src') + path.sep)}.*\\.(cc|h)*" ignoredPaths.push "#{escapeRegExp(path.join('nslog', 'src') + path.sep)}.*\\.(cc|h)*" - ignoredPaths.push "#{escapeRegExp(path.join('oniguruma', 'src') + path.sep)}.*\\.(cc|h)*" ignoredPaths.push "#{escapeRegExp(path.join('pathwatcher', 'src') + path.sep)}.*\\.(cc|h)*" ignoredPaths.push "#{escapeRegExp(path.join('runas', 'src') + path.sep)}.*\\.(cc|h)*" ignoredPaths.push "#{escapeRegExp(path.join('scrollbar-style', 'src') + path.sep)}.*\\.(cc|h)*" @@ -153,7 +150,7 @@ module.exports = (grunt) -> pathToCopy = path.resolve(pathToCopy) nodeModulesFilter.test(pathToCopy) or testFolderPattern.test(pathToCopy) or exampleFolderPattern.test(pathToCopy) - packageFilter = new RegExp("(#{ignoredPaths.join('|')})|(.+\\.(coffee|cjsx|jsx)$)") + packageFilter = new RegExp("(#{ignoredPaths.join('|')})|(.+\\.(cson|coffee|cjsx|jsx)$)") filterPackage = (pathToCopy) -> return true if benchmarkFolderPattern.test(pathToCopy) @@ -171,9 +168,9 @@ module.exports = (grunt) -> cp 'src', path.join(appDir, 'src'), filter: /.+\.(cson|coffee|cjsx|jsx)$/ cp 'static', path.join(appDir, 'static') - cp path.join('apm', 'node_modules', 'atom-package-manager'), path.join(appDir, 'apm'), filter: filterNodeModule + cp path.join('apm', 'node_modules', 'atom-package-manager'), path.resolve(appDir, '..', 'new-app', 'apm'), filter: filterNodeModule if process.platform isnt 'win32' - fs.symlinkSync(path.join('..', '..', 'bin', 'apm'), path.join(appDir, 'apm', 'node_modules', '.bin', 'apm')) + fs.symlinkSync(path.join('..', '..', 'bin', 'apm'), path.resolve(appDir, '..', 'new-app', 'apm', 'node_modules', '.bin', 'apm')) if process.platform is 'darwin' grunt.file.recurse path.join('resources', 'mac'), (sourcePath, rootDirectory, subDirectory='', filename) -> @@ -186,9 +183,10 @@ module.exports = (grunt) -> cp path.join('resources', 'win', 'atom.js'), path.join(shellAppDir, 'resources', 'cli', 'atom.js') cp path.join('resources', 'win', 'apm.sh'), path.join(shellAppDir, 'resources', 'cli', 'apm.sh') + if process.platform is 'linux' + cp path.join('resources', 'linux', 'icons'), path.join(buildDir, 'icons') + dependencies = ['compile', 'generate-license:save', 'generate-module-cache', 'compile-packages-slug'] dependencies.push('copy-info-plist') if process.platform is 'darwin' dependencies.push('set-exe-icon') if process.platform is 'win32' grunt.task.run(dependencies...) - - grunt.log.ok("Built Edgehill into #{buildDir}") diff --git a/build/tasks/codesign-task.coffee b/build/tasks/codesign-task.coffee index e7f854a9c..d5513213c 100644 --- a/build/tasks/codesign-task.coffee +++ b/build/tasks/codesign-task.coffee @@ -44,14 +44,14 @@ module.exports = (grunt) -> # TODO: Don't do anything now, because we need a certificate pfx file # issued from a certificate authority, and we don't have one. return callback() - spawn {cmd: 'taskkill', args: ['/F', '/IM', 'atom.exe']}, -> + spawn {cmd: 'taskkill', args: ['/F', '/IM', 'nylas.exe']}, -> cmd = process.env.JANKY_SIGNTOOL ? 'signtool' - args = ['sign', path.join(grunt.config.get('atom.shellAppDir'), 'atom.exe')] + args = ['sign', path.join(grunt.config.get('atom.shellAppDir'), 'nylas.exe')] spawn {cmd, args}, (error) -> return callback(error) if error? - setupExePath = path.resolve(grunt.config.get('atom.buildDir'), 'installer', 'AtomSetup.exe') + setupExePath = path.resolve(grunt.config.get('atom.buildDir'), 'installer', 'NylasSetup.exe') if fs.isFileSync(setupExePath) args = ['sign', setupExePath] spawn {cmd, args}, (error) -> callback(error) diff --git a/build/tasks/copy-info-plist-task.coffee b/build/tasks/copy-info-plist-task.coffee index 76b8d6f0c..69aa8dd60 100644 --- a/build/tasks/copy-info-plist-task.coffee +++ b/build/tasks/copy-info-plist-task.coffee @@ -9,5 +9,5 @@ module.exports = (grunt) -> helperPlistPath = path.join(contentsDir, 'Frameworks/Atom Helper.app/Contents/Info.plist') # Copy custom plist files - cp 'resources/mac/edgehill-Info.plist', plistPath + cp 'resources/mac/nylas-Info.plist', plistPath cp 'resources/mac/helper-Info.plist', helperPlistPath diff --git a/build/tasks/install-task.coffee b/build/tasks/install-task.coffee index 0a4d823d2..3083ef63c 100644 --- a/build/tasks/install-task.coffee +++ b/build/tasks/install-task.coffee @@ -18,7 +18,7 @@ module.exports = (grunt) -> grunt.log.error("Failed to copy #{shellAppDir} to #{installDir}") createShortcut = path.resolve 'script', 'create-shortcut.cmd' - runas('cmd', ['/c', createShortcut, path.join(installDir, 'edgehill.exe'), 'Edgehill']) + runas('cmd', ['/c', createShortcut, path.join(installDir, 'nylas.exe'), 'Nylas']) else if process.platform is 'darwin' rm installDir mkdir path.dirname(installDir) @@ -29,27 +29,27 @@ module.exports = (grunt) -> fs.renameSync(tempFolder, installDir) else binDir = path.join(installDir, 'bin') - shareDir = path.join(installDir, 'share', 'edgehill') + shareDir = path.join(installDir, 'share', 'nylas') - iconName = path.join(shareDir,'resources', 'app', 'resources', 'edgehill.png') + iconName = path.join(shareDir,'resources', 'app', 'resources', 'nylas.png') mkdir binDir - # Note that `atom.sh` can't be renamed `edgehill.sh` because `apm` + # Note that `atom.sh` can't be renamed `nylas.sh` because `apm` # is currently hard-coded to call `atom.sh` - cp 'atom.sh', path.join(binDir, 'edgehill') + cp 'atom.sh', path.join(binDir, 'nylas') rm shareDir mkdir path.dirname(shareDir) cp shellAppDir, shareDir - # Create edgehill.desktop if installation not in temporary folder + # Create nylas.desktop if installation not in temporary folder tmpDir = if process.env.TMPDIR? then process.env.TMPDIR else '/tmp' if installDir.indexOf(tmpDir) isnt 0 - desktopFile = path.join('resources', 'linux', 'edgehill.desktop.in') - desktopInstallFile = path.join(installDir, 'share', 'applications', 'edgehill.desktop') + desktopFile = path.join('resources', 'linux', 'nylas.desktop.in') + desktopInstallFile = path.join(installDir, 'share', 'applications', 'nylas.desktop') {description} = grunt.file.readJSON('package.json') - iconName = path.join(shareDir, 'resources', 'app', 'resources', 'edgehill.png') - installDir = path.join(installDir, '.') # To prevent "Exec=/usr/local//share/edgehill/edgehill" + iconName = path.join(shareDir, 'resources', 'app', 'resources', 'nylas.png') + installDir = path.join(installDir, '.') # To prevent "Exec=/usr/local//share/nylas/nylas" template = _.template(String(fs.readFileSync(desktopFile))) filled = template({description, installDir, iconName}) @@ -58,8 +58,8 @@ module.exports = (grunt) -> # Create relative symbol link for apm. process.chdir(binDir) rm('apm') - fs.symlinkSync(path.join('..', 'share', 'edgehill', 'resources', 'app', 'apm', 'node_modules', '.bin', 'apm'), 'apm') + fs.symlinkSync(path.join('..', 'share', 'nylas', 'resources', 'app', 'apm', 'node_modules', '.bin', 'apm'), 'apm') - fs.chmodSync(path.join(shareDir, 'edgehill'), "755") + fs.chmodSync(path.join(shareDir, 'nylas'), "755") - grunt.log.ok("Installed Edgehill into #{installDir}") + grunt.log.ok("Installed Nylas into #{installDir}") diff --git a/build/tasks/mkdeb-task.coffee b/build/tasks/mkdeb-task.coffee index 8104462b8..46b9c0f2c 100644 --- a/build/tasks/mkdeb-task.coffee +++ b/build/tasks/mkdeb-task.coffee @@ -15,7 +15,7 @@ module.exports = (grunt) -> getInstalledSize = (buildDir, callback) -> cmd = 'du' - args = ['-sk', path.join(buildDir, 'Edgehill')] + args = ['-sk', path.join(buildDir, 'Nylas')] spawn {cmd, args}, (error, {stdout}) -> installedSize = stdout.split(/\s+/)?[0] or '200000' # default to 200MB callback(null, installedSize) @@ -35,12 +35,12 @@ module.exports = (grunt) -> section = 'devel' maintainer = 'Nylas ' installDir = '/usr' - iconName = 'edgehill' + iconName = 'nylas' getInstalledSize buildDir, (error, installedSize) -> data = {name, version, description, section, arch, maintainer, installDir, iconName, installedSize} controlFilePath = fillTemplate(path.join('resources', 'linux', 'debian', 'control'), data) - desktopFilePath = fillTemplate(path.join('resources', 'linux', 'edgehill.desktop'), data) - icon = path.join('resources', 'edgehill.png') + desktopFilePath = fillTemplate(path.join('resources', 'linux', 'nylas.desktop'), data) + icon = path.join('resources', 'nylas.png') cmd = path.join('script', 'mkdeb') args = [version, arch, controlFilePath, desktopFilePath, icon, buildDir] @@ -48,5 +48,5 @@ module.exports = (grunt) -> if error? done(error) else - grunt.log.ok "Created #{buildDir}/edgehill-#{version}-#{arch}.deb" + grunt.log.ok "Created #{buildDir}/nylas-#{version}-#{arch}.deb" done() diff --git a/build/tasks/mkdmg-task.coffee b/build/tasks/mkdmg-task.coffee index cf488a892..7f0b394b0 100644 --- a/build/tasks/mkdmg-task.coffee +++ b/build/tasks/mkdmg-task.coffee @@ -30,12 +30,12 @@ module.exports = (grunt) -> executeDmgMaker = (dmgExecutable) -> new Promise (resolve, reject) -> - console.log("---> Bulding Edgehill DMG") + console.log("---> Bulding Nylas DMG") spawn cmd: dmgExecutable args: [ - "--volname", "Edgehill Installer", - "--volicon", path.join("resources", "edgehill.png"), + "--volname", "Nylas Installer", + "--volicon", path.join("resources", "nylas.png"), "--window-pos", "200", "120", "--window-size", "800", "400", "--icon-size", "100", diff --git a/build/tasks/mkrpm-task.coffee b/build/tasks/mkrpm-task.coffee index cc4372aff..8982cce88 100644 --- a/build/tasks/mkrpm-task.coffee +++ b/build/tasks/mkrpm-task.coffee @@ -31,12 +31,12 @@ module.exports = (grunt) -> mkdir rpmDir installDir = grunt.config.get('atom.installDir') - shareDir = path.join(installDir, 'share', 'edgehill') - iconName = path.join(shareDir, 'resources', 'app', 'resources', 'edgehill.png') + shareDir = path.join(installDir, 'share', 'nylas') + iconName = path.join(shareDir, 'resources', 'app', 'resources', 'nylas.png') data = {name, version, description, installDir, iconName} - specFilePath = fillTemplate(path.join('resources', 'linux', 'redhat', 'edgehill.spec'), data) - desktopFilePath = fillTemplate(path.join('resources', 'linux', 'edgehill.desktop'), data) + specFilePath = fillTemplate(path.join('resources', 'linux', 'redhat', 'nylas.spec'), data) + desktopFilePath = fillTemplate(path.join('resources', 'linux', 'nylas.desktop'), data) cmd = path.join('script', 'mkrpm') args = [specFilePath, desktopFilePath, buildDir] diff --git a/build/tasks/publish-build-task.coffee b/build/tasks/publish-build-task.coffee index dceed7f4b..da6a5340e 100644 --- a/build/tasks/publish-build-task.coffee +++ b/build/tasks/publish-build-task.coffee @@ -62,13 +62,13 @@ getAssets = -> switch process.platform when 'darwin' [ - {assetName: 'edgehill-mac.zip', sourcePath: 'Edgehill.app'} - {assetName: 'edgehill-mac-symbols.zip', sourcePath: 'Atom.breakpad.syms'} - {assetName: 'edgehill-api.json', sourcePath: 'edgehill-api.json'} + {assetName: 'nylas-mac.zip', sourcePath: 'Nylas.app'} + {assetName: 'nylas-mac-symbols.zip', sourcePath: 'Nylas.breakpad.syms'} + {assetName: 'nylas-api.json', sourcePath: 'nylas-api.json'} ] when 'win32' - assets = [{assetName: 'atom-windows.zip', sourcePath: 'Atom'}] - for squirrelAsset in ['AtomSetup.exe', 'RELEASES', "atom-#{version}-full.nupkg"] + assets = [{assetName: 'nylas-windows.zip', sourcePath: 'Nylas'}] + for squirrelAsset in ['NylasSetup.exe', 'RELEASES', "nylas-#{version}-full.nupkg"] cp path.join(buildDir, 'installer', squirrelAsset), path.join(buildDir, squirrelAsset) assets.push({assetName: squirrelAsset, sourcePath: assetName}) assets diff --git a/docs-atom/writing-specs.md b/docs-atom/writing-specs.md index 38db57439..3966c6823 100644 --- a/docs-atom/writing-specs.md +++ b/docs-atom/writing-specs.md @@ -117,7 +117,7 @@ For a more detailed documentation on asynchronous tests please visit the [jasmin ## Running specs -Most of the time you'll want to run specs by triggering the `window:run-package-specs` command. This command is not only to run package specs, it is also for Atom core specs. This will run all the specs in the current project's spec directory. If you want to run the Atom core specs and **all** the default package specs trigger the `window:run-all-specs` command. +Most of the time you'll want to run specs by triggering the `application:run-package-specs` command. This command is not only to run package specs, it is also for Atom core specs. This will run all the specs in the current project's spec directory. If you want to run the Atom core specs and **all** the default package specs trigger the `window:run-all-specs` command. To run a limited subset of specs use the `fdescribe` or `fit` methods. You can use those to focus a single spec or several specs. In the example above, focusing an individual spec looks like this: diff --git a/docs-templates/_footer.html b/docs-templates/_footer.html index f2c77e8af..dd2eb354a 100644 --- a/docs-templates/_footer.html +++ b/docs-templates/_footer.html @@ -2,7 +2,7 @@ diff --git a/docs-templates/_header.html b/docs-templates/_header.html index 9cfc10ee2..ffd341dda 100644 --- a/docs-templates/_header.html +++ b/docs-templates/_header.html @@ -9,7 +9,7 @@ diff --git a/docs/Index.md b/docs/Index.md index 8c1b38ae5..5e1016b5b 100644 --- a/docs/Index.md +++ b/docs/Index.md @@ -4,7 +4,7 @@ TitleHidden: true Section: Getting Started --- - +

Nylas Package API

The Nylas Package API allows you to create powerful extensions to Nylas Mail. The client is built on top of Electron and runs on Mac OS X, Windows, and Linux. It exposes rich APIs for working with the mail, contacts, and calendar and a robust local cache layer. Your packages can leverage NodeJS and other web technologies to create innovative new experiences. diff --git a/docs/images/edgehill.png b/docs/images/nylas.png similarity index 100% rename from docs/images/edgehill.png rename to docs/images/nylas.png diff --git a/internal_packages/developer-bar/lib/developer-bar-store.coffee b/internal_packages/developer-bar/lib/developer-bar-store.coffee index b8c606f8d..fb54e3dfb 100644 --- a/internal_packages/developer-bar/lib/developer-bar-store.coffee +++ b/internal_packages/developer-bar/lib/developer-bar-store.coffee @@ -10,7 +10,6 @@ DeveloperBarStore = Reflux.createStore @_setStoreDefaults() @_registerListeners() - ########### PUBLIC ##################################################### curlHistory: -> @_curlHistory @@ -39,6 +38,7 @@ DeveloperBarStore = Reflux.createStore @listenTo Actions.longPollStateChanged, @_onLongPollStateChange @listenTo Actions.clearDeveloperConsole, @_onClear @listenTo Actions.showDeveloperConsole, @_onShow + @listenTo Actions.sendFeedback, @_onSendFeedback _onShow: -> @_visible = true @@ -86,4 +86,58 @@ DeveloperBarStore = Reflux.createStore @triggerThrottled(@) +<<<<<<< HEAD:internal_packages/inbox-activity-bar/lib/activity-bar-store.coffee + _onSendFeedback: -> + {NamespaceStore, + Contact, + Message, + DatabaseStore} = require 'nylas-exports' + + user = NamespaceStore.current().name + + debugData = JSON.stringify({ + queries: @_curlHistory + }, null, '\t') + + # Remove API tokens from URLs included in the debug data + # This regex detects ://user:pass@ and removes it. + debugData = debugData.replace(/:\/\/(\w)*:(\w)?@/g, '://') + + draft = new Message + from: [NamespaceStore.current().me()] + to: [ + new Contact + name: "Nylas Team" + email: "feedback@nylas.com" + ] + date: (new Date) + draft: true + subject: "Feedback" + namespaceId: NamespaceStore.current().id + body: """ + Hi, Nylas team! I have some feedback for you.
+
+ What happened:
+
+
+ Impact:
+
+
+ Feedback:
+
+
+ Environment:
+ I'm using Edgehill #{atom.getVersion()} and my platform is #{process.platform}-#{process.arch}.
+ --
+ #{user}
+ -- Extra Debugging Data --
+ #{debugData} + """ + DatabaseStore.persistModel(draft).then -> + DatabaseStore.localIdForModel(draft).then (localId) -> + Actions.composePopoutDraft(localId) + +module.exports = ActivityBarStore +======= module.exports = DeveloperBarStore +>>>>>>> master:internal_packages/developer-bar/lib/developer-bar-store.coffee diff --git a/internal_packages/developer-bar/lib/developer-bar.cjsx b/internal_packages/developer-bar/lib/developer-bar.cjsx index 301ee9de1..5214382ab 100644 --- a/internal_packages/developer-bar/lib/developer-bar.cjsx +++ b/internal_packages/developer-bar/lib/developer-bar.cjsx @@ -1,5 +1,4 @@ _ = require 'underscore' -ipc = require 'ipc' React = require 'react/addons' {DatabaseStore, NamespaceStore, @@ -28,7 +27,6 @@ class DeveloperBar extends React.Component filter: '' componentDidMount: => - ipc.on 'report-issue', => @_onFeedback() @taskQueueUnsubscribe = TaskQueue.listen @_onChange @activityStoreUnsubscribe = DeveloperBarStore.listen @_onChange @@ -62,7 +60,7 @@ class DeveloperBar extends React.Component

-
Feedback
+
Feedback
Component Regions
@@ -152,53 +150,6 @@ class DeveloperBar extends React.Component _onToggleRegions: => Actions.toggleComponentRegions() - _onFeedback: => - user = NamespaceStore.current().name - - debugData = JSON.stringify({ - queries: @state.curlHistory, - queue: @state.queue, - completed: @state.completed - }, null, '\t') - - # Remove API tokens from URLs included in the debug data - # This regex detects ://user:pass@ and removes it. - debugData = debugData.replace(/:\/\/(\w)*:(\w)?@/g, '://') - - draft = new Message - from: [NamespaceStore.current().me()] - to: [ - new Contact - name: "Nylas Team" - email: "feedback@nylas.com" - ] - date: (new Date) - draft: true - subject: "Feedback" - namespaceId: NamespaceStore.current().id - body: """ - Hi, Nylas team! I have some feedback for you.
-
- What happened:
-
-
- Impact:
-
-
- Feedback:
-
-
- Environment:
- I'm using Edgehill #{atom.getVersion()} and my platform is #{process.platform}-#{process.arch}.
- --
- #{user}
- -- Extra Debugging Data --
- #{debugData} - """ - DatabaseStore.persistModel(draft).then -> - DatabaseStore.localIdForModel(draft).then (localId) -> - Actions.composePopoutDraft(localId) - _getStateFromStores: => visible: DeveloperBarStore.visible() queue: TaskQueue._queue diff --git a/internal_packages/onboarding/lib/container-view.cjsx b/internal_packages/onboarding/lib/container-view.cjsx index efdd3186b..c127686cc 100644 --- a/internal_packages/onboarding/lib/container-view.cjsx +++ b/internal_packages/onboarding/lib/container-view.cjsx @@ -31,6 +31,9 @@ class ContainerView extends React.Component if webview node = React.findDOMNode(webview) if node.hasListeners is undefined + # Remove as soon as possible. Initial src is not correctly loaded + # on webview, and this fixes it. Electron 0.26.0 + setTimeout -> node.src = node.src node.addEventListener 'new-window', (e) -> require('shell').openExternal(e.url) node.addEventListener 'did-start-loading', (e) -> diff --git a/keymaps/darwin.cson b/keymaps/darwin.cson index e331d8f50..48665371e 100644 --- a/keymaps/darwin.cson +++ b/keymaps/darwin.cson @@ -38,7 +38,7 @@ 'alt-cmd-i': 'window:toggle-dev-tools' 'cmd-ctrl-f': 'window:toggle-full-screen' 'ctrl-alt-cmd-l': 'window:reload' - 'cmd-alt-ctrl-p': 'window:run-package-specs' + 'cmd-alt-ctrl-p': 'application:run-package-specs' 'body div *[contenteditable]': 'cmd-z': 'core:undo' diff --git a/keymaps/emacs.cson b/keymaps/emacs.cson deleted file mode 100644 index e69de29bb..000000000 diff --git a/keymaps/linux.cson b/keymaps/linux.cson index 9b233c040..e4a04c45c 100644 --- a/keymaps/linux.cson +++ b/keymaps/linux.cson @@ -10,7 +10,6 @@ # Linux application keys 'ctrl-q': 'application:quit' - 'ctrl-alt-s': 'application:run-all-specs' # Linux core keys 'ctrl-z': 'core:undo' @@ -30,10 +29,11 @@ 'ctrl--': 'window:decrease-font-size' 'ctrl-_': 'window:decrease-font-size' 'ctrl-0': 'window:reset-font-size' - 'ctrl-alt-i': 'window:toggle-dev-tools' - 'F11': 'window:toggle-full-screen' 'ctrl-alt-r': 'window:reload' - 'ctrl-alt-p': 'window:run-package-specs' + 'ctrl-shift-i': 'window:toggle-dev-tools' + 'ctrl-alt-p': 'application:run-package-specs' + 'ctrl-alt-s': 'application:run-all-specs' + 'F11': 'window:toggle-full-screen' 'body div *[contenteditable]': 'ctrl-z': 'core:undo' diff --git a/keymaps/win32.cson b/keymaps/win32.cson index a286ca691..804da99d9 100644 --- a/keymaps/win32.cson +++ b/keymaps/win32.cson @@ -30,10 +30,11 @@ 'ctrl--': 'window:decrease-font-size' 'ctrl-_': 'window:decrease-font-size' 'ctrl-0': 'window:reset-font-size' - 'ctrl-alt-i': 'window:toggle-dev-tools' - 'F11': 'window:toggle-full-screen' 'ctrl-alt-r': 'window:reload' - 'ctrl-alt-p': 'window:run-package-specs' + 'ctrl-alt-i': 'window:toggle-dev-tools' + 'ctrl-alt-p': 'application:run-package-specs' + 'ctrl-alt-s': 'application:run-all-specs' + 'F11': 'window:toggle-full-screen' 'body div *[contenteditable]': 'ctrl-z': 'core:undo' diff --git a/menus/darwin.cson b/menus/darwin.cson index c5cfd8ace..046b11a0d 100644 --- a/menus/darwin.cson +++ b/menus/darwin.cson @@ -49,7 +49,16 @@ { label: 'Toggle Full Screen', command: 'window:toggle-full-screen' } ] } - + { + label: 'Developer' + submenu: [ + { label: 'Open In Dev Mode...', command: 'application:open-dev' } + { type: 'separator' } + { label: 'Run Nylas Mail Specs', command: 'application:run-all-specs' } + { label: 'Run Package Specs', command: 'application:run-package-specs' } + { label: 'Toggle Developer Tools', command: 'window:toggle-dev-tools' } + ] + } { label: 'Window' submenu: [ @@ -65,7 +74,7 @@ { label: 'Help' submenu: [ - { label: 'Report Issue', command: 'application:report-issue' } + { label: 'Send Feedback to Nylas', command: 'application:send-feedback' } ] } ] diff --git a/menus/linux.cson b/menus/linux.cson index 6f1faa293..91755c224 100644 --- a/menus/linux.cson +++ b/menus/linux.cson @@ -32,13 +32,22 @@ { label: 'Toggle Menu Bar', command: 'window:toggle-menu-bar' } ] } - + { + label: 'Developer' + submenu: [ + { label: 'Open In &Dev Mode...', command: 'application:open-dev' } + { type: 'separator' } + { label: 'Run &Nylas Mail Specs', command: 'application:run-all-specs' } + { label: 'Run Package &Specs', command: 'application:run-package-specs' } + { label: 'Toggle Developer &Tools', command: 'window:toggle-dev-tools' } + ] + } { label: '&Help' submenu: [ { label: "VERSION", enabled: false } { type: 'separator' } - { label: 'Report Issue', command: 'application:report-issue' } + { label: 'Send Feedback to Nylas', command: 'application:send-feedback' } ] } ] diff --git a/menus/win32.cson b/menus/win32.cson index 98dbe783b..eb82a71c3 100644 --- a/menus/win32.cson +++ b/menus/win32.cson @@ -34,7 +34,16 @@ { label: 'Toggle Menu Bar', command: 'window:toggle-menu-bar' } ] } - + { + label: 'Developer' + submenu: [ + { label: 'Open In &Dev Mode...', command: 'application:open-dev' } + { type: 'separator' } + { label: 'Run &Atom Specs', command: 'application:run-all-specs' } + { label: 'Run Package &Specs', command: 'application:run-package-specs' } + { label: 'Toggle Developer &Tools', command: 'window:toggle-dev-tools' } + ] + } { label: '&Help' submenu: [ @@ -43,7 +52,7 @@ { label: 'Check for Update', command: 'application:check-for-update', visible: false} { label: 'Downloading Update', enabled: false, visible: false} { type: 'separator' } - { label: 'Report Issue', command: 'application:report-issue' } + { label: 'Send Feedback to Nylas', command: 'application:send-feedback' } ] } ] diff --git a/package.json b/package.json index dad092855..ac25dfc43 100644 --- a/package.json +++ b/package.json @@ -11,8 +11,9 @@ "bugs": { "url": "https://github.com/nylas/edgehill/issues" }, - "atomShellVersion": "0.21.0", + "atomShellVersion": "0.26.0", "dependencies": { + "asar": "^0.5.0", "6to5-core": "^3.5", "async": "^0.9", "atom-keymap": "^3.1", @@ -43,9 +44,8 @@ "moment": "^2.8", "moment-timezone": "^0.3", "nslog": "^2.0.0", - "oniguruma": "^4.0.0", "optimist": "0.4.0", - "pathwatcher": "^4.0", + "pathwatcher": "^4.4.0", "property-accessors": "^1", "q": "^1.0.1", "raven": "0.7.2", @@ -73,7 +73,8 @@ "underscore": "^1.8", "underscore.string": "^3.0", "vm-compatibility-layer": "0.1.0", - "spellchecker": "2.2.0" + "spellchecker": "2.2.0", + "aws-sdk": "2.1.28" }, "devDependencies": { "proxyquire": "git+https://github.com/bengotow/proxyquire", diff --git a/resources/linux/debian/control.in b/resources/linux/debian/control.in index 5dc3c2e2e..6876b2440 100644 --- a/resources/linux/debian/control.in +++ b/resources/linux/debian/control.in @@ -8,4 +8,4 @@ Architecture: <%= arch %> Installed-Size: <%= installedSize %> Maintainer: <%= maintainer %> Description: <%= description %> - Edgehill is an email OS + Nylas is an email OS diff --git a/resources/linux/edgehill.desktop.in b/resources/linux/nylas.desktop.in similarity index 100% rename from resources/linux/edgehill.desktop.in rename to resources/linux/nylas.desktop.in diff --git a/resources/linux/redhat/edgehill.spec.in b/resources/linux/redhat/edgehill.spec.in deleted file mode 100644 index 737015a1e..000000000 --- a/resources/linux/redhat/edgehill.spec.in +++ /dev/null @@ -1,26 +0,0 @@ -Name: <%= name %> -Version: <%= version %> -Release: 0.1%{?dist} -Summary: Edgehill is an Email OS -License: Proprietary -URL: https://nylas.com/ -AutoReqProv: no # Avoid libchromiumcontent.so missing dependency - -%description -<%= description %> - -%install -mkdir -p %{buildroot}/usr/local/share/edgehill -cp -r /tmp/edgehill-build/Edgehill/* %{buildroot}/usr/local/share/edgehill -mkdir -p %{buildroot}/usr/local/bin/ -ln -sf /usr/local/share/edgehill/resources/app/apm/node_modules/.bin/apm %{buildroot}/usr/local/bin/apm -cp atom.sh %{buildroot}/usr/local/bin/edgehill -chmod 755 edgehill -mkdir -p %{buildroot}/usr/local/share/applications/ -mv edgehill.desktop %{buildroot}/usr/local/share/applications/ - -%files -/usr/local/bin/edgehill -/usr/local/bin/apm -/usr/local/share/edgehill/ -/usr/local/share/applications/edgehill.desktop diff --git a/resources/linux/redhat/nylas.spec.in b/resources/linux/redhat/nylas.spec.in new file mode 100644 index 000000000..6f9896c9f --- /dev/null +++ b/resources/linux/redhat/nylas.spec.in @@ -0,0 +1,26 @@ +Name: <%= name %> +Version: <%= version %> +Release: 0.1%{?dist} +Summary: Nylas is an Email OS +License: Proprietary +URL: https://nylas.com/ +AutoReqProv: no # Avoid libchromiumcontent.so missing dependency + +%description +<%= description %> + +%install +mkdir -p %{buildroot}/usr/local/share/Nylas +cp -r /tmp/nylas-build/Nylas/* %{buildroot}/usr/local/share/Nylas +mkdir -p %{buildroot}/usr/local/bin/ +ln -sf /usr/local/share/nylas/resources/app/apm/node_modules/.bin/apm %{buildroot}/usr/local/bin/apm +cp atom.sh %{buildroot}/usr/local/bin/nylas +chmod 755 nylas +mkdir -p %{buildroot}/usr/local/share/applications/ +mv nylas.desktop %{buildroot}/usr/local/share/applications/ + +%files +/usr/local/bin/nylas +/usr/local/bin/apm +/usr/local/share/nylas/ +/usr/local/share/applications/nylas.desktop diff --git a/resources/mac/edgehill-Info.plist b/resources/mac/nylas-Info.plist similarity index 93% rename from resources/mac/edgehill-Info.plist rename to resources/mac/nylas-Info.plist index 82b6bc651..2e1a35b36 100644 --- a/resources/mac/edgehill-Info.plist +++ b/resources/mac/nylas-Info.plist @@ -3,9 +3,9 @@ CFBundleExecutable - Edgehill + Nylas CFBundleIconFile - edgehill.icns + nylas.icns CFBundleIdentifier com.nylas.nylas-mail CFBundleInfoDictionaryVersion @@ -48,7 +48,7 @@ edgehill CFBundleURLName - Edgehill Shared Session Protocol + Nylas Shared Session Protocol CFBundleURLSchemes diff --git a/resources/mac/edgehill.icns b/resources/mac/nylas.icns similarity index 100% rename from resources/mac/edgehill.icns rename to resources/mac/nylas.icns diff --git a/resources/edgehill.png b/resources/nylas.png similarity index 100% rename from resources/edgehill.png rename to resources/nylas.png diff --git a/resources/win/edgehill.ico b/resources/win/nylas.ico similarity index 100% rename from resources/win/edgehill.ico rename to resources/win/nylas.ico diff --git a/script/bootstrap b/script/bootstrap index bc26b2e85..e24277722 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -47,7 +47,7 @@ function bootstrap() { var gruntPath = path.resolve(__dirname, '..', 'build', 'node_modules', '.bin', 'grunt'); - var packagesToDedupe = ['fs-plus', 'humanize-plus', 'oniguruma', 'roaster', 'season', 'grim']; + var packagesToDedupe = ['fs-plus', 'humanize-plus', 'roaster', 'season', 'grim']; var buildInstallCommand = initialNpmCommand + npmFlags + 'install'; var buildInstallOptions = {cwd: path.resolve(__dirname, '..', 'build')}; diff --git a/script/clean b/script/clean index ad72a74a3..b22e03391 100755 --- a/script/clean +++ b/script/clean @@ -11,7 +11,7 @@ process.chdir(path.dirname(__dirname)); var home = process.env[(process.platform === 'win32') ? 'USERPROFILE' : 'HOME']; var tmpdir = os.tmpdir(); -// Windows: Use START as a way to ignore error if Edgehill.exe isnt running +// Windows: Use START as a way to ignore error if nylas.exe isnt running var killatom = process.platform === 'win32' ? 'START taskkill /F /IM ' + productName + '.exe' : 'pkill -9 ' + productName + ' || true'; var commands = [ diff --git a/script/mkdeb b/script/mkdeb index fa0e743e9..9ee46d9d0 100755 --- a/script/mkdeb +++ b/script/mkdeb @@ -17,7 +17,7 @@ FILE_MODE=755 TARGET_ROOT="`mktemp -d`" chmod $FILE_MODE "$TARGET_ROOT" -TARGET="$TARGET_ROOT/edgehill-$VERSION-$ARCH" +TARGET="$TARGET_ROOT/nylas-$VERSION-$ARCH" mkdir -m $FILE_MODE -p "$TARGET/usr" env INSTALL_PREFIX="$TARGET/usr" script/grunt install @@ -31,17 +31,17 @@ cp "$DESKTOP_FILE" "$TARGET/usr/share/applications" mkdir -m $FILE_MODE -p "$TARGET/usr/share/pixmaps" cp "$ICON_FILE" "$TARGET/usr/share/pixmaps" -# Copy generated LICENSE.md to /usr/share/doc/edgehill/copyright -mkdir -m $FILE_MODE -p "$TARGET/usr/share/doc/edgehill" -cp "$TARGET/usr/share/edgehill/resources/app/LICENSE.md" "$TARGET/usr/share/doc/edgehill/copyright" +# Copy generated LICENSE.md to /usr/share/doc/nylas/copyright +mkdir -m $FILE_MODE -p "$TARGET/usr/share/doc/nylas" +cp "$TARGET/usr/share/nylas/resources/app/LICENSE.md" "$TARGET/usr/share/doc/nylas/copyright" # Add lintian overrides mkdir -m $FILE_MODE -p "$TARGET/usr/share/lintian/overrides" -cp "$ROOT/resources/linux/debian/lintian-overrides" "$TARGET/usr/share/lintian/overrides/edgehill" +cp "$ROOT/resources/linux/debian/lintian-overrides" "$TARGET/usr/share/lintian/overrides/nylas" # Remove executable bit from .node files find "$TARGET" -type f -name "*.node" -exec chmod a-x {} \; fakeroot dpkg-deb -b "$TARGET" -mv "$TARGET_ROOT/edgehill-$VERSION-$ARCH.deb" "$DEB_PATH" +mv "$TARGET_ROOT/nylas-$VERSION-$ARCH.deb" "$DEB_PATH" rm -rf "$TARGET_ROOT" diff --git a/script/mkrpm b/script/mkrpm index d21482695..58f3fb5a2 100755 --- a/script/mkrpm +++ b/script/mkrpm @@ -11,13 +11,13 @@ ARCH=`uname -m` rpmdev-setuptree -cp -r $BUILD_DIRECTORY/Edgehill/* $RPM_BUILD_ROOT/BUILD +cp -r $BUILD_DIRECTORY/Nylas/* $RPM_BUILD_ROOT/BUILD cp $SPEC_FILE $RPM_BUILD_ROOT/SPECS cp ./atom.sh $RPM_BUILD_ROOT/BUILD -cp $RPM_BUILD_ROOT/BUILD/atom.sh $RPM_BUILD_ROOT/BUILD/edgehill +cp $RPM_BUILD_ROOT/BUILD/atom.sh $RPM_BUILD_ROOT/BUILD/nylas cp $DESKTOP_FILE $RPM_BUILD_ROOT/BUILD rpmbuild -ba $SPEC_FILE -cp $RPM_BUILD_ROOT/RPMS/$ARCH/edgehill-*.rpm $BUILD_DIRECTORY/rpm +cp $RPM_BUILD_ROOT/RPMS/$ARCH/nylas-*.rpm $BUILD_DIRECTORY/rpm rm -rf $RPM_BUILD_ROOT diff --git a/script/set-version b/script/set-version index 8893ed2c7..c6120cf9d 100755 --- a/script/set-version +++ b/script/set-version @@ -4,8 +4,8 @@ set -e BUILT_PRODUCTS_DIR=$1 VERSION=$2 -PLIST_PATH="$BUILT_PRODUCTS_DIR/Edgehill.app/Contents/Info.plist" -HELPER_PLIST_PATH="$BUILT_PRODUCTS_DIR/Edgehill.app/Contents/Frameworks/Atom Helper.app/Contents/Info.plist" +PLIST_PATH="$BUILT_PRODUCTS_DIR/Nylas.app/Contents/Info.plist" +HELPER_PLIST_PATH="$BUILT_PRODUCTS_DIR/Nylas.app/Contents/Frameworks/Atom Helper.app/Contents/Info.plist" # Update version /usr/libexec/PlistBuddy -c "Set CFBundleShortVersionString $VERSION" "$PLIST_PATH" diff --git a/spec-nylas/action-bridge-spec.coffee b/spec-nylas/action-bridge-spec.coffee index c7bdecb2b..04597dc40 100644 --- a/spec-nylas/action-bridge-spec.coffee +++ b/spec-nylas/action-bridge-spec.coffee @@ -1,9 +1,9 @@ Reflux = require 'reflux' Actions = require '../src/flux/actions' -ActionBridge = require '../src/flux/action-bridge' Message = require '../src/flux/models/message' DatabaseStore = require '../src/flux/stores/database-store' NamespaceStore = require '../src/flux/stores/namespace-store' +ActionBridge = require '../src/flux/action-bridge', _ = require 'underscore' ipc = @@ -86,17 +86,16 @@ describe "ActionBridge", -> Actions.didSwapModel.firing = false @bridge.onRebroadcast(ActionBridge.TargetWindows.ALL, 'didSwapModel', [{oldModel: '1', newModel: 2}]) expect(ipc.send).toHaveBeenCalledWith('action-bridge-rebroadcast-to-all', 'popout', 'didSwapModel', '[{"oldModel":"1","newModel":2}]') - + describe "when called with TargetWindows.MAIN", -> it "should broadcast the action over IPC to the main window only", -> spyOn(ipc, 'send') Actions.didSwapModel.firing = false @bridge.onRebroadcast(ActionBridge.TargetWindows.MAIN, 'didSwapModel', [{oldModel: '1', newModel: 2}]) expect(ipc.send).toHaveBeenCalledWith('action-bridge-rebroadcast-to-main', 'popout', 'didSwapModel', '[{"oldModel":"1","newModel":2}]') - + it "should not do anything if the current invocation of the Action was triggered by itself", -> spyOn(ipc, 'send') Actions.didSwapModel.firing = true @bridge.onRebroadcast(ActionBridge.TargetWindows.ALL, 'didSwapModel', [{oldModel: '1', newModel: 2}]) expect(ipc.send).not.toHaveBeenCalled() - diff --git a/spec/atom-reporter.coffee b/spec/atom-reporter.coffee index c041000f3..bdc28e22f 100644 --- a/spec/atom-reporter.coffee +++ b/spec/atom-reporter.coffee @@ -206,8 +206,8 @@ class AtomReporter extends View specComplete: (spec) -> specSummaryElement = $("#spec-summary-#{spec.id}") specSummaryElement.removeClass('pending') - specSummaryElement.setTooltip(title: spec.getFullName(), container: '.spec-reporter') - + specSummaryElement.attr('title', spec.getFullName()) + results = spec.results() if results.skipped specSummaryElement.addClass("skipped") diff --git a/src/atom.coffee b/src/atom.coffee index e4e46dc45..5fef916bc 100644 --- a/src/atom.coffee +++ b/src/atom.coffee @@ -201,10 +201,12 @@ class Atom extends Model if event.binding.command.indexOf('application:') is 0 and event.binding.selector is "body" ipc.send('command', event.binding.command) + unless @inSpecMode() + @actionBridge = new ActionBridge(ipc) + @commands = new CommandRegistry @packages = new PackageManager({devMode, configDirPath, resourcePath, safeMode}) @styles = new StyleManager - @actionBridge = new ActionBridge(ipc) document.head.appendChild(new StylesElement) @themes = new ThemeManager({packageManager: @packages, configDirPath, resourcePath, safeMode}) @menu = new MenuManager({resourcePath}) diff --git a/src/browser/application.coffee b/src/browser/application.coffee index 9aacb9d2c..4ccea9b9f 100644 --- a/src/browser/application.coffee +++ b/src/browser/application.coffee @@ -5,6 +5,7 @@ NylasProtocolHandler = require './nylas-protocol-handler' AutoUpdateManager = require './auto-update-manager' WindowManager = require './window-manager' Config = require '../config' +dialog = require 'dialog' fs = require 'fs-plus' Menu = require 'menu' @@ -25,6 +26,8 @@ socketPath = else path.join(os.tmpdir(), 'edgehill.sock') +configDirPath = fs.absolute('~/.nylas') + # The application's singleton class. # # It's the entry point into the Atom application and maintains the global state @@ -34,7 +37,7 @@ module.exports = class Application _.extend @prototype, EventEmitter.prototype - # Public: The entry point into the Atom application. + # Public: The entry point into the Nylas Mail application. @open: (options) -> createApplication = -> new Application(options) @@ -69,8 +72,7 @@ class Application global.application = this - configDirPath = fs.absolute('~/.nylas') - @config = new Config({configDirPath}) + @config = new Config({configDirPath, @resourcePath}) @config.load() @databases = {} @@ -187,8 +189,27 @@ class Application # needs to manually bubble them up to the Application instance via IPC or they won't be # handled. This happens in workspace-element.coffee handleEvents: -> - @on 'application:run-all-specs', -> @runSpecs(exitWhenDone: false, resourcePath: global.devResourcePath, safeMode: @windowManager.focusedWindow()?.safeMode) - @on 'application:run-benchmarks', -> @runBenchmarks() + @on 'application:run-all-specs', -> + @runSpecs + exitWhenDone: false + resourcePath: global.devResourcePath + safeMode: @windowManager.focusedWindow()?.safeMode + + @on 'application:run-package-specs', -> + dialog.showOpenDialog { + title: 'Choose a Package Directory' + defaultPath: configDirPath, + properties: ['openDirectory'] + }, (filenames) => + return if filenames.length is 0 + @runSpecs + exitWhenDone: false + resourcePath: global.devResourcePath + specDirectory: filenames[0] + + @on 'application:run-benchmarks', -> + @runBenchmarks() + @on 'application:logout', => for path, val of @databases @teardownDatabase(path) @@ -202,18 +223,17 @@ class Application atomWindow ?= @windowManager.focusedWindow() atomWindow?.browserWindow.inspectElement(x, y) - @on 'application:open-documentation', -> require('shell').openExternal('https://atom.io/docs/latest/?app') - @on 'application:open-discussions', -> require('shell').openExternal('https://discuss.atom.io') - @on 'application:open-roadmap', -> require('shell').openExternal('https://atom.io/roadmap?app') - @on 'application:open-faq', -> require('shell').openExternal('https://atom.io/faq') - @on 'application:open-terms-of-use', -> require('shell').openExternal('https://atom.io/terms') - @on 'application:report-issue', => @_reportIssue() - @on 'application:search-issues', -> require('shell').openExternal('https://github.com/issues?q=+is%3Aissue+user%3Aatom') + @on 'application:send-feedback', => @windowManager.sendToMainWindow('send-feedback') @on 'application:show-main-window', => @windowManager.ensurePrimaryWindowOnscreen() @on 'application:check-for-update', => @autoUpdateManager.check() @on 'application:install-update', => @quitting = true @autoUpdateManager.install() + @on 'application:open-dev', => + @devMode = true + @windowManager.closeMainWindow() + @windowManager.devMode = true + @windowManager.ensurePrimaryWindowOnscreen() if process.platform is 'darwin' @on 'application:about', -> Menu.sendActionToFirstResponder('orderFrontStandardAboutPanel:') @@ -257,9 +277,6 @@ class Application win = BrowserWindow.fromWebContents(event.sender) @applicationMenu.update(win, template, keystrokesByCommand) - ipc.on 'run-package-specs', (event, specDirectory) => - @runSpecs({resourcePath: global.devResourcePath, specDirectory: specDirectory, exitWhenDone: false}) - ipc.on 'command', (event, command) => @emit(command) @@ -380,6 +397,3 @@ class Application isSpec = true devMode = true new AtomWindow({bootstrapScript, @resourcePath, exitWhenDone, isSpec, specDirectory, devMode}) - - _reportIssue: -> - @windowManager.sendToMainWindow('report-issue') diff --git a/src/browser/atom-window.coffee b/src/browser/atom-window.coffee index d260abcac..35f612e80 100644 --- a/src/browser/atom-window.coffee +++ b/src/browser/atom-window.coffee @@ -10,7 +10,7 @@ module.exports = class AtomWindow _.extend @prototype, EventEmitter.prototype - @iconPath: path.resolve(__dirname, '..', '..', 'resources', 'edgehill.png') + @iconPath: path.resolve(__dirname, '..', '..', 'resources', 'nylas.png') @includeShellLoadTime: true browserWindow: null @@ -40,6 +40,8 @@ class AtomWindow show: false title: title ? 'Nylas' frame: frame ? true + #https://atomio.slack.com/archives/electron/p1432056952000608 + 'standard-window': frame ? true width: width height: height resizable: resizable ? true diff --git a/src/browser/auto-update-manager.coffee b/src/browser/auto-update-manager.coffee index 3db000c23..001211a41 100644 --- a/src/browser/auto-update-manager.coffee +++ b/src/browser/auto-update-manager.coffee @@ -33,6 +33,10 @@ class AutoUpdateManager else autoUpdater = require 'auto-updater' + autoUpdater.on 'error', (event, message) => + @setState(ErrorState) + console.error "Error Downloading Update: #{message}" + autoUpdater.setFeedUrl @feedUrl autoUpdater.on 'checking-for-update', => @@ -44,10 +48,6 @@ class AutoUpdateManager autoUpdater.on 'update-available', => @setState(DownladingState) - autoUpdater.on 'error', (event, message) => - @setState(ErrorState) - console.error "Error Downloading Update: #{message}" - autoUpdater.on 'update-downloaded', (event, @releaseNotes, @releaseVersion) => @setState(UpdateAvailableState) @emitUpdateAvailableEvent(@getWindows()...) @@ -84,7 +84,7 @@ class AutoUpdateManager autoUpdater.quitAndInstall() iconURL: -> - url = path.join(process.resourcesPath, 'app', 'edgehill.png') + url = path.join(process.resourcesPath, 'app', 'nylas.png') return undefined unless fs.existsSync(url) url diff --git a/src/browser/main.coffee b/src/browser/main.coffee index 9b90177eb..5e3209402 100644 --- a/src/browser/main.coffee +++ b/src/browser/main.coffee @@ -1,6 +1,7 @@ global.shellStartTime = Date.now() errorReporter = new (require '../error-reporter') + app = require 'app' fs = require 'fs' path = require 'path' diff --git a/src/buffered-node-process.coffee b/src/buffered-node-process.coffee index 5501ea86a..bb1a1c655 100644 --- a/src/buffered-node-process.coffee +++ b/src/buffered-node-process.coffee @@ -6,14 +6,11 @@ path = require 'path' # # This is necessary on Windows since it doesn't support shebang `#!` lines. # -# Section: Atom -# # ## Examples # # ```coffee # {BufferedNodeProcess} = require 'atom' # ``` -# module.exports = class BufferedNodeProcess extends BufferedProcess @@ -51,5 +48,8 @@ class BufferedNodeProcess extends BufferedProcess options.env ?= Object.create(process.env) options.env['ATOM_SHELL_INTERNAL_RUN_AS_NODE'] = 1 + args = args?.slice() ? [] args.unshift(command) + args.unshift('--no-deprecation') + super({command: node, args, options, stdout, stderr, exit}) diff --git a/src/buffered-process.coffee b/src/buffered-process.coffee index 3ca092356..77495119b 100644 --- a/src/buffered-process.coffee +++ b/src/buffered-process.coffee @@ -6,8 +6,6 @@ path = require 'path' # Extended: A wrapper which provides standard error/output line buffering for # Node's ChildProcess. # -# Section: Atom -# # ## Examples # # ```coffee @@ -19,7 +17,6 @@ path = require 'path' # exit = (code) -> console.log("ps -ef exited with #{code}") # process = new BufferedProcess({command, args, stdout, exit}) # ``` -# module.exports = class BufferedProcess ### @@ -51,6 +48,7 @@ class BufferedProcess constructor: ({command, args, options, stdout, stderr, exit}={}) -> @emitter = new Emitter options ?= {} + @command = command # Related to joyent/node#2318 if process.platform is 'win32' # Quote all arguments and escapes inner quotes @@ -72,50 +70,12 @@ class BufferedProcess cmdArgs = ['/s', '/c', "\"#{cmdArgs.join(' ')}\""] cmdOptions = _.clone(options) cmdOptions.windowsVerbatimArguments = true - @process = ChildProcess.spawn(@getCmdPath(), cmdArgs, cmdOptions) + @spawn(@getCmdPath(), cmdArgs, cmdOptions) else - @process = ChildProcess.spawn(command, args, options) + @spawn(command, args, options) + @killed = false - - stdoutClosed = true - stderrClosed = true - processExited = true - exitCode = 0 - triggerExitCallback = -> - return if @killed - if stdoutClosed and stderrClosed and processExited - exit?(exitCode) - - if stdout - stdoutClosed = false - @bufferStream @process.stdout, stdout, -> - stdoutClosed = true - triggerExitCallback() - - if stderr - stderrClosed = false - @bufferStream @process.stderr, stderr, -> - stderrClosed = true - triggerExitCallback() - - if exit - processExited = false - @process.on 'exit', (code) -> - exitCode = code - processExited = true - triggerExitCallback() - - @process.on 'error', (error) => - handled = false - handle = -> handled = true - - @emitter.emit 'will-throw-error', {error, handle} - - if error.code is 'ENOENT' and error.syscall.indexOf('spawn') is 0 - error = new Error("Failed to spawn command `#{command}`. Make sure `#{command}` is installed and on your PATH", error.path) - error.name = 'BufferedProcessError' - - throw error unless handled + @handleEvents(stdout, stderr, exit) ### Section: Event Subscription @@ -167,6 +127,8 @@ class BufferedProcess # This is required since killing the cmd.exe does not terminate child # processes. killOnWindows: -> + return unless @process? + parentPid = @process.pid cmd = 'wmic' args = [ @@ -177,7 +139,12 @@ class BufferedProcess 'processid' ] - wmicProcess = ChildProcess.spawn(cmd, args) + try + wmicProcess = ChildProcess.spawn(cmd, args) + catch spawnError + @killProcess() + return + wmicProcess.on 'error', -> # ignore errors output = '' wmicProcess.stdout.on 'data', (data) -> output += data @@ -223,3 +190,55 @@ class BufferedProcess @killProcess() undefined + + spawn: (command, args, options) -> + try + @process = ChildProcess.spawn(command, args, options) + catch spawnError + process.nextTick => @handleError(spawnError) + + handleEvents: (stdout, stderr, exit) -> + return unless @process? + + stdoutClosed = true + stderrClosed = true + processExited = true + exitCode = 0 + triggerExitCallback = -> + return if @killed + if stdoutClosed and stderrClosed and processExited + exit?(exitCode) + + if stdout + stdoutClosed = false + @bufferStream @process.stdout, stdout, -> + stdoutClosed = true + triggerExitCallback() + + if stderr + stderrClosed = false + @bufferStream @process.stderr, stderr, -> + stderrClosed = true + triggerExitCallback() + + if exit + processExited = false + @process.on 'exit', (code) -> + exitCode = code + processExited = true + triggerExitCallback() + + @process.on 'error', (error) => @handleError(error) + return + + handleError: (error) -> + handled = false + handle = -> handled = true + + @emitter.emit 'will-throw-error', {error, handle} + + if error.code is 'ENOENT' and error.syscall.indexOf('spawn') is 0 + error = new Error("Failed to spawn command `#{@command}`. Make sure `#{@command}` is installed and on your PATH", error.path) + error.name = 'BufferedProcessError' + + throw error unless handled diff --git a/src/error-reporter.js b/src/error-reporter.js index 51a4f0bba..689f184d4 100644 --- a/src/error-reporter.js +++ b/src/error-reporter.js @@ -1,9 +1,20 @@ // This file cannot be Coffeescript because it loads before the Coffeescript // interpreter. Note that it runs in both browser and renderer processes. -var ErrorReporter, raven, _; +var ErrorReporter, raven, _, fs, path, app, os, remote; raven = require('raven'); +os = require('os'); _ = require('underscore'); +fs = require('fs-plus'); +path = require('path'); +if (process.type === 'renderer') { + remote = require('remote'); + app = remote.require('app'); +} else { + app = require('app'); +} + +tmpPath = app.getPath('temp'); module.exports = ErrorReporter = (function() { function ErrorReporter() { @@ -17,6 +28,91 @@ module.exports = ErrorReporter = (function() { return console.log(e.response); }); + // If we're the browser process, remove log files that are more than + // two days old. These log files get pretty big because we're logging + // so verbosely. + if (process.type === 'browser') { + fs.readdir(tmpPath, function(err, files) { + if (err) { + console.error(err); + return; + } + + var logFilter = new RegExp("edgehill-[.0-9]*.log$"); + files.forEach(function(file) { + if (logFilter.test(file) === true) { + var filepath = path.join(tmpPath, file); + fs.stat(filepath, function(err, stats) { + var lastModified = new Date(stats['mtime']); + var fileAge = Date.now() - lastModified.getTime(); + if (fileAge > (1000 * 60 * 60 * 24 * 2)) { // two days + fs.unlink(filepath); + } + }); + } + }); + }); + } + + // Open a file write stream to log output from this process + var pid = process.pid; + if (process.type === 'renderer') { + pid = remote.process.pid + "." + process.pid; + } + var logpath = path.join(tmpPath, 'edgehill-' + pid + '.log'); + console.log("Streaming log data to "+logpath); + + this.loghost = os.hostname(); + this.logstream = fs.createWriteStream(logpath, { + flags: 'a', + encoding: 'utf8', + fd: null, + mode: 0666 + }); + + // Override stdout and stderr to pipe their output to the file + // in addition to calling through to the existing implementation + function hook_process_output(channel, callback) { + var old_write = process[channel].write; + process[channel].write = (function(write) { + return function(string, encoding, fd) { + write.apply(process[channel], arguments) + callback(string, encoding, fd) + } + })(process[channel].write) + + // Return a function that can be used to undo this change + return function() { + process[channel].write = old_write + }; + } + + hook_process_output('stdout', function(string, encoding, fd) { + self.appendLog.apply(self, [string]); + }); + hook_process_output('stderr', function(string, encoding, fd) { + self.appendLog.apply(self, [string]); + }); + + this.shipLogsQueued = false; + this.shipLogsTime = 0; + + // Create a new console.debug option, which takes `true` (print) + // or `false`, don't print in console as the first parameter. + // This makes it easy for developers to turn on and off + // "verbose console" mode. + console.debug = function() { + var args = []; + var showIt = arguments[0]; + for (var ii = 1; ii < arguments.length; ii++) { + args.push(arguments[ii]); + } + if ((self.dev === true) && (showIt === true)) { + console.log.apply(this, args); + } + self.appendLog.apply(self, [args]); + }; + // Link to the appropriate error handlers for the browser // or renderer process if (process.type === 'renderer') { @@ -48,6 +144,63 @@ module.exports = ErrorReporter = (function() { } } + ErrorReporter.prototype.appendLog = function(obj) { + try { + var message = JSON.stringify({ + host: this.loghost, + timestamp: (new Date()).toISOString(), + payload: obj + })+"\n"; + + this.logstream.write(message, 'utf8', function (err) { + if (err) { + console.error("ErrorReporter: Unable to write to the log stream!" + err.toString()); + } + }); + } catch (err) { + console.error("ErrorReporter: Unable to write to the log stream." + err.toString()); + } + }; + + ErrorReporter.prototype.shipLogs = function(reason) { + var self = this; + + this.shipLogsTime = Date.now(); + + if (!reason) { + reason = ""; + } + var logPattern = null; + if (process.type === 'renderer') { + logPattern = "edgehill-"+remote.process.pid+"[.0-9]*.log$"; + } else { + logPattern = "edgehill-"+process.pid+"[.0-9]*.log$"; + } + + console.log("ErrorReporter: Shipping Logs. " + reason); + + Task = require('./task') + ship = Task.once(fs.absolute('./tasks/ship-logs-task.coffee'), tmpPath, logPattern, function() { + self.appendLog("ErrorReporter: Shipped Logs."); + }); + }; + + ErrorReporter.prototype.shipLogsThrottled = function(reason) { + if (!this.shipLogsQueued) { + var timeSinceLogShip = Date.now() - this.shipLogsTime; + if (timeSinceLogShip > 5000) { + this.shipLogs(reason); + } else { + this.shipLogsQueued = true; + var self = this; + setTimeout(function() { + self.shipLogs(reason); + self.shipLogsQueued = false; + }, 5000 - timeSinceLogShip); + } + } + }; + ErrorReporter.prototype.getVersion = function() { var _ref; return (typeof atom !== "undefined" && atom !== null ? atom.getVersion() : void 0) || @@ -61,13 +214,17 @@ module.exports = ErrorReporter = (function() { if (this.dev) { return; } - return this.client.captureError(err, { + + this.client.captureError(err, { extra: metadata, tags: { 'platform': process.platform, 'version': this.getVersion() } }); + + this.appendLog(err, metadata); + this.shipLogsThrottled('Exception occurred'); }; return ErrorReporter; diff --git a/src/flux/action-bridge.coffee b/src/flux/action-bridge.coffee index 864cacbff..5eddbbaf3 100644 --- a/src/flux/action-bridge.coffee +++ b/src/flux/action-bridge.coffee @@ -14,6 +14,8 @@ TargetWindows = Message = DATABASE_STORE_TRIGGER: 'db-store-trigger' +printToConsole = false + # Public: ActionBridge # # The ActionBridge has two responsibilities: @@ -37,7 +39,6 @@ class ActionBridge @ipc = ipc @initiatorId = atom.getWindowType() @role = if atom.isMainWindow() then Role.ROOT else Role.SECONDARY - @logging = false # Listen for action bridge messages from other windows @ipc.on('action-bridge-message', @onIPCMessage) @@ -63,7 +64,7 @@ class ActionBridge onIPCMessage: (initiatorId, name, json) => - console.log("#{@initiatorId} Action Bridge Received: #{name}") if @logging + console.debug(printToConsole, "ActionBridge: #{@initiatorId} Action Bridge Received: #{name}") # Inflate the arguments using the modelReviver to get actual # Models, tasks, etc. out of the JSON @@ -96,7 +97,7 @@ class ActionBridge params.push(arg[0]) json = JSON.stringify(params) - console.log("#{@initiatorId} Action Bridge Broadcasting: #{name}") if @logging + console.debug(printToConsole, "ActionBridge: #{@initiatorId} Action Bridge Broadcasting: #{name}") @ipc.send("action-bridge-rebroadcast-to-#{target}", @initiatorId, name, json) diff --git a/src/flux/actions.coffee b/src/flux/actions.coffee index eac4617b7..f798e7b5c 100644 --- a/src/flux/actions.coffee +++ b/src/flux/actions.coffee @@ -125,6 +125,7 @@ class Actions @longPollConnected: ActionScopeMainWindow @longPollOffline: ActionScopeMainWindow @didMakeAPIRequest: ActionScopeMainWindow + @sendFeedback: ActionScopeMainWindow ### diff --git a/src/flux/models/utils.coffee b/src/flux/models/utils.coffee index 7ea7be330..3f0f29c6f 100644 --- a/src/flux/models/utils.coffee +++ b/src/flux/models/utils.coffee @@ -259,7 +259,7 @@ Utils = # WARNING. This is a fairly expensive operation and should be used # sparingly. nodeIsVisible: (node) -> - while node + while node and node isnt window.document style = window.getComputedStyle(node) node = node.parentNode continue unless style? diff --git a/src/flux/stores/analytics-store.coffee b/src/flux/stores/analytics-store.coffee index 855f010a3..bcfd33c92 100644 --- a/src/flux/stores/analytics-store.coffee +++ b/src/flux/stores/analytics-store.coffee @@ -5,6 +5,8 @@ Mixpanel = require 'mixpanel' Actions = require '../actions' NamespaceStore = require './namespace-store' +printToConsole = false + module.exports = AnalyticsStore = Reflux.createStore init: -> @@ -61,14 +63,17 @@ AnalyticsStore = Reflux.createStore fileDownloaded: -> {} coreGlobalActions: -> - logout: -> {} fileAborted: (uploadData={}) -> {fileSize: uploadData.fileSize} fileUploaded: (uploadData={}) -> {fileSize: uploadData.fileSize} sendDraftSuccess: ({draftLocalId}) -> {draftLocalId: draftLocalId} track: (action, data={}) -> + # send to the analytics service @analytics.track(action, _.extend(data, namespaceId: NamespaceStore.current()?.id)) + # send to the logs that we ship to LogStash + console.debug(printToConsole, {action, data}) + identify: -> namespace = NamespaceStore.current() if namespace diff --git a/src/flux/stores/contact-store.coffee b/src/flux/stores/contact-store.coffee index 61c817b66..008648178 100644 --- a/src/flux/stores/contact-store.coffee +++ b/src/flux/stores/contact-store.coffee @@ -88,7 +88,8 @@ class ContactStore @_contactCache = contacts @trigger() resolve() - .catch(reject) + .catch (err) -> + console.warn("Request for Contacts failed. #{err}") _refreshCache: _.debounce(ContactStore::__refreshCache, 20) _onDatabaseChanged: (change) => diff --git a/src/flux/stores/database-store.coffee b/src/flux/stores/database-store.coffee index 96067ea5c..2cdfbe483 100644 --- a/src/flux/stores/database-store.coffee +++ b/src/flux/stores/database-store.coffee @@ -20,8 +20,7 @@ ipc = require 'ipc' CoffeeHelpers = require '../coffee-helpers' silent = atom.getLoadSettings().isSpec -verboseFilter = (query) -> - false +printToConsole = false # The DatabaseProxy dispatches queries to the Browser process via IPC and listens # for results. It maintains a hash of `queryRecords` representing queries that are @@ -37,8 +36,16 @@ class DatabaseProxy record = @queryRecords[queryKey] return unless record - {callback, options} = record - console.timeStamp("DB END #{queryKey}. #{result?.length} chars") + {callback, options, start, query} = record + + duration = Date.now() - start + metadata = + duration: duration + result_length: result?.length + + console.debug(printToConsole, "DatabaseStore: #{query}", metadata) + if duration > 300 + atom.errorReporter.shipLogs("Poor Query Performance") waits = Promise.resolve() waits = PriorityUICoordinator.settle unless options.evaluateImmediately @@ -53,10 +60,10 @@ class DatabaseProxy queryKey = "#{@windowId}-#{@queryId}" @queryRecords[queryKey] = { callback: callback, - options: options + query: query, + options: options, + start: Date.now() } - console.timeStamp("DB SEND #{queryKey}: #{query}") - console.log(query,values) if verboseFilter(query) ipc.send('database-query', {@databasePath, queryKey, query, values}) # DatabasePromiseTransaction converts the callback syntax of the Database @@ -71,7 +78,7 @@ class DatabasePromiseTransaction # Wrap any user-provided success callback in one that checks query time callback = (err, result) => if err - console.log("Query #{query}, #{JSON.stringify(values)} failed #{err.message}") + console.error("DatabaseStore: Query #{query}, #{JSON.stringify(values)} failed #{err.message}") queryFailure(err) if queryFailure @_reject(err) else diff --git a/src/flux/stores/metadata-store.coffee b/src/flux/stores/metadata-store.coffee index 5bd6c1fab..507b449ee 100644 --- a/src/flux/stores/metadata-store.coffee +++ b/src/flux/stores/metadata-store.coffee @@ -103,7 +103,8 @@ MetadataStore = Reflux.createStore @_metadata[metadatum.type][metadatum.publicId][metadatum.key] = metadatum.value @trigger() resolve() - .catch(reject) + .catch (err) -> + console.warn("Request for Metadata failed. #{err}") _onNamespaceChanged: -> @_namespaceId = NamespaceStore.current()?.id diff --git a/src/flux/stores/namespace-store.coffee b/src/flux/stores/namespace-store.coffee index 522f7b100..c3dba8401 100644 --- a/src/flux/stores/namespace-store.coffee +++ b/src/flux/stores/namespace-store.coffee @@ -35,6 +35,8 @@ class NamespaceStore @_current = current @_namespaces = namespaces @trigger(@) + .catch (err) => + console.warn("Request for Namespaces failed. #{err}") # Inbound Events diff --git a/src/module-cache.coffee b/src/module-cache.coffee index b4d51dd51..e9245cf40 100644 --- a/src/module-cache.coffee +++ b/src/module-cache.coffee @@ -200,7 +200,7 @@ registerBuiltins = (devMode) -> cache.builtins.atom = atomCoffeePath if fs.isFileSync(atomCoffeePath) cache.builtins.atom ?= path.join(cache.resourcePath, 'exports', 'atom.js') - atomShellRoot = path.join(process.resourcesPath, 'atom') + atomShellRoot = path.join(process.resourcesPath, 'atom.asar') commonRoot = path.join(atomShellRoot, 'common', 'api', 'lib') commonBuiltins = ['callbacks-registry', 'clipboard', 'crash-reporter', 'screen', 'shell'] diff --git a/src/space-pen-extensions.coffee b/src/space-pen-extensions.coffee index 7aeafd268..47ce4bf36 100644 --- a/src/space-pen-extensions.coffee +++ b/src/space-pen-extensions.coffee @@ -3,6 +3,16 @@ _ = _.extend(_, require('./space-pen-utils')) SpacePen = require 'space-pen' {Subscriber} = require 'emissary' +### +Edgehill Note: + +I want this file gone—plan is to slowly delete stuff below as we clean spacepen +and it's helpers out of the project. + +- Ben +### + + Subscriber.includeInto(SpacePen.View) jQuery = SpacePen.jQuery @@ -94,65 +104,6 @@ jQuery.contains = (a, b) -> JQueryContains.call(this, a, b) -tooltipDefaults = - delay: - show: 1000 - hide: 100 - container: 'body' - html: true - placement: 'auto top' - viewportPadding: 2 - -humanizeKeystrokes = (keystroke) -> - keystrokes = keystroke.split(' ') - keystrokes = (_.humanizeKeystroke(stroke) for stroke in keystrokes) - keystrokes.join(' ') - -getKeystroke = (bindings) -> - if bindings?.length - "#{humanizeKeystrokes(bindings[0].keystrokes)}" - else - '' - -requireBootstrapTooltip = _.once -> - atom.requireWithGlobals('bootstrap/js/tooltip', {jQuery}) - -# options from http://getbootstrap.com/javascript/#tooltips -jQuery.fn.setTooltip = (tooltipOptions, {command, commandElement}={}) -> - requireBootstrapTooltip() - - tooltipOptions = {title: tooltipOptions} if _.isString(tooltipOptions) - - if commandElement - bindings = atom.keymaps.findKeyBindings(command: command, target: commandElement[0]) - else if command - bindings = atom.keymaps.findKeyBindings(command: command) - - tooltipOptions.title = "#{tooltipOptions.title} #{getKeystroke(bindings)}" - - @tooltip(jQuery.extend({}, tooltipDefaults, tooltipOptions)) - -jQuery.fn.hideTooltip = -> - tip = @data('bs.tooltip') - if tip - tip.leave(currentTarget: this) - tip.hide() - -jQuery.fn.destroyTooltip = -> - @hideTooltip() - requireBootstrapTooltip() - @tooltip('destroy') - -# Hide tooltips when window is resized -jQuery(document.body).on 'show.bs.tooltip', ({target}) -> - windowHandler = -> jQuery(target).hideTooltip() - jQuery(window).one('resize', windowHandler) - jQuery(target).one 'hide.bs.tooltip', -> - jQuery(window).off('resize', windowHandler) - -jQuery.fn.setTooltip.getKeystroke = getKeystroke -jQuery.fn.setTooltip.humanizeKeystrokes = humanizeKeystrokes - Object.defineProperty jQuery.fn, 'element', get: -> @[0] module.exports = SpacePen diff --git a/src/task-bootstrap.coffee b/src/task-bootstrap.coffee index 4ed618d1a..3939bcdd6 100644 --- a/src/task-bootstrap.coffee +++ b/src/task-bootstrap.coffee @@ -10,6 +10,14 @@ setupGlobals = -> trace: -> global.__defineGetter__ 'console', -> console + fs = require 'fs' + fs.existsSync = (path) -> + try + fs.accessSync(path) + return true + catch + return false + global.document = createElement: -> setAttribute: -> @@ -24,7 +32,7 @@ setupGlobals = -> global.emit = (event, args...) -> process.send({event, args}) - global.navigator = {userAgent} + global.navigator = {userAgent: userAgent} global.window = global handleEvents = -> @@ -41,6 +49,14 @@ handleEvents = -> result = handler.bind({async})(args...) emit('task:completed', result) unless isAsync +setupDeprecations = -> + Grim = require 'grim' + Grim.on 'updated', -> + deprecations = Grim.getDeprecations().map (deprecation) -> deprecation.serialize() + Grim.clearDeprecations() + emit('task:deprecations', deprecations) + setupGlobals() handleEvents() +setupDeprecations() handler = require(taskPath) diff --git a/src/task.coffee b/src/task.coffee index 2df974dfb..080c0b61e 100644 --- a/src/task.coffee +++ b/src/task.coffee @@ -1,6 +1,7 @@ _ = require 'underscore' {fork} = require 'child_process' {Emitter} = require 'emissary' +Grim = require 'grim' # Extended: Run a node script in a separate process. # @@ -78,12 +79,15 @@ class Task taskPath = require.resolve(taskPath) taskPath = taskPath.replace(/\\/g, "\\\\") - env = _.extend({}, process.env, {taskPath, userAgent: navigator.userAgent}) - @childProcess = fork '--eval', [bootstrap], {env, cwd: __dirname} + env = _.extend({}, process.env, {taskPath, userAgent: process.env.userAgent}) + @childProcess = fork '--eval', [bootstrap], {env, silent: true} @on "task:log", -> console.log(arguments...) @on "task:warn", -> console.warn(arguments...) @on "task:error", -> console.error(arguments...) + @on "task:deprecations", (deprecations) -> + Grim.addSerializedDeprecation(deprecation) for deprecation in deprecations + return @on "task:completed", (args...) => @callback?(args...) @handleEvents() @@ -93,6 +97,11 @@ class Task @childProcess.removeAllListeners() @childProcess.on 'message', ({event, args}) => @emit(event, args...) if @childProcess? + # Catch the errors that happened before task-bootstrap. + @childProcess.stdout.on 'data', (data) -> + console.log data.toString() + @childProcess.stderr.on 'data', (data) -> + console.error data.toString() # Public: Starts the task. # diff --git a/src/tasks/ship-logs-task.coffee b/src/tasks/ship-logs-task.coffee new file mode 100644 index 000000000..bc56a3b89 --- /dev/null +++ b/src/tasks/ship-logs-task.coffee @@ -0,0 +1,55 @@ +fs = require 'fs' +path = require 'path' +request = require 'request' + +module.exports = (dir, regexPattern) -> + callback = @async() + + console.log("Running log ship: #{dir}, #{regexPattern}") + + fs.readdir dir, (err, files) -> + console.log("readdir error: #{err}") if err + logs = [] + logFilter = new RegExp(regexPattern) + for file in files + if logFilter.test(file) + filepath = path.join(dir, file) + stats = fs.statSync(filepath) + logs.push(filepath) if stats["size"] > 0 + + remaining = 0 + finished = -> + remaining -= 1 + if remaining is 0 + callback() + + if logs.length is 0 + console.log("No logs found to upload.") + callback() + console.log("Callback.") + return + + console.log("Uploading #{logs} to S3") + + # Note: These credentials are only good for uploading to this + # specific bucket and can't be used for anything else. + AWS = require 'aws-sdk' + AWS.config.update + accessKeyId: 'AKIAIEGVDSVLK3Z7UVFA', + secretAccessKey: '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' + + bucket = new AWS.S3({params: {Bucket: 'edgehill-client-logs'}}) + uploadTime = Date.now() + + logs.forEach (log) -> + stream = fs.createReadStream(log, {flags: 'r'}) + key = "#{uploadTime}-#{path.basename(log)}" + params = {Key: key, Body: stream} + remaining += 1 + bucket.upload params, (err, data) -> + if err + console.log("Error uploading #{key}: #{err.toString()}") + else + console.log("Successfully uploaded #{key}") + fs.truncate(log) + finished() diff --git a/src/window-event-handler.coffee b/src/window-event-handler.coffee index 7ac796108..1b179d1ad 100644 --- a/src/window-event-handler.coffee +++ b/src/window-event-handler.coffee @@ -31,6 +31,10 @@ class WindowEventHandler when 'update-available' atom.updateAvailable(detail) + when 'send-feedback' + Actions = require './flux/actions' + Actions.sendFeedback() + @subscribe ipc, 'command', (command, args...) -> activeElement = document.activeElement # Use the workspace element view if body has focus