feat(logging): Developer bar, verbose logging to logstash, Electron 0.26.0

Summary:
- We now make verbose log files continuously as you use the app
- We ship the logs to LogStash via S3 when an exception occurs
- We log the DatabaseStore, ActionBridge and Analytics packages

- We are now on the latest version of Electron 0.26.0
- We are now on Chrome 42 and io.js 1.4.3
- We should be setup to use ASAR soon.

Update atom.sh to reflect that we're now electron

oniguruma was unnecessary

correctly find log files that haven't been shipped yet

Fix a small issue with nodeIsVisible after upgrade to Chrome 42

Delete old logs, better logging from database store, don't ship empty logs

Test Plan: Run existing tests

Reviewers: evan

Reviewed By: evan

Differential Revision: https://phab.nylas.com/D1531
This commit is contained in:
Ben Gotow 2015-05-19 17:02:46 -07:00
parent f274bbbf30
commit 8599bc0397
64 changed files with 650 additions and 368 deletions

View file

@ -6,6 +6,6 @@
"url": "https://github.com/atom/atom.git"
},
"dependencies": {
"atom-package-manager": "0.135.0"
"atom-package-manager": "0.167.0"
}
}

18
atom.sh
View file

@ -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

View file

@ -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':

View file

@ -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}")

View file

@ -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)

View file

@ -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

View file

@ -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}")

View file

@ -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 <edgehill@nylas.com>'
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()

View file

@ -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",

View file

@ -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]

View file

@ -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

View file

@ -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:

View file

@ -2,7 +2,7 @@
<div id="footer">
<div class="container">
<img src="images/edgehill.png" class="logo" />
<img src="images/nylas.png" class="logo" />
<div class="small">Nylas Mail Developer Preview<br><em>© 2014-2015 Nylas, Inc.</em></div>
</div>
</div>

View file

@ -9,7 +9,7 @@
<body>
<div id="header">
<div class="container">
<img src="images/edgehill.png" class="logo" />
<img src="images/nylas.png" class="logo" />
<div class="title">Nylas Mail<div class="small">Developer Preview</div></div>
</div>
</div>

View file

@ -4,7 +4,7 @@ TitleHidden: true
Section: Getting Started
---
<img src="images/edgehill.png" class="center-logo"/>
<img src="images/nylas.png" class="center-logo"/>
<h2 style="text-align:center;">Nylas Package API</h2>
<p style="text-align:center; margin:auto; margin-bottom:60px;">
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.

View file

Before

Width:  |  Height:  |  Size: 200 KiB

After

Width:  |  Height:  |  Size: 200 KiB

View file

@ -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.<br/>
<br/>
<b>What happened:</b><br/>
<br/>
<br/>
<b>Impact:</b><br/>
<br/>
<br/>
<b>Feedback:</b><br/>
<br/>
<br/>
<b>Environment:</b><br/>
I'm using Edgehill #{atom.getVersion()} and my platform is #{process.platform}-#{process.arch}.<br/>
--<br/>
#{user}<br/>
-- Extra Debugging Data --<br/>
#{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

View file

@ -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
</div>
</div>
<div className="btn-container pull-right">
<div className="btn" onClick={@_onFeedback}>Feedback</div>
<div className="btn" onClick={Actions.sendFeedback}>Feedback</div>
</div>
<div className="btn-container pull-right">
<div className="btn" onClick={@_onToggleRegions}>Component Regions</div>
@ -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.<br/>
<br/>
<b>What happened:</b><br/>
<br/>
<br/>
<b>Impact:</b><br/>
<br/>
<br/>
<b>Feedback:</b><br/>
<br/>
<br/>
<b>Environment:</b><br/>
I'm using Edgehill #{atom.getVersion()} and my platform is #{process.platform}-#{process.arch}.<br/>
--<br/>
#{user}<br/>
-- Extra Debugging Data --<br/>
#{debugData}
"""
DatabaseStore.persistModel(draft).then ->
DatabaseStore.localIdForModel(draft).then (localId) ->
Actions.composePopoutDraft(localId)
_getStateFromStores: =>
visible: DeveloperBarStore.visible()
queue: TaskQueue._queue

View file

@ -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) ->

View file

@ -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'

View file

View file

@ -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'

View file

@ -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'

View file

@ -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' }
]
}
]

View file

@ -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' }
]
}
]

View file

@ -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' }
]
}
]

View file

@ -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",

View file

@ -8,4 +8,4 @@ Architecture: <%= arch %>
Installed-Size: <%= installedSize %>
Maintainer: <%= maintainer %>
Description: <%= description %>
Edgehill is an email OS
Nylas is an email OS

View file

@ -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

View file

@ -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

View file

@ -3,9 +3,9 @@
<plist version="1.0">
<dict>
<key>CFBundleExecutable</key>
<string>Edgehill</string>
<string>Nylas</string>
<key>CFBundleIconFile</key>
<string>edgehill.icns</string>
<string>nylas.icns</string>
<key>CFBundleIdentifier</key>
<string>com.nylas.nylas-mail</string>
<key>CFBundleInfoDictionaryVersion</key>
@ -48,7 +48,7 @@
<string>edgehill</string>
</array>
<key>CFBundleURLName</key>
<string>Edgehill Shared Session Protocol</string>
<string>Nylas Shared Session Protocol</string>
</dict>
<dict>
<key>CFBundleURLSchemes</key>

View file

Before

Width:  |  Height:  |  Size: 200 KiB

After

Width:  |  Height:  |  Size: 200 KiB

View file

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 58 KiB

View file

@ -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')};

View file

@ -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 = [

View file

@ -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"

View file

@ -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

View file

@ -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"

View file

@ -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()

View file

@ -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")

View file

@ -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})

View file

@ -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')

View file

@ -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

View file

@ -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

View file

@ -1,6 +1,7 @@
global.shellStartTime = Date.now()
errorReporter = new (require '../error-reporter')
app = require 'app'
fs = require 'fs'
path = require 'path'

View file

@ -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})

View file

@ -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

View file

@ -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;

View file

@ -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)

View file

@ -125,6 +125,7 @@ class Actions
@longPollConnected: ActionScopeMainWindow
@longPollOffline: ActionScopeMainWindow
@didMakeAPIRequest: ActionScopeMainWindow
@sendFeedback: ActionScopeMainWindow
###

View file

@ -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?

View file

@ -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

View file

@ -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) =>

View file

@ -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

View file

@ -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

View file

@ -35,6 +35,8 @@ class NamespaceStore
@_current = current
@_namespaces = namespaces
@trigger(@)
.catch (err) =>
console.warn("Request for Namespaces failed. #{err}")
# Inbound Events

View file

@ -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']

View file

@ -3,6 +3,16 @@ _ = _.extend(_, require('./space-pen-utils'))
SpacePen = require 'space-pen'
{Subscriber} = require 'emissary'
###
Edgehill Note:
I want this file goneplan 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
"<span class=\"keystroke\">#{humanizeKeystrokes(bindings[0].keystrokes)}</span>"
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

View file

@ -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)

View file

@ -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.
#

View file

@ -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()

View file

@ -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