
1. 為什么突然想給Homebrew配個(gè)界面BrewUI到底做了什么如果你一直用Mac做開(kāi)發(fā)大概率對(duì)Homebrew不陌生。brew install、brew update、brew upgrade這一串命令我們幾乎天天在終端里敲已經(jīng)熟到條件反射。但用久了總會(huì)遇到幾個(gè)讓人頭疼的瞬間明明記得自己裝過(guò)某個(gè)包brew list一翻卻認(rèn)不出來(lái)是干什么用的brew outdated列了一堆更新不知道該升還是不該升更別提裝了一堆依賴之后brew uninstall完還可能殘留一串孤兒依賴看著就讓人膈應(yīng)。我第一次接觸BrewUI就是被這些零零碎碎的小問(wèn)題煩到不行之后。說(shuō)實(shí)話最開(kāi)始我不太看好這種給命令行工具套界面的事情——命令行包管理器的精髓就在于快非要開(kāi)個(gè)網(wǎng)頁(yè)去點(diǎn)按鈕多此一舉不說(shuō)反而可能丟失命令行的靈活性。但真正裝起來(lái)用了一周之后我的看法有了明顯變化。BrewUI不是簡(jiǎn)單地把brew list輸出變成HTML表格它把Homebrew這套龐大的軟件包生態(tài)重新組織成了一個(gè)能“看明白、點(diǎn)得動(dòng)、查得清”的圖形化管理面板。這篇博客我就從頭整理一下BrewUI能做什么、怎么裝、實(shí)際用下來(lái)哪些功能值得依賴、哪些場(chǎng)景根本不需要它。無(wú)論你是Mac開(kāi)發(fā)新手還是老手這篇文章應(yīng)該都能給你一些參考。先定義清楚BrewUI是一個(gè)圍繞Homebrew構(gòu)建的開(kāi)源Web可視化管理界面它讀取本機(jī)的Homebrew數(shù)據(jù)通過(guò)瀏覽器提供軟件包搜索、安裝、卸載、升級(jí)、清理依賴、查看服務(wù)運(yùn)行狀態(tài)等操作能力。簡(jiǎn)單說(shuō)它把原本只能在終端里通過(guò)命令完成的事情搬到了一個(gè)更直觀的Web面板里。你不需要額外配置數(shù)據(jù)庫(kù)不需要改Homebrew本身BrewUI只作為一個(gè)管理入口存在底層的安裝、卸載、更新操作執(zhí)行的依然是Homebrew原生命令。它適合誰(shuí)適合那些希望更直觀地審視自己這臺(tái)機(jī)器上到底裝了什么東西、希望用圖形化方式做日常包管理又不愿意折騰復(fù)雜運(yùn)維工具的人。當(dāng)然它也適合像我一樣平時(shí)重度使用終端但偶爾想換個(gè)視角、用更全局的方式審視自己的開(kāi)發(fā)環(huán)境的人。2. 從命令行到localhostBrewUI的安裝與啟動(dòng)全流程2.1 環(huán)境要求與依賴說(shuō)明BrewUI有當(dāng)前比較流行的兩種使用方式一種是直接通過(guò)Homebrew安裝另一種是使用Docker容器運(yùn)行。這兩種方式我實(shí)測(cè)下來(lái)都不復(fù)雜但對(duì)應(yīng)的使用場(chǎng)景差別挺大我分別說(shuō)。如果你的機(jī)器上已經(jīng)裝了Homebrew最直接的方式就是brew install brewui這個(gè)包在Homebrew的官方core倉(cāng)庫(kù)里已經(jīng)存在安裝過(guò)程會(huì)順帶把Node.js運(yùn)行時(shí)一類的依賴帶進(jìn)來(lái)。裝完之后不需要額外初始化數(shù)據(jù)庫(kù)首次啟動(dòng)時(shí)BrewUI會(huì)在用戶目錄下自動(dòng)創(chuàng)建它自己的數(shù)據(jù)目錄。另一種方式是用Dockerdocker run -d -p 8080:8080 -v /var/run/docker.sock:/var/run/docker.sock brewui/brewui等等——這里我要插一句。Docker版本和直接安裝在功能邊界上有個(gè)很重要的區(qū)別容器內(nèi)的BrewUI是跑在Linux容器里的但你真正要管理的Homebrew環(huán)境是宿主機(jī)上的macOS。這意味著容器方式通常需要額外配置掛載目錄和權(quán)限把宿主機(jī)上的Homebrew數(shù)據(jù)卷雨露均沾地暴露給容器。實(shí)際操作中多數(shù)人其實(shí)用不上這種隔離方式所以我更推薦直接在宿主機(jī)上安裝運(yùn)行省事也少踩權(quán)限的坑。2.2 首次啟動(dòng)與訪問(wèn)控制安裝完成后啟動(dòng)方式也很簡(jiǎn)單brewui serve正常情況下終端會(huì)輸出一段提示告訴你服務(wù)已經(jīng)跑起來(lái)了默認(rèn)端口是3000。這時(shí)候打開(kāi)瀏覽器訪問(wèn)http://localhost:3000就能看到BrewUI的主界面。但這里有一個(gè)很重要的安全設(shè)計(jì)BrewUI默認(rèn)不允許局域網(wǎng)內(nèi)其他設(shè)備直接訪問(wèn)。它默認(rèn)綁定的是127.0.0.1也就是只有本機(jī)能打開(kāi)。這是合理的——你能用這個(gè)面板做的操作本質(zhì)上就是能在終端執(zhí)行brew命令的用戶能做的操作如果開(kāi)放到網(wǎng)絡(luò)里又沒(méi)有鑒權(quán)相當(dāng)于把機(jī)器上的包管理權(quán)限拱手讓給了局域網(wǎng)內(nèi)任何人。如果你確實(shí)需要在另一臺(tái)設(shè)備比如iPad或者手機(jī)上臨時(shí)打開(kāi)這個(gè)面板可以在啟動(dòng)時(shí)指定監(jiān)聽(tīng)地址brewui serve --host 0.0.0.0但強(qiáng)烈建議只在臨時(shí)需要時(shí)這樣用用完就關(guān)掉或者配合系統(tǒng)防火墻限制來(lái)源IP。這不是矯情包管理權(quán)限一旦被惡意利用能干的壞事比你想象得多——?jiǎng)e人可以往你機(jī)器上塞一個(gè)包含惡意腳本的包也可以把你已經(jīng)裝好的工具替換成帶后門的版本。2.3 開(kāi)機(jī)自啟與退出方式如果你的目標(biāo)是把BrewUI變成一個(gè)隨時(shí)可用的管理工具每次手動(dòng)啟動(dòng)確實(shí)不夠優(yōu)雅。兩個(gè)方案比較常見(jiàn)使用brew services托管brew services start brewui這個(gè)命令會(huì)讓BrewUI作為后臺(tái)服務(wù)常駐運(yùn)行開(kāi)機(jī)自啟日志統(tǒng)一由Homebrew管理。日常不用管它想停掉就brew services stop brewui。使用LaunchAgent自啟這種方式更為靈活適合不想依賴Homebrew services的用戶。但維護(hù)成本略高需要自己寫plist文件。我建議優(yōu)先用第一種簡(jiǎn)單可靠。還有個(gè)細(xì)節(jié)如果你用CtrlC結(jié)束了前臺(tái)運(yùn)行的BrewUI再啟動(dòng)時(shí)偶爾會(huì)遇到端口被占用的情況。這是因?yàn)檫M(jìn)程沒(méi)有完全退出。這時(shí)候用lsof -i :3000查一下占用PID然后kill -9清掉即可不用慌。3. 把常用操作搬到瀏覽器搜索、安裝、升級(jí)、清理的實(shí)際體驗(yàn)3.1 儀表盤與全局狀態(tài)總覽打開(kāi)BrewUI的主界面后第一眼看到的是一屏狀態(tài)摘要。它不像brew doctor那樣輸出一大段文字而是把信息拆成幾個(gè)面板當(dāng)前Homebrew環(huán)境是否健康是否有警告、是否有需要處理的沖突。已安裝的軟件包總數(shù)、需要升級(jí)的軟件包數(shù)量。已安裝的cask應(yīng)用數(shù)量macOS圖形應(yīng)用的安裝管理方式與命令行工具分屬兩個(gè)倉(cāng)庫(kù)。當(dāng)前正在運(yùn)行的服務(wù)狀態(tài)。這個(gè)總覽的價(jià)值在于它解決了一個(gè)我之前的痛長(zhǎng)期不升級(jí)的話我根本不知道機(jī)器上哪些包已經(jīng)落后了多少版本。brew outdated輸出看多了也就麻木了。但在BrewUI里一個(gè)數(shù)字?jǐn)[在那里清清楚楚。該做的維護(hù)就是欠下的債一眼看到債主在那等你行動(dòng)力會(huì)強(qiáng)很多。3.2 搜索與安裝比終端多走半步搜索框是BrewUI里我使用最頻繁的功能。終端里的brew search只輸出一個(gè)扁平列表看起來(lái)費(fèi)勁。BrewUI的搜索結(jié)果會(huì)區(qū)分formula和cask把同一個(gè)關(guān)鍵字下的兩類結(jié)果分開(kāi)展示。每個(gè)包名旁邊還能直接看到一句話簡(jiǎn)介、所屬倉(cāng)庫(kù)、最新版本號(hào)。選中一個(gè)包后包詳情頁(yè)會(huì)展示更完整的依賴關(guān)系樹(shù)、版本歷史、維護(hù)者信息。這個(gè)信息密度遠(yuǎn)超終端。舉個(gè)例子你想裝FFmpeg但不確定自己是否已經(jīng)裝過(guò)它依賴的某些庫(kù)。在終端里你只能先brew deps ffmpeg再逐個(gè)列表比對(duì)在BrewUI里依賴關(guān)系是可視化展示的哪些已裝、哪些缺失一眼就知道。安裝操作本身沒(méi)有太多特殊之處點(diǎn)擊安裝按鈕BrewUI會(huì)調(diào)用后臺(tái)的brew install命令并在界面上實(shí)時(shí)滾動(dòng)輸出日志。安裝完成或失敗都會(huì)有明確的狀態(tài)反饋。如果你不想等日志刷新也可以把頁(yè)面關(guān)掉安裝過(guò)程會(huì)在后臺(tái)繼續(xù)。這個(gè)設(shè)計(jì)很貼心大型編譯類包比如某些依賴源碼編譯的庫(kù)動(dòng)輒要裝十幾分鐘一直盯著日志窗口太折磨人了。3.3 批量升級(jí)與定向升級(jí)的策略選擇軟件包升級(jí)是BrewUI做得比較順手的地方。終端里brew upgrade會(huì)讓你一次性升級(jí)所有可升級(jí)的包但很多情況下我并不想這么做——有些工具的新版本可能引入破壞性變更或者某些包是項(xiàng)目依賴版本一變項(xiàng)目就編不過(guò)了。在BrewUI里你可以勾選只想升級(jí)的那幾個(gè)包然后一鍵執(zhí)行批量升級(jí)。它還區(qū)分了“所有更新”和“安全性更新”兩種視角雖然安全性更新的判定邏輯依賴上游倉(cāng)庫(kù)提供的元數(shù)據(jù)并不完美但至少有這個(gè)過(guò)濾維度用起來(lái)比無(wú)差別升級(jí)安心得多。一個(gè)我自己的心得升級(jí)大版本major version bump之前先點(diǎn)擊包詳情里的更新日志鏈接看一眼改動(dòng)內(nèi)容再?zèng)Q定升不升。這一點(diǎn)在終端里做起來(lái)比較麻煩但在BrewUI里跳轉(zhuǎn)很方便。我吃了不止一次“無(wú)腦升級(jí)”的虧后來(lái)學(xué)乖了升級(jí)前必看changelog。3.4 卸載與孤兒依賴清理終于不用手動(dòng)折騰了卸載包這件事終端里一條brew uninstall xxx就完了。但Homebrew有個(gè)老毛病——包卸載之后它當(dāng)初帶進(jìn)來(lái)的依賴并不會(huì)自動(dòng)清理時(shí)間一長(zhǎng)系統(tǒng)里堆滿了一堆“孤兒依賴”即不再被任何包依賴的庫(kù)。brew autoremove能清理一部分但很多人根本不知道這個(gè)命令的存在甚至不知道什么是孤兒依賴。BrewUI把這一項(xiàng)做成了獨(dú)立功能塊叫“清理建議”。它會(huì)掃描所有已安裝的包標(biāo)記出哪些依賴已經(jīng)沒(méi)有任何根包引用了并且會(huì)先展示這些依賴的磁盤占用量。你可以選擇一條條清理也可以全選批量執(zhí)行。對(duì)我這種有輕度潔癖的人來(lái)說(shuō)這個(gè)功能解放感極強(qiáng)——每次清理完發(fā)現(xiàn)多出好幾個(gè)G的空間那種爽快感是brew cleanup給不了的。4. 進(jìn)階玩法與實(shí)測(cè)中的坑依賴圖、服務(wù)管理、權(quán)限問(wèn)題4.1 可視化依賴圖理解你的開(kāi)發(fā)環(huán)境如果說(shuō)安裝、卸載這些操作只是把終端命令包了一層皮那依賴圖可視化就是BrewUI真正稱得上“UI”價(jià)值的地方。點(diǎn)進(jìn)任意一個(gè)包的詳情頁(yè)切到“依賴關(guān)系”標(biāo)簽?zāi)芸吹揭粡垖?shí)時(shí)生成的依賴圖。所有依賴節(jié)點(diǎn)按層級(jí)展開(kāi)已安裝的節(jié)點(diǎn)會(huì)有明顯標(biāo)記缺失或者過(guò)期的節(jié)點(diǎn)則用不同顏色警示。我記得第一次看這個(gè)圖的時(shí)候我才發(fā)現(xiàn)原來(lái)自己裝的某個(gè)圖像處理庫(kù)底下拖了整整三層幾十個(gè)依賴包。之前brew deps輸出的長(zhǎng)串文本完全沒(méi)讓我意識(shí)到這個(gè)庫(kù)的依賴有多龐大。這張圖的實(shí)際用處不僅僅是“看著好玩”。排查編譯錯(cuò)誤時(shí)特別有用——某個(gè)包編譯不過(guò)往往不是它自身的問(wèn)題而是某個(gè)底層依賴庫(kù)版本沖突。從依賴圖里你能直觀看到?jīng)_突節(jié)點(diǎn)的位置然后定向去檢查那個(gè)包的狀態(tài)比在終端里逐個(gè)brew info高效太多。4.2 服務(wù)管理數(shù)據(jù)庫(kù)、Redis這類常駐進(jìn)程有了統(tǒng)一面板用過(guò)Homebrew的同學(xué)都知道安裝MySQL或PostgreSQL之后它不會(huì)自動(dòng)啟動(dòng)需要手動(dòng)執(zhí)行brew services start mysql。如果同時(shí)管理好幾個(gè)數(shù)據(jù)庫(kù)和服務(wù)進(jìn)程brew services list輸出的表格雖然夠用但每次要記清哪個(gè)服務(wù)在什么狀態(tài)也挺費(fèi)腦子。BrewUI的服務(wù)管理頁(yè)把這一塊做了非常順手的整合。它列出所有通過(guò)brew services管理的常駐服務(wù)每項(xiàng)都顯示當(dāng)前狀態(tài)啟動(dòng)中、已停止、異常退出旁邊直接是啟動(dòng)、停止、重啟按鈕。頁(yè)面上還有每個(gè)服務(wù)的運(yùn)行日志入口想看日志不用再去翻/usr/local/var/log/那種深層目錄。這個(gè)功能尤其適合經(jīng)常同時(shí)跑Redis、PostgreSQL、Nginx的人把所有服務(wù)集中到一個(gè)面板里管理使用起來(lái)非常方便。4.3 權(quán)限坑為什么有時(shí)候按鈕點(diǎn)了沒(méi)反應(yīng)這是我在使用過(guò)程中遇到最多的問(wèn)題類型。BrewUI界面上的操作按鈕本質(zhì)上執(zhí)行的是你當(dāng)前macOS用戶有沒(méi)有權(quán)限執(zhí)行的brew命令。如果你的Homebrew安裝在系統(tǒng)默認(rèn)目錄比如Apple Silicon Mac上的/opt/homebrew并且當(dāng)前用戶對(duì)該目錄有寫權(quán)限那一切正常。但如果你之前用管理員賬號(hào)裝過(guò)Homebrew現(xiàn)在用普通用戶登錄執(zhí)行brew install往往會(huì)遭遇權(quán)限失敗。BrewUI的界面里安裝任務(wù)的日志會(huì)直接顯示命令輸出的錯(cuò)誤信息。常見(jiàn)的報(bào)錯(cuò)包括Permission denied或者cannot write to /opt/homebrew/...。解決辦法是回到終端修正目錄所有權(quán)sudo chown -R $(whoami) /opt/homebrew然后在BrewUI里重試。如果你不想改整個(gè)目錄的權(quán)限歸屬也可以在系統(tǒng)偏好設(shè)置里給BrewUI終端進(jìn)程分配更高的權(quán)限但那樣做等于給所有后臺(tái)命令都加了sudo我不推薦風(fēng)險(xiǎn)太大。4.4 多用戶環(huán)境與共享機(jī)器的注意事項(xiàng)如果你在團(tuán)隊(duì)共用的開(kāi)發(fā)機(jī)上使用BrewUI需要特別注意Homebrew本身設(shè)計(jì)上就不是為多用戶共享使用而生的。當(dāng)多個(gè)用戶同時(shí)通過(guò)BrewUI執(zhí)行安裝命令Homebrew的鎖機(jī)制可能會(huì)讓其中一個(gè)操作排隊(duì)等待極端情況下會(huì)直接報(bào)錯(cuò)Another active Homebrew process is already in progress。BrewUI并沒(méi)有做復(fù)雜的用戶隔離。它的設(shè)計(jì)假設(shè)是“單用戶使用”。在這種場(chǎng)景下我給你的建議是如果一定要多用戶共用一臺(tái)機(jī)器就把BrewUI服務(wù)跑在特定的宿主用戶下其他用戶通過(guò)瀏覽器訪問(wèn)但不要多人同時(shí)執(zhí)行寫操作。團(tuán)隊(duì)內(nèi)部靠君子協(xié)定或者干脆給緊急操作配上腳本鎖避免并發(fā)問(wèn)題。5. 什么時(shí)候該用、什么時(shí)候別用BrewUI的邊界與選型建議5.1 與命令行的效率對(duì)比說(shuō)了這么多好處我也得公正地說(shuō)說(shuō)它的短板。在純操作效率上終端依然是最終答案。舉個(gè)例子我想快速安裝一個(gè)包終端里輸入brew install jq回車完事。整個(gè)過(guò)程兩秒。在BrewUI里你需要打開(kāi)面板、在搜索框輸入關(guān)鍵字、等待搜索結(jié)果加載、點(diǎn)擊包名、再點(diǎn)擊安裝按鈕、最后等日志滾動(dòng)。步驟數(shù)遠(yuǎn)多于終端。所以我的習(xí)慣是搜索、瀏覽、分析依賴、查看版本和更新?tīng)顟B(tài)這類“讀操作”用BrewUI真正確認(rèn)要裝某個(gè)包、執(zhí)行升級(jí)清理的時(shí)候我還是會(huì)回到終端快速執(zhí)行。BrewUI更適合做“管理”而非“執(zhí)行”理解這一點(diǎn)你對(duì)它的預(yù)期就不會(huì)出錯(cuò)。5.2 哪些場(chǎng)景下你會(huì)愛(ài)上BrewUI最適合用BrewUI的場(chǎng)景我總結(jié)下來(lái)有三個(gè)你對(duì)這臺(tái)機(jī)器上的環(huán)境不熟或者幾個(gè)月難得維護(hù)一次。打開(kāi)面板一眼看清全局哪些包需要更新、哪些服務(wù)異常、哪些是孤兒依賴全部擺在明面上。這種整體狀態(tài)掃描能力遠(yuǎn)勝終端的一次次詢問(wèn)。你想給同事或者團(tuán)隊(duì)提供一個(gè)“自助式”的包管理入口。團(tuán)隊(duì)內(nèi)部如果把BrewUI做成常駐服務(wù)新同事入職之后打開(kāi)面板就能看到當(dāng)前開(kāi)發(fā)環(huán)境預(yù)裝了哪些常用工具也避免了新人在終端里亂敲命令導(dǎo)致環(huán)境出問(wèn)題。你自己維護(hù)著多臺(tái)Mac設(shè)備需要統(tǒng)一查看各臺(tái)機(jī)器上的Homebrew狀態(tài)。雖然BrewUI目前不提供原生的多機(jī)集群管理但你可以分別啟動(dòng)每臺(tái)機(jī)器的面板通過(guò)瀏覽器標(biāo)簽頁(yè)并排查看至少比SSH進(jìn)每臺(tái)機(jī)器逐個(gè)跑命令要省心一些。5.3 等待上游版本迭代時(shí)你可以做的三件優(yōu)化BrewUI還在持續(xù)迭代中某些方面的體驗(yàn)還不夠完善。在等待上游更新的同時(shí)你可以做幾件小優(yōu)化提升自己的使用體驗(yàn)將brew update和brew upgrade配置為定時(shí)任務(wù)比如每天凌晨執(zhí)行BrewUI面板上的數(shù)據(jù)就能保持比較新鮮的狀態(tài)。這比自己每次手動(dòng)點(diǎn)更新要省事得多。訂閱倉(cāng)庫(kù)的GitHub Releases通知。新版本發(fā)布后Homebrew會(huì)自動(dòng)更新這個(gè)包偶爾會(huì)有配置變更留意一下更新日志避免視覺(jué)上沒(méi)變化但行為已經(jīng)變了的情況。如果你發(fā)現(xiàn)自己常用的某個(gè)操作在BrewUI里沒(méi)有覆蓋到去倉(cāng)庫(kù)提Issue或者直接看源碼。它是開(kāi)源項(xiàng)目你可以自己加想要的按鈕編譯成本并不高。寫在最后一點(diǎn)實(shí)際使用的碎碎念其實(shí)寫了這么多我心里很明白BrewUI不可能取代命令行它也不應(yīng)該試圖取代。它最打動(dòng)我的地方是提供了一種“復(fù)盤”能力——讓我重新審視這臺(tái)我每天都在用的機(jī)器上Homebrew這個(gè)龐大的生態(tài)到底是什么樣的。很多裝了之后就沒(méi)用過(guò)的包、一堆說(shuō)不清來(lái)源的依賴、幾個(gè)默默在跑但從沒(méi)注意過(guò)的服務(wù)都在這個(gè)面板上顯形了。這種感覺(jué)像是給自己的開(kāi)發(fā)環(huán)境做了一次徹底的家務(wù)大掃除打掃完神清氣爽。最后分享一個(gè)我自己常用的搭配早上到工位后我會(huì)先開(kāi)一下BrewUI看一眼有沒(méi)有異常狀態(tài)順手在后臺(tái)順手升級(jí)幾個(gè)安全補(bǔ)丁然后關(guān)掉面板回到終端開(kāi)始干活。這個(gè)小習(xí)慣陪我度過(guò)了挺多本來(lái)要在終端里一頓猛查才能消滅的事故。如果你也受夠了在brew list和brew outdated輸出里來(lái)回翻找不如裝一個(gè)BrewUI試試也許你的感受會(huì)和我一樣這個(gè)界面并非多此一舉。