實(shí)戰(zhàn)與踩坑記錄)
大多數(shù) macOS 開發(fā)者的包里都有那么幾十個(gè)甚至上百個(gè) brew 包但沒幾個(gè)人能說得清自己機(jī)器上到底裝了什么、哪些已經(jīng)沒人維護(hù)、哪些緩存占了幾個(gè) G。我也不例外。某天我盯著終端里刷了上千行的 brew upgrade 輸出突然意識到一個(gè)事情我們天天嫌棄那些“低效的 GUI 工具”自己卻一直在用最原始的方式管理包依賴。于是就有了 BrewUI 這個(gè)項(xiàng)目——一個(gè)把 Homebrew 日常操作搬到圖形界面的工具。它能看包列表、搜索、安裝、卸載、查看依賴關(guān)系、清理緩存也能跑 brew doctor 和升級操作適合那些不想背命令、或者需要幫不熟悉終端的朋友維護(hù)機(jī)器的人。做這個(gè)項(xiàng)目的過程中我踩了不少坑從技術(shù)選型推翻重來到 brew 輸出解析的細(xì)節(jié)再到權(quán)限問題的折騰今天干脆把整個(gè)過程完整整理出來。這篇東西不會只給你看成品而是把“為什么這么做”“為什么踩坑”“怎么排查”都講清楚。如果你也想過給 Homebrew 套一層圖形界面或者單純對 macOS 包管理工具鏈感興趣這里面的經(jīng)驗(yàn)應(yīng)該能讓你少走很多彎路。1. 為什么我放著終端不用非要給 brew 做一層圖形界面是什么讓我決定做這個(gè)工具直接原因是連續(xù)三次幫人處理 Homebrew 問題都被同一個(gè)問題困住了——終端輸出對大多數(shù)人來說并不友好。1.1 終端裝包的三次“勸退”經(jīng)歷第一次是幫一個(gè)產(chǎn)品經(jīng)理配置環(huán)境。他在終端里跑了 brew install裝到一半輸出一堆紅色錯(cuò)誤他不知道該怎么辦只能截圖發(fā)我。我看了一下是依賴沖突。對終端熟悉的人可能幾分鐘就能定位但對他而言那一屏滾動輸出完全是天書根本不知道從哪里看起。第二次是我自己的機(jī)器。brew upgrade 跑了一半被網(wǎng)絡(luò)中斷剩下一堆不確定的狀態(tài)哪些包裝好了、哪些包裝到一半、哪些依賴被更新過完全不明朗。我用 brew list 和 brew deps 逐個(gè)排查勉強(qiáng)恢復(fù)了但這個(gè)過程讓我明顯感覺到命令行輸出的信息密度雖然高“可讀性”卻幾乎為零。如果有一個(gè)界面能把“已完成”“進(jìn)行中”“出錯(cuò)了”直接區(qū)分開處理這種狀態(tài)會快得多。第三次更典型。一個(gè)同事想卸載某個(gè) cask 應(yīng)用順手在我給他的命令后面加了 --force結(jié)果把配置文件也清了。命令行的容錯(cuò)性太差一個(gè)不起眼的參數(shù)就能造成不可逆的后果。對老手來說這是功能對普通用戶來說這就是陷阱。這三次經(jīng)歷讓我確認(rèn)了一個(gè)真實(shí)需求Homebrew 需要一個(gè)給高頻操作兜底的界面。它不一定能覆蓋所有參數(shù)但應(yīng)該讓人看得見自己做了什么、將要做什么、結(jié)果是什么。正是在這種背景下BrewUI 的定位逐漸清晰起來。1.2 現(xiàn)有工具沒有一個(gè)能打的短暫嘗試與放棄開工之前我習(xí)慣先找現(xiàn)成的輪子。事情比我想象的要冷清Homebrew 生態(tài)里真正能用的 GUI 工具少得可憐。Cakebrew 是最有名的一個(gè)但它的最后一個(gè)版本停留在好幾年前打開就是一個(gè)半死不活的舊界面在 Apple Silicon 上運(yùn)行還有兼容性問題。還有一些開源項(xiàng)目停留在“能跑”階段比如某個(gè)用 Electron 包一層的應(yīng)用界面是做了但內(nèi)存占用比 brew 本身還離譜裝著都嫌礙事。我還試過基于 Web 的方案比如給 brew 起一個(gè)本地 HTTP 服務(wù)然后用瀏覽器去操作。這個(gè)方案的問題也很明顯每次使用要手動起服務(wù)瀏覽器里拿不到 macOS 原生的憑證流進(jìn)程管理繞來繞去??偨Y(jié)下來就是四個(gè)字要么太老、要么太重、要么太繞。與其等一個(gè)理想工具出現(xiàn)不如自己做一個(gè)。這是 BrewUI 項(xiàng)目最初的動機(jī)。我還給自己定了幾個(gè)硬性目標(biāo)安裝包盡量小、內(nèi)存盡量低、打開是原生窗口并且能完成 Homebrew 日常 80% 的操作。這幾個(gè)目標(biāo)直接決定了后面的技術(shù)選型方向。1.3 BrewUI 的定位給高頻操作做可視化而不是復(fù)刻全部命令項(xiàng)目一開始我就排除了一個(gè)錯(cuò)誤方向試圖把 brew 的每個(gè)子命令都做成按鈕。Homebrew 有幾百個(gè)子命令和大量參數(shù)全部圖形化只會得到一個(gè)比終端還難用的怪物。所以我做了一個(gè)非??酥频娜∩嶂蛔鏊念惛哳l操作包管理瀏覽、搜索、安裝、卸載、升級。信息查看包的基本信息、依賴關(guān)系、被誰依賴。維護(hù)清理cleanup、doctor、analytics 開關(guān)。批量操作一鍵升級全部、Brewfile 導(dǎo)入導(dǎo)出。這個(gè)定位很重要。它決定了 BrewUI 不是一個(gè)“懶人工具”而是一個(gè)“安全感工具”——你不用記命令但界面會明確告訴你每一步在干什么。基于這個(gè)定位后面的架構(gòu)設(shè)計(jì)和功能優(yōu)先級都有了明確依據(jù)。2. 技術(shù)棧的取舍從 Electron 到 Tauri 的轉(zhuǎn)向技術(shù)選型是我在這個(gè)項(xiàng)目里反復(fù)推翻最多次的決定。第一天我理所當(dāng)然地選了 Electron第二天就后悔了。2.1 最初的原型Electron 版只撐了一天Electron 的優(yōu)勢不用多說生態(tài)成熟、文檔多、什么都能干。但我的場景里有個(gè)致命問題BrewUI 本質(zhì)上是一個(gè)調(diào)用本地命令的工具絕大部分時(shí)候只是啟動一個(gè) brew 進(jìn)程然后顯示輸出。這意味著我需要的是“輕殼子 高效進(jìn)程通信”而不是一個(gè)內(nèi)置瀏覽器的運(yùn)行時(shí)。Electron 哪怕做一個(gè)空窗口內(nèi)存也要占到 150MB 以上這對一個(gè)打開頻率很高的包管理工具來說太浪費(fèi)了。而且 Electron 打包出來的應(yīng)用動輒 200MB發(fā)布一個(gè)工具讓用戶下載一個(gè)比 brew 安裝包還大幾十倍的應(yīng)用我自己心理上就過不去。當(dāng)時(shí)用 Electron 花了一個(gè)晚上搭出原型功能能跑但那種“為一個(gè)列表頁面背上整個(gè) Chromium”的荒謬感越來越強(qiáng)第二天早上就決定全部推翻重來。2.2 最終方案Tauri 2.0 React Rust 的底氣最后我選用了 Tauri 2.0。它的思路和 Electron 正好相反前端用系統(tǒng)自帶的 WebView 渲染后端用 Rust 編譯成原生庫窗口和系統(tǒng)交互走系統(tǒng) API打包體積在 10MB 左右內(nèi)存占用通常比 Electron 少一半以上。對于 BrewUI 這種“命令執(zhí)行器 數(shù)據(jù)展示器”的場景非常合適。前端部分我用 React TypeScript組件庫選了 Mantine表格用 TanStack Table依賴圖用 vis-network。這套組合的好處是省力表格的虛擬滾動、搜索、排序開箱即用不需要自己造輪子我可以把精力集中在和 brew 的交互邏輯上。Rust 側(cè)用 std::process::Command 調(diào) brew輸出通過 Tauri 的 command 機(jī)制異步返回給前端再用事件推送把進(jìn)度實(shí)時(shí)刷到界面上。這里有個(gè)經(jīng)驗(yàn)Tauri 2.0 的事件系統(tǒng)比 1.x 干凈不少流式場景建議用 Channel 而不是頻繁 emit 事件性能和代碼可讀性都會好一些。2.3 整體架構(gòu)與數(shù)據(jù)流BrewUI 是怎么跟 Homebrew 對話的整條數(shù)據(jù)流可以用一句話概括前端只負(fù)責(zé)渲染和交互所有和 brew 的對話都發(fā)生在 Rust 進(jìn)程里。具體流程是這樣的用戶在界面上點(diǎn)擊“安裝”按鈕前端調(diào)用 Tauri commandRust 側(cè)拼好參數(shù)通過 std::process::Command 執(zhí)行/opt/homebrew/bin/brew install xxx捕獲 stdout 和 stderr實(shí)時(shí)解析輸出里的進(jìn)度信息把解析結(jié)果通過事件通道發(fā)給前端。命令結(jié)束后Rust 把退出碼和最終輸出打包返回前端再刷新列表。一個(gè)重要的設(shè)計(jì)決策是我在 Rust 和 brew 之間保持了一個(gè)純文本接口的邊界。不直接讀 brew 的數(shù)據(jù)庫或內(nèi)部目錄只用 brew 自己提供的命令和 JSON 輸出。原因有兩個(gè)第一Homebrew 沒有公開的穩(wěn)定數(shù)據(jù)格式只保證命令行的兼容性直接讀內(nèi)部文件升級一次就可能崩第二通過命令行接口BrewUI 永遠(yuǎn)只做 brew 權(quán)限范圍內(nèi)的事不會把自己置于“繞過 brew”的危險(xiǎn)位置。這個(gè)邊界雖然讓某些操作多了一層解析開銷但換來了長期穩(wěn)定性。3. 核心功能逐個(gè)拆解從 brew list 到依賴圖譜這一章挑四個(gè)最有代表性的功能講實(shí)現(xiàn)細(xì)節(jié)。每個(gè)功能背后都至少踩過一個(gè)坑我把關(guān)鍵設(shè)計(jì)單獨(dú)拿出來說。3.1 包列表與搜索把 brew list --json 變成可交互表格BrewUI 的主界面是一個(gè)已安裝包列表。數(shù)據(jù)來源是brew list --formula --jsonv2這條命令會返回一個(gè) JSON最外層是 formula 數(shù)組每個(gè)元素包含 name、full_name、versions、installed、dependencies 等字段。注意這里不能用brew list的純文本輸出因?yàn)槲谋緛G信息JSON 才是給程序用的。拿到數(shù)據(jù)后我會做三件事第一用 installed 數(shù)組里的版本信息組裝出“當(dāng)前版本”第二用 dependencies 和 reverse_dependencies 構(gòu)建一棵依賴索引第三把包的緩存大小也算出來。前端拿到這個(gè)結(jié)構(gòu)后用 TanStack Table 做虛擬滾動支持按名稱、狀態(tài)、依賴數(shù)量排序搜索框走一個(gè) 200ms 的防抖體驗(yàn)上接近 Raycast 的搜索手感。這里有個(gè)細(xì)節(jié)必須處理brew list --jsonv2在包多的時(shí)候要跑 1 到 3 秒不能每次打開都跑全量。我的做法是首次啟動后把結(jié)果緩存到應(yīng)用支持目錄啟動時(shí)先展示緩存后臺再刷新刷新完對比數(shù)據(jù)版本有變化才更新列表。這樣能保證啟動速度又不會讓數(shù)據(jù)過期。3.2 安裝與卸載進(jìn)程輸出怎么“翻譯”成 GUI 進(jìn)度安裝和卸載是用戶感知最強(qiáng)的功能難點(diǎn)不在執(zhí)行命令而在輸出處理。brew install在終端里會輸出帶顏色的日志和進(jìn)度條。進(jìn)度條是用回車符\r刷新同一行在 GUI 里直接顯示就會變成一行行疊在一起的亂碼。我的處理方式是在 Rust 側(cè)把 stdout 按行拆開先剔除 ANSI 轉(zhuǎn)義序列再判斷當(dāng)前行是否包含進(jìn)度百分比。如果包含就更新任務(wù)進(jìn)度不包含就追加到日志區(qū)。卸載功能我特意設(shè)計(jì)成兩步確認(rèn)。先點(diǎn)“卸載”按鈕彈出一個(gè)對話框列出這個(gè)包會被卸掉什么、有哪些包依賴它。如果還有依賴它的包會顯示醒目的警告。用戶必須手動輸入包名確認(rèn)才能執(zhí)行卸載。這個(gè)設(shè)計(jì)比命令行繁瑣但正是這種繁瑣能避免誤操作。順手多做的這個(gè)確認(rèn)框后來收到的好評比任何華麗功能都多。3.3 依賴可視化一層還是兩層這是個(gè)性能問題依賴圖譜是 BrewUI 里最直觀的功能也是性能壓力最大的功能。數(shù)據(jù)來源本來是brew deps --tree但那是文本輸出不方便渲染。我改用brew info --jsonv2 --formula拿 dependencies自己建圖。為了性能我做了三個(gè)限制。第一默認(rèn)只展開兩級依賴想看更多就點(diǎn)擊節(jié)點(diǎn)展開。第二圖渲染用 vis-network 的 hierarchical 布局而不是力導(dǎo)向布局避免節(jié)點(diǎn)亂飛。第三對重復(fù)的依賴節(jié)點(diǎn)做合并把共同依賴提出來。這一點(diǎn)很有必要因?yàn)橐粋€(gè) Java 相關(guān)的包可能拉出幾百個(gè)節(jié)點(diǎn)不去重的話頁面直接卡死。實(shí)測下來 80 個(gè)包的依賴圖默認(rèn)兩層展開大約 200 到 300 個(gè)節(jié)點(diǎn)渲染在 2 秒內(nèi)完成交互滑動基本流暢。3.4 清理、診斷與更新把 brew cleanup 和 brew doctor 搬進(jìn)界面清理功能我的做法是先跑brew cleanup --dry-run把將要釋放的緩存大小列出來讓用戶確認(rèn)之后再真正執(zhí)行。診斷功能直接跑brew doctor把輸出分類展示——警告、建議、錯(cuò)誤分別用不同顏色區(qū)分。升級功能默認(rèn)也是 dry-run 模式先展示有哪些包可以升級用戶勾選后再逐個(gè)執(zhí)行。這三個(gè)功能的共同設(shè)計(jì)思路是“先預(yù)覽后執(zhí)行”。brew 這些命令本身支持 dry-run 或 check 模式我只需要在 GUI 流程里把這一步變成硬性步驟就能最大程度降低誤操作概率。對于包管理場景看到將要發(fā)生什么比執(zhí)行本身更重要。4. 開發(fā)過程中踩得最深的四個(gè)坑這一章是全文最有價(jià)值的部分。我在 BrewUI 的整個(gè)開發(fā)過程中踩過不少坑這里挑四個(gè)影響最大的每個(gè)都給出完整的排查思路和解決方案。4.1 權(quán)限問題GUI 不是終端憑據(jù)彈窗繞了很遠(yuǎn)的路第一個(gè)大坑是權(quán)限。終端里執(zhí)行 brew install 大多數(shù)時(shí)候不需要 sudo但 brew cask 里不少包在安裝時(shí)會觸發(fā)系統(tǒng)安裝器的密碼彈窗比如 pkg 類型的 cask。終端運(yùn)行時(shí)系統(tǒng)會自動彈出密碼框但 GUI 應(yīng)用直接執(zhí)行同樣的命令時(shí)部分場景拿不到正確的權(quán)限上下文。一開始我嘗試在代碼里用 osascript 彈管理員授權(quán)把整條命令包在do shell script with administrator privileges里。執(zhí)行是能成功但處理 root 權(quán)限下的 PATH 很麻煩——系統(tǒng)的 root 環(huán)境里沒有 Homebrew 路徑必須顯式寫全路徑。同時(shí)不是所有 cask 都希望用 root 裝有些裝完還會出現(xiàn)文件屬主不一致的問題。后來我放棄了全權(quán)提權(quán)改成兩段式設(shè)計(jì)普通命令直接用當(dāng)前用戶執(zhí)行遇到真正需要權(quán)限的 caskGUI 會彈提示告訴用戶去終端跑一次。這個(gè)妥協(xié)換來了穩(wěn)定性也避免了很多權(quán)限相關(guān)的邊界情況。教訓(xùn)是GUI 應(yīng)用不要試圖去模擬終端的所有能力系統(tǒng)該讓你彈窗授權(quán)的時(shí)候老老實(shí)實(shí)把上下文交還給用戶體系。4.2 JSON 結(jié)構(gòu)只有跑過才知道formula 和 cask 的字段地雷第二個(gè)坑是 JSON 結(jié)構(gòu)的兼容性。brew info --jsonv2輸出的字段非常多但不同版本的 Homebrew 返回字段并不完全一致比如 deprecated、disabled、caveats 這些字段有的包有、有的包沒有。更麻煩的是 cask 和 formula 完全是兩套結(jié)構(gòu)cask 有 url、sha256、artifacts 這樣的字段formula 里根本沒有。如果直接強(qiáng)類型解析遇到缺失字段整個(gè)命令就崩了。我的方案是所有可選字段全部用 Option 包裹每一個(gè)字段都做默認(rèn)值兜底。代碼會顯得啰嗦但換來的是兼容性。這個(gè)項(xiàng)目跑了幾十臺不同系統(tǒng)版本、不同 Homebrew 版本的機(jī)器沒因?yàn)樽侄稳笔П肋^一次。如果你也要解析 brew 的 JSON記住一條原則永遠(yuǎn)別假設(shè)字段一定存在除非你在某個(gè)固定版本上鎖死過。4.3 ANSI 轉(zhuǎn)義符與進(jìn)度條終端輸出不能直接塞進(jìn)界面第三個(gè)坑看起來不起眼處理不好體驗(yàn)極差。brew 輸出里大量存在 ANSI 轉(zhuǎn)義序列比如顏色代碼\x1b[32m、光標(biāo)控制\x1b[2K、回車換行\(zhòng)r。直接把 stdout 文本塞進(jìn)前端日志窗口顯示出來的是一堆[32m開頭的亂碼。排查過程并不復(fù)雜先確認(rèn)亂碼來源是轉(zhuǎn)義字符然后決定在 Rust 側(cè)統(tǒng)一處理。我把行緩沖和 ANSI 清理封裝成一個(gè)模塊規(guī)則就三條按行切分清掉 ANSI處理\r覆蓋邏輯。這樣前端拿到的永遠(yuǎn)是干凈的純文本渲染的時(shí)候再用 CSS 給不同日志級別上色既保留了可讀性又避免了將原始終端序列直接注入頁面的安全風(fēng)險(xiǎn)。4.4 并發(fā)鎖沖突用戶多點(diǎn)一次brew 就罷工一次第四個(gè)坑是并發(fā)問題。Homebrew 自身有鎖機(jī)制同一時(shí)間只允許一個(gè) brew 進(jìn)程執(zhí)行寫操作。如果用戶手快在 GUI 里同時(shí)點(diǎn)了兩個(gè)升級任務(wù)Rust 側(cè)會同時(shí) spawn 兩個(gè) brew 進(jìn)程第二個(gè)進(jìn)程就會報(bào)錯(cuò)Another active Homebrew process is already in progress。這個(gè)報(bào)錯(cuò)信息本身有誤導(dǎo)性用戶會以為是自己搞壞了系統(tǒng)。我的解決辦法是在 Rust 側(cè)實(shí)現(xiàn)一個(gè)全局任務(wù)隊(duì)列所有 brew 寫操作都進(jìn)入隊(duì)列排隊(duì)同一時(shí)間只執(zhí)行一個(gè)。隊(duì)列的進(jìn)度顯示在側(cè)邊欄用戶隨時(shí)能取消排隊(duì)任務(wù)。這個(gè)改動之后鎖沖突的反饋就再也沒有出現(xiàn)過。如果你在寫類似的工具記住永遠(yuǎn)不要讓用戶的操作直接觸發(fā)并發(fā)命令GUI 比終端更需要任務(wù)隊(duì)列。5. 跑了幾十臺機(jī)器后的實(shí)測感受項(xiàng)目做到這個(gè)階段我在朋友和同事的機(jī)器上做了幾輪實(shí)測。分享一下真實(shí)表現(xiàn)有好有壞。5.1 性能數(shù)據(jù)一次 upgrade 從點(diǎn)擊到完成要多久在 128 個(gè)包的中等規(guī)模系統(tǒng)上BrewUI 打開主列表首次全量刷新耗時(shí)約 2.4 秒之后走緩存啟動大約 0.4 秒。一次brew upgrade --dry-run預(yù)覽約 1.8 秒真正執(zhí)行升級的時(shí)間取決于網(wǎng)絡(luò)和包大小GUI 本身的開銷可以忽略不計(jì)。對比終端GUI 每次操作多出來的就是進(jìn)程調(diào)度的 20~50ms用戶感知不到。比較明顯的體驗(yàn)提升是在輸出的可視化上。終端里 install 輸出 40 行你往往只關(guān)心最后幾行有沒有報(bào)錯(cuò)GUI 把進(jìn)度條、日志、錯(cuò)誤定位分開展示掃一眼就知道發(fā)生了什么。5.2 依賴圖譜在 80 包的環(huán)境下能不能撐住我最擔(dān)心的依賴圖譜在實(shí)際測試?yán)镆馔獾乜缸×恕?0 個(gè)包的依賴圖默認(rèn)兩層展開大約 200 到 300 個(gè)節(jié)點(diǎn)vis-network 的 hierarchical 渲染在 2 秒內(nèi)完成交互滑動基本流暢。但如果全量展開所有依賴節(jié)點(diǎn)數(shù)會到 1500 以上此時(shí)縮放和拖拽已經(jīng)明顯掉幀。所以默認(rèn)只展開兩層的限制非常有必要既保有探索能力又不會讓沒經(jīng)驗(yàn)的用戶一上來就把頁面拖垮。5.3 BrewUI 的優(yōu)勢與勸退點(diǎn)我分別講清楚我不能只講好話。實(shí)測里也發(fā)現(xiàn)幾個(gè)勸退場景。第一如果你已經(jīng)把 brew 命令背得滾瓜爛熟GUI 無論如何替代不了你已有的肌肉記憶硬要切換反而低效。第二brew 在后臺自動更新、日志查看這類低頻操作GUI 的價(jià)值不大終端里一個(gè)命令能搞定的事情沒必要打開應(yīng)用。第三Tauri 應(yīng)用的窗口在某些舊版 macOS 上有渲染小瑕疵雖然不影響功能但看著不如原生 Swift 應(yīng)用精致。但如果你是剛開始用 Homebrew或者經(jīng)常需要幫別人處理 brew 問題BrewUI 的價(jià)值就很明顯。它把操作變成了“看得見的流程”而不是一串需要記憶的命令。特別是在幫別人解決問題的時(shí)候GUI 讓對方看著你的每一步操作溝通成本會低很多。6. 后續(xù)還能怎么玩我的擴(kuò)展計(jì)劃項(xiàng)目做到這里后續(xù)方向已經(jīng)比較清晰。接下來最想做三個(gè)方向都是來自真實(shí)需求。6.1 Brewfile 圖形化管理Homebrew 官方支持 Brewfile一個(gè)文件就能描述整套環(huán)境。想在 BrewUI 里加一個(gè) Brewfile 編輯器左邊是當(dāng)前已安裝包的可勾選列表右邊是生成的 Brewfile 文本支持導(dǎo)入、導(dǎo)出和差異對比。換新機(jī)器時(shí)不用再手動敲brew bundle install拖一個(gè)文件進(jìn)界面就能還原環(huán)境。這個(gè)功能對經(jīng)常在多臺 Mac 之間切換的開發(fā)者會很實(shí)用。6.2 集成 mas連 Mac App Store 一起管macOS 上很多人同時(shí)用 Homebrew 和 mas 管理應(yīng)用一個(gè)管命令行工具一個(gè)管 App Store 應(yīng)用。如果 BrewUI 能一步把這兩個(gè)來源合并到同一個(gè)界面會比單純做 brew GUI 更貼近真實(shí)使用場景。目前我在調(diào)研 mas 的接口方式核心思路還是走命令行邊界不直接碰 App Store 的數(shù)據(jù)庫。6.3 與 CI 場景的結(jié)合最后一個(gè)方向是把 BrewUI 做成一類“包管理儀表盤”在 CI 或開發(fā)容器里用純 Web 模式跑方便團(tuán)隊(duì)共享一個(gè)可視化的包依賴狀態(tài)。這個(gè)想法來自一個(gè)朋友的建議他在團(tuán)隊(duì)里經(jīng)常要統(tǒng)一所有人的本地環(huán)境版本如果能有一個(gè)共享的視圖看哪些包過時(shí)、哪些有安全更新會方便很多。目前這個(gè)方向還在調(diào)研Tauri 的 Web 預(yù)編譯模式理論上可行但需要更多驗(yàn)證?;仡^做這個(gè)項(xiàng)目我最大的體會是工具的價(jià)值不在于“高級”而在于它是否降低了使用者內(nèi)心的不確定感。BrewUI 沒有發(fā)明任何新命令它做的事情只是把 brew 原來就有的能力用圖形界面重新表達(dá)了一遍。開發(fā)過程中我反復(fù)提醒自己不要貪多、不要炫技每加一個(gè)功能都要問一句“這個(gè)操作讓用戶更安心了嗎”。答案不明確的功能寧可先不做。如果你也在用 Homebrew不妨試試給自己配一個(gè)可視化入口也許你會發(fā)現(xiàn)管理包這件事并沒有想象中那么枯燥。