Homebrew에 Cask 추가하기
Homebrew에 Cask 추가하기

Homebrew에 Cask 추가하기

Published
September 16, 2024
Tags
homebrew
software
macOS

개요

macOS 관련 좋은 앱을 찾아보던 도중에 발견한 프로그램을 Homebrew에 등록해보려고 한다.

등록 확인

먼저 프로그램이 등록되지 않은게 맞는지 확인한다.
> brew search pronotes ==> Formulae proto ==> Casks pronterface > brew search pro-notes ==> Formulae protoc-gen-js ==> Casks box-notes
아무래도 등록되지 않은 프로그램이 맞는 것 같다.

프로그램 분석

프로그램을 직접 다운로드하고 설치하면서 다양한 정보들을 취득해야 한다.

다운로드

다운로드 단계에서 알아내야 할 정보는 크게 2가지다.
  • 프로그램의 버전 정보를 얻을 수 있는 경로
  • 프로그램을 버전별로 다운로드할 수 있는 경로
다운로드 버튼을 클릭하니, 이메일로 링크를 전송해준다고 한다.
  • https://pronotes.app/direct-download
그리고 링크를 따라가니 파일이 다운로드된 실제 경로는 다음과 같다.
  • https://assets.pronotes.app/downloads/ProNotes-0.7.6.zip
direct-download 경로의 헤더 정보를 통해서 다운로드할 버전 링크로 연결되는 것으로 보인다.
> curl -X HEAD -I https://pronotes.app/direct-download HTTP/1.1 301 Moved Permanently Date: Mon, 16 Sep 2024 05:11:39 GMT Content-Type: text/html Content-Length: 167 Connection: close Cache-Control: max-age=3600 Expires: Mon, 16 Sep 2024 06:11:39 GMT Location: https://www.pronotes.app/direct-download Report-To: {"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v4?s=08vt%2FxYzFj0xfeJUeTw%2BzWFGkAbQPc9QbllsVtvhdZAB586pAbEC%2BVHit%2Fo1gri1shY98JQ7PTr%2FHeGYGxi0eAx0BErjjHl%2FgNNUR0R2FD%2FFH07A5mTf5Sb94bWoTos%3D"}],"group":"cf-nel","max_age":604800} NEL: {"success_fraction":0,"report_to":"cf-nel","max_age":604800} Server: cloudflare CF-RAY: 8c3e5b680f22c171-ICN > curl -X HEAD -I https://www.pronotes.app/direct-download HTTP/1.1 307 Temporary Redirect Date: Mon, 16 Sep 2024 05:12:06 GMT Connection: close Location: https://assets.pronotes.app/downloads/ProNotes-0.7.6.zip Report-To: {"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v4?s=jKyJ%2FiJb4iupwxS9rfCh%2FQRhJCwawkdwF2mvwgEHS6ixqBE1SrvuJaHIy5Q3mkvFaR8Bnrcp0yvJnqG%2Fe1vIPp%2B3zQX5dh%2B7%2B22dtASVGpSoErXlyIA6wzafxi1CpK7nwqcZ"}],"group":"cf-nel","max_age":604800} NEL: {"success_fraction":0,"report_to":"cf-nel","max_age":604800} Vary: Accept-Encoding CF-Cache-Status: DYNAMIC Server: cloudflare CF-RAY: 8c3e5c0d8a398b68-ICN alt-svc: h3=":443"; ma=86400
예상대로다. 그러면 버전 정보를 제공해줄 수 있는 링크는 다음과 같다.
  • https://www.pronotes.app/direct-download
버전별 다운로드 링크는 다음과 같다.
  • https://assets.pronotes.app/downloads/ProNotes-0.7.6.zip

설치

다운로드한 파일이 .app, .dmg가 아니라서 .zip 파일 내부에 어떻게 저장되어 있는지 알아야 한다. 압축을 풀어보니 .app 파일이 나타난다.
notion image
앱에 필요한 권한을 제공하여 실행도 해본다.
notion image
그 다음에 createzap 명령을 통해서 해당 프로그램을 통해 추가된 파일들을 식별, 향후 특정 명령어를 통해서 모두 지울 수 있도록 한다.
> brew createzap pro-notes zap trash: [ "~/Library/Caches/com.dexterleng.ProNotes", "~/Library/Preferences/com.dexterleng.ProNotes.plist", "~/Library/Saved Application State/com.dexterleng.ProNotes.savedState", "~/Library/WebKit/com.dexterleng.ProNotes", ]

Cask 파일 작성

필요한 정보들은 다 준비된 걸로 보인다. 이제 Cask 파일을 작성해보도록 하자.
> brew create --cask pro-notes Cask name [pro-notes]: Please run `brew audit --cask --new pro-notes` before submitting, thanks. Editing /opt/homebrew/Library/Taps/homebrew/homebrew-cask/Casks/p/pro-notes.rb
그러면 루비 파일이 열리는데, 필요한 항목들을 모두 채운 다음에 테스트를 완료하면 Cask 추가 요청을 제출할 수 있다.

템플릿

# Documentation: https://docs.brew.sh/Cask-Cookbook # https://docs.brew.sh/Adding-Software-to-Homebrew#cask-stanzas # PLEASE REMOVE ALL GENERATED COMMENTS BEFORE SUBMITTING YOUR PULL REQUEST! cask "pro-notes" do version "" sha256 "" url "pro-notes" name "pro-notes" desc "" homepage "" # Documentation: https://docs.brew.sh/Brew-Livecheck livecheck do url "" strategy "" end depends_on macos: "" app "" # Documentation: https://docs.brew.sh/Cask-Cookbook#stanza-zap zap trash: "" end

version

버전은 현재 다운로드한 파일에 포함된 0.7.6을 입력한다.
version "0.7.6"

sha256

다운로드 받은 ProNotes-0.7.6.zip 파일의 sha256 값을 구해서 입력하면 된다.
> shasum --algorithm 256 ~/Downloads/ProNotes-0.7.6.zip 655cd62f286539610c0523fee835ac5f3dcccd74bb9d63e245ee51f5a898e8f5 /Users/daeho.ro/Downloads/ProNotes-0.7.6.zip sha256 "655cd62f286539610c0523fee835ac5f3dcccd74bb9d63e245ee51f5a898e8f5"

url

프로그램 다운로드 url을 변수 형태로 입력한다.
url "https://assets.pronotes.app/downloads/ProNotes-#{version}.zip"

name

Cask 이름은 정식 명칭을 사용하도록 하자.
name "ProNotes"

desc

Cask를 설명하는 문구를 입력한다.
desc "Apple Notes extension"

homepage

프로그램 홈페이지 주소를 입력한다.
homepage "https://www.pronotes.app/"

livecheck url

신규 버전이 존재하는지 파악하기 위한 정보를 얻을 수 있는 url을 입력한다.
url "https://www.pronotes.app/direct-download"

livecheck strategy

신규 버전 정보를 위 url의 어떤 정보에서 어떻게 가져올지 정한다. 301 Redirect 방식으로 최신 버전과 연결되기 때문에 Location header에서 최신 버전을 확인할 수 있는데, brew에서는 이미 이러한 방식을 표준화해놨기 때문에 별다른 설정 없이 다음과 같이 사전 저장된 전략을 사용하도록 한다.
strategy :header_match

depends_on

의존성을 명시하는 항목으로, 필요 없으면 지워도 된다. ProNotes는 macOS 13+ 라고 의존성을 명시하고 있기 때문에 다음과 같이 입력한다.
depends_on macos: ">= :ventura"

app

.zip 파일은 자동으로 압축 해제가 되고, 그 안에 들어있던 .app 파일 이름을 적어준다.
app "ProNotes.app"

zap

특정 명령을 통해서 삭제할, 해당 프로그램에 의해 생성된 파생 파일들의 경로를 명시한다. 이미 위에서 간단한 명령을 통해서 파악해놨으므로 그대로 복사한다.
zap trash: [ "~/Library/Caches/com.dexterleng.ProNotes", "~/Library/Preferences/com.dexterleng.ProNotes.plist", "~/Library/Saved Application State/com.dexterleng.ProNotes.savedState", "~/Library/WebKit/com.dexterleng.ProNotes", ]

완성된 파일

전체 조각들을 모아보면 다음과 같은 파일이 완성된다.
cask "pro-notes" do version "0.7.6" sha256 "655cd62f286539610c0523fee835ac5f3dcccd74bb9d63e245ee51f5a898e8f5" url "https://assets.pronotes.app/downloads/ProNotes-#{version}.zip" name "ProNotes" desc "Apple Notes extension" homepage "https://www.pronotes.app/" livecheck do url "https://www.pronotes.app/appcast.xml" strategy :sparkle, &:short_version end auto_updates true depends_on macos: ">= :ventura" app "ProNotes.app" zap trash: [ "~/Library/Caches/com.dexterleng.ProNotes", "~/Library/Preferences/com.dexterleng.ProNotes.plist", "~/Library/Saved Application State/com.dexterleng.ProNotes.savedState", "~/Library/WebKit/com.dexterleng.ProNotes", ] end

테스트

생성한 Cask 파일의 정합성, 정상 설치 유무, Homebrew 기준 충족 등을 확인하기 위해서 여러 테스트를 거쳐야 한다. 똑같은 테스트가 향후 PR 요청에서 자동으로 진행되고, 이를 통과하지 못하면 제출한 PR이 머지되지 않는다. 그래서 로컬에서 점검하는 것이 효율적이다.
가장 먼저 생성한 파일의 lint를 점검하기 위해서 style 명령을 수행한다.
> brew style --fix pro-notes 1 file inspected, no offenses detected
문제가 자동 수정이 가능한 항목이라면 위 명령의 결과로 수정 여부가 표시되는데, 단 하나도 남지 않을 때까지 파일을 수정해가면서 반복 수행하면 된다.
이번에는 전반적인 설치 정합성을 확인하기 위해서 audit을 수행한다.
> brew audit --cask --new pro-notes ==> Downloading and extracting artifacts ==> Downloading https://assets.pronotes.app/downloads/ProNotes-0.7.6.zip ############################################################################# 100.0% ==> Downloading https://assets.pronotes.app/downloads/ProNotes-0.7.6.zip Already downloaded: /Users/daeho.ro/Library/Caches/Homebrew/downloads/48822f1ea2640c7a882a351e84dd5f8893b886ad76beda3d157be83856c06678--ProNotes-0.7.6.zip
아무런 에러 없이 잘 종료가 되었다. 마찬가지로 걸리는 항목에 없어질 때까지 반복 수행하면 된다. 프로그램에 따라서 Homebrew 의 기준 미충족에 따라서 아예 제출이 불가능할 수 있다.
마지막으로 실제 설치 테스트를 진행해본다.
> HOMEBREW_NO_INSTALL_FROM_API=1 brew install pro-notes -v ==> Downloading https://assets.pronotes.app/downloads/ProNotes-0.7.6.zip Already downloaded: /Users/daeho.ro/Library/Caches/Homebrew/downloads/48822f1ea2640c7a882a351e84dd5f8893b886ad76beda3d157be83856c06678--ProNotes-0.7.6.zip ==> Verifying checksum for '48822f1ea2640c7a882a351e84dd5f8893b886ad76beda3d157be83856c06678--ProNotes-0.7.6.zip' ==> Installing Cask pro-notes /usr/bin/env PATH=/opt/homebrew/Library/Homebrew/shims/shared:/usr/bin:/bin:/usr/sbin:/sbin unzip -o /Users/daeho.ro/Library/Caches/Homebrew/downloads/48822f1ea2640c7a882a351e84dd5f8893b886ad76beda3d157be83856c06678--ProNotes-0.7.6.zip -d /private/tmp/homebrew-unpack20240916-26911-1arrhn /usr/bin/env cp -pR /private/tmp/homebrew-unpack20240916-26911-1arrhn/ProNotes.app/. /opt/homebrew/Caskroom/pro-notes/0.7.6/ProNotes.app ==> Moving App 'ProNotes.app' to '/Applications/ProNotes.app' 🍺 pro-notes was successfully installed!
성공적으로 설치되었다고 한다. 정말일까?
notion image
notion image
정말이다. 잘 실행된다. 삭제 동작도 테스트해본다.
> brew uninstall pro-notes ==> Uninstalling Cask pro-notes ==> Backing App 'ProNotes.app' up to '/opt/homebrew/Caskroom/pro-notes/0.7.6/ProNote ==> Removing App '/Applications/ProNotes.app' ==> Purging files for version 0.7.6 of Cask pro-notes
잘 지워졌다.

Cask 등록 요청

위의 모든 변경사항을 git에서 PR을 생성해서 제출하면 된다.

포크

작업한 git에서는 직접 푸시할 권한이 없기 때문에, 메인 리포를 포크(Fork)하여 진행한다.
homebrew-cask
HomebrewUpdated Feb 22, 2025
notion image
포크가 완료되었으면 git 주소를 복사해서 다음과 같이 진행한다.

리포 설정

git 루트로 이동한다.
> cd $(brew --repo homebrew/cask) > pwd /opt/homebrew/Library/Taps/homebrew/homebrew-cask
git remote에 포크한 리포지토리를 설정하고 내 GitHub 정보도 설정한다.
> git remote set-url origin --push git@github.com:daeho-ro/homebrew-cask.git > git config --local user.email lamanus@outlook.kr > git config --local user.name "Daeho Ro"
설정된 내용은 다음과 같이 확인할 수 있다.
> git remote -v origin https://github.com/Homebrew/homebrew-cask (fetch) origin git@github.com:daeho-ro/homebrew-cask.git (push)

푸시

git 변경사항을 커밋하고 푸시하면 된다. 먼저 변경사항을 확인하자.
> git status On branch master Your branch is up to date with 'origin/master'. Untracked files: (use "git add <file>..." to include in what will be committed) Casks/p/pro-notes.rb nothing added to commit but untracked files present (use "git add" to track)
변경사항을 새 브랜치에서 커밋해야 하므로, 신규 브랜치를 만들고 방금 작업한 파일만 선택하여 커밋한다.
> git checkout -b pro-notes-0.7.6 Switched to a new branch 'pro-notes-0.7.6' > git add Casks/p/pro-notes.rb > git commit -m "pro-notes: 0.7.6 (new cask)" [pro-notes-0.7.6 3b3778c274d] pro-notes: 0.7.6 (new cask) 1 file changed, 25 insertions(+) create mode 100644 Casks/p/pro-notes.rb > git push Enumerating objects: 8, done. Counting objects: 100% (8/8), done. Delta compression using up to 11 threads Compressing objects: 100% (5/5), done. Writing objects: 100% (5/5), 778 bytes | 778.00 KiB/s, done. Total 5 (delta 3), reused 0 (delta 0), pack-reused 0 (from 0) remote: Resolving deltas: 100% (3/3), completed with 3 local objects. remote: remote: Create a pull request for 'pro-notes-0.7.6' on GitHub by visiting: remote: https://github.com/daeho-ro/homebrew-cask/pull/new/pro-notes-0.7.6 remote: To github.com:daeho-ro/homebrew-cask.git * [new branch] pro-notes-0.7.6 -> pro-notes-0.7.6 branch 'pro-notes-0.7.6' set up to track 'origin/pro-notes-0.7.6'. # 작업이 완료되면 Homebrew 메인으로 반드시 되돌려놔야 향후 brew 사용에 지장이 없다. > git checkout master Switched to branch 'master' Your branch is up to date with 'origin/master'.

PR 생성

GitHub의 내 포크 리포지토리로 가서 PR 요청을 생성한다.
변경사항의 PR 추천이 자동으로 나타난다.
변경사항의 PR 추천이 자동으로 나타난다.
Compare & pull request를 클릭하여 PR 템플릿을 작성한다.
PR 작성
PR 작성
템플릿에서 지시하는 사항들을 살펴보고, 모든 준비가 완료되면 PR을 생성한다.
pro-notes: 0.7.6 (new cask)
생성 즉시 자동화 테스트가 돌아가는데, 이를 통과하지 못하면 머지가 불가능하므로 테스트 결과에 유의한다.
테스트 결과 확인하기
테스트 결과 확인하기
모든 테스트를 통과했다.

머지

앞서 제출한 PR에 문제가 없다면 Homebrew 메인테이너가 PR을 승인해준다. 그렇지만 한 번에 통과하지 못하고 몇 가지 제안사항을 전달받았다.
name 항목은 프로그램의 본래 이름을 사용해야 하는데, 이를 놓쳤다.
name 항목은 프로그램의 본래 이름을 사용해야 하는데, 이를 놓쳤다.
appcast.xml을 제공하는 프로그램이라서 명시적으로 버전을 가져올 수 있다.
appcast.xml을 제공하는 프로그램이라서 명시적으로 버전을 가져올 수 있다.
추가적으로 자동 업데이트 기능을 제공하기 때문에 이를 이용하기를 권고받았다. 어떻게 저런 정보를 알아온거지? 이런 부분은 피드백을 받아서 꼭 개선해야겠다.
cd $(brew --repo homebrew/cask) git checkout pro-notes-0.7.6 # 수정사항 적용 brew edit pro-notes # 수정사항 적용 이후 git add Casks/p/pro-notes.rb git commit --amend git push -f git checkout master
수정사항 반영하기
커밋이 굉장히 많은 프로젝트들은 일반적으로 PR 요청을 하나의 커밋에 담기를 요구한다. 그래서 변경사항을 바로 적용해서 2개의 커밋이 생성되지 않도록, 기존 커밋에 제안받은 사항을 모두 amend 하여 기존 커밋을 업데이트 하는 방식으로 처리하는 것이다.
notion image
승인이 되면 일반적으로 자동화 봇이 머지를 진행한다. 이번에는 메인테이너가 처리해줬다. 그렇게 끝난 줄 알았는데, 마지막에 네이밍 룰때문에 이름이 교체되었다.
notion image

Cask 사용하기

앞서 제출한 PR이 master 브랜치에 머지되는 순간, Homebrew 사용자들은 누구나 ProNotes를 설치할 수 있게 되었다. 이름이 교체된 점을 참고하여 업데이트된 정보를 찾아보자.
> brew search pronotes ==> Formulae proto ==> Casks pronotes pronterface > brew info pronotes ==> pronotes: 0.7.6 (auto_updates) https://www.pronotes.app/ Not installed From: https://github.com/Homebrew/homebrew-cask/blob/HEAD/Casks/p/pronotes.rb ==> Name ProNotes ==> Description Apple Notes extension ==> Artifacts ProNotes.app (App) ==> Analytics install: 0 (30 days), 0 (90 days), 0 (365 days)
이제 다음 명령을 통해서 Cask를 설치할 수 있다.
brew install pronotes