diff --git a/.gitignore b/.gitignore index 27d05ec7c..b25ec853a 100644 --- a/.gitignore +++ b/.gitignore @@ -51,5 +51,8 @@ version.js android/package android/*.apk - coverage/ + +shell/bin/linux +shell/bin/darwin +shell/bin/win32 diff --git a/README.md b/README.md index 66fa78531..80d1d4302 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,7 @@ Then visit localhost:3000 in your browser. ## Running copay + To run on a different port: ``` PORT=3001 npm start @@ -66,6 +67,25 @@ require('copay').start(3000, function(location) { ``` +## Running in the Native Shell + +Copay can be executed from within a "native" application shell, providing some +additional features such as native menus, notifications, tray integration, etc. +This is accomplished using [Atom Shell](https://github.com/atom/atom-shell). + +To run and test Copay from within this context, first download the atom-shell +package to `shell/bin/{platform}` (ignored by git), by running: + +``` +npm run setup-shell +``` + +Once this script has completed, you can launch the shell-based Copay by running: + +``` +npm run shell +``` + ## Configuration @@ -89,7 +109,36 @@ One solution is to use Copay with a Python version manager for 2.6. # Development +## Native Shell + +To add features that enhance the native experience of Copay, first follow the +directions above under "Running in the Native Shell". It's important to ensure +that functionality within this context should either hook into existing features +or supplement the experience of those features. Copay should continue to operate +full-featured from within a modern web browser. + +Shell functionality works by sending and receiving messages between the Copay +application and the shell wrapper. Native functionality should be handled mostly +from within `shell/lib/message-handler.js`, which receives messages conditionally +from the front-end Angular controllers. + +Look at `js/shell.js` to see how we determine if Copay is running from within the +native shell context. If we are running within the shell, Copay has access to the +global variable `window.cshell`, which provides access to the messenger. For +instance, to Copay might want to use a native dialog alert in favor of a regular +one if running in this context. You would do this like so: + +```js +if (window.cshell) { + window.cshell.send('alert', 'info', 'Please select a wallet.'); +} +else { + window.alert('Please select a wallet.'); +} +``` + ## Google Chrome Extension + When you need to compile a *Chrome Extension* of Copay, you only need to run: ``` $ sh chrome/build.sh @@ -98,6 +147,7 @@ $ sh chrome/build.sh - The ZIP file is *chrome/copay-chrome-extension.zip* ## Firefox Add-on + System Requirements * Download [Add-on SDK](https://addons.mozilla.org/en-US/developers/builder) @@ -112,7 +162,8 @@ $ sh firefox/build.sh - Copy the content of *firefox/firefox-addon* (lib, data, package.json) to your development path. - Compile the XPI file. [Mozilla Docs](https://developer.mozilla.org/en-US/Add-ons/SDK/Tutorials/Getting_started) -##Web App +## Web App + The Web App is a clean version of Copay, only the neededs files (html, css, js) for run Copay locally or in your own server. @@ -125,6 +176,7 @@ $ sh webapp/build.sh - The *webapp/copay-webapp* is the unzipped version ## Android APK + System Requirements * Download [Android SDK](http://developer.android.com/sdk/index.html) diff --git a/app.js b/app.js index 69407cdcb..08351a4f5 100644 --- a/app.js +++ b/app.js @@ -13,3 +13,8 @@ app.start = function(port, callback) { }; module.exports = app; + +// if we are running in the copay shell context, initialize the shell bindings +if (process.versions && process.versions['atom-shell']) { + require('./shell')(app); +} diff --git a/package.json b/package.json index 51909b037..e8d80f94e 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,9 @@ "scripts": { "start": "node server.js", "test": "node_modules/.bin/istanbul cover ./node_modules/mocha/bin/_mocha --report lcovonly -- -R spec && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js", - "coverage": "./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha -- --reporter spec test" + "coverage": "./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha -- --reporter spec test", + "shell": "node shell/scripts/launch.js", + "setup-shell": "node shell/scripts/download-atom-shell.js" }, "homepage": "https://github.com/bitpay/copay", "devDependencies": { @@ -50,7 +52,9 @@ "sinon": "~1.9.1", "soop": "~0.1.5", "travis-cov": "^0.2.5", - "uglifyify": "~1.2.3" + "uglifyify": "~1.2.3", + "github-releases": "~0.2.0", + "cli-color": "~0.3.2" }, "dependencies": { "mocha": "^1.18.2" diff --git a/shell/README.md b/shell/README.md new file mode 100644 index 000000000..970f9882d --- /dev/null +++ b/shell/README.md @@ -0,0 +1,11 @@ +Copay Shell +=========== + +Native application wrapper for [Copay](https://bitpay.github.io/copay) +using [Atom Shell](https://github.com/atom/atom-shell). + +![copay-shell](https://cloud.githubusercontent.com/assets/1188875/3153630/ccaacbae-ea9d-11e3-85d6-ac0ec2820ae2.png) + +## Building + +Automated build scripts are currently being developed. diff --git a/shell/assets/darwin/Info.plist b/shell/assets/darwin/Info.plist new file mode 100644 index 000000000..4f1d06bc8 --- /dev/null +++ b/shell/assets/darwin/Info.plist @@ -0,0 +1,40 @@ + + + + + BuildMachineOSBuild + 12F45 + CFBundleDisplayName + Copay + CFBundleExecutable + Copay + CFBundleIconFile + copay.icns + CFBundleIdentifier + com.bitpay.copay + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + Copay + CFBundlePackageType + APPL + CFBundleVersion + 0.0.1 + DTSDKBuild + 13C64 + DTSDKName + macosx + DTXcode + 0511 + DTXcodeBuild + 5B1008 + LSMinimumSystemVersion + 10.8.0 + NSMainNibFile + MainMenu + NSPrincipalClass + AtomApplication + NSSupportsAutomaticGraphicsSwitching + + + diff --git a/shell/assets/darwin/copay.icns b/shell/assets/darwin/copay.icns new file mode 100644 index 000000000..c587d6e60 Binary files /dev/null and b/shell/assets/darwin/copay.icns differ diff --git a/shell/assets/linux/Copay.desktop b/shell/assets/linux/Copay.desktop new file mode 100644 index 000000000..12a5957bf --- /dev/null +++ b/shell/assets/linux/Copay.desktop @@ -0,0 +1,7 @@ +[Desktop Entry] +Encoding=UTF-8 +Name=Copay +Exec=/opt/Copay/copay +Icon=/opt/Copay/resources/app/node_modules/copay/android/icon.png +Type=Application +Categories=Bitcoin; diff --git a/shell/assets/linux/install.sh b/shell/assets/linux/install.sh new file mode 100755 index 000000000..a32b88810 --- /dev/null +++ b/shell/assets/linux/install.sh @@ -0,0 +1,13 @@ +#!/bin/bash +cd /tmp +# download pre-built pacakge to tmp +wget https://github.com/bitpay/copay/releases/download/v0.1.0/Copay-linux-x64.tar.gz +# extract archive +tar -xvf /tmp/Copay-linux-x64.tar.gz +# move the package to opt +mv /tmp/Copay /opt/Copay +# symlink `copay` to user path +ln -s /opt/Copay/copay /usr/local/bin/copay +cd /usr/share/applications +# download desktop entry +wget https://raw.githubusercontent.com/bitpay/copay-shell/master/assets/linux/Copay.desktop diff --git a/shell/assets/win32/build-installer.nsi b/shell/assets/win32/build-installer.nsi new file mode 100644 index 000000000..9b55dd1d5 --- /dev/null +++ b/shell/assets/win32/build-installer.nsi @@ -0,0 +1,148 @@ +# This installs two files, app.exe and logo.ico, creates a start menu shortcut, builds an uninstaller, and +# adds uninstall information to the registry for Add/Remove Programs + +# To get started, put this script into a folder with the two files (app.exe, logo.ico, and license.rtf - +# You'll have to create these yourself) and run makensis on it + +# If you change the names "app.exe", "logo.ico", or "license.rtf" you should do a search and replace - they +# show up in a few places. +# All the other settings can be tweaked by editing the !defines at the top of this script +!define APPNAME "Copay" +!define COMPANYNAME "BitPay, Inc" +!define DESCRIPTION "Multisignature Wallet" +# These three must be integers +!define VERSIONMAJOR 0 +!define VERSIONMINOR 0 +!define VERSIONBUILD 7 +# These will be displayed by the "Click here for support information" link in "Add/Remove Programs" +# It is possible to use "mailto:" links in here to open the email client +!define HELPURL "http://github.com/bitpay/copay/issues" # "Support Information" link +!define UPDATEURL "http://github.com/bitpay/copay" # "Product Updates" link +!define ABOUTURL "http://bitpay.github.io/copay" # "Publisher" link +# This is the size (in kB) of all the files copied into "Program Files" +!define INSTALLSIZE 7233 + +RequestExecutionLevel admin ;Require admin rights on NT6+ (When UAC is turned on) + +InstallDir "$PROGRAMFILES\${COMPANYNAME}\${APPNAME}" + +# rtf or txt file - remember if it is txt, it must be in the DOS text format (\r\n) +# This will be in the installer/uninstaller's title bar +Name "${APPNAME} - Multisignature Wallet" +Icon "logo.ico" +outFile "copay-setup.exe" + +!include LogicLib.nsh + +# Just three pages - license agreement, install location, and installation +# page license +page directory +Page instfiles + +!macro VerifyUserIsAdmin +UserInfo::GetAccountType +pop $0 +${If} $0 != "admin" ;Require admin rights on NT4+ + messageBox mb_iconstop "Administrator rights required!" + setErrorLevel 740 ;ERROR_ELEVATION_REQUIRED + quit +${EndIf} +!macroend + +function .onInit + setShellVarContext all + !insertmacro VerifyUserIsAdmin +functionEnd + +section "install" + # Files for the install directory - to build the installer, these should be in the same directory as the install script (this file) + setOutPath $INSTDIR + # Files added here should be removed by the uninstaller (see section "uninstall") + file "Copay.exe" + file "logo.ico" + # Add any other files for the install directory (license files, app data, etc) here + file /r "locales" + file /r "resources" + file "chromiumcontent.dll" + file "content_shell.pak" + file "d3dcompiler_43.dll" + file "ffmpegsumo.dll" + file "icudt.dll" + file "libEGL.dll" + file "libGLESv2.dll" + file "LICENSE" + file "version" + file "xinput1_3.dll" + + # Uninstaller - See function un.onInit and section "uninstall" for configuration + writeUninstaller "$INSTDIR\uninstall.exe" + + # Start Menu + createDirectory "$SMPROGRAMS\${COMPANYNAME}" + createShortCut "$SMPROGRAMS\${COMPANYNAME}\${APPNAME}.lnk" "$INSTDIR\Copay.exe" "" "$INSTDIR\logo.ico" + + # Registry information for add/remove programs + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "DisplayName" "${COMPANYNAME} - ${APPNAME} - ${DESCRIPTION}" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "UninstallString" "$\"$INSTDIR\uninstall.exe$\"" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "QuietUninstallString" "$\"$INSTDIR\uninstall.exe$\" /S" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "InstallLocation" "$\"$INSTDIR$\"" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "DisplayIcon" "$\"$INSTDIR\logo.ico$\"" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "Publisher" "$\"${COMPANYNAME}$\"" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "HelpLink" "$\"${HELPURL}$\"" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "URLUpdateInfo" "$\"${UPDATEURL}$\"" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "URLInfoAbout" "$\"${ABOUTURL}$\"" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "DisplayVersion" "$\"${VERSIONMAJOR}.${VERSIONMINOR}.${VERSIONBUILD}$\"" + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "VersionMajor" ${VERSIONMAJOR} + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "VersionMinor" ${VERSIONMINOR} + # There is no option for modifying or repairing the install + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "NoModify" 1 + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "NoRepair" 1 + # Set the INSTALLSIZE constant (!defined at the top of this script) so Add/Remove Programs can accurately report the size + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "EstimatedSize" ${INSTALLSIZE} +sectionEnd + +# Uninstaller + +function un.onInit + SetShellVarContext all + + #Verify the uninstaller - last chance to back out + MessageBox MB_OKCANCEL "Permanantly remove ${APPNAME}?" IDOK next + Abort + next: + !insertmacro VerifyUserIsAdmin +functionEnd + +section "uninstall" + + # Remove Start Menu launcher + delete "$SMPROGRAMS\${COMPANYNAME}\${APPNAME}.lnk" + # Try to remove the Start Menu folder - this will only happen if it is empty + rmDir "$SMPROGRAMS\${COMPANYNAME}" + + # Remove files + delete $INSTDIR\*.pak + delete $INSTDIR\logo.ico + rmDir /r $INSTDIR\locales + rmDir /r $INSTDIR\resources + delete $INSTDIR\chromiumcontent.dll + delete $INSTDIR\content_shell.pak + delete $INSTDIR\d3dcompiler_43.dll + delete $INSTDIR\ffmpegsumo.dll + delete $INSTDIR\icudt.dll + delete $INSTDIR\libEGL.dll + delete $INSTDIR\libGLESv2.dll + delete $INSTDIR\LICENSE + delete $INSTDIR\version + delete $INSTDIR\xinput1_3.dll + delete $INSTDIR\Copay.exe + + # Always delete uninstaller as the last action + delete $INSTDIR\uninstall.exe + + # Try to remove the install directory - this will only happen if it is empty + rmDir $INSTDIR + + # Remove uninstaller information from the registry + DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" +sectionEnd \ No newline at end of file diff --git a/shell/assets/win32/logo.ico b/shell/assets/win32/logo.ico new file mode 100644 index 000000000..183e3cb13 Binary files /dev/null and b/shell/assets/win32/logo.ico differ diff --git a/shell/bin/README.md b/shell/bin/README.md new file mode 100644 index 000000000..fcac9e275 --- /dev/null +++ b/shell/bin/README.md @@ -0,0 +1,8 @@ +# Atom Shell Binaries + +This directory is a placeholder for local copies of the Atom Shell package. The +package for your platform will be installed to this directory under a directory +named for the value of your `process.platform`. + +Running `npm run shell` will launch Copay using the installed Atom Shell +package. diff --git a/shell/config.json b/shell/config.json new file mode 100644 index 000000000..896666d7c --- /dev/null +++ b/shell/config.json @@ -0,0 +1,10 @@ +{ + "window": { + "height": 720, + "width": 880 + }, + "copay": { + "port": 3000 + }, + "debug": true +} diff --git a/shell/index.js b/shell/index.js new file mode 100644 index 000000000..4f9f5abf8 --- /dev/null +++ b/shell/index.js @@ -0,0 +1,63 @@ +/* +** copay-shell - initilization +*/ + +var config = require('./config'); +var app = require('app'); +var BrowserWindow = require('browser-window'); +var Menu = require('menu'); +var mainWindow = null; + +module.exports = function(copay) { + + // quit when all windows are closed + app.on('window-all-closed', function() { + if (process.platform !== 'darwin') app.quit(); + }); + + // initilization when ready + app.on('ready', function() { + + // start up the copay server + copay.start(config.copay.port, function(loc) { + + // create the main window + mainWindow = new BrowserWindow({ + width: config.window.width, + height: config.window.height + }); + + // hide the empty window + mainWindow.hide(); + + // setup the native application menu + Menu.setApplicationMenu( + require('./lib/app-menu')(app, mainWindow.webContents) + ); + + // setup the message handler + require('./lib/message-handler')(mainWindow); + + // setup the native system tray integration + // require('./lib/system-tray')(app, mainWindow.webContents); + + // load our local copay server + mainWindow.loadUrl(loc); + + // kind of hacky - but let's avoid the white "flash" before rendering + setTimeout(mainWindow.show.bind(mainWindow), 1000); + + // deref the browser window when we close it so it can be GC'ed + mainWindow.on('closed', function() { + mainWindow = null; + }); + + // mainWindow.toggleDevTools(); + + }); + + }); + + return app; + +}; diff --git a/shell/lib/app-menu.js b/shell/lib/app-menu.js new file mode 100644 index 000000000..f32ab750d --- /dev/null +++ b/shell/lib/app-menu.js @@ -0,0 +1,143 @@ +/* +** copay-shell - native app menu +*/ + +module.exports = function(app, web) { + + var Menu = require('menu'); + var menu = [] + + // add the mac osx app menu entry + if (process.platform === 'darwin') { + menu.push({ + label: 'Copay', + submenu: [ + { + label: 'About Copay', + selector: 'orderFrontStandardAboutPanel:' + }, + { + type: 'separator' + }, + { + label: 'Hide Copay', + accelerator: 'Command+H', + selector: 'hide:' + }, + { + label: 'Hide Others', + accelerator: 'Command+Shift+H', + selector: 'hideOtherApplications:' + }, + { + label: 'Show All', + selector: 'unhideAllApplications:' + }, + { + type: 'separator' + }, + { + label: 'Quit', + accelerator: 'Command+Q', + click: function() { + app.quit(); + } + } + ] + }); + } + + menu.push({ + label: 'Addresses', + submenu: [ + { + label: 'Create New', + click: function() { + web.send('address:create'); + } + } + ] + }); + + menu.push({ + label: 'Transactions', + submenu: [ + { + label: 'Send Money', + click: function() { + web.send('transactions:send'); + } + }, + { + type: 'separator' + }, + { + label: 'Pending', + click: function() { + web.send('transactions:pending'); + } + }, + { + label: 'All', + click: function() { + web.send('transactions:all'); + } + } + ] + }); + + menu.push({ + label: 'Backup', + submenu: [ + { + label: 'Download File', + click: function() { + web.send('backup:download'); + } + }, + { + label: 'Backup to Email', + click: function() { + web.send('backup:email'); + } + }, + { + type: 'separator' + }, + { + label: 'Import a Backup', + click: function() { + web.send('backup:import'); + } + } + ] + }); + + if (process.platform === 'darwin') { + menu.push({ + label: 'Window', + submenu: [ + { + label: 'Minimize', + accelerator: 'Command+M', + selector: 'performMiniaturize:' + }, + { + label: 'Close', + accelerator: 'Command+W', + selector: 'performClose:' + }, + { + type: 'separator' + }, + { + label: 'Bring All to Front', + selector: 'arrangeInFront:' + } + ] + }); + } + + return Menu.buildFromTemplate(menu); + +}; diff --git a/shell/lib/message-handler.js b/shell/lib/message-handler.js new file mode 100644 index 000000000..2fc1f239a --- /dev/null +++ b/shell/lib/message-handler.js @@ -0,0 +1,81 @@ +/* +** copay-shell - message handler +*/ + +var ipc = require('ipc'); +var dialog = require('dialog'); +var config = require('../config'); +var windows = (process.platform === 'win32'); +var HOME = process.env[windows ? 'USERPROFILE' : 'HOME']; +var fs = require('fs'); +var shell = require('shell'); + +module.exports = function(renderer) { + + // handle alerts sent from renderer (browser window) + ipc.on('alert', function(e, type, message) { + dialog.showMessageBox(renderer, { + type: type || 'info', + buttons: ['Okay'], + title: 'Copay', + message: message + }); + }); + + // handle saving a wallet backup + ipc.on('backup:download', function(e, data) { + var backup = new Buffer(data.wallet); + var filename = data.name + '-' + (+(new Date)) + '.json.aes'; + // open save dialog + dialog.showSaveDialog(renderer, { + title: 'Backup Wallet', + defaultPath: HOME + '/' + filename + }, function(path) { + if (!path) return; + fs.writeFile(path, backup, function(err) { + dialog.showMessageBox(renderer, { + type: err ? 'warning' : 'info', + buttons: ['Okay'], + title: 'Copay', + message: err ? err.message : 'Wallet backup saved!' + }); + }); + }); + }); + + // handle emailing a wallet backup + ipc.on('backup:email', function(e, href) { + // open email client + shell.openExternal(href) + }); + + // handle importing a wallet backup + ipc.on('backup:import', function(e, data) { + + // open file dialog + dialog.showOpenDialog(renderer, { + title: 'Import Wallet Backup', + defaultPath: HOME, + properties: ['openFile'] + }, function(path) { + if (!path) return; + fs.readFile(path[0], function(err, contents) { + if (err) { + return dialog.showMessageBox(renderer, { + type: 'warning', + buttons: ['Okay'], + title: 'Copay', + message: err.message + }); + } + renderer.send('backup:import:data', contents.toString()); + }); + }); + }); + + // if we get an error, let's pop open the console for the user + ipc.on('error', function() { + if (config.debug) renderer.toggleDevTools(); + }); + +}; diff --git a/shell/lib/system-tray.js b/shell/lib/system-tray.js new file mode 100644 index 000000000..8fbb30921 --- /dev/null +++ b/shell/lib/system-tray.js @@ -0,0 +1,16 @@ +/* +** copay-shell - system tray integration +*/ + +var Menu = require('menu'); +var Tray = require('tray'); +var tray = new Tray('../assets/copay.icns'); + +module.exports = function(app, web) { + + var menu = Menu.buildFromTemplate([]); + + tray.setToolTip('Copay'); + tray.setContextMenu(menu); + +}; diff --git a/shell/scripts/build-linux.js b/shell/scripts/build-linux.js new file mode 100644 index 000000000..9fb4e8f8f --- /dev/null +++ b/shell/scripts/build-linux.js @@ -0,0 +1,70 @@ +/* +** copay-shell - linux builder +*/ + +var os = require('os'); +var downloadAtom = require('./download-atom-shell'); +var async = require('async'); +var fs = require('fs'); +var path = require('path'); +var color = require('cli-color'); +var exec = require('child_process').exec; + +var shell_target = path.normalize(__dirname + '/../build/linux/Copay') +var app_target = path.normalize(__dirname + '/../build/linux/Copay/resources/app') + +async.series( + [ + function getBinary(done) { + downloadAtom(done); + }, + function copyBuildFiles(done) { + console.log(color.blue('{copay}'), 'copying app files'); + var ignore = ['.git','assets','build','scripts'].map(function(dir) { + return '--exclude ' + dir + }).join(' '); + var appDir = path.normalize(__dirname + '/../'); + + exec('rsync -av --progress ' + appDir + ' ' + app_target + ' ' + ignore, { + maxBuffer: Infinity // LOL + }, function(err, stdout, stderr) { + done(err || stderr); + }); + }, + function removeDefaultApp(done) { + console.log(color.blue('{copay}'), 'removing default application'); + rmdir(path.normalize(__dirname + '/../build/linux/Copay/resources/default_app')); + done(); + }, + function renameExecutable(done) { + console.log(color.blue('{copay}'), 'renaming executable'); + fs.rename(shell_target + '/atom', shell_target + '/Copay', done); + }, + function zipBuild(done) { + console.log(color.blue('{copay}'), 'zipping distributable package'); + exec('zip -r ' + path.normalize(shell_target, '/../Copay-' + process.platform) + ' ' + shell_target, { + maxBuffer: Infinity // LOL x 2 + },function(err, stdout, stderr) { + done(err || stderr); + }); + } + ], + function(err, results) { + if (err) return console.log(color.blue('{copay}'), color.red(err)); + console.log(color.blue('{copay}'), color.green('success!')); + } +); + +function rmdir(dir) { + if (fs.existsSync(dir)) { + var list = fs.readdirSync(dir); + for(var i = 0; i < list.length; i++) { + var filename = path.join(dir, list[i]); + var stat = fs.statSync(filename); + if (filename == '.' || filename == '..') null; + else if (stat.isDirectory()) rmdir(filename); + else fs.unlinkSync(filename); + } + fs.rmdirSync(dir); + } +}; diff --git a/shell/scripts/download-atom-shell.js b/shell/scripts/download-atom-shell.js new file mode 100644 index 000000000..2bb29591c --- /dev/null +++ b/shell/scripts/download-atom-shell.js @@ -0,0 +1,106 @@ +/* +** copay-shell - atom shell downloader +*/ + +var path = require('path'); +var fs = require('fs'); +var GitHub = require('github-releases'); +var async = require('async'); +var readl = require('readline'); +var color = require('cli-color'); +var github = new GitHub({ repo: 'atom/atom-shell' }); +var exec = require('child_process').exec; +var os = require('os'); + +var version = 'v0.13.0'; +var target = path.normalize(__dirname + '/../bin/' + process.platform); + + +console.log(color.blue('{copay}'), 'ensuring existence of output directory'); + +ensureOutputTargets(); + +console.log(color.blue('{copay}'), 'getting atom-shell release ' + version); + +github.getReleases({ tag_name: version }, function(err, releases) { + + if (err || !releases.length) { + return console.log('Release not found'); + } + + switch (process.platform) { + case 'linux': + var filename = 'atom-shell-' + version + '-' + process.platform + '-x64.zip'; + break; + case 'darwin': + var filename = 'atom-shell-' + version + '-' + process.platform + '-x64.zip'; + break; + case 'win32': + var filename = 'atom-shell-' + version + '-' + process.platform + '-ia32.zip'; + break; + default: + console.log('platform ' + process.platform + ' not supported'); + process.exit(); + } + + console.log(color.blue('{copay}'), 'looking for prebuilt binary ' + filename); + + for (var a = 0; a < releases[0].assets.length; a++) { + var asset = releases[0].assets[a]; + + if (asset.name === filename) { + + console.log(color.blue('{copay}'), 'downloading ' + asset.name); + + var rl = readl.createInterface({ + input: process.stdin, + output: process.stdout + }); + + rl.write(' bytes received: 0'); + + return github.downloadAsset(asset, function(err, inStream) { + if (err) { + console.log(err); + process.exit(); + } + + var bytes = 0; + + inStream.on('data', function(chunk) { + rl.write(null, { ctrl: true, name: 'u' }); + rl.write(' bytes received: ' + (bytes + chunk.length)); + bytes += chunk.length; + }); + + inStream.on('end', function() { + rl.close(); + console.log(''); + console.log(color.blue('{copay}'), 'downloaded!'); + }); + + var out = target; + var tmp = os.tmpDir() + '/atom-shell.zip'; + var outStream = fs.createWriteStream(tmp); + + outStream.on('finish', function() { + console.log(color.blue('{copay}'), 'unzipping archive'); + exec('unzip -o ' + tmp + ' -d ' + out, function(err, stdout, stderr) { + console.log(err || stderr || (color.blue('{copay}') + ' done!')) + }); + }); + + inStream.pipe(outStream); + + }); + } + } + +}); + +function ensureOutputTargets() { + if (!fs.existsSync(target)) fs.mkdirSync(target); + // if (!fs.existsSync(target + process.platform)) { + // fs.mkdirSync(target + process.platform); + // } +}; diff --git a/shell/scripts/launch.js b/shell/scripts/launch.js new file mode 100644 index 000000000..d50522674 --- /dev/null +++ b/shell/scripts/launch.js @@ -0,0 +1,39 @@ +/* +** copay-shell - launch +*/ + +var color = require('cli-color'); +var path = require('path'); +var appPath = path.normalize(__dirname + '/../../'); +var execPath = path.normalize(__dirname + '/../bin/' + process.platform); +var spawn = require('child_process').spawn; + +// update execPath with platform specific binary locations +switch (process.platform) { + case 'linux': + execPath += '/atom'; + break; + case 'darwin': + execPath += '/Atom.app/Contents/MacOS/Atom'; + break; + case 'win32': + execPath += '\\atom.exe' + break; + default: + console.log('Platform not supported.'); + process.exit(); +} + +var copay = spawn(execPath, [appPath]); + +copay.stdout.on('data', function (data) { + console.log(data); +}); + +copay.stderr.on('data', function (data) { + console.log(color.red(data)); +}); + +copay.on('close', function (code) { + console.log('child process exited with code ' + code); +});