Архив

Перенос mac приложений в brew

Думаю что многим разработчикам, работающим в macOS, знаком Homebrew. Изначально он развивался как менеджер пакетов пришедший на замену macports и fink, но, со временем, начал обрастать дополнениями которые поддерживаются сообществом.

Homebrew

Одно из таких дополнений это Cask, оно позволят работать с приложениями с закрытым исходным кодом, которые устанавливаются в систему путем перемещения в директорию /Applications. Cask реализует возможность устанавливать и следить за обновлениями таких приложений как:

Homebrew Cask Install

После установки приложения через Cask ничего не меняется. Оно будет работать также как работало до этого, будет предлагать обновления через собственные механизмы. Однако, brew тоже будет отслеживать состояние приложения и обновлять его если выйдет новая версия, brew создает команды запуска для терминала, если они предусмотрены и сможет удалить приложение под чистую, если это потребуется. А еще, с его помощью, можно сделать Brewfile, который позволит установить нужные приложения на новом компьютере одной командой.

В какой-то момент я обратил внимание что у меня не систематизированы установленные приложения: часть установлена через brew, другая часть руками и мне захотелось навести порядок в этой системе.

Как перенести уже установленные приложения под управление homebrew?

Чтобы brew начал отслеживать приложение оно должно быть установлено через него. План выглядит следующим образом:

  • Собрать список всех приложений установленных в /Applications
  • Произвести поиск в brew
  • При наличии совпадений установить их через homebrew, удалив или заменив исходное приложение

Поиск аналогов в Homebrew

Для поиска приложений через homebrew я написал shell скрипт, который берет список всех приложений в /Applications и последовательно делает поиск для каждого из них через brew

#!/bin/bash

FILES="$(bash <<EOF
ls "/Applications/" | grep '.app' | sed -e "s/\.app//"
EOF
)"

IFS='
'

for line in $FILES; do
    echo "Searching cask for $line"
    brew search --casks "$line"
    sleep 3
    echo ""
done

В первой секции скрипта мы делаем листинг директории ls "/Applications/",  фильтруем строки в которых содержится вхождение grep '.app', чтобы не смотреть на директории, и делаем замену подстроки удаляя расширение sed -e "s/\.app//"

Таким образом в переменную $FILES у нас запишется список вида

1Password Angry IP Scanner App Store AppCleaner Atom Bear BetterZip BitBar Docker Dropbox ...

Далее нам нужно переопределить переменную $IFS на символ новой строки, в противном случае наш скрипт будет интерпретировать пробел в название приложения как разделить и мы будем искать в brew не Google Chrome, а сначала Google и затем Chrome.

$IFS разделитель полей во вводимой строке (IFS -- Input Field Separator)

По-умолчанию -- пробельный символ (пробел, табуляция и перевод строки), но может быть изменен, например, для разбора строк, в которых отдельные поля разделены запятыми. Обратите внимание: при составлении содержимого переменной $*, Bash использует первый символ из $IFS для разделения аргументов. Подробнее

После этого запускается цикл, в котором для каждой строки с названием нашего приложения будет произведен поиск в brew. Команда sleep 3 нужна чтобы Github не начинал ругаться что мы делаем слишком много запросов.

Я запустил этот скрипт, перенаправил вывод в файл и оставил его работать

./cask-finder.sh > casks.txt

Анализ результатов

После того как скрипт закончил работать, у нас появился файл casks.txt с результатами поиска приложений через brew.

Searching cask for 1Password
==> Casks
1password
1password-cli
homebrew/cask-versions/1password-beta

Searching cask for Angry IP Scanner
==> Casks
angry-ip-scanner

Searching cask for App Store
==> Casks
appstore-quickview

Searching cask for AppCleaner
==> Casks
appcleaner

Searching cask for Atom
==> Formulae
atomicparsley
atomist-cli
libatomic_ops
sratom

==> Casks
atom
homebrew/cask-versions/atom-beta

...

Мне показалось что программировать систему которая будет все делать автоматически слишком избыточно, да и она потребует много времени для отладки, поэтому полученные результаты быстрее всего проанализировать руками.

Я был очень удивлен, результатов оказалось сильно больше чем я ожидал. Примерно 50% установленных у меня приложений имеют копию в brew. Переустанавливать каждое из них мне не хотелось, поэтому для принятие решение я использовал следующие критерии:

  • Это приложение мне нужно на всех компьютерах которыми я пользуюсь
  • Я пользуюсь этим приложением минимум раз в месяц
  • Мне важно чтобы оно обновлялось автоматически и было свежим

Далее я просто пошел по списку, проверяя информацию о приложение в brew

~ » brew cask info 1password
1password: 7.2.5 (auto_updates)
https://1password.com/
Not installed
From: https://github.com/Homebrew/homebrew-cask/blob/master/Casks/1password.rb
==> Name
1Password
==> Artifacts
1Password 7.app (App)

Например, 1password я не стал подключать к brew, т.к. в нем лежит более свежая версия чем та которой я пользуюсь. У меня куплена 6 версия, а подписочная модель в 7 версии мне не подходит.

Searching cask for AppCleaner
==> Casks
appcleaner

Для AppCleaner у меня была более старая версия чем предлагаемая в brew и сходились все правила. Поэтому я удалил приложение из /Applications и переустановил его через brew.

cd /Applications
rm -rf ./AppCleaner
brew cask install appcleaner

Теоретически можно было и не удалять приложение из /Applications, а использовать ключ --force при установке через brew, но такой вариант мне показался менее надеждным, так как могут появиться баги с дублированием приложений.

При удалении приложения его настройки не удалятся, они хранятся в ~/Library/Application\ Support и будут подхвачены новым приложением.

Заключение

Весь процесс занял у меня около одного часа времени, но теперь большая часть приложений которые мне нужны будут обновляться автоматически.

К слову, для удобного обновления brew я завел себе алиас и добавил его в ~/.zshrc

alias brew_update="brew update && brew upgrade && brew cask upgrade && brew cleanup"

При его использование автоматически выполнятся все команды обновления, а в завершение сработает cleanup, который удалят старые файлы приложений и скачанные версии. Это позволяет экономить местно на жестком диске.