|
#!/usr/bin/env python |
|
|
|
from __future__ import print_function |
|
|
|
import os |
|
import re |
|
import subprocess |
|
from distutils.version import LooseVersion |
|
from shutil import rmtree |
|
|
|
|
|
INSTALLED_PATH = '/usr/local/Caskroom' |
|
METADATA_ROOT_PATH = os.path.join(os.getenv('HOMEBREW_PREFIX', '/usr/local'), 'Homebrew/Library/Taps') |
|
METADATA_PATHS = [] |
|
|
|
|
|
def main(): |
|
populate_metadata_paths() |
|
|
|
if not METADATA_PATHS: |
|
print('Error: No "Casks" folders were found underneath {}, are you sure you have Homebrew-Cask installed?'.format(METADATA_ROOT_PATH)) |
|
exit(1) |
|
|
|
outdated_applications = get_outdated_applications() |
|
|
|
if not outdated_applications: |
|
exit(0) |
|
|
|
auto_updating_apps = False |
|
print('The following upgrades are available:') |
|
for application in sorted(outdated_applications): |
|
data = outdated_applications[application] |
|
if data['auto_updates']: |
|
print('- {}: {} -> {} (*)'.format(application, data['installed'], data['latest'])) |
|
auto_updating_apps = True |
|
else: |
|
print('- {}: {} -> {}'.format(application, data['installed'], data['latest'])) |
|
|
|
if auto_updating_apps: |
|
print('\n(*) = May have auto updated\n') |
|
|
|
if not confirmed('Upgrade? '): |
|
exit(0) |
|
|
|
apps_upgraded = False |
|
for application in sorted(outdated_applications): |
|
data = outdated_applications[application] |
|
|
|
print() |
|
upgrade(application) |
|
apps_upgraded = True |
|
|
|
if apps_upgraded and not os.getenv('HOMEBREW_NO_INSTALL_CLEANUP'): |
|
subprocess.call(['brew', 'cleanup'] + outdated_applications.keys()) |
|
|
|
|
|
def populate_metadata_paths(): |
|
for root, directories, _ in os.walk(METADATA_ROOT_PATH): |
|
if root == METADATA_ROOT_PATH: |
|
# We don't care about the folders in the top level directory |
|
continue |
|
|
|
if 'Casks' in directories: |
|
METADATA_PATHS.append(os.path.join(root, 'Casks')) |
|
|
|
if '.git' in directories: |
|
# Don't proceed any further if we found the top of the repository |
|
directories = [] |
|
|
|
|
|
def get_outdated_applications(): |
|
outdated_applications = {} |
|
for application in os.listdir(INSTALLED_PATH): |
|
if not os.path.isdir(os.path.join(INSTALLED_PATH, application)): |
|
continue |
|
|
|
latest_installed_version, old_installed_versions = get_installed_versions(application) |
|
|
|
if not latest_installed_version: |
|
continue |
|
|
|
latest_version, auto_updates = get_latest_version_and_auto_update(application) |
|
|
|
if not latest_version: |
|
continue |
|
|
|
if latest_version > latest_installed_version: |
|
outdated_applications.update({ |
|
application: { |
|
'installed': latest_installed_version, |
|
'latest': latest_version, |
|
'auto_updates': auto_updates, |
|
'old_versions': old_installed_versions, |
|
} |
|
}) |
|
|
|
return outdated_applications |
|
|
|
|
|
def get_installed_versions(application): |
|
versions = os.listdir(os.path.join(INSTALLED_PATH, application)) |
|
versions = [LooseVersion(version) for version in versions if version != '.metadata'] |
|
|
|
if versions: |
|
versions.sort(reverse=True) |
|
return versions[0], versions[1:] |
|
|
|
return False, False |
|
|
|
|
|
def get_latest_version_and_auto_update(application): |
|
for directory in METADATA_PATHS: |
|
metadata_path = os.path.join(directory, '{}.rb'.format(application)) |
|
if not os.path.isfile(metadata_path): |
|
continue |
|
|
|
with open(metadata_path, 'r') as metadata_file: |
|
metadata = metadata_file.read() |
|
|
|
versions = re.findall(r'^\W*version (\S+)', metadata, re.MULTILINE) |
|
if not versions: |
|
continue |
|
|
|
latest_version = None |
|
for version in versions: |
|
version = LooseVersion(version.strip('\'":')) |
|
|
|
if special_snowflake(application, version): |
|
continue |
|
|
|
if version.vstring == 'latest': |
|
latest_version = version |
|
break |
|
|
|
if not latest_version or version > latest_version: |
|
latest_version = version |
|
|
|
auto_updates = re.search(r'^\W*auto_updates true\W', metadata, re.MULTILINE) |
|
|
|
return latest_version, bool(auto_updates) |
|
|
|
return False, False |
|
|
|
|
|
def special_snowflake(application, version): |
|
if application == 'docker' and version.version[0] == 18: |
|
# Docker for Mac changed versioning scheme and went from 18.x to 2.x |
|
return True |
|
|
|
return False |
|
|
|
|
|
def confirmed(message): |
|
try: |
|
# Python 2 |
|
ask = raw_input |
|
except NameError: |
|
# Python 3 |
|
ask = input |
|
|
|
while True: |
|
response = ask(message).strip().lower() |
|
|
|
if response in {'y', 'yes'}: |
|
return True |
|
if response in {'n', 'no'}: |
|
return False |
|
|
|
|
|
def upgrade(application): |
|
# '--greedy' is necessary to upgrade auto updating casks |
|
subprocess.call(['brew', 'upgrade', '--cask', '--greedy', application]) |
|
|
|
|
|
if __name__ == '__main__': |
|
main() |
Awesome script! I hack on line 125 to always return
True
. Thank you!