9 คะแนน โดย GN⁺ 2025-09-18 | 2 ความคิดเห็น | แชร์ทาง WhatsApp
  • Homebrew เป็นตัวจัดการแพ็กเกจที่ช่วยให้ติดตั้งและจัดการ เครื่องมือ CLI บน macOS ได้ง่าย ทำให้นักพัฒนาสามารถจัดสภาพแวดล้อมของระบบได้อย่างมีประสิทธิภาพด้วยเครื่องมือที่ใช้บ่อย
  • คู่มือนี้อธิบายกระบวนการเผยแพร่สคริปต์ CLI ส่วนตัวด้วย Homebrew และแสดงวิธีทำให้การดูแลรักษาง่ายขึ้นผ่าน การเชื่อมต่อกับ GitHub และเวิร์กโฟลว์อัตโนมัติ
  • กระบวนการเผยแพร่จะเป็นลำดับ สร้าง CLI → GitHub release → สร้าง Tap → เขียนและอัปเดต Formula และสุดท้ายสามารถติดตั้งได้ด้วยคำสั่ง brew tap และ brew install เท่านั้น
  • หากเข้าใจ ระบบคำศัพท์ และ แนวทางปฏิบัติที่ดี ของ Homebrew ก็จะสามารถเผยแพร่ได้อย่างเสถียรพร้อมเสริมความสามารถในการทำซ้ำและความปลอดภัยของซัพพลายเชน
  • สามารถทำให้เป็นอัตโนมัติด้วยเวิร์กโฟลว์ GitHub Actions ได้ และเมื่อตั้งค่าไว้ครั้งหนึ่งแล้ว การเผยแพร่ CLI ตัวอื่นในภายหลังก็จะ ง่ายมาก

พื้นหลังและแรงจูงใจ

  • Homebrew เป็น ตัวจัดการแพ็กเกจที่นิยมใช้เมื่อติดตั้งเครื่องมือ CLI และมีนักพัฒนาจำนวนมากใช้งาน
  • แต่หลายคนมักเผยแพร่ CLI ที่สร้างเองผ่าน npm หรือ RubyGem ทำให้แนวทางการเผยแพร่ผ่าน Homebrew อาจรู้สึกไม่คุ้นขั้นตอน
  • เนื่องจากคลัง core อย่างเป็นทางการของ Homebrew มีนโยบายไม่ค่อยรับเครื่องมือทำเองเข้าไป ทีม Homebrew จึงทำให้ นักพัฒนาทั่วไปต้องเผยแพร่ผ่าน tap และ formula แยกต่างหาก
  • คู่มือนี้อธิบายโดยอิงจาก ประสบการณ์การเผยแพร่ CLI แบบง่ายที่เขียนด้วย Ruby

คำอธิบายคำศัพท์

  • Homebrew ใช้ คำศัพท์เฉพาะ ที่สะท้อนธีมการต้มเบียร์ ดังนั้นหากเข้าใจก็จะมองโครงสร้างระบบได้ง่ายขึ้น
    • Formula คือไฟล์ นิยามแพ็กเกจ ที่มีคำสั่งสำหรับติดตั้งซอร์สโค้ดหรือไบนารี
    • Tap คือ Git repository ของ Formula หลายตัว ใช้จัดการแพ็กเกจแบบกำหนดเองตามผู้ใช้หรือองค์กร
    • Cask คือแมนิเฟสต์สำหรับติดตั้ง แอป GUI หรือไบนารีขนาดใหญ่ คล้าย Formula แต่ใช้กับไฟล์ที่สร้างเสร็จแล้ว
    • Bottle คือรูปแบบที่คัดลอก แพ็กเกจไบนารีที่ build ไว้ล่วงหน้า แทนการ build จากซอร์ส ช่วยให้ติดตั้งได้เร็วขึ้น
    • Cellar คือ ไดเรกทอรี ที่เก็บ Formula ที่ติดตั้งแล้ว เช่นพาธ /opt/homebrew/Cellar
    • Keg คือ ไดเรกทอรีของอินสแตนซ์การติดตั้ง ของ Formula เฉพาะตัวหนึ่ง ซึ่งจะถูกจัดวางตามเวอร์ชันภายใน Cellar

ภาพรวม

  • เนื่องจากคลัง core ของ Homebrew ไม่รับคอนเทนต์ที่เฉพาะทางมากหรือเป็นการส่งเข้ามาแบบส่วนบุคคล ผู้ใช้จึงต้อง สร้าง tap repository แยกต่างหาก เพื่อเผยแพร่ CLI
    • 1. สร้าง CLI แล้วอัปขึ้น GitHub และทำ tag release
    • 2. สร้าง Tap ด้วย brew tap-new แล้ว push ขึ้น GitHub
    • 3. สร้าง Formula ด้วย brew create (รวม URL ของ tarball และ SHA256)
    • 4. ทุกครั้งที่ออกเวอร์ชันใหม่ ให้อัปเดต Formula เพื่อให้ผู้ใช้ติดตั้งได้ง่ายด้วยคำสั่ง brew install
  • เมื่อเผยแพร่เสร็จ ผู้ใช้จะติดตั้ง CLI ได้ด้วยสองคำสั่ง: brew tap your_github_handle/tap และ brew install your_cool_cli
    • คู่มือนี้จะข้ามขั้นตอนการพัฒนา CLI และเน้นที่การสร้าง tap, การสร้าง Formula และกระบวนการอัปเดต
    • ตัวอย่างที่ใช้คือ CLI ชื่อ imsg ซึ่งสร้าง เว็บอาร์ไคฟ์แบบโต้ตอบได้ จากฐานข้อมูล iMessage

การสร้าง tap

  • ให้ทำตาม คู่มือการสร้าง tap ของ Homebrew โดยแทนที่ด้วยชื่อผู้ใช้หรือชื่อองค์กรบน GitHub ของคุณ
    • เพื่อรวบรวมเครื่องมือ CLI ทั้งหมดไว้ใน tap เดียวกันในอนาคต จึงแนะนำชื่อ homebrew-tap โดยคำนำหน้า homebrew จะถูกจัดการเป็นพิเศษใน CLI และคำนำหน้า tap ก็เป็นธรรมเนียมที่ใช้กัน
  • รันคำสั่งสร้าง tap: brew tap-new searlsco/homebrew-tap
    • คำสั่งนี้จะสร้าง scaffold ไว้ที่ /opt/homebrew/Library/Taps/searlsco/homebrew-tap
    • จากนั้นสร้าง repository ที่สอดคล้องกันบน GitHub และ push เนื้อหาที่สร้างขึ้น: cd /opt/homebrew/Library/Taps/searlsco/homebrew-tap, git remote add origin git@github.com:searlsco/homebrew-tap.git, git push -u origin main
  • เมื่อเป็นเจ้าของ tap แล้ว ผู้ใช้อื่นสามารถ clone repository นี้ด้วยคำสั่ง brew tap searlsco/tap เพื่อนำไปวางใน /opt/homebrew/Library/Taps ได้
    • ช่วงแรกอาจยังไม่มีอะไรที่มีประโยชน์อยู่ภายใน แต่ก็สามารถยืนยันการทำงานพื้นฐานได้

การสร้าง Formula

  • แม้ Homebrew จะอ้างอิง GitHub repository ได้โดยตรง แต่แนะนำให้ใช้ tarball แบบมีเวอร์ชัน และ checksum เพื่อเพิ่มความสามารถในการทำซ้ำและความปลอดภัยของซอฟต์แวร์โอเพนซอร์สในซัพพลายเชน
  • คำสั่งสร้าง Formula: brew create https://github.com/searlsco/imsg/archive/refs/tags/v0.0.5.tar.gz --tap searlsco/homebrew-tap --set-name imsg --ruby
    • แฟล็ก --tap ใช้ระบุ tap แบบกำหนดเอง และวาง Formula ไว้ที่ /opt/homebrew/Library/Taps/searlsco/homebrew-tap/Formula
    • --set-name imsg ใช้ตั้งชื่อ Formula อย่างชัดเจน ควรเลือกให้ไม่ซ้ำเพื่อหลีกเลี่ยงการชนชื่อ (เช่น ระวังชนกับ TLDR หรือ standard CLI ที่มีอยู่แล้ว)
    • --ruby คือ template preset สำหรับ Ruby CLI ซึ่งเป็นหนึ่งในหลายตัวเลือกที่ช่วยให้การปรับแต่งง่ายขึ้น
  • Formula ที่สร้างขึ้นอาจยังใช้งานไม่ได้ในตอนแรก จึงสามารถใช้ LLM ช่วยแก้ไขได้: รัน brew install --verbose imsg แล้วนำข้อผิดพลาดไปใส่ใน ChatGPT จากนั้นอัปเดต Formula ซ้ำไปเรื่อย ๆ
    • ไฟล์สุดท้าย Formula/imsg.rb สามารถคัดลอกไปใช้เป็นจุดเริ่มต้นสำหรับการเผยแพร่ Ruby CLI ได้
    • การเผยแพร่ผ่าน Homebrew แทนตัวจัดการแพ็กเกจเฉพาะภาษา ช่วยให้แม้จะเปลี่ยนภาษา implementation ในภายหลัง ผู้ใช้ก็ยังอัปเกรดได้อย่างราบรื่น

จุดสำคัญของ Formula

  • Formula ทุกตัวจะเขียนด้วย Ruby เพราะเครื่องมือพัฒนาที่เคยได้รับความนิยมก่อนยุค JavaScript หรือ AI นั้นจำนวนมากสร้างอยู่บน Ruby
    • สามารถใช้เมธอด head เพื่อระบุ Git repository ได้ แต่ผลลัพธ์จริงยังไม่แน่ชัด
    • การเพิ่ม livecheck มีคุณค่าเพราะช่วยให้อัปเดตเวอร์ชันของ Formula ได้ง่ายขึ้น
    • การทดสอบการรันไบนารีสามารถทำอย่างง่ายได้ด้วยการตรวจสอบผลลัพธ์ของ help output จึงไม่ต้องกังวลกับคอมเมนต์ที่ถูกสร้างมาให้
    • ใช้คำสั่ง brew style searlsco/tap เพื่อตรวจสอบข้อผิดพลาดด้านสไตล์
    • ค่าเริ่มต้น uses_from_macos "ruby" ของ template --ruby จะใช้เวอร์ชัน 2.6.10 (ออกก่อนช่วง COVID และหมดอายุการสนับสนุนมา 3 ปีแล้ว) ดังนั้นจึงแนะนำให้พึ่งพา Ruby Formula รุ่นใหม่ด้วย depends_on "ruby@3"
  • เมื่อพอใจกับ Formula แล้ว ก็เผยแพร่จริงได้ด้วย git push และผู้ใช้จะติดตั้งได้ด้วย brew tap searlsco/tap และ brew install imsg

การอัปเดต Formula สำหรับแต่ละ CLI release

  • ค่า url และ hash sha256 ด้านบนของ Formula ต้องอัปเดตด้วยตนเองทุก release ซึ่งยุ่งยาก และผู้เขียนยังชี้ว่าการ push tag หรือสร้าง GitHub release เองก็ชวนเหนื่อยเช่นกัน
    • สามารถใช้คำสั่ง bump-formula-pr ของ Homebrew หรือ GitHub Action เพื่อสร้าง PR ได้ แต่ขั้นตอน fork และ PR นั้นซับซ้อนเกินจำเป็น
    • หากคุณเป็นเจ้าของ tap วิธีที่ตรงไปตรงมาและเหมาะกว่าคือ commit เข้า branch main โดยตรง
  • เพื่อหลีกเลี่ยงเรื่องนี้ จึงแนะนำให้เพิ่ม GitHub workflow ใน repository ของ Formula เพื่ออัปเดต tap อัตโนมัติเมื่อมี release
    • สามารถคัดลอก workflow example ไปใช้ได้
    • ต้องตั้งค่าบางอย่าง: สร้าง GitHub personal access token (PAT) แล้วให้สิทธิ์ ContentWrite กับ repository homebrew-tap จากนั้นเก็บไว้ใน Secrets ของ repository Formula โดยใช้ชื่อ HOMEBREW_TAP_TOKEN
    • ระบุ tap และ Formula ผ่าน environment variables (เช่น บรรทัด 13-15)
    • แนะนำให้อัปเดตค่าบัญชีบอตของ GitHub: GH_EMAIL: 41898282+github-actions[bot]@users.noreply.github.com, GH_NAME: github-actions[bot]
  • หลังสร้าง release แล้ว เมื่อรัน git push --tags จะมีการอัปเดตอัตโนมัติภายในไม่กี่วินาที และผู้ใช้จะอัปเกรดได้ด้วย brew update และ brew upgrade imsg

ส่วนที่ดีที่สุด

  • แม้กระบวนการนี้จะซับซ้อน แต่เมื่อทำการตั้งค่า tap และมีตัวอย่าง Formula หนึ่งตัวเสร็จแล้ว การเผยแพร่ CLI เพิ่มเติมจะ แทบเป็นเรื่องเล็กน้อย
    • สามารถเผยแพร่ Formula ใหม่ได้ภายในไม่กี่นาที จึงสะดวกมาก
  • แม้กระบวนการอย่างเป็นทางการของ Homebrew จะค่อนข้างซับซ้อน แต่ระบบอัตโนมัติช่วยให้ใช้งานสะดวกขึ้น
    • ลดความยุ่งยากระหว่างการ release และการเผยแพร่ของแต่ละเครื่องมือ และยังรองรับการขยายไปสู่ CLI ที่เขียนด้วยภาษาหลากหลายได้
  • ผู้เขียนเองก็ยังไม่แน่ใจว่าจะเผยแพร่ Formula อื่นเพิ่มอีกหรือไม่ แต่ก็พอใจกับการที่อย่างน้อยมีความเป็นไปได้นี้เปิดอยู่

2 ความคิดเห็น

 
lamanus 2025-09-18

สามารถสร้าง PR ได้ด้วยคำสั่ง bump-formula-pr ของ Homebrew หรือผ่าน GitHub Actions แต่กระบวนการ fork และ PR ซับซ้อนเกินความจำเป็น

มีออปชัน --no-fork จึงสามารถ push ไปยัง branch ได้โดยตรงและ merge ได้ พร้อมทั้งมีฟีเจอร์อัปเดตอัตโนมัติ

 
GN⁺ 2025-09-18
ความคิดเห็นจาก Hacker News
  • แม้ว่ากฎการตั้งชื่อของ Homebrew บางครั้งจะทำให้งงอยู่บ้าง แต่ก็ยังรู้สึกอยู่เสมอว่าโดยรวมแล้วมันเป็นเครื่องมือที่มีประโยชน์มาก
    และก็ไม่คิดมาก่อนว่ากระบวนการสร้าง tap ของตัวเองเพื่อแจกจ่ายเครื่องมือจะง่ายขนาดนี้
    เลยสงสัยว่าเมื่อเทียบกับตัวจัดการแพ็กเกจตามภาษาแต่ละภาษา (เช่น uv) แล้ว มันดีกว่าในแง่ไหน
    โดยเฉพาะอยากรู้ว่ามันง่ายกว่าสำหรับคนที่ไม่ได้อยู่ใน ecosystem ใด ecosystem หนึ่งหรือไม่ หรือก็คือได้เปรียบกว่าในแง่ความเป็นสากลหรือเปล่า

    • ขอบคุณมาก และเครื่องมืออื่นที่ใช้ package registry โดยทั่วไปมักต้องมีการสร้างบัญชี, ยืนยันตัวตนสองขั้นตอน, ขั้นตอนการเซ็นลายเซ็น ฯลฯ
      แต่ Homebrew ใช้ข้อกำหนดการให้บริการของ GitHub (ToS) เป็นฐานของความน่าเชื่อถือ จึงทำให้ภาพรวมง่ายกว่ามาก
      ทีม Homebrew เองก็ลดความซับซ้อนได้มากด้วยวิธีนี้

    • ถ้าพูดในเกณฑ์ของแพ็กเกจ Python ความพยายามแบบ uv ที่จะจัดแพ็กทุกอย่างไว้ในที่เดียวทำได้ยากในทางปฏิบัติ
      เพราะแบบนั้นโดยทั่วไปจึงใช้วิธีติดตั้งเฉพาะ dependency ที่ตรึงเวอร์ชันไว้ในสภาพแวดล้อม venv
      ดูตัวอย่างแบบเจาะจงได้ที่formula นี้
      ส่วนเรื่อง uv นั้น เคยพยายามให้รองรับแพ็กเกจส่วนตัวด้วยเครื่องมือทางการอย่าง brew update-python-resources, homebrew-pypi-poet แต่ทำได้ไม่สำเร็จ
      เลยสร้าง uvbrew ขึ้นมาเองเพื่อช่วยสร้าง resource
      และมีเอกสารทางการสำหรับใช้อ้างอิงเวลาเขียน Python formula บน Homebrew

  • ถ้าเป็นนักพัฒนา Go ขอแนะนำเครื่องมือ Goreleaser
    มันช่วยให้แจกจ่ายไบนารีใน tap ส่วนตัวได้ง่ายมาก (แต่เป็นวิธีที่ห้ามใช้ใน core อย่างเป็นทางการ)

    • เพิ่งมารู้ไม่นานนี้ว่า Goreleaser ตอนนี้ไม่ได้รองรับแค่ Go แต่ยังรองรับ Rust, TypeScript, Python, Zig และภาษาอื่น ๆ อีกด้วย
      เลยมีประโยชน์มากในการจัดการโปรเจ็กต์ของแต่ละภาษา
  • ส่วนตัวคิดว่าการให้ฝั่ง tap จัดการอัปเดตโดยตรงน่าจะเป็นแนวทางที่เหมาะกว่า
    โดยทั่วไปก็คล้ายกับวิธีที่ upstream เป็นคนอัปเดตเอง
    ถ้าดูworkflow นี้ ก็จะเห็นว่าสามารถอัปเดต formula/cask ที่เราไม่ได้เป็นเจ้าของได้ง่าย ๆ เช่นกัน
    ใช้คำสั่ง brew bump สแกนทั้งหมด แล้วสร้าง PR พร้อมให้ brew test-bot ทดสอบให้อัตโนมัติได้
    ตัวอย่าง PR จริงดูได้ที่นี่

    • คิดว่าเป็นไอเดียที่ดี
      ปกติลังเลเพราะเสียดายเวลาใช้งาน GitHub Actions แต่ถ้าเป็นโอเพนซอร์สที่ใช้ฟรี แบบนี้ก็น่าใช้เหมือนกัน
  • ผมเคยเขียน homebrew-bump-revision ขึ้นมาเองเป็น workflow สำหรับ bump เวอร์ชันของ Homebrew tap แบบอัตโนมัติ
    ตอนนี้ใช้งานมันได้ดีในหลายโปรเจ็กต์ส่วนตัว

    • ดูเจ๋งดี
      ผมขี้เกียจเลยไม่ได้ลองเอง แต่เป็นเครื่องมือที่ดี
  • ในพอดแคสต์ Ruby Rogues เคยมีตอนหนึ่งที่พูดถึงทิปต่าง ๆ สำหรับการแจกจ่าย CLI ผ่าน Homebrew
    ฟังรายละเอียดเพิ่มเติมได้จากลิงก์ตอนที่เกี่ยวข้อง

  • พบจุดที่น่าสนใจเกี่ยวกับการแพ็กเครื่องมือ Python
    แพ็กเกจ Python บางตัวมี dependency loop ระหว่างขั้นตอน build จึงไม่เข้ากับ Homebrew
    ส่วน pip ไม่มีปัญหาเพราะดาวน์โหลดไบนารีรีลีสมาใช้ แต่ Homebrew ต้อง build เองแม้กระทั่ง dependency ทั้งหมด ทำให้ใช้เวลานานกว่ามาก
    เพราะงั้นโปรเจ็กต์ Python ขนาดกลางก็อาจใช้เวลามากกว่าหนึ่งชั่วโมงในการ build "bottle"

  • ตั้งแต่เริ่มใช้ nix เพื่อจัดการระบบ ก็ไม่เคยเสียใจเลยสักครั้ง
    มีอย่างเดียวที่น่าเสียดายคือยังต้องพึ่ง windows เพราะเกมแบบมัลติเพลเยอร์