Mailspring/internal_packages/settings/lib/apm-wrapper.coffee
Ben Gotow aa10ddfd1c fix(thread-list): Narrow mode, and new selection rules for three-pane
Summary:
Fix bug in apm_wrapper

Refactor model selection so that it's easier to understand the logic for split vs list mode

Test Plan: Run new specs (WIP)

Reviewers: evan

Reviewed By: evan

Differential Revision: https://phab.nylas.com/D1619
2015-06-11 18:00:40 -07:00

264 lines
8.2 KiB
CoffeeScript

_ = require 'underscore'
{BufferedProcess} = require 'nylas-exports'
Q = require 'q'
semver = require 'semver'
module.exports =
class APMWrapper
constructor: ->
@packagePromises = []
runCommand: (args, callback) ->
command = atom.packages.getApmPath()
outputLines = []
stdout = (lines) -> outputLines.push(lines)
errorLines = []
stderr = (lines) -> errorLines.push(lines)
exit = (code) ->
callback(code, outputLines.join('\n'), errorLines.join('\n'))
options =
env:
ATOM_API_URL: 'https://edgehill-packages.nylas.com/api'
ATOM_HOME: atom.getConfigDirPath()
args.push('--no-color')
new BufferedProcess({command, args, stdout, stderr, exit, options})
runCommandReturningPackages: (args, errorMessage, callback) ->
@runCommand args, (code, stdout, stderr) ->
if code is 0
try
packages = JSON.parse(stdout) ? []
catch parseError
error = createJsonParseError(errorMessage, parseError, stdout)
return callback(error)
callback(null, packages)
else
error = new Error(errorMessage)
error.stdout = stdout
error.stderr = stderr
callback(error)
loadInstalled: (callback) ->
args = ['ls', '--json']
errorMessage = 'Fetching local packages failed.'
apmProcess = @runCommandReturningPackages(args, errorMessage, callback)
handleProcessErrors(apmProcess, errorMessage, callback)
loadFeatured: (options, callback) ->
args = ['featured', '--json']
version = atom.getVersion()
args.push('--themes') if options.themes
args.push('--compatible', version) if semver.valid(version)
errorMessage = 'Fetching featured packages failed.'
apmProcess = @runCommandReturningPackages(args, errorMessage, callback)
handleProcessErrors(apmProcess, errorMessage, callback)
loadOutdated: (callback) ->
args = ['outdated', '--json']
version = atom.getVersion()
args.push('--compatible', version) if semver.valid(version)
errorMessage = 'Fetching outdated packages and themes failed.'
apmProcess = @runCommandReturningPackages(args, errorMessage, callback)
handleProcessErrors(apmProcess, errorMessage, callback)
loadPackage: (packageName, callback) ->
args = ['view', packageName, '--json']
errorMessage = "Fetching package '#{packageName}' failed."
apmProcess = @runCommandReturningPackages(args, errorMessage, callback)
handleProcessErrors(apmProcess, errorMessage, callback)
loadCompatiblePackageVersion: (packageName, callback) ->
args = ['view', packageName, '--json', '--compatible', @normalizeVersion(atom.getVersion())]
errorMessage = "Fetching package '#{packageName}' failed."
apmProcess = @runCommandReturningPackages(args, errorMessage, callback)
handleProcessErrors(apmProcess, errorMessage, callback)
getInstalled: ->
Promise.promisify(@loadInstalled, this)()
getFeatured: (options = {}) ->
Promise.promisify(@loadFeatured, this)(options)
getOutdated: ->
Promise.promisify(@loadOutdated, this)()
getPackage: (packageName) ->
@packagePromises[packageName] ?= Promise.promisify(@loadPackage, this, packageName)()
satisfiesVersion: (version, metadata) ->
engine = metadata.engines?.atom ? '*'
return false unless semver.validRange(engine)
return semver.satisfies(version, engine)
normalizeVersion: (version) ->
[version] = version.split('-') if typeof version is 'string'
version
search: (query, options = {}) ->
deferred = Promise.defer()
args = ['search', query, '--json']
if options.themes
args.push '--themes'
else if options.packages
args.push '--packages'
errorMessage = "Searching for \u201C#{query}\u201D failed."
apmProcess = @runCommand args, (code, stdout, stderr) ->
if code is 0
try
packages = JSON.parse(stdout) ? []
deferred.resolve(packages)
catch parseError
error = createJsonParseError(errorMessage, parseError, stdout)
deferred.reject(error)
else
error = new Error(errorMessage)
error.stdout = stdout
error.stderr = stderr
deferred.reject(error)
handleProcessErrors apmProcess, errorMessage, (error) ->
deferred.reject(error)
deferred.promise
update: (pack, newVersion, callback) ->
{name, theme} = pack
if theme
activateOnSuccess = atom.packages.isPackageActive(name)
else
activateOnSuccess = not atom.packages.isPackageDisabled(name)
activateOnFailure = atom.packages.isPackageActive(name)
atom.packages.deactivatePackage(name) if atom.packages.isPackageActive(name)
atom.packages.unloadPackage(name) if atom.packages.isPackageLoaded(name)
errorMessage = "Updating to \u201C#{name}@#{newVersion}\u201D failed."
onError = (error) =>
error.packageInstallError = not theme
callback(error)
args = ['install', "#{name}@#{newVersion}"]
exit = (code, stdout, stderr) =>
if code is 0
if activateOnSuccess
atom.packages.activatePackage(name)
else
atom.packages.loadPackage(name)
callback?()
else
atom.packages.activatePackage(name) if activateOnFailure
error = new Error(errorMessage)
error.stdout = stdout
error.stderr = stderr
onError(error)
apmProcess = @runCommand(args, exit)
handleProcessErrors(apmProcess, errorMessage, onError)
unload: (packageName) ->
if atom.packages.isPackageLoaded(name)
atom.packages.deactivatePackage(name) if atom.packages.isPackageActive(name)
atom.packages.unloadPackage(name)
install: (pack, callback) ->
{name, version, theme} = pack
activateOnSuccess = not theme and not atom.packages.isPackageDisabled(name)
activateOnFailure = atom.packages.isPackageActive(name)
@unload(name)
args = ['install', "#{name}@#{version}"]
errorMessage = "Installing \u201C#{name}@#{version}\u201D failed."
onError = (error) =>
error.packageInstallError = not theme
callback(error)
exit = (code, stdout, stderr) =>
if code is 0
if activateOnSuccess
atom.packages.activatePackage(name)
else
atom.packages.loadPackage(name)
callback?()
else
atom.packages.activatePackage(name) if activateOnFailure
error = new Error(errorMessage)
error.stdout = stdout
error.stderr = stderr
onError(error)
apmProcess = @runCommand(args, exit)
handleProcessErrors(apmProcess, errorMessage, onError)
uninstall: (pack, callback) ->
{name} = pack
atom.packages.deactivatePackage(name) if atom.packages.isPackageActive(name)
errorMessage = "Uninstalling \u201C#{name}\u201D failed."
onError = (error) =>
callback(error)
apmProcess = @runCommand ['uninstall', '--hard', name], (code, stdout, stderr) =>
if code is 0
@unload(name)
callback?()
else
error = new Error(errorMessage)
error.stdout = stdout
error.stderr = stderr
onError(error)
handleProcessErrors(apmProcess, errorMessage, onError)
canUpgrade: (installedPackage, availableVersion) ->
return false unless installedPackage?
installedVersion = installedPackage.metadata.version
return false unless semver.valid(installedVersion)
return false unless semver.valid(availableVersion)
semver.gt(availableVersion, installedVersion)
checkNativeBuildTools: ->
deferred = Promise.defer()
apmProcess = @runCommand ['install', '--check'], (code, stdout, stderr) ->
if code is 0
deferred.resolve()
else
deferred.reject(new Error())
apmProcess.onWillThrowError ({error, handle}) ->
handle()
deferred.reject(error)
deferred.promise
createJsonParseError = (message, parseError, stdout) ->
error = new Error(message)
error.stdout = ''
error.stderr = "#{parseError.message}: #{stdout}"
error
createProcessError = (message, processError) ->
error = new Error(message)
error.stdout = ''
error.stderr = processError.message
error
handleProcessErrors = (apmProcess, message, callback) ->
apmProcess.onWillThrowError ({error, handle}) ->
handle()
callback(createProcessError(message, error))