十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

BrewUI:给Homebrew装上图形界面,让包管理变得清晰可控

BrewUI:给Homebrew装上图形界面,让包管理变得清晰可控 前阵子帮同事清理开发机打开终端敲了下brew list好家伙几百个包有一半我自己都认不出是干嘛用的。当时我就在想要是这些包能像 App Store 那样列出来能看用途、看依赖、点一下就能更新那就省事多了。BrewUI 就是干这个的给 Homebrew 套一个图形界面把brew list、brew deps、brew outdated、brew cleanup、brew services这些命令变成浏览器里可以点来点去的操作。这篇东西不是官方文档是我自己把 BrewUI 装起来、用了小半个月之后的完整记录。里面会有它解决了什么问题、安装时容易卡在哪里、日常怎么把它和命令行配合着用以及我踩过的几个坑。如果你也到了brew list长到要翻页的地步这篇文章应该能帮上忙。1. 命令行管理包的日子哪里最难受1.1 装得越久越不知道自己装了什么Homebrew 装包是真的简单一条brew install xxx就完事。可问题也出在这太简单了导致你会在各种场景下无意识装一堆东西。比如我同事那台开发机装的东西五花八门有做 Android 构建拉下来的 JDK有跑一个 Python 老项目装的 OpenSSL 链接库还有当时临时调试用过一次的图像处理工具。每个包装的时候都很合理但三个月之后再看根本想不起来当初为什么装它。命令行不是不能查brew list能看到包名brew info xxx能看到某个包的详细描述。但问题在于你得一个个去brew info几百个包一个个查过去这谁受得了。而且brew list输出的是纯文本列表没有分类、没有图标、没有这个包最近有没有更新的直观展示。信息都在但人眼处理不过来。1.2 依赖关系像蛛网谁也不敢乱动比不知道装了什么更难受的是不知道哪个包能删、删了会不会伤到别的包。Homebrew 的依赖关系比大多数人想象中复杂。比如你装了一个ffmpeg它背后会拉起来一长串依赖x264、x265、libvpx、libogg、libvorbis……其中有些库同时被别的包依赖着。你用命令行去查不是不行brew deps --tree ffmpeg也能画出依赖树但那个输出满屏字符层级深了以后根本没法看。更麻烦的是卸载场景。你想卸掉一个闲置的包 A但不确定有没有包 B 依赖它。直接brew uninstall A可能会连带删掉 B 依赖的公共库不卸吧磁盘空间白占着。这个想动又不敢动的状态就是包管理里的典型焦虑。1.3 更新、清理、服务管理都靠猜brew outdated能给出一堆等着升级的包但它不会告诉你怎么升级更稳妥。全量brew upgrade看着省事可如果其中有几个包之间存在版本约束升级完构建直接挂掉的情况我也遇到过不止一次。再说清理。brew cleanup能清掉旧版本和下载缓存但很多人不敢跑因为不知道它到底会清掉多少东西、清完之后会不会影响什么。磁盘空间到底被什么吃掉了是旧版本残留还是几个月前下载的安装包缓存命令行很难给你一个直观的答案。还有个被很多人忽略的点brew services。数据库、缓存服务这一类常驻进程用命令行管理要敲brew services start mysql、brew services stop redis一两个还好多了确实容易记混。说到底命令行是高效率工具适合明确知道要干什么的场景。但我到底装了啥哪些能清升级谁比较稳这种偏探索式、可视化为先的日常管理需求命令行天然就不擅长。2. BrewUI把哪些命令变成了可见的操作2.1 包列表从编号规则到信息面板BrewUI 最基础的功能就是把brew list变成了一个信息面板。每个包占一行包名、当前版本、安装时间、包的描述都在同一屏里展示出来。我不用再一个个去敲brew info滚动鼠标就能扫完整个环境里装了哪些东西。这东西用起来的感觉就像手机通讯录和下拉刷新消息列表眼睛扫一遍就能找到目标。尤其适合那种我记得我装过一个处理 JSON 的工具但名字想不起来的场景直接搜关键词就行。选择框支持地址过滤、状态过滤例如只看有过更新的包或者只看是依赖项的包。这些过滤条件在命令行里要靠写脚本才能实现在 UI 里就是一个下拉框的事。2.2 依赖图让你看清卸载和升级的影响面这是我觉得整个工具最值钱的功能依赖可视化。点开任意一个包就能展开它依赖了哪些库反过来也能看到哪些包依赖了它。这个反向依赖在命令行里查起来比较绕要一层层往上找。BrewUI 把它变成了一张可以展开收起的图点一下就能看清影响面。对日常使用来说这个功能直接解决了一个高频问题这个包能不能卸载不用再猜点开依赖图看一眼如果没有任何其他包依赖它就可以放心卸如果有一堆包指过来就得掂量掂量了。需要说明的是BrewUI 不是自己发明了依赖关系数据它底层调用的还是brew deps那一套命令。UI 的价值在于把层级结构画出来让人类能看得懂。2.3 更新界面从全量升级到定向操作BrewUI 会把brew outdated的结果变成一张待更新列表每行显示当前版本、可更新的目标版本以及这个包属于直接安装还是作为依赖被带进来。相比命令行里干巴巴地brew upgrade xxxUI 里能先看清楚再动手。尤其是有几十个包等着更新的时候我可以在列表里挑自己真正需要更新的两三个而不是闭着眼睛全量升级。这里多说一句BrewUI 里的更新操作本质上还是在后台跑brew upgrade 包名但它帮我多做了一个动作——把盲目执行变成了先看清再做决定。别小看这一步对稳定性的提升是很明显的。2.4 缓存清理与旧版本磁盘空间一目了然这个功能对应的是brew cleanup但做成了可视化的形态。BrewUI 会扫描出当前系统里有哪些包的旧版本残留以及下载缓存占了多少空间然后告诉你清理之后能释放多少磁盘。命令行里brew cleanup --dry-run也能预览但它的输出是一大堆文字一眼看过去很难估量整体收益。UI 把所有信息汇总成一个汇总表看着可清理 XXX MB这个数字你才会有动力去执行清理。它还可能把brew autoremove的逻辑整合进来——即清理那些不再被任何包依赖的遗留依赖。这个操作在命令行里要小心执行但在 UI 里看到它列出这些包不再被依赖可以安全移除的清单心里就有底多了。2.5 服务管理把 brew services 做成开关BrewUI 如果做得到位还会包含服务管理界面。brew services list能列出现在所有通过 Homebrew 管理的服务UI 里对应就是一排开关启动、停止、重启点一下就行。这个功能对我来说属于没用之前觉得无所谓用了之后回不去的那种几台数据库服务的状态一眼就能扫完不用逐个敲命令确认确实方便很多。3. 安装与首次启动把BrewUI跑起来3.1 先确认环境Homebrew本身要能正常工作BrewUI 再怎么好看底层还是通过 Homebrew 的命令行接口干活。所以安装前先把 Homebrew 本身收拾利索了。brew --version brew doctorbrew doctor的警告如果一堆建议先处理干净。尤其是Unbrewed header files或者broken symlink这类问题别看它不影响普通安装卸载但 BrewUI 在调底层命令做全量扫描时这些历史遗留问题可能导致异常。从原理上讲BrewUI 这类工具运行在系统用户权限下它能做的操作和你自己在终端里执行brew install、brew cleanup是一样的没有额外权限。所以如果brew doctor报错UI 里大概率也会有不正常的提示。3.2 以源码方式运行BrewUI的通用流程BrewUI 的安装方式取决于具体实现但这类本地 Web 工具普遍采用下载源码 安装依赖 启动服务的套路。以我用的这个版本为例流程大概是这样的git clone 项目仓库地址 cd BrewUI python3 -m venv venv source venv/bin/activate pip install -r requirements.txt python run.py启动后会看到终端输出一行地址一般是http://127.0.0.1:XXXX用浏览器打开就是 BrewUI 的界面了。这里有几个点值得说透。首先为什么要创建 Python 虚拟环境venv因为 BrewUI 这类工具如果直接全局安装依赖可能会和你系统里其他 Python 项目的依赖打架。放在虚拟环境里相当于给它一个独立的小房间隔离风险不用了可以直接整个删掉。其次我这边给的是一个通用路径具体项目仓库地址和启动命令以你看到的 README 为准不同版本细节可能有差异但大思路大差不差。3.3 从启动到打开浏览器几个容易卡住的点第一次启动时最常见的坑是终端提示brew: command not found。这不怪 BrewUI因为 Web 服务进程的环境变量和你自己登录终端时不一样。命令行没加载/opt/homebrew/binIntel Mac 是/usr/local/bin到 PATH 里子进程自然找不到 brew 命令。解决办法也简单在启动脚本或者 shell 配置文件里把 Homebrew 的 bin 目录补进去export PATH/opt/homebrew/bin:$PATH还有个常见问题是端口被占用。BrewUI 默认监听某个端口如果已经被其他服务占了启动会报错。一般工具的配置文件里都能改端口或者启动时加参数指定。另外提个安全细节这类本地工具默认监听127.0.0.1也就是只有你自己这台机器能访问。不要图方便改成0.0.0.0否则同一局域网内的设备都能访问你的包管理界面相当于把遥控器递到别人手里了。4. 用BrewUI做一次完整的包管理操作更新、卸载与清理4.1 场景搭建这台机器上装了什么假设我现在面对一台典型开发机装了 Python、Node.js、OpenJDK、ffmpeg、MySQL还有一些七七八八的小工具。打开 BrewUI 首页列表一眼扫过去每个包带版本号、安装时间、描述信息信息密度比终端高得多。这时候我做的第一件事不是更新也不是清理而是先把列表从头到尾过一遍。看见那些安装日期特别老、而且我完全想不起来用途的包就用搜索框定位一下点进去看看它的依赖关系。这一步是纯信息梳理不产生任何修改但价值很大相当于给系统做了一次体检心里有个底。4.2 先看依赖再决定卸载一次安全的卸载示例我同事那台机器上装了一个叫libsass的图像样式处理库一看就是当年某项目拉进来的。正常情况下他会直接brew uninstall libsass但这次我让他先在 BrewUI 里点开依赖图看一眼。结果发现libsass虽然没有被其他包依赖但它自己依赖了一堆底层库而在那些库下面还挂着另一个正在用的包也会用到的一个公共组件。如果直接卸载Homebrew 的自动依赖清理brew autoremove的隐含逻辑可能把那个公共组件一并清掉导致正在用的包出问题。正确做法是在 UI 里把这个依赖树的每个节点过一遍确定没有交叉引用之后再执行卸载。BrewUI 里如果提供卸载时保留依赖的选项那就选它先把独立包卸了再看看剩余依赖里有没有变成孤儿依赖的有的话再单独处理。这个流程看着繁琐但实际花不了多少时间远比卸载完发现一堆包挂掉再回头排查来得快。4.3 定向更新只升级某个版本影响大的包再看更新场景。那天 BrewUI 的更新列表里躺了 27 个包其中有一个openssl3的版本更新。按以往的习惯我可能直接brew upgrade全部搞定。但这次我多看了一眼发现列表里还有php和nginx它们对 OpenSSL 的版本有很强的耦合。全量升级的风险在于php可能依赖openssl3的旧 API而升级后新版本移除了这个 API导致 PHP 的扩展直接编译失败。这种问题一旦出现排查起来要折腾半天。所以我只在 BrewUI 里单独搜出openssl3先看了它的更新说明再点更新。更新完成之后到终端里跑了一下php -v确认没问题再去处理其他包的更新。这个小步快跑、逐个验证的节奏在命令行里操作起来非常别扭要一个个敲brew upgrade但在 UI 里就是勾选、点击、验证三个动作体验完全不同。4.4 清理缓存与旧版本把磁盘空间找回来最后是清理环节。BrewUI 扫描完之后告诉我系统里有 1.2GB 的旧版本残留和 800MB 的下载缓存。看到这个数字我才意识到之前brew install下载的那些安装包Homebrew 默认是保留一份的方便你随时切换回旧版本但代价就是磁盘空间持续被吃掉。在 UI 里执行清理前我习惯先看一眼可清理列表里有没有最近还切过版本的工具。比如你最近刚把 Python 3.12 降回 3.11 调试过问题那就别急着把 3.12 的残留清掉。除此之外旧版本残留和下载缓存清掉基本没什么副作用最多下次再装某个旧版本时需要重新下载一遍。清理执行完之后BrewUI 会重新统计一次那个可清理空间的数字变成 0磁盘空闲看着舒服多了。这个操作在命令行里一个brew cleanup也能做到但如果你不知道那 2GB 到底是从哪来的清理完反而心里发虚。UI 的价值就在这里让你清楚地知道自己做了什么以及为什么做。5. 进阶玩法与避坑记录5.1 把UI操作翻译回命令行用了 BrewUI 一段时间之后我发现一个额外的收获通过观察 UI 操作对应的后台行为我反而更理解 Homebrew 命令行的一些细节了。比如在 UI 里触发一次包更新如果运行日志可见你就能看到它实际执行的是brew upgrade 具体包名而不是brew upgrade。这解释了为什么 UI 里定向更新不会动其他包——它发的就是单包命令。再比如卸载场景UI 里那个保留依赖的选项对应的就是brew uninstall --ignore-dependencies这个命令参数。以前我在命令行里根本不知道有这个参数现在反而因为用 UI 学会了。所以我的建议是BrewUI 和终端不冲突UI 适合做信息和依赖关系的梳理真正需要写脚本批量处理的时候还是得回到终端。两边配合效率最高。5.2 定期做一次包体检而不是天天折腾我不是每天都打开 BrewUI一个东西用得太频繁容易手痒总想去点两下更新。我的习惯是固定每周做一次包体检打开 BrewUI扫一遍更新列表看看那些本周有新版本的包里哪些是我的主力工具逐个看一眼更新说明再决定是否升级顺便检查有没有变成孤儿依赖的包再决定要不要清。这套流程在纯命令行时代我是做不到的因为每次都要敲一堆命令、记一堆状态坚持不下来。变成 UI 之后整个流程大概五分钟就结束了每周一次几乎没有负担。长期下来系统一直保持在一个稳定且不臃肿的状态。5.3 我踩过的几个坑坑一看到依赖图就顺手卸载了看起来独立的包有一次我在 UI 里看一个包 A依赖图显示它没有被任何直接依赖指向于是断定它是孤儿包直接卸载。后来构建某个项目时发现失败排查半天才搞明白项目里的配置文件通过pkg-config间接引用了 A 提供的一个动态库只是 Homebrew 的依赖系统没把它记录成显式依赖。经验是依赖图是重要参考但不是唯一依据。卸载任何包之前最好再搜一下它的名字看看有没有项目配置文件、脚本还在引用它。坑二一键清理缓存后重新编译旧版本等了很久之前清理缓存时没有想太多把下载缓存全清了。结果没过两天我需要给一个老项目装回 Python 3.9Homebrew 找不到缓存直接触发从源码编译安装在那等了大半天。现在我的习惯是清理前看一眼缓存列表如果里面有自己近期可能还会用到的版本先保留。好在 BrewUI 的清理界面一般可以勾选具体清理项不用全选。坑三短时间内升级了多个互相关联的包构建直接挂掉有一阵子看到 UI 里好几个包都有更新觉得一个一个点太慢索性全选了。结果升级过程中某个底层库的 API 变更导致上层好几个工具全部失去响应。虽然可以通过brew upgrade的版本回滚来处理但整个过程很折腾。现在我的原则是互相关联的包比如数据库和它的客户端、语言运行时和它的包管理器不要在同一天内全部升级分批次来每批升级完跑一遍正常用例确认没坏再动下一批。风险场景我的处理方式卸载被间接引用的包先搜索项目配置和脚本是否有引用再动手清理旧版本缓存保留近期可能切换的版本不无脑全清多个关联包同时升级分批升级每批结束后验证一遍功能这些坑说到底都是同一个底层原因UI 降低了操作门槛让原本需要谨慎思考的动作变得太容易触发。工具本身没错但我们要学会用它来看清全局而不是只图操作快。BrewUI 我用下来最大的价值是让包管理从黑盒命令变成了可理解的信息尤其是依赖关系那张图让我敢动手去清理那些积压已久的系统包袱了这一点是命令行给不了的踏实感。
返回列表