From ce503cb80ac6a1ee1aebfd34d5de3b3c2fa159cf Mon Sep 17 00:00:00 2001 From: Glavin Wiechert Date: Mon, 8 Jun 2015 11:20:52 -0300 Subject: [PATCH] Retrieve/use absolute path of executable within Beautifier::run See #375. Will help with debugging Windows CI --- package.json | 1 + src/beautifiers/beautifier.coffee | 153 ++++++++++++++++++------------ 2 files changed, 91 insertions(+), 63 deletions(-) diff --git a/package.json b/package.json index 468b02c..878ad91 100644 --- a/package.json +++ b/package.json @@ -79,6 +79,7 @@ "tidy-markdown": "^0.3.2", "typescript-formatter": "~0.3.2", "underscore-plus": "^1.6.6", + "which": "^1.1.1", "winston": "^1.0.0", "yaml-front-matter": "^3.2.3" }, diff --git a/src/beautifiers/beautifier.coffee b/src/beautifiers/beautifier.coffee index f1c1f5b..67c8d31 100644 --- a/src/beautifiers/beautifier.coffee +++ b/src/beautifiers/beautifier.coffee @@ -5,11 +5,12 @@ temp = require("temp").track() exec = require("child_process").exec spawn = require("child_process").spawn readFile = Promise.promisify(fs.readFile) +which = require('which') module.exports = class Beautifier ### - + Promise ### Promise: Promise @@ -132,77 +133,103 @@ module.exports = class Beautifier resolve(environment) ) + ### + Like the unix which utility. + + Finds the first instance of a specified executable in the PATH environment variable. + Does not cache the results, + so hash -r is not needed when the PATH changes. + See https://github.com/isaacs/node-which + ### + which: (exe, options = {}) -> + # Get PATH and other environment variables + @getShellEnvironment() + .then((env) -> + new Promise((resolve, reject) -> + options.path ?= env.PATH + which(exe, options, (err, path) -> + resolve(exe) if err + resolve(path) + ) + ) + ) + ### Run command-line interface command ### run: (executable, args, {ignoreReturnCode, help} = {}) -> - # Resolve executable - Promise.resolve(executable) - .then((exe) => - # Flatten args first - args = _.flatten(args) - # Resolve all args - Promise.all(args) - .then((args) => - return new Promise((resolve, reject) => - # Remove undefined/null values - args = _.without(args, undefined) - args = _.without(args, null) - # Get PATH and other environment variables - @getShellEnvironment() - .then((env) => - # Spawn command - stdout = "" - stderr = "" - options = { - env: env - } - @debug('spawn', exe, args) - cmd = spawn(exe, args, options) - # add a 'data' event listener for the spawn instance - cmd.stdout.on('data', (data) -> stdout += data ) - cmd.stderr.on('data', (data) -> stderr += data ) - # when the spawn child process exits, check if there were any errors and close the writeable stream - cmd.on('exit', (returnCode) => - @debug('spawn done', returnCode, stderr, stdout) - # If return code is not 0 then error occured - if not ignoreReturnCode and returnCode isnt 0 - reject(stderr) - else - resolve(stdout) - ) - cmd.on('error', (err) => - @debug('error', err) - # Check if error is ENOENT (command could not be found) - if err.code is 'ENOENT' or err.errno is 'ENOENT' - # Create new improved error - # notify user that it may not be installed or in path - message = "Could not find '#{exe}'. The program may not be installed." - er = new Error(message) # +(if help then "\n\n#{help}" else "") - if help? - if typeof help is "object" - helpStr = "See #{help.link} for program installation instructions.\n" - helpStr += "You can configure Atom Beautify with the absolute path \ - to '#{help.program or exe}' by setting '#{help.pathOption}' in \ - the Atom Beautify package settings.\n" if help.pathOption - helpStr += help.additional if help.additional - er.description = helpStr - else #if typeof help is "string" - er.description = help - er.code = 'CommandNotFound' - er.errno = er.code - er.syscall = 'beautifier::run' - er.file = exe - reject(er) - else - # continue as normal error - reject(err) - ) + # Flatten args first + args = _.flatten(args) + # Resolve executable and all args + Promise.all([executable, Promise.all(args)]) + .then(([exeName, args]) => + @debug('exeName, args:', exeName, args) + return new Promise((resolve, reject) => + # Remove undefined/null values + args = _.without(args, undefined) + args = _.without(args, null) + # Get PATH and other environment variables + Promise.all([@getShellEnvironment(), @which(exeName)]) + .then(([env, exePath]) => + @debug('exePath, env:', exePath, env) + exe = exePath ? exeName + # Spawn command + stdout = "" + stderr = "" + options = { + env: env + } + @debug('spawn', exe, args) + cmd = spawn(exe, args, options) + # add a 'data' event listener for the spawn instance + cmd.stdout.on('data', (data) -> stdout += data ) + cmd.stderr.on('data', (data) -> stderr += data ) + # when the spawn child process exits, + # check if there were any errors and + # close the writeable stream + cmd.on('exit', (returnCode) => + @debug('spawn done', returnCode, stderr, stdout) + # If return code is not 0 then error occured + if not ignoreReturnCode and returnCode isnt 0 + reject(stderr) + else + resolve(stdout) + ) + cmd.on('error', (err) => + @debug('error', err) + # Check if error is ENOENT + # (command could not be found) + if err.code is 'ENOENT' or err.errno is 'ENOENT' + # Create new improved error + # notify user that it may not be + # installed or in path + message = "Could not find '#{exe}'. \ + The program may not be installed." + er = new Error(message) + if help? + if typeof help is "object" + helpStr = "See #{help.link} for program installation instructions.\n" + helpStr += "You can configure Atom Beautify with the absolute path \ + to '#{help.program or exe}' by setting '#{help.pathOption}' in \ + the Atom Beautify package settings.\n" if help.pathOption + helpStr += help.additional if help.additional + er.description = helpStr + else #if typeof help is "string" + er.description = help + er.code = 'CommandNotFound' + er.errno = er.code + er.syscall = 'beautifier::run' + er.file = exe + reject(er) + else + # continue as normal error + reject(err) ) ) ) ) + ### Logger instance ###