Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Uninstall packages not listed in packages.cson #22

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ Where the contents of the array is a list of packages to ensure are installed.

* `createOnChange` — Create the package list when packages are installed or removed via the Atom settings. You must restart Atom after installing Package Sync for this setting to take effect, and it works best when paired with the `forceOverwrite` setting.
* `forceOverwrite` — Forces the `create-package-list` command to overwrite the `packages.cson` if it exists.
* `uninstall` — Uninstall packages missing from the `package.cson` on sync.

### Commands

Expand Down
5 changes: 5 additions & 0 deletions lib/index.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,8 @@ module.exports =
description: 'Create package list when packages are installed or removed.'
type: 'boolean'
default: false
uninstall:
title: 'Uninstall packages on sync'
description: 'Uninstall packages not listed in packages.cson on sync.'
type: 'boolean'
default: false
57 changes: 45 additions & 12 deletions lib/package-sync.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ class PackageSync
# Internal: Packages in the process of being installed.
packagesToInstall: []

# Internal: Packages in the process of being uninstalled.
packagesToUninstall: []

# Internal: Timeout for messages that should be up for only a short time.
shortMessageTimeout: 1000

Expand All @@ -41,11 +44,16 @@ class PackageSync
openPackageList: ->
atom.workspace.open(PackageList.getPackageListPath())

# Public: Installs any packages that are missing from the `packages.cson` configuration file.
# Public: Installs any packages that are missing from the `packages.cson` configuration file,
# and optionally uninstalls any packages that are installed but not in `packages.cson`.
sync: ->
missing = @getMissingPackages()
missing = @getMissingPackages(false)
@installPackages(missing)

if atom.config.get('package-sync.uninstall')
uninstall = @getMissingPackages(true)
@uninstallPackages(uninstall)

# Internal: Displays a message in the status bar.
#
# If `timeout` is specified, the message will automatically be cleared in `timeout` milliseconds.
Expand All @@ -65,20 +73,20 @@ class PackageSync
# Internal: Execute APM to install the given package.
#
# pkg - A {String} containing the name of the package to install.
executeApm: (pkg) ->
@displayMessage("Installing #{pkg}")
executeApm: (action, pkg) ->
@displayMessage("#{if action == "install" then "Install" else "Uninstall"}ing #{pkg}")
command = @apmPath
args = ['install', pkg]
args = [action, pkg]
stdout = (output) ->
stderr = (output) ->
exit = (exitCode) =>
if exitCode is 0
if @packagesToInstall.length > 0
@displayMessage("#{pkg} installed!", @shortMessageTimeout)
if @packagesToInstall.length > 0 or @packagesToUninstall.length > 0
@displayMessage("#{pkg} #{action}ed!", @shortMessageTimeout)
else
@displayMessage('Package Sync complete!', @longMessageTimeout)
else
@displayMessage("An error occurred installing #{pkg}", @longMessageTimeout)
@displayMessage("An error occurred #{action}ing #{pkg}", @longMessageTimeout)

@currentInstall = null
@installPackage()
Expand All @@ -87,19 +95,30 @@ class PackageSync

# Internal: Gets the list of packages that are missing.
#
# uninstall - A {Boolean} indicating whether to install packages in the list
# but missing from Atom (false), or uninstall packages in Atom but missing
# from the list (true).
#
# Returns an {Array} of names of packages that need to be installed.
getMissingPackages: ->
getMissingPackages: (uninstall = false) ->
list = new PackageList()
syncPackages = list.getPackages()
availablePackages = atom.packages.getAvailablePackageNames()
value for value in syncPackages when value not in availablePackages

# Only compare against the list of non-bundled packages
availablePackageNames = atom.packages.getAvailablePackageNames()
availablePackages = (pkg for pkg in availablePackageNames when not atom.packages.isBundledPackage(pkg))

if uninstall
value for value in availablePackages when value not in syncPackages
else
value for value in syncPackages when value not in availablePackages

# Internal: Installs the next package in the list.
installPackage: ->
# Exit if there is already an installation running or if there are no more
# packages to install.
return if @currentInstall? or @packagesToInstall.length is 0
@executeApm(@packagesToInstall.shift())
@executeApm("install", @packagesToInstall.shift())

# Internal: Installs each of the packages in the given list.
#
Expand All @@ -108,6 +127,20 @@ class PackageSync
@packagesToInstall.push(packages...)
@installPackage()

# Internal: Uninstalls the next package in the list.
uninstallPackage: ->
# Exit if there is already an installation running or if there are no more
# packages to uninstall.
return if @currentInstall? or @packagesToUninstall.length is 0
@executeApm("uninstall", @packagesToUninstall.shift())

# Internal: Uninstalls each of the packages in the given list.
#
# packages - An {Array} containing the names of packages to uninstall.
uninstallPackages: (packages) ->
@packagesToUninstall.push(packages...)
@uninstallPackage()

# Internal: Sets a timeout to remove the status bar message.
#
# timeout - The {Number} of milliseconds until the message should be removed.
Expand Down
9 changes: 8 additions & 1 deletion spec/package-sync-spec.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,11 @@ describe 'PackageSync', ->
spyOn(atom, 'getConfigDirPath').andReturn(os.tmpdir())
spyOn(atom.packages, 'getAvailablePackageNames').andReturn(['foo'])

expect(@sync.getMissingPackages()).toEqual(['bar', 'baz'])
expect(@sync.getMissingPackages(false)).toEqual(['bar', 'baz'])

it 'gets a list of installed packages, excluding ones that are in the list', ->
h.createPackages({'packages': ['foo', 'bar']})
spyOn(atom, 'getConfigDirPath').andReturn(os.tmpdir())
spyOn(atom.packages, 'getAvailablePackageNames').andReturn(['foo', 'bar', 'baz'])

expect(@sync.getMissingPackages(true)).toEqual(['baz'])