-
-
Save ZPascal/b21c652b811872b3f56db9d54d61d6c6 to your computer and use it in GitHub Desktop.
require "formula" | |
require_relative "lib/private_strategy" | |
class Hoge < Formula | |
homepage "https://github.com/yourcompany/hoge" | |
url "https://github.com/yourcompany/hoge/releases/download/v0.1.0/hoge_v0.1.0_darwin_amd64.tar.gz", :using => GitHubPrivateRepositoryReleaseDownloadStrategy | |
sha256 "6de411ff3e4b1658a413dd6181fcXXXXXXXXXXXXXXXXXXXX" | |
head "https://github.com/yourcompany/hoge.git" | |
version "0.1.0" | |
def install | |
bin.install "hoge" | |
end | |
end |
brew tap yourcompany/tap [email protected]:yourcompany/homebrew-tap.git | |
export HOMEBREW_GITHUB_API_TOKEN=xxx | |
brew install hoge |
# Save this file as `lib/private_strategy.rb` | |
# Add `require_relative "lib/private_strategy"` to your formula. | |
# | |
# This is based on the following, with minor fixes. | |
# https://github.com/Homebrew/brew/blob/193af1442f6b9a19fa71325160d0ee2889a1b6c9/Library/Homebrew/compat/download_strategy.rb#L48-L157 | |
# BSD 2-Clause License | |
# | |
# Copyright (c) 2009-present, Homebrew contributors | |
# All rights reserved. | |
# | |
# Redistribution and use in source and binary forms, with or without | |
# modification, are permitted provided that the following conditions are met: | |
# | |
# * Redistributions of source code must retain the above copyright notice, this | |
# list of conditions and the following disclaimer. | |
# | |
# * Redistributions in binary form must reproduce the above copyright notice, | |
# this list of conditions and the following disclaimer in the documentation | |
# and/or other materials provided with the distribution. | |
# | |
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | |
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | |
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
# GitHubPrivateRepositoryDownloadStrategy downloads contents from GitHub | |
# Private Repository. To use it, add | |
# `:using => GitHubPrivateRepositoryDownloadStrategy` to the URL section of | |
# your formula. This download strategy uses GitHub access tokens (in the | |
# environment variables `HOMEBREW_GITHUB_API_TOKEN`) to sign the request. This | |
# strategy is suitable for corporate use just like S3DownloadStrategy, because | |
# it lets you use a private GitHub repository for internal distribution. It | |
# works with public one, but in that case simply use CurlDownloadStrategy. | |
class GitHubPrivateRepositoryDownloadStrategy < CurlDownloadStrategy | |
require "utils/formatter" | |
require "utils/github" | |
def initialize(url, name, version, **meta) | |
super | |
parse_url_pattern | |
set_github_token | |
end | |
def parse_url_pattern | |
unless match = url.match(%r{https://github.com/([^/]+)/([^/]+)/(\S+)}) | |
raise CurlDownloadStrategyError, "Invalid url pattern for GitHub Repository." | |
end | |
_, @owner, @repo, @filepath = *match | |
end | |
def download_url | |
"https://#{@github_token}@github.com/#{@owner}/#{@repo}/#{@filepath}" | |
end | |
private | |
def _fetch(url:, resolved_url:, timeout:) | |
curl_download download_url, to: temporary_path | |
end | |
def set_github_token | |
@github_token = ENV["HOMEBREW_GITHUB_API_TOKEN"] | |
unless @github_token | |
raise CurlDownloadStrategyError, "Environmental variable HOMEBREW_GITHUB_API_TOKEN is required." | |
end | |
validate_github_repository_access! | |
end | |
def validate_github_repository_access! | |
# Test access to the repository | |
GitHub.repository(@owner, @repo) | |
rescue GitHub::API::HTTPNotFoundError | |
# We switched to GitHub::API::HTTPNotFoundError, | |
# because we can now handle bad credentials messages | |
message = <<~EOS | |
HOMEBREW_GITHUB_API_TOKEN can not access the repository: #{@owner}/#{@repo} | |
This token may not have permission to access the repository or the url of formula may be incorrect. | |
EOS | |
raise CurlDownloadStrategyError, message | |
end | |
end | |
# GitHubPrivateRepositoryReleaseDownloadStrategy downloads tarballs from GitHub | |
# Release assets. To use it, add | |
# `:using => GitHubPrivateRepositoryReleaseDownloadStrategy` to the URL section of | |
# your formula. This download strategy uses GitHub access tokens (in the | |
# environment variables HOMEBREW_GITHUB_API_TOKEN) to sign the request. | |
class GitHubPrivateRepositoryReleaseDownloadStrategy < GitHubPrivateRepositoryDownloadStrategy | |
def initialize(url, name, version, **meta) | |
super | |
end | |
def parse_url_pattern | |
url_pattern = %r{https://github.com/([^/]+)/([^/]+)/releases/download/([^/]+)/(\S+)} | |
unless @url =~ url_pattern | |
raise CurlDownloadStrategyError, "Invalid url pattern for GitHub Release." | |
end | |
_, @owner, @repo, @tag, @filename = *@url.match(url_pattern) | |
end | |
def download_url | |
"https://#{@github_token}@api.github.com/repos/#{@owner}/#{@repo}/releases/assets/#{asset_id}" | |
end | |
private | |
def _fetch(url:, resolved_url:, timeout:) | |
# HTTP request header `Accept: application/octet-stream` is required. | |
# Without this, the GitHub API will respond with metadata, not binary. | |
curl_download download_url, "--header", "Accept: application/octet-stream", to: temporary_path | |
end | |
def asset_id | |
@asset_id ||= resolve_asset_id | |
end | |
def resolve_asset_id | |
release_metadata = fetch_release_metadata | |
assets = release_metadata["assets"].select { |a| a["name"] == @filename } | |
raise CurlDownloadStrategyError, "Asset file not found." if assets.empty? | |
assets.first["id"] | |
end | |
def fetch_release_metadata | |
#release_url = "https://api.github.com/repos/#{@owner}/#{@repo}/releases/tags/#{@tag}" | |
#GitHub::API.open_rest(release_url) | |
GitHub.get_release(@owner, @repo, @tag) | |
end | |
end |
Hello! I don't even know if it's possible, but I want to do something of this sort with a Font Cask, by hiding my fonts behind a private repository, however I don't know where to start, since the font-cask requires to only create the cask, and the cask will not let me import the private_strategy.rb. Any idea of how can I make this work?
Hi @alejakun, I'm not so familiar with the Homebrew Cask, but could you please post a dummy example of your cask and the corresponding error message. Maybe it's also possible to use the "dirty way" and inject the Token directly inside the URL.
Hi, I think it should be possible to do it "the dirty way", just trying to find a "clean way" to do it, I've been trying several approaches and I have come up with some solutions but all of them seem a bit odd or quirky:
First what's working right now:
FAMILY: Some Name
cask 'FIXME' do
version "?"
sha256 "51481a4ac783bb8cf21cd202f3d7eb27437bc29eb49874b1ab58301234567890"
url "file://#{ENV['HOMEBREW_PREFIX']}/Homebrew/Library/Taps/<user>/homebrew-cask-fonts/Fonts/some-font.zip"
name "Some Font"
homepage ""
font "SOME_FONT.TTF"
end
I've been looking around for a while now and there are two options which seem more clean, the first is with your approach which involves importing a file, however this is not working because I have no access to the Formula (this is handled by the cask-font, and meddling with it seems a little bit too much) and when I try to import it to the cask it says something in the line of "invalid file format".
The other option I've been trying as explained here is to add an authorization token like this:
cask 'XXXX' do
...
url "https://raw.githubusercontent.com/....",
header : "Authorization: token #{ENV['HOMEBREW_GITHUB_API_TOKEN']}"
...
end
However I feel like this is outdated because the variable HOMEBREW_GITHUB_API_TOKEN
is set in the environment correctly (I've tested this concatenating this to the URL as the URL is displayed in the command line when homebrew works its magic.
FAMILY: Some Name
cask 'FIXME' do
version "?"
sha256 "51481a4ac783bb8cf21cd202f3d7eb27437bc29eb49874b1ab58301234567890"
url "https://github.com/<user>/homebrew-cask-fonts/raw/master/Fonts/<some-font>.zip"
name "Some Font"
homepage ""
font "SOME_FONT.TTF"
end
Now that I'm writing all this I believe I have to use the second approach but changing the URL from what I have to something more like in your code like this "https://#{ENV['HOMEBREW_GITHUB_API_TOKEN']}@github.com/<user>/<repo>/<file_path>"
I'll be testing this approach and let you know.
I've tested my proposed strategy, but it is not working
==> Downloading https://<api-token>@github.com/alej
curl: (22) The requested URL returned error: 404
hope someone has a clue.
@alejakun Could you please test if it's possible to download the artifact without the cask? Maybe, an access problem.
I've tried with several formats:
https://#{ENV['HOMEBREW_GITHUB_API_TOKEN']}@github.com/<user>/<repo>/Fonts/<file_name>.zip
https://#{ENV['HOMEBREW_GITHUB_API_TOKEN']}@github.com/<user>/<repo>/raw/master/Fonts/<file_name>.zip
The first one fails in the browser, the second one is successful in the browser as long as I'm logged in GH, otherwise it returns a HTTP 404.
Could you please try curl
or wget
and the corresponding TOKEN directly? If the file is located inside the repository, please use the raw
mode.
You are correct, using just curl it returns the 404 page. Any suggestions of what might be wrong with my environment?
Are you sure, that your access token got the correct access rights? Could you please validate that?
Tests were performed with a token with the following permissions granted:
- Repo (and all sub permissions)
- Workflow
- Gist
I've started a test on my side to download a file from a private repository directly via curl
. I've used the following command curl https://<TOKEN>@raw.githubusercontent.com/<OWNER>/<REPO>/<BRANCH>/<PATH TO FILE> --output <NAME OF THE FILE>
. Could you please try it out? I think it should be possible, to use the URL at the end directly inside the cask.
Hi, sorry for the late reply, the curl
command with the suggested format is working, however I don't know how to translate that to the URL in my cask definition.
Edit: Finally made it work, it seems I was so into it that I missed some parts of the url. Anyways you nailed it, thanks for the support!
Hi @alejakun, great to hear that now everything works as aspected.
I am not sure this is the exact place for it but has anyone using this seen an error Error: can't modify frozen class
while using these scripts? I am running a brew install for a private github repo when I get this error. My google-fu is weak so taking a shot in the dark in case it is more widespread.
Hi @mtintes, could you please post the output of the --debug
flag of the installation command?
One of coworkers ended up finding an answer (sort of). This was the original error. konductor is a cli written in go in this case.
➜ brew install konductor --debug --verbose
/opt/homebrew/Library/Homebrew/brew.rb (Formulary::FormulaLoader): loading /opt/homebrew/Library/Taps/<org>/homebrew-konductor/formula/konductor.rb
/opt/homebrew/Library/Homebrew/shims/shared/curl --disable --cookie /dev/null --globoff --show-error --user-agent Homebrew/3.6.4\ \(Macintosh\;\ arm64\ Mac\ OS\ X\ 12.5.1\)\ curl/7.79.1 --header Accept-Language:\ en --retry 3 --location https://api.github.com/repos/<org>/konductor --header Accept:\ application/vnd.github\+json --write-out '
'\%\{http_code\} --header Authorization:\ token\ ****** --dump-header /private/tmp/github_api_headers20221005-47324-1n0405m
/opt/homebrew/Library/Homebrew/shims/shared/curl --disable --cookie /dev/null --globoff --show-error --user-agent Homebrew/3.6.4\ \(Macintosh\;\ arm64\ Mac\ OS\ X\ 12.5.1\)\ curl/7.79.1 --header Accept-Language:\ en --retry 3 --location --silent --head --request GET https://github.com/<org>/konductor/releases/download/v1.17.9/konductor-osx-amd64.tar.gz
==> Downloading https://github.com/<org>/konductor/releases/download/v1.17.9/konductor-osx-amd64.tar.gz
/opt/homebrew/Library/Homebrew/shims/shared/curl --disable --cookie /dev/null --globoff --show-error --user-agent Homebrew/3.6.4\ \(Macintosh\;\ arm64\ Mac\ OS\ X\ 12.5.1\)\ curl/7.79.1 --header Accept-Language:\ en --retry 3 --location https://api.github.com/repos/<org>/konductor/releases/tags/v1.17.9 --header Accept:\ application/vnd.github\+json --write-out '
'\%\{http_code\} --header Authorization:\ token\ ****** --dump-header /private/tmp/github_api_headers20221005-47324-f7o4v1
/opt/homebrew/Library/Homebrew/shims/shared/curl --disable --cookie /dev/null --globoff --show-error --user-agent Homebrew/3.6.4\ \(Macintosh\;\ arm64\ Mac\ OS\ X\ 12.5.1\)\ curl/7.79.1 --header Accept-Language:\ en --fail --retry 3 --location --remote-time --output /Users/miketintes/Library/Caches/Homebrew/downloads/e21e5b9112fcd59c4caec2c40316e0fe0c08cab973ba491d5ccd1460569812b8--konductor-osx-amd64.tar.gz.incomplete https://******@api.github.com/repos/<org>/konductor/releases/assets/79035814 --header Accept:\ application/octet-stream
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0
100 30.6M 100 30.6M 0 0 13.2M 0 0:00:02 0:00:02 --:--:-- 16.4M
==> Verifying checksum for 'e21e5b9112fcd59c4caec2c40316e0fe0c08cab973ba491d5ccd1460569812b8--konductor-osx-amd64.tar.gz'
==> Installing konductor from <org>/konductor
/opt/homebrew/Library/Homebrew/brew.rb (Formulary::TapLoader): loading /opt/homebrew/Library/Taps/<org>/homebrew-konductor/Formula/konductor.rb
Error: can't modify frozen class
/opt/homebrew/Library/Taps/<org>/homebrew-konductor/Formula/lib/private_strategy.rb:98:in `<class:GitHubPrivateRepositoryReleaseDownloadStrategy>'
/opt/homebrew/Library/Taps/<org>/homebrew-konductor/Formula/lib/private_strategy.rb:97:in `<top (required)>'
/opt/homebrew/Library/Taps/<org>/homebrew-konductor/Formula/konductor.rb:2:in `require_relative'
/opt/homebrew/Library/Taps/<org>/homebrew-konductor/Formula/konductor.rb:2:in `block in load_formula'
/opt/homebrew/Library/Homebrew/formulary.rb:90:in `module_eval'
/opt/homebrew/Library/Homebrew/formulary.rb:90:in `block in load_formula'
/opt/homebrew/Library/Homebrew/formulary.rb:102:in `load_formula'
/opt/homebrew/Library/Homebrew/formulary.rb:122:in `load_formula_from_path'
/opt/homebrew/Library/Homebrew/formulary.rb:321:in `load_file'
/opt/homebrew/Library/Homebrew/formulary.rb:478:in `load_file'
/opt/homebrew/Library/Homebrew/formulary.rb:311:in `klass'
/opt/homebrew/Library/Homebrew/formulary.rb:306:in `get_formula'
/opt/homebrew/Library/Homebrew/formulary.rb:468:in `get_formula'
/opt/homebrew/Library/Homebrew/formulary.rb:560:in `factory'
/opt/homebrew/Library/Homebrew/formula_installer.rb:374:in `install'
/opt/homebrew/Library/Homebrew/upgrade.rb:212:in `install_formula'
/opt/homebrew/Library/Homebrew/install.rb:333:in `install_formula'
/opt/homebrew/Library/Homebrew/install.rb:323:in `block in install_formulae'
/opt/homebrew/Library/Homebrew/install.rb:322:in `each'
/opt/homebrew/Library/Homebrew/install.rb:322:in `install_formulae'
/opt/homebrew/Library/Homebrew/cmd/install.rb:226:in `install'
/opt/homebrew/Library/Homebrew/brew.rb:95:in `<main>'
We had to copy a bunch of old code from homebrew that looks like it got deprecated and put that in the private strategy. This is a copy of that. No idea if that is the best path since neither of us does anything with ruby.
For anyone recently having issues with this, see Homebrew/brew#15169. CurlDownloadStrategy
changed a bit.
This fix worked for me.
For anyone recently having issues with this, see Homebrew/brew#15169.
CurlDownloadStrategy
changed a bit.This fix worked for me.
It worked for me too. Thanks for sharing.
@gaarutyunov Thanks for the hint. I think both methods used, at the end, the same/ a similar call to get the expected result. I'll update that because, I think, it's better to use the central wrapper method to extract the release version.