9 điểm bởi GN⁺ 2025-09-18 | 2 bình luận | Chia sẻ qua WhatsApp
  • Homebrew là trình quản lý gói trên macOS giúp cài đặt và quản lý công cụ CLI dễ dàng, cho phép nhà phát triển cấu hình môi trường hệ thống hiệu quả bằng các công cụ họ thường dùng
  • Hướng dẫn này giải thích quy trình phân phối các script CLI cá nhân bằng Homebrew, đồng thời chỉ ra cách đơn giản hóa việc bảo trì thông qua tích hợp GitHub và quy trình tự động hóa
  • Quy trình phân phối diễn ra theo chuỗi tạo CLI → phát hành GitHub Release → tạo Tap → viết và cập nhật Formula, và cuối cùng có thể cài đặt chỉ với các lệnh brew tapbrew install
  • Nếu hiểu hệ thống thuật ngữbest practice của Homebrew, bạn có thể triển khai phân phối ổn định với khả năng tái lập cao hơn và bảo mật chuỗi cung ứng tốt hơn
  • Có thể tự động hóa bằng workflow GitHub Actions, và một khi đã thiết lập xong thì việc phát hành các CLI khác về sau sẽ rất đơn giản

Bối cảnh và động lực

  • Homebrew là trình quản lý gói được ưa chuộng khi cài đặt công cụ CLI và được nhiều nhà phát triển sử dụng
  • Tuy nhiên, khi phân phối CLI do chính mình tạo ra, nhiều người thường chọn npm hoặc RubyGem, còn cách phân phối qua Homebrew có thể tạo cảm giác xa lạ về mặt quy trình
  • Kho core chính thức của Homebrew có chủ trương không mặn mà với việc đưa các công cụ tự tạo vào, nên nhà phát triển thông thường sẽ phân phối qua tap và formula riêng
  • Hướng dẫn này được giải thích dựa trên kinh nghiệm phân phối một CLI đơn giản viết bằng Ruby

Giải thích thuật ngữ

  • Homebrew dùng bộ thuật ngữ đặc trưng theo chủ đề nấu bia, nên nếu hiểu chúng thì sẽ dễ nắm cấu trúc hệ thống hơn
    • Formula là tệp định nghĩa gói, chứa hướng dẫn cài đặt mã nguồn hoặc binary
    • Tap là kho Git chứa các Formula, dùng để quản lý các gói tùy chỉnh theo người dùng hoặc tổ chức
    • Cask là manifest cài đặt cho ứng dụng GUI hoặc binary lớn; tương tự Formula nhưng xử lý các tệp đã build sẵn
    • Bottle là dạng gói binary build sẵn được sao chép thay vì build từ mã nguồn, giúp tăng tốc cài đặt
    • Cellar là thư mục nơi các Formula đã cài được đặt vào, ví dụ đường dẫn /opt/homebrew/Cellar
    • Keg là thư mục instance cài đặt của một Formula cụ thể, được bố trí theo từng phiên bản bên trong Cellar

Tổng quan

  • Kho Homebrew core không nhận nội dung ngách hoặc do cá nhân gửi lên, vì vậy người dùng phải tạo kho tap riêng để phân phối CLI
    • 1. Tạo CLI, đưa lên GitHub và phát hành theo tag
    • 2. Tạo Tap bằng brew tap-new rồi push lên GitHub
    • 3. Viết Formula bằng brew create (gồm URL tarball và SHA256)
    • 4. Mỗi khi phát hành phiên bản mới thì cập nhật Formula để người dùng có thể dễ dàng cài bằng lệnh brew install
  • Sau khi phân phối xong, người dùng có thể cài CLI chỉ với hai lệnh: brew tap your_github_handle/tapbrew install your_cool_cli
    • Hướng dẫn này bỏ qua phần phát triển CLI và tập trung vào việc tạo tap, tạo Formula và quy trình cập nhật
    • Ví dụ sử dụng CLI imsg, công cụ tạo web archive tương tác từ cơ sở dữ liệu iMessage
    Quảng cáo

Tạo tap

  • Làm theo hướng dẫn tạo tap của Homebrew, thay thế bằng tên người dùng GitHub hoặc tên tổ chức của bạn
    • Để gom tất cả công cụ CLI sau này vào một tap, tác giả khuyến nghị đặt tên homebrew-tap; tiền tố homebrew được CLI xử lý đặc biệt và hậu tố tap là thông lệ phổ biến
  • Chạy lệnh tạo tap: brew tap-new searlsco/homebrew-tap
    • Lệnh này tạo scaffold tại /opt/homebrew/Library/Taps/searlsco/homebrew-tap
    • Tạo kho tương ứng trên GitHub rồi push nội dung vừa sinh ra: cd /opt/homebrew/Library/Taps/searlsco/homebrew-tap, git remote add origin git@github.com:searlsco/homebrew-tap.git, git push -u origin main
  • Sau khi sở hữu tap, người dùng khác có thể clone kho này bằng lệnh brew tap searlsco/tap để đặt vào /opt/homebrew/Library/Taps
    • Ban đầu chưa có nội dung hữu ích, nhưng có thể dùng để xác nhận hành vi cơ bản

Tạo Formula

  • Homebrew có thể tham chiếu trực tiếp kho GitHub, nhưng khuyến nghị dùng tarball theo phiên bản và checksum để tăng khả năng tái lập và bảo mật chuỗi cung ứng mã nguồn mở
    Quảng cáo
  • Lệnh tạo Formula: brew create https://github.com/searlsco/imsg/archive/refs/tags/v0.0.5.tar.gz --tap searlsco/homebrew-tap --set-name imsg --ruby
    • Cờ --tap chỉ định tap tùy chỉnh và đặt Formula vào /opt/homebrew/Library/Taps/searlsco/homebrew-tap/Formula
    • --set-name imsg đặt tên Formula một cách tường minh; nên chọn tên đủ độc nhất để tránh trùng (ví dụ lưu ý xung đột với các CLI như TLDR hay standard đã có)
    • --rubypreset template cho Ruby CLI, một trong nhiều tùy chọn giúp đơn giản hóa việc tùy biến
  • Formula tạo ra lúc đầu có thể chưa chạy được, nên có thể dùng LLM để chỉnh sửa: chạy brew install --verbose imsg, đưa lỗi vào ChatGPT rồi lặp lại việc cập nhật Formula
    • Tệp cuối cùng Formula/imsg.rb có thể được sao chép làm điểm khởi đầu cho việc phân phối Ruby CLI
    • Khi phân phối qua Homebrew thay vì trình quản lý gói đặc thù ngôn ngữ, người dùng vẫn có thể nâng cấp mượt mà ngay cả khi bạn thay đổi ngôn ngữ triển khai

Những điểm nổi bật chính của Formula

  • Mọi Formula đều được viết bằng Ruby, vì trước thời JavaScript hay AI thì nhiều công cụ phát triển phổ biến được xây trên Ruby
    • Có thể chỉ định kho Git bằng phương thức head, nhưng hiệu quả thực tế của nó không rõ ràng
    • Việc thêm livecheck rất đáng giá vì giúp cập nhật phiên bản Formula dễ hơn
    • Bài kiểm tra chạy binary có thể được triển khai đơn giản bằng cách kiểm tra đầu ra trợ giúp; đừng bị các comment được sinh ra làm nản lòng
    • Dùng lệnh brew style searlsco/tap để kiểm tra lỗi style
    • Mặc định uses_from_macos "ruby" trong template --ruby dùng phiên bản 2.6.10 (phát hành từ trước COVID, đã EOL 3 năm), nên tác giả khuyến nghị phụ thuộc vào Formula ruby mới hơn bằng depends_on "ruby@3"
  • Khi đã hài lòng với Formula, chỉ cần git push để phát hành live; người dùng có thể cài bằng brew tap searlsco/tapbrew install imsg
Quảng cáo

Cập nhật Formula cho từng bản phát hành CLI

  • Việc phải cập nhật thủ công url và hash sha256 ở đầu Formula sau mỗi lần phát hành khá phiền; tác giả còn cho rằng ngay cả thao tác push tag hay tạo GitHub Release cũng đã mệt mỏi
    • Có thể tạo PR bằng lệnh bump-formula-pr của Homebrew hoặc bằng GitHub Actions, nhưng quy trình fork và PR lại phức tạp một cách không cần thiết
    • Nếu bạn là chủ sở hữu tap, cách đơn giản hơn là commit trực tiếp vào nhánh main
  • Để tránh điều này, tác giả khuyến nghị thêm workflow GitHub vào kho Formula để tự động cập nhật tap khi có release
    • Có thể sao chép và dùng ví dụ workflow
    • Cần cấu hình: tạo GitHub Personal Access Token (PAT), cấp quyền ContentWrite cho kho homebrew-tap, rồi lưu vào Secrets của kho Formula dưới tên HOMEBREW_TAP_TOKEN
    • Chỉ định tap và Formula bằng biến môi trường (ví dụ dòng 13-15)
    • Nên dùng cấu hình tài khoản bot GitHub để cập nhật: GH_EMAIL: 41898282+github-actions[bot]@users.noreply.github.com, GH_NAME: github-actions[bot]
  • Sau khi tạo release rồi chạy git push --tags, bản cập nhật sẽ được áp dụng tự động chỉ trong vài giây; người dùng có thể nâng cấp bằng brew updatebrew upgrade imsg

Điểm hay nhất

  • Quy trình này tuy phức tạp, nhưng sau khi đã thiết lập tap và hoàn thành một ví dụ Formula thì việc phân phối thêm CLI gần như trở nên rất nhỏ nhặt
    • Có thể xuất bản Formula mới chỉ trong vài phút nên khá tiện lợi
  • Quy trình chính thức của Homebrew có phần rườm rà, nhưng tự động hóa sẽ giúp mọi thứ dễ chịu hơn
    • Nó giảm bớt sự phiền hà giữa mỗi lần release và phân phối công cụ, đồng thời có thể mở rộng để hỗ trợ CLI viết bằng nhiều ngôn ngữ khác nhau
  • Tác giả không chắc mình có thực sự đăng thêm Formula nào nữa hay không, nhưng cảm thấy hài lòng vì khả năng đó giờ đã rộng mở

2 bình luận

 
lamanus 2025-09-18

Có thể tạo PR bằng lệnh bump-formula-pr của Homebrew hoặc GitHub Actions, nhưng quy trình fork và PR lại phức tạp một cách không cần thiết

Có tùy chọn --no-fork, nên có thể push trực tiếp lên nhánh và merge, đồng thời cung cấp tính năng tự động cập nhật.

 
GN⁺ 2025-09-18
Ý kiến trên Hacker News
  • Quy tắc đặt tên của Homebrew đôi khi cũng khiến mình thấy hơi rối, nhưng càng dùng càng thấy đây thực sự là một công cụ rất hữu ích
    Ngoài ra mình cũng không ngờ quy trình tạo tap riêng để phân phối công cụ của mình lại đơn giản đến vậy
    Mình tò mò không biết so với các trình quản lý gói theo ngôn ngữ (ví dụ: uv) thì nó tốt hơn ở điểm nào
    Đặc biệt là liệu nó có dễ hơn cho những người không thuộc một hệ sinh thái cụ thể nào hay không, tức là có lợi thế hơn về tính phổ quát hay không

    • Xin gửi lời cảm ơn; các công cụ khác dùng package registry thường cần tạo tài khoản, xác thực hai bước và quy trình ký các thứ
      Homebrew thì dựa vào điều khoản dịch vụ (ToS) của GitHub như một nền tảng tin cậy, nên tổng thể được đơn giản hóa hơn rất nhiều
      Nhờ cách này mà đội ngũ Homebrew cũng có thể giảm được rất nhiều độ phức tạp

    • Nếu nói theo tiêu chuẩn package Python, thì việc cố gắng đóng gói mọi thứ cùng lúc như uv là điều khá khó trong thực tế
      Vì vậy thông thường người ta dùng cách chỉ cài các dependency đã được cố định trong môi trường venv
      Có thể xem ví dụ cụ thể ở formula này
      Về uv, mình đã thử hỗ trợ package riêng tư bằng các công cụ chính thức (brew update-python-resources, homebrew-pypi-poet) nhưng không hoạt động đúng như mong muốn,
      nên đã tự tạo uvbrew để hỗ trợ tạo resource
      Cũng có tài liệu chính thức để tham khảo khi viết formula Python cho Homebrew

  • Nếu là lập trình viên Go thì mình khuyên dùng công cụ Goreleaser
    Nó giúp phân phối binary trong tap cá nhân cực kỳ dễ dàng (dù cách này bị cấm trong core chính thức)

    • Gần đây mình mới biết Goreleaser giờ không chỉ hỗ trợ Go mà còn cả Rust, TypeScript, Python, Zig và nhiều ngôn ngữ khác
      Nó khá hữu dụng trong việc quản lý dự án theo từng ngôn ngữ
  • Cá nhân mình nghĩ việc quản lý cập nhật trực tiếp từ phía tap sẽ lý tưởng hơn một chút
    Nói chung nó tương tự cách cập nhật từ upstream
    Nếu tham khảo workflow này, bạn có thể dễ dàng cập nhật cả formula/cask mà mình không sở hữu
    Có thể quét tất cả bằng lệnh brew bump, tự động tạo PR và còn chạy brew test-bot để kiểm thử
    Có thể xem ví dụ PR thực tế ở đây

    • Mình thấy đây là một ý tưởng hay
      Bình thường mình cứ ngại vì nghĩ thời lượng GitHub Actions là thứ cần tiết kiệm, nhưng vì mã nguồn mở thì được miễn phí nên dùng như vậy cũng hợp lý
  • Mình đã tự viết homebrew-bump-revision làm workflow tự động bump phiên bản cho Homebrew tap của riêng mình
    Mình đang dùng nó khá tốt cho nhiều dự án cá nhân

    • Trông rất hay
      Mình lười nên chưa thử, nhưng đúng là một công cụ tốt
  • Trong podcast Ruby Rogues từng có một tập nói về nhiều mẹo khác nhau khi phân phối CLI bằng Homebrew
    Có thể nghe thêm ở liên kết tập này

  • Mình phát hiện một điểm thú vị về việc đóng gói công cụ Python
    Một số package Python tạo ra vòng lặp dependency trong quá trình build nên không tương thích với Homebrew
    pip thì không gặp vấn đề này vì tải binary release, nhưng Homebrew phải tự build cả dependency nên quá trình này mất nhiều thời gian hơn rất nhiều
    Vì vậy ngay cả một dự án Python cỡ trung bình cũng có thể mất hơn một giờ để build "bottle"

  • Từ khi bắt đầu dùng nix để quản lý hệ thống, mình chưa hối hận lấy một lần
    Điều duy nhất mình thấy tiếc là vẫn phải phụ thuộc vào Windows vì game nhiều người chơi