{"action":"created","comment":{"url":"https://api.github.com/repos/nteract/nteract/pulls/comments/83563251","pull_request_review_id":4399312,"id":83563251,"diff_hunk":"@@ -1,102 +1,109 @@\n+import Rx from 'rxjs/Rx';\n import { join } from 'path';\n import { dialog } from 'electron';\n-import { existsSync, writeFile, chmod, mkdirSync } from 'fs';\n-import { exec } from 'child_process';\n+import { spawn } from 'spawn-rx';\n+// We probably want to move this function\n+import { writeFileObservable } from '../notebook/epics/config';\n \n+const fs = require('fs');\n \n-function getStartCommand() {\n+const getStartCommand = () => {\n   const subdir = (process.platform === 'darwin') ? 'MacOS' : '';\n   const ext = (process.platform === 'win32') ? '.exe' : '';\n+  const win = (process.platform === 'win32') ? 'win' : '';\n   const dir = join(process.resourcesPath, '..', subdir);\n \n   const nteractPath = join(dir, `nteract${ext}`);\n   const electronPath = join(dir, `electron${ext}`);\n \n-  if (existsSync(nteractPath)) {\n-    return [nteractPath, dir];\n-  } else if (existsSync(electronPath)) {\n+  if (fs.existsSync(nteractPath)) {\n+    return [nteractPath, '', join(dir, 'bin', win)];\n+  } else if (fs.existsSync(electronPath)) {\n     // Developer install\n-    const mainDir = join(dir.split('node_modules')[0], 'app');\n-    return [`${electronPath} ${mainDir}`, mainDir];\n+    const rootDir = dir.split('node_modules')[0];\n+    return [electronPath, join(rootDir, 'app'), join(rootDir, 'bin', win)];\n   }\n-  return [null, null];\n-}\n+  return [null, null, null];\n+};\n \n-function writeExecutable(installDir, script, callback) {\n-  writeFile(installDir, script, (err) => {\n-    if (err) {\n-      callback(err);\n-    } else {\n-      chmod(installDir, '755', (error) => {\n-        callback(error);\n-      });\n-    }\n+const createNewSymlinkObservable = (target, path) =>\n+  Rx.Observable.create(observer => {\n+    fs.symlink(target, path, (error) => {\n+      if (error) {\n+        observer.error(error);\n+      } else {\n+        observer.next({ target, path });\n+        observer.complete();\n+      }\n+    });\n   });\n-}\n-\n-function showDialog(error) {\n-  if (error) {\n-    dialog.showErrorBox(\n-      'Could not write shell script.',\n-      error.message\n-    );\n-  } else {\n-    dialog.showErrorBox(\n-      'Command installed.',\n-      'The shell command \"nteract\" is installed.\\nOpen notebooks with \"nteract notebook.ipynb\".'\n-    );\n-  }\n-}\n \n-function writeWinExecutable(mainDir, bashScript, winScript, callback) {\n-  const binDir = join(mainDir, 'bin');\n-  if (!existsSync(binDir)) {\n-    mkdirSync(binDir);\n-  }\n-  writeExecutable(join(binDir, 'nteract'), bashScript, (err) => {\n-    if (err) {\n-      callback(err);\n-    } else {\n-      writeExecutable(join(binDir, 'nteract.cmd'), winScript, (error) => {\n+const unlinkObservable = (path) =>\n+  Rx.Observable.create(observer => {\n+    if (fs.existsSync(path)) {\n+      fs.unlink(path, (error) => {\n         if (error) {\n-          callback(error);\n+          observer.error(error);\n         } else {\n-          // Remove duplicates because SETX throws a error if path is to long\n-          const env = process.env.PATH.split(';')\n-            .filter(item => !/nteract/.test(item))\n-            .filter((item, index, array) => array.indexOf(item) === index);\n-          env.push(binDir);\n-          const envPath = env.join(';');\n-          exec(`SETX PATH \"${envPath}`, (e, stdout, stderr) => {\n-            callback(e || stderr);\n-          });\n+          observer.next({ path });\n+          observer.complete();\n         }\n       });\n+    } else {\n+      observer.next({ path });\n+      observer.complete();\n     }\n   });\n-}\n \n-export function installShellCommand() {\n-  const [shellCommands, mainDir] = getStartCommand();\n-  const script = `#!/bin/bash\\nnohup ${shellCommands.replace(/\\\\/g, '/')} \"$@\" > /dev/null 2>&1 &`;\n-  if (!shellCommands) {\n+const setWinPathObservable = (exe, rootDir, binDir) => {\n+  // Remove duplicates because SETX throws a error if path is to long\n+  const env = process.env.PATH.split(';')\n+    .filter(item => !/nteract/.test(item))\n+    .filter((item, index, array) => array.indexOf(item) === index);\n+  env.push(binDir);\n+  const envPath = env.join(';');\n+  return spawn('SETX', ['PATH', `${envPath}`])\n+    .switchMap(() => spawn('SETX', ['NTERACT_EXE', exe]))\n+    .switchMap(() => spawn('SETX', ['NTERACT_DIR', rootDir]));\n+};\n+\n+const createSymlinkObservable = (target, path) =>\n+  unlinkObservable(path)\n+    .switchMap(() => createNewSymlinkObservable(target, path));\n+\n+const installShellCommandsObservable = (exe, rootDir, binDir) => {\n+  if (process.platform === 'win32') {\n+    return setWinPathObservable(exe, rootDir, binDir);\n+  }\n+  const envFile = join(binDir, 'nteract-env');\n+  return writeFileObservable(envFile, `NTERACT_EXE=\"${exe}\"\\nNTERACT_DIR=\"${rootDir}\"`)\n+    .switchMap(() => {\n+      const target = join(binDir, 'nteract.sh');\n+      return createSymlinkObservable(target, '/usr/local/bin/nteract')\n+        .catch(() => {\n+          const dest = join(process.env.HOME, '.local/bin/nteract');\n+          return createSymlinkObservable(target, dest);\n+        });\n+    });\n+};\n+\n+export const installShellCommand = () => {\n+  const [exe, rootDir, binDir] = getStartCommand();\n+  if (!exe) {\n     dialog.showErrorBox(\n       'nteract application not found.',\n       'Could not locate nteract executable.'\n     );\n+    return;\n   }\n-  if (process.platform === 'win32') {\n-    const winScript = `@echo off\\n${shellCommands} %*`;\n-    writeWinExecutable(mainDir, script, winScript, showDialog);\n-  } else {\n-    const installDir = join('/usr/local/bin', 'nteract');\n-    const installDirUbuntu = join(process.env.HOME, '.local', 'bin', 'nteract');\n-    writeExecutable(installDir, script, (err) => {\n-      if (err) {\n-        writeExecutable(installDirUbuntu, script, showDialog);\n-      } else {\n-        showDialog(null);\n-      }\n-    });\n-  }\n-}\n+\n+  installShellCommandsObservable(exe, rootDir, binDir)\n+    .subscribe(\n+      () => {},\n+      (err) => dialog.showErrorBox('Could not write shell script.', err.message),\n+      () => dialog.showErrorBox(\n+        'Command installed.',\n+        'The shell command \"nteract\" is installed.\\nOpen notebooks with \"nteract notebook.ipynb\".'\n+        )","path":"src/main/cli.js","position":181,"original_position":181,"commit_id":"4db10471250ee6d320d5d5073a686542b1dbae16","original_commit_id":"4db10471250ee6d320d5d5073a686542b1dbae16","user":{"login":"rgbkrk","id":836375,"avatar_url":"https://avatars.githubusercontent.com/u/836375?v=3","gravatar_id":"","url":"https://api.github.com/users/rgbkrk","html_url":"https://github.com/rgbkrk","followers_url":"https://api.github.com/users/rgbkrk/followers","following_url":"https://api.github.com/users/rgbkrk/following{/other_user}","gists_url":"https://api.github.com/users/rgbkrk/gists{/gist_id}","starred_url":"https://api.github.com/users/rgbkrk/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rgbkrk/subscriptions","organizations_url":"https://api.github.com/users/rgbkrk/orgs","repos_url":"https://api.github.com/users/rgbkrk/repos","events_url":"https://api.github.com/users/rgbkrk/events{/privacy}","received_events_url":"https://api.github.com/users/rgbkrk/received_events","type":"User","site_admin":false},"body":"This is awesome, well done.","created_at":"2016-10-16T23:27:06Z","updated_at":"2016-10-16T23:27:06Z","html_url":"https://github.com/nteract/nteract/pull/1028#discussion_r83563251","pull_request_url":"https://api.github.com/repos/nteract/nteract/pulls/1028","_links":{"self":{"href":"https://api.github.com/repos/nteract/nteract/pulls/comments/83563251"},"html":{"href":"https://github.com/nteract/nteract/pull/1028#discussion_r83563251"},"pull_request":{"href":"https://api.github.com/repos/nteract/nteract/pulls/1028"}}},"pull_request":{"url":"https://api.github.com/repos/nteract/nteract/pulls/1028","id":89532878,"html_url":"https://github.com/nteract/nteract/pull/1028","diff_url":"https://github.com/nteract/nteract/pull/1028.diff","patch_url":"https://github.com/nteract/nteract/pull/1028.patch","issue_url":"https://api.github.com/repos/nteract/nteract/issues/1028","number":1028,"state":"open","locked":false,"title":"[WIP] Better cli interface","user":{"login":"lgeiger","id":13285808,"avatar_url":"https://avatars.githubusercontent.com/u/13285808?v=3","gravatar_id":"","url":"https://api.github.com/users/lgeiger","html_url":"https://github.com/lgeiger","followers_url":"https://api.github.com/users/lgeiger/followers","following_url":"https://api.github.com/users/lgeiger/following{/other_user}","gists_url":"https://api.github.com/users/lgeiger/gists{/gist_id}","starred_url":"https://api.github.com/users/lgeiger/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lgeiger/subscriptions","organizations_url":"https://api.github.com/users/lgeiger/orgs","repos_url":"https://api.github.com/users/lgeiger/repos","events_url":"https://api.github.com/users/lgeiger/events{/privacy}","received_events_url":"https://api.github.com/users/lgeiger/received_events","type":"User","site_admin":false},"body":"This is heavily inspired by Atom.\r\n\r\nOS X works, Linux should as well.\r\nRun \"Install Shell Comands\" from the nteract menu then try `nteract --help` in a new terminal.\r\n\r\nWindows is a bit tricky since it doesn't has a `nohup` command for detaching the process from the terminal. Atom uses it's bundled node to start Atom from javascript.\r\n\r\nMaybe there is a way of using Electrons node from the command line. I'll try this approach on windows. Lets see if it works.","created_at":"2016-10-16T17:43:43Z","updated_at":"2016-10-16T23:27:06Z","closed_at":null,"merged_at":null,"merge_commit_sha":"7bf1a907bc96b61024fa257e4048db7dce451b4a","assignee":null,"assignees":[],"milestone":null,"commits_url":"https://api.github.com/repos/nteract/nteract/pulls/1028/commits","review_comments_url":"https://api.github.com/repos/nteract/nteract/pulls/1028/comments","review_comment_url":"https://api.github.com/repos/nteract/nteract/pulls/comments{/number}","comments_url":"https://api.github.com/repos/nteract/nteract/issues/1028/comments","statuses_url":"https://api.github.com/repos/nteract/nteract/statuses/4db10471250ee6d320d5d5073a686542b1dbae16","head":{"label":"lgeiger:better-cli","ref":"better-cli","sha":"4db10471250ee6d320d5d5073a686542b1dbae16","user":{"login":"lgeiger","id":13285808,"avatar_url":"https://avatars.githubusercontent.com/u/13285808?v=3","gravatar_id":"","url":"https://api.github.com/users/lgeiger","html_url":"https://github.com/lgeiger","followers_url":"https://api.github.com/users/lgeiger/followers","following_url":"https://api.github.com/users/lgeiger/following{/other_user}","gists_url":"https://api.github.com/users/lgeiger/gists{/gist_id}","starred_url":"https://api.github.com/users/lgeiger/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lgeiger/subscriptions","organizations_url":"https://api.github.com/users/lgeiger/orgs","repos_url":"https://api.github.com/users/lgeiger/repos","events_url":"https://api.github.com/users/lgeiger/events{/privacy}","received_events_url":"https://api.github.com/users/lgeiger/received_events","type":"User","site_admin":false},"repo":{"id":69985108,"name":"nteract","full_name":"lgeiger/nteract","owner":{"login":"lgeiger","id":13285808,"avatar_url":"https://avatars.githubusercontent.com/u/13285808?v=3","gravatar_id":"","url":"https://api.github.com/users/lgeiger","html_url":"https://github.com/lgeiger","followers_url":"https://api.github.com/users/lgeiger/followers","following_url":"https://api.github.com/users/lgeiger/following{/other_user}","gists_url":"https://api.github.com/users/lgeiger/gists{/gist_id}","starred_url":"https://api.github.com/users/lgeiger/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lgeiger/subscriptions","organizations_url":"https://api.github.com/users/lgeiger/orgs","repos_url":"https://api.github.com/users/lgeiger/repos","events_url":"https://api.github.com/users/lgeiger/events{/privacy}","received_events_url":"https://api.github.com/users/lgeiger/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/lgeiger/nteract","description":":notebook: Interactive literate coding notebook!","fork":true,"url":"https://api.github.com/repos/lgeiger/nteract","forks_url":"https://api.github.com/repos/lgeiger/nteract/forks","keys_url":"https://api.github.com/repos/lgeiger/nteract/keys{/key_id}","collaborators_url":"https://api.github.com/repos/lgeiger/nteract/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/lgeiger/nteract/teams","hooks_url":"https://api.github.com/repos/lgeiger/nteract/hooks","issue_events_url":"https://api.github.com/repos/lgeiger/nteract/issues/events{/number}","events_url":"https://api.github.com/repos/lgeiger/nteract/events","assignees_url":"https://api.github.com/repos/lgeiger/nteract/assignees{/user}","branches_url":"https://api.github.com/repos/lgeiger/nteract/branches{/branch}","tags_url":"https://api.github.com/repos/lgeiger/nteract/tags","blobs_url":"https://api.github.com/repos/lgeiger/nteract/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/lgeiger/nteract/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/lgeiger/nteract/git/refs{/sha}","trees_url":"https://api.github.com/repos/lgeiger/nteract/git/trees{/sha}","statuses_url":"https://api.github.com/repos/lgeiger/nteract/statuses/{sha}","languages_url":"https://api.github.com/repos/lgeiger/nteract/languages","stargazers_url":"https://api.github.com/repos/lgeiger/nteract/stargazers","contributors_url":"https://api.github.com/repos/lgeiger/nteract/contributors","subscribers_url":"https://api.github.com/repos/lgeiger/nteract/subscribers","subscription_url":"https://api.github.com/repos/lgeiger/nteract/subscription","commits_url":"https://api.github.com/repos/lgeiger/nteract/commits{/sha}","git_commits_url":"https://api.github.com/repos/lgeiger/nteract/git/commits{/sha}","comments_url":"https://api.github.com/repos/lgeiger/nteract/comments{/number}","issue_comment_url":"https://api.github.com/repos/lgeiger/nteract/issues/comments{/number}","contents_url":"https://api.github.com/repos/lgeiger/nteract/contents/{+path}","compare_url":"https://api.github.com/repos/lgeiger/nteract/compare/{base}...{head}","merges_url":"https://api.github.com/repos/lgeiger/nteract/merges","archive_url":"https://api.github.com/repos/lgeiger/nteract/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/lgeiger/nteract/downloads","issues_url":"https://api.github.com/repos/lgeiger/nteract/issues{/number}","pulls_url":"https://api.github.com/repos/lgeiger/nteract/pulls{/number}","milestones_url":"https://api.github.com/repos/lgeiger/nteract/milestones{/number}","notifications_url":"https://api.github.com/repos/lgeiger/nteract/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/lgeiger/nteract/labels{/name}","releases_url":"https://api.github.com/repos/lgeiger/nteract/releases{/id}","deployments_url":"https://api.github.com/repos/lgeiger/nteract/deployments","created_at":"2016-10-04T16:40:31Z","updated_at":"2016-10-04T16:40:32Z","pushed_at":"2016-10-16T23:09:46Z","git_url":"git://github.com/lgeiger/nteract.git","ssh_url":"git@github.com:lgeiger/nteract.git","clone_url":"https://github.com/lgeiger/nteract.git","svn_url":"https://github.com/lgeiger/nteract","homepage":"","size":5004,"stargazers_count":0,"watchers_count":0,"language":"JavaScript","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"nteract:master","ref":"master","sha":"7d86163a1a147dd5673a9d5abf208efbef84d414","user":{"login":"nteract","id":12401040,"avatar_url":"https://avatars.githubusercontent.com/u/12401040?v=3","gravatar_id":"","url":"https://api.github.com/users/nteract","html_url":"https://github.com/nteract","followers_url":"https://api.github.com/users/nteract/followers","following_url":"https://api.github.com/users/nteract/following{/other_user}","gists_url":"https://api.github.com/users/nteract/gists{/gist_id}","starred_url":"https://api.github.com/users/nteract/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nteract/subscriptions","organizations_url":"https://api.github.com/users/nteract/orgs","repos_url":"https://api.github.com/users/nteract/repos","events_url":"https://api.github.com/users/nteract/events{/privacy}","received_events_url":"https://api.github.com/users/nteract/received_events","type":"Organization","site_admin":false},"repo":{"id":37496521,"name":"nteract","full_name":"nteract/nteract","owner":{"login":"nteract","id":12401040,"avatar_url":"https://avatars.githubusercontent.com/u/12401040?v=3","gravatar_id":"","url":"https://api.github.com/users/nteract","html_url":"https://github.com/nteract","followers_url":"https://api.github.com/users/nteract/followers","following_url":"https://api.github.com/users/nteract/following{/other_user}","gists_url":"https://api.github.com/users/nteract/gists{/gist_id}","starred_url":"https://api.github.com/users/nteract/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nteract/subscriptions","organizations_url":"https://api.github.com/users/nteract/orgs","repos_url":"https://api.github.com/users/nteract/repos","events_url":"https://api.github.com/users/nteract/events{/privacy}","received_events_url":"https://api.github.com/users/nteract/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/nteract/nteract","description":":notebook: Interactive literate coding notebook!","fork":false,"url":"https://api.github.com/repos/nteract/nteract","forks_url":"https://api.github.com/repos/nteract/nteract/forks","keys_url":"https://api.github.com/repos/nteract/nteract/keys{/key_id}","collaborators_url":"https://api.github.com/repos/nteract/nteract/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/nteract/nteract/teams","hooks_url":"https://api.github.com/repos/nteract/nteract/hooks","issue_events_url":"https://api.github.com/repos/nteract/nteract/issues/events{/number}","events_url":"https://api.github.com/repos/nteract/nteract/events","assignees_url":"https://api.github.com/repos/nteract/nteract/assignees{/user}","branches_url":"https://api.github.com/repos/nteract/nteract/branches{/branch}","tags_url":"https://api.github.com/repos/nteract/nteract/tags","blobs_url":"https://api.github.com/repos/nteract/nteract/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/nteract/nteract/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/nteract/nteract/git/refs{/sha}","trees_url":"https://api.github.com/repos/nteract/nteract/git/trees{/sha}","statuses_url":"https://api.github.com/repos/nteract/nteract/statuses/{sha}","languages_url":"https://api.github.com/repos/nteract/nteract/languages","stargazers_url":"https://api.github.com/repos/nteract/nteract/stargazers","contributors_url":"https://api.github.com/repos/nteract/nteract/contributors","subscribers_url":"https://api.github.com/repos/nteract/nteract/subscribers","subscription_url":"https://api.github.com/repos/nteract/nteract/subscription","commits_url":"https://api.github.com/repos/nteract/nteract/commits{/sha}","git_commits_url":"https://api.github.com/repos/nteract/nteract/git/commits{/sha}","comments_url":"https://api.github.com/repos/nteract/nteract/comments{/number}","issue_comment_url":"https://api.github.com/repos/nteract/nteract/issues/comments{/number}","contents_url":"https://api.github.com/repos/nteract/nteract/contents/{+path}","compare_url":"https://api.github.com/repos/nteract/nteract/compare/{base}...{head}","merges_url":"https://api.github.com/repos/nteract/nteract/merges","archive_url":"https://api.github.com/repos/nteract/nteract/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/nteract/nteract/downloads","issues_url":"https://api.github.com/repos/nteract/nteract/issues{/number}","pulls_url":"https://api.github.com/repos/nteract/nteract/pulls{/number}","milestones_url":"https://api.github.com/repos/nteract/nteract/milestones{/number}","notifications_url":"https://api.github.com/repos/nteract/nteract/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/nteract/nteract/labels{/name}","releases_url":"https://api.github.com/repos/nteract/nteract/releases{/id}","deployments_url":"https://api.github.com/repos/nteract/nteract/deployments","created_at":"2015-06-15T23:21:49Z","updated_at":"2016-10-15T15:42:52Z","pushed_at":"2016-10-16T23:09:46Z","git_url":"git://github.com/nteract/nteract.git","ssh_url":"git@github.com:nteract/nteract.git","clone_url":"https://github.com/nteract/nteract.git","svn_url":"https://github.com/nteract/nteract","homepage":"","size":5260,"stargazers_count":452,"watchers_count":452,"language":"JavaScript","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":64,"mirror_url":null,"open_issues_count":80,"forks":64,"open_issues":80,"watchers":452,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/nteract/nteract/pulls/1028"},"html":{"href":"https://github.com/nteract/nteract/pull/1028"},"issue":{"href":"https://api.github.com/repos/nteract/nteract/issues/1028"},"comments":{"href":"https://api.github.com/repos/nteract/nteract/issues/1028/comments"},"review_comments":{"href":"https://api.github.com/repos/nteract/nteract/pulls/1028/comments"},"review_comment":{"href":"https://api.github.com/repos/nteract/nteract/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/nteract/nteract/pulls/1028/commits"},"statuses":{"href":"https://api.github.com/repos/nteract/nteract/statuses/4db10471250ee6d320d5d5073a686542b1dbae16"}}}}
{"actor":{"display_login":"rgbkrk"}}