Homebrew로 패키지 제공하기 🍺

제가 만든 도구는 대부분 Homebrew, Binary, RubyGem, Snapcraft 등을 통해 배포합니다. 특히 macOS의 경우 Homebrew를 통한 설치 비중이 엄청 높기 떄문에 Go 기반 앱들은 가급적이면 Homebrew를 지원하려고 하고 있습니다. 그리고 이러한 대부분의 작업은 Goreleaser란 도구를 통해서 진행하기 때문에 매우 편리하게 진행했었죠.

최근에 Crystal 기반 앱의 Homebrew를 배포하면서 정말 오랜만에 수동으로 작업해보게 되었는데, 겸사겸사 글로 정리해볼까 합니다.

Homebrew

macOS의 대표적인 패키지 매니저로 Ruby로 작성된 도구입니다. 초기에는 macOS를 타겟으로 만들어졌지만 현재는 다른 OS에서도 사용할 수 있습니다.

Formulae and Cask

Homebrew 패키지는 크게 2가지로 분류할 수 있습니다. Formulae와 Cask인데, 각각 아래와 같은 의미를 가집니다.

  • Formuale: 패키지를 다운로드하고 컴파일 하는 방법 (바이너리를 받아 설치시키는 방법도 가능함)
  • Cask: formula와 유사하지만 컴파일이 아닌 단일 바이너리를 설치하는 방법

오픈소스의 경우 Formulae 비중이 높고 패키지 설치 파일이나 바이너리로 배포되는 앱의 경우 Cask를 이용하는 경우가 많습니다.

Taps (Third-Party Repositories)

Homebrew에서 사용하는 Default 패키지는 공식 Repo에서 관리됩니다. 이는 모든 Homebrew 사용자에게 노출되기 때문에 공신력을 얻을 수 있지만, 반대로 핫 픽스 등이 있을 때 많은 딜레이를 주게됩니다.

버전별 Build, Test 등의 과정을 통과 해야합니다.

그래서 좀 더 쉬운 확장을 위해 Taps라고 Git 기반으로 확장해서 사용할 수 있도록 제공하고 있습니다.

# 제가 Dalfox 설치에서 제공하는 형태가 Taps 입니다.

brew tap hahwul/dalfox
brew install dalfox

Manual Setup

Create Formulae

직접 파일을 만들고 shasum -a 256 <filename> 을 통해 hash를 뜨거나, brew create <filename>을 통해 생성해도 됩니다. Formulae 파일은 아래와 같은 형태를 지니고 있습니다.

class Eoyc < Formula # Formual 클래스를 상속받을 class 이름으로 보통 앱 이름으로 들어갑니다.
  desc "Encoding Only Your Choices" 
  homepage "https://github.com/hahwul/eoyc" 
  url "https://github.com/hahwul/eoyc/archive/refs/tags/v0.1.0.tar.gz" # 파일 url (해당 파일을 다운로드받고 압축을 풉니다.)
  sha256 "89235f88697fd3bc5ef3c62d81fdb1461af8b7f3bd99b0365efcf404cb9d4b5b" # Checksum
  version "0.1.0"
  depends_on "crystal" # 의존성을 가지는 패키지 (설치 과정에서 먼저 설치됩니다.)

  # install method로 정의한 부분은 설치 과정에서 수행할 명령을 의미합니다.
  # 보통 빌드 과정이 포함됩니다. 
  def install
    system "shards install"
    system "shards build"
    bin.install "bin/eoyc" # bin.install로 homebrew bin 디렉토리로 이동시킬 수 있습니다.
  end

  # 설치가 잘 되었는지 테스트 코드를 작성할 수 있습니다.
  test do
    system "{bin}/eoyc", "-v"
  end
end

Push to github

원하는 Repo에 repo/Formula/<appname>.rb 파일 경로로 위 파일을 업로드하면 됩니다. tap을 통한 설치를 지원할 예정이라면 repo 주소를 homebrew-<appname> 형태로 만들어주시면 됩니다.

# e.g
homebrew-eoyc
└── Formula
    └── eoyc.rb

Install

이제 아래와 같이 Github repo 주소를 tap으로 넣고 install 하면 우리가 작성한 코드로 빌드되며 설치됩니다.

brew tap hahwul/eoyc
brew install eoyc

# hahwul/homebrew-eoyc repo 였기 때문에 tap에선
# hahwul/eoyc로 표기됩니다. (homebrew- 제외됨)

또는 그냥 경로를 직접 호출하여 설치할 수도 있습니다. 만약 패키지 Repo 내부에서 관리한다면 이런 방식으로 제공할 수도 있습니다.

brew install hahwul/homebrew-eoyc/eoyc

For Enterprise

Github enterprise와 같이 사설망에 별도로 구축된 Git의 경우도 동일하게 Repo를 세팅하여 배포할 수 있습니다. 다만 하나 차이점이 있다면 사용자가 설치 시 외부가 아닌 내부 Github을 참조할 수 있도록 HOMEBREW_BOTTLE_DOMAIN 환경 변수를 통해 Repo 주소를 변경하여 설치하도록 바꿔줍니다. (설치 커맨드에 한번에 제공해주는게 편하겠네요)

HOMEBREW_BOTTLE_DOMAIN=github-enterprise

이러한 방식은 사설 도메인에서 Go get하는 것과 유사하네요 :D