From 61675c2e77b8adffd3ccf15db62c4ee506cfc73f Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Wed, 15 May 2013 10:28:31 -0700 Subject: [PATCH] Install apm command when Atom starts This changes the command installation to use symlinks instead of copying over the contents of the file. --- spec/app/window-spec.coffee | 17 -------- spec/stdlib/command-installer-spec.coffee | 34 +++++++++++++++ src/app/window.coffee | 27 ++++-------- src/stdlib/command-installer.coffee | 50 +++++++++++++++++++++++ src/stdlib/fs-utils.coffee | 14 ++++++- 5 files changed, 104 insertions(+), 38 deletions(-) create mode 100644 spec/stdlib/command-installer-spec.coffee create mode 100644 src/stdlib/command-installer.coffee diff --git a/spec/app/window-spec.coffee b/spec/app/window-spec.coffee index 989face89..1f3e69ca7 100644 --- a/spec/app/window-spec.coffee +++ b/spec/app/window-spec.coffee @@ -154,23 +154,6 @@ describe "Window", -> window.unloadEditorWindow() expect(atom.saveWindowState.callCount).toBe 1 - describe ".installAtomCommand(commandPath)", -> - commandPath = '/tmp/installed-atom-command/atom' - - afterEach -> - fsUtils.remove(commandPath) if fsUtils.exists(commandPath) - - describe "when the command path doesn't exist", -> - it "copies atom.sh to the specified path", -> - expect(fsUtils.exists(commandPath)).toBeFalsy() - window.installAtomCommand(commandPath) - - waitsFor -> - fsUtils.exists(commandPath) - - runs -> - expect(fsUtils.read(commandPath).length).toBeGreaterThan 1 - describe ".deserialize(state)", -> class Foo @deserialize: ({name}) -> new Foo(name) diff --git a/spec/stdlib/command-installer-spec.coffee b/spec/stdlib/command-installer-spec.coffee new file mode 100644 index 000000000..47aa100ad --- /dev/null +++ b/spec/stdlib/command-installer-spec.coffee @@ -0,0 +1,34 @@ +fs = require 'fs' +fsUtils = require 'fs-utils' +installer = require 'command-installer' + +describe "install(commandPath, callback)", -> + directory = '/tmp/install-atom-command/atom' + commandPath = "#{directory}/source" + destinationPath = "#{directory}/bin/source" + + beforeEach -> + spyOn(installer, 'findInstallDirectory').andCallFake (callback) -> + callback(directory) + + fsUtils.remove(directory) if fsUtils.exists(directory) + + it "symlinks the command and makes it executable", -> + fsUtils.write(commandPath, 'test') + expect(fsUtils.isFile(commandPath)).toBeTruthy() + expect(fsUtils.isExecutable(commandPath)).toBeFalsy() + expect(fsUtils.isFile(destinationPath)).toBeFalsy() + + installDone = false + installError = null + installer.install commandPath, (error) -> + installDone = true + installError = error + + waitsFor -> installDone + + runs -> + expect(installError).toBeNull() + expect(fsUtils.isFile(destinationPath)).toBeTruthy() + expect(fs.realpathSync(destinationPath)).toBe fs.realpathSync(commandPath) + expect(fsUtils.isExecutable(destinationPath)).toBeTruthy() diff --git a/src/app/window.coffee b/src/app/window.coffee index ebedffe5d..e6a04b1bc 100644 --- a/src/app/window.coffee +++ b/src/app/window.coffee @@ -35,11 +35,8 @@ window.setUpEnvironment = -> # This method is only called when opening a real application window window.startEditorWindow = -> - directory = _.find ['/opt/boxen', '/opt/github', '/usr/local'], (dir) -> fsUtils.isDirectory(dir) - if directory - installAtomCommand(fsUtils.join(directory, 'bin/atom')) - else - console.warn "Failed to install `atom` binary" + installAtomCommand() + installApmCommand() atom.windowMode = 'editor' handleEvents() @@ -85,21 +82,13 @@ window.unloadEditorWindow = -> window.project = null window.git = null -window.installAtomCommand = (commandPath, done) -> - fs.exists commandPath, (exists) -> - return if exists +window.installAtomCommand = (callback) -> + commandPath = fsUtils.join(window.resourcePath, 'atom.sh') + require('command-installer').install(commandPath, callback) - bundledCommandPath = fsUtils.resolve(window.resourcePath, 'atom.sh') - if bundledCommandPath? - fs.readFile bundledCommandPath, (error, data) -> - if error? - console.warn "Failed to install `atom` binary", error - else - fsUtils.writeAsync commandPath, data, (error) -> - if error? - console.warn "Failed to install `atom` binary", error - else - fs.chmod(commandPath, 0o755, commandPath) +window.installApmCommand = (callback) -> + commandPath = fsUtils.join(window.resourcePath, 'node_modules', '.bin', 'apm') + require('command-installer').install(commandPath, callback) window.unloadConfigWindow = -> return if not configView diff --git a/src/stdlib/command-installer.coffee b/src/stdlib/command-installer.coffee new file mode 100644 index 000000000..7bc6e6120 --- /dev/null +++ b/src/stdlib/command-installer.coffee @@ -0,0 +1,50 @@ +path = require 'path' +fs = require 'fs' +_ = require 'underscore' +async = require 'async' +mkdirp = require 'mkdirp' +fsUtils = require 'fs-utils' + +symlinkCommand = (sourcePath, destinationPath, callback) -> + mkdirp fsUtils.directory(destinationPath), (error) -> + if error? + callback(error) + else + fs.symlink sourcePath, destinationPath, (error) -> + if error? + callback(error) + else + fs.chmod(destinationPath, 0o755, callback) + +unlinkCommand = (destinationPath, callback) -> + fs.exists destinationPath, (exists) -> + if exists + fs.unlink(destinationPath, callback) + else + callback() + +module.exports = + findInstallDirectory: (callback) -> + directories = ['/opt/boxen','/opt/github','/usr/local'] + async.detect(directories, fsUtils.isDirectoryAsync, callback) + + install: (commandPath, commandName, callback) -> + if not commandName? or _.isFunction(commandName) + callback = commandName + commandName = path.basename(commandPath, path.extname(commandPath)) + + installCallback = (error) -> + if error? + console.warn "Failed to install `#{commandName}` binary", error + callback?(error) + + @findInstallDirectory (directory) -> + if directory? + destinationPath = path.join(directory, 'bin', commandName) + unlinkCommand destinationPath, (error) -> + if error? + installCallback(error) + else + symlinkCommand(commandPath, destinationPath, installCallback) + else + installCallback(new Error("No destination directory exists to install")) diff --git a/src/stdlib/fs-utils.coffee b/src/stdlib/fs-utils.coffee index 4539751d6..139fb32d9 100644 --- a/src/stdlib/fs-utils.coffee +++ b/src/stdlib/fs-utils.coffee @@ -73,8 +73,11 @@ module.exports = return done(false) unless path?.length > 0 fs.exists path, (exists) -> if exists - fs.stat path, (err, stat) -> - done(stat?.isDirectory() ? false) + fs.stat path, (error, stat) -> + if error? + done(false) + else + done(stat.isDirectory()) else done(false) @@ -87,6 +90,13 @@ module.exports = catch e false + # Returns true if the specified path is exectuable. + isExecutable: (path) -> + try + (fs.statSync(path).mode & 0o777 & 1) isnt 0 + catch e + false + # Returns an array with all the names of files contained # in the directory path. list: (rootPath, extensions) ->