Support bottle uploads to GitHub Releases

Co-authored-by: Mike McQuaid <mike@mikemcquaid.com>
This commit is contained in:
Dawid Dziurla 2020-08-20 14:21:55 +02:00
parent 455965de80
commit f6601cee10
No known key found for this signature in database
GPG Key ID: 7B6D8368172E9B0B
6 changed files with 100 additions and 26 deletions

View File

@ -148,11 +148,7 @@ class Bintray
end
end
def upload_bottle_json(json_files, publish_package: false, warn_on_error: false)
bottles_hash = json_files.reduce({}) do |hash, json_file|
hash.deep_merge(JSON.parse(IO.read(json_file)))
end
def upload_bottles(bottles_hash, publish_package: false, warn_on_error: false)
formula_packaged = {}
bottles_hash.each do |formula_name, bottle_hash|

View File

@ -11,7 +11,7 @@ module Homebrew
usage_banner <<~EOS
`pr-upload` [<options>]
Apply the bottle commit and publish bottles to Bintray.
Apply the bottle commit and publish bottles to Bintray or GitHub Releases.
EOS
switch "--no-publish",
description: "Apply the bottle commit and upload the bottles, but don't publish them."
@ -30,30 +30,37 @@ module Homebrew
end
end
def check_bottled_formulae(json_files)
hashes = json_files.reduce({}) do |hash, json|
hash.deep_merge(JSON.parse(IO.read(json)))
end
hashes.each do |name, hash|
formula_path = HOMEBREW_REPOSITORY/hash["formula"]["path"]
def check_bottled_formulae(bottles_hash)
bottles_hash.each do |name, bottle_hash|
formula_path = HOMEBREW_REPOSITORY/bottle_hash["formula"]["path"]
formula_version = Formulary.factory(formula_path).pkg_version
bottle_version = PkgVersion.parse hash["formula"]["pkg_version"]
bottle_version = PkgVersion.parse bottle_hash["formula"]["pkg_version"]
next if formula_version == bottle_version
odie "Bottles are for #{name} #{bottle_version} but formula is version #{formula_version}!"
end
end
def github_releases?(bottles_hash)
@github_releases ||= bottles_hash.values.all? do |bottle_hash|
root_url = bottle_hash["bottle"]["root_url"]
url_match = root_url.match HOMEBREW_RELEASES_URL_REGEX
_, _, _, tag = *url_match
tag
end
end
def pr_upload
args = pr_upload_args.parse
bintray_org = args.bintray_org || "homebrew"
bintray = Bintray.new(org: bintray_org)
json_files = Dir["*.json"]
odie "No JSON files found in the current working directory" if json_files.empty?
bottles_hash = json_files.reduce({}) do |hash, json_file|
hash.deep_merge(JSON.parse(IO.read(json_file)))
end
bottle_args = ["bottle", "--merge", "--write"]
bottle_args << "--verbose" if args.verbose?
bottle_args << "--debug" if args.debug?
@ -62,14 +69,51 @@ module Homebrew
bottle_args += json_files
if args.dry_run?
service = if github_releases?(bottles_hash)
"GitHub Releases"
else
"Bintray"
end
puts "brew #{bottle_args.join " "}"
puts "Upload bottles described by these JSON files to Bintray:\n #{json_files.join("\n ")}"
puts "Upload bottles described by these JSON files to #{service}:\n #{json_files.join("\n ")}"
return
end
check_bottled_formulae(bottles_hash)
safe_system HOMEBREW_BREW_FILE, *bottle_args
if github_releases?(bottles_hash)
# Handle uploading to GitHub Releases.
bottles_hash.each_value do |bottle_hash|
root_url = bottle_hash["bottle"]["root_url"]
url_match = root_url.match HOMEBREW_RELEASES_URL_REGEX
_, user, repo, tag = *url_match
# Ensure a release is created.
release = begin
GitHub.get_release user, repo, tag
odebug "Existing GitHub release \"#{tag}\" found"
rescue GitHub::HTTPNotFoundError
odebug "Creating new GitHub release \"#{tag}\""
GitHub.create_or_update_release user, repo, tag
end
# Upload bottles as release assets.
bottle_hash["bottle"]["tags"].each_value do |tag_hash|
remote_file = tag_hash["filename"]
local_file = tag_hash["local_filename"]
odebug "Uploading #{remote_file}"
GitHub.upload_release_asset user, repo, release["id"], local_file: local_file, remote_file: remote_file
end
end
else
check_bottled_formulae(json_files)
safe_system HOMEBREW_BREW_FILE, *bottle_args
bintray.upload_bottle_json(json_files,
publish_package: !args.no_publish?,
warn_on_error: args.warn_on_upload_failure?)
# Handle uploading to Bintray.
bintray_org = args.bintray_org || "homebrew"
bintray = Bintray.new(org: bintray_org)
bintray.upload_bottles(bottles_hash,
publish_package: !args.no_publish?,
warn_on_error: args.warn_on_upload_failure?)
end
end
end

View File

@ -112,6 +112,8 @@ HOMEBREW_PULL_API_REGEX =
%r{https://api\.github\.com/repos/([\w-]+)/([\w-]+)?/pulls/(\d+)}.freeze
HOMEBREW_PULL_OR_COMMIT_URL_REGEX =
%r[https://github\.com/([\w-]+)/([\w-]+)?/(?:pull/(\d+)|commit/[0-9a-fA-F]{4,40})].freeze
HOMEBREW_RELEASES_URL_REGEX =
%r{https://github\.com/([\w-]+)/([\w-]+)?/releases/download/(.+)}.freeze
require "PATH"

View File

@ -175,7 +175,7 @@ module GitHub
end
end
def open_api(url, data: nil, request_method: nil, scopes: [].freeze, parse_json: true)
def open_api(url, data: nil, data_binary_path: nil, request_method: nil, scopes: [].freeze, parse_json: true)
# This is a no-op if the user is opting out of using the GitHub API.
return block_given? ? yield({}) : {} if Homebrew::EnvConfig.no_github_api?
@ -200,6 +200,11 @@ module GitHub
end
end
if data_binary_path.present?
args += ["--data-binary", "@#{data_binary_path}"]
args += ["--header", "Content-Type: application/gzip"]
end
headers_tmpfile = Tempfile.new("github_api_headers", HOMEBREW_TEMP)
begin
if data
@ -467,6 +472,33 @@ module GitHub
scopes: CREATE_ISSUE_FORK_OR_PR_SCOPES)
end
def get_release(user, repo, tag)
url = "#{API_URL}/repos/#{user}/#{repo}/releases/tags/#{tag}"
open_api(url, request_method: :GET)
end
def create_or_update_release(user, repo, tag, id: nil, name: nil, draft: false)
url = "#{API_URL}/repos/#{user}/#{repo}/releases"
method = if id
url += "/#{id}"
:PATCH
else
:POST
end
data = {
tag_name: tag,
name: name || tag,
draft: draft,
}
open_api(url, data: data, request_method: method, scopes: CREATE_ISSUE_FORK_OR_PR_SCOPES)
end
def upload_release_asset(user, repo, id, local_file: nil, remote_file: nil)
url = "https://uploads.github.com/repos/#{user}/#{repo}/releases/#{id}/assets"
url += "?name=#{remote_file}" if remote_file
open_api(url, data_binary_path: local_file, request_method: :POST, scopes: CREATE_ISSUE_FORK_OR_PR_SCOPES)
end
def get_artifact_url(user, repo, pr, workflow_id: "tests.yml", artifact_name: "bottles")
scopes = CREATE_ISSUE_FORK_OR_PR_SCOPES
base_url = "#{API_URL}/repos/#{user}/#{repo}"

View File

@ -1113,7 +1113,7 @@ Requires write access to the repository.
### `pr-upload` [*`options`*]
Apply the bottle commit and publish bottles to Bintray.
Apply the bottle commit and publish bottles to Bintray or GitHub Releases.
* `--no-publish`:
Apply the bottle commit and upload the bottles, but don't publish them.

View File

@ -1547,7 +1547,7 @@ Use the specified \fIURL\fR as the root of the bottle\'s URL instead of Homebrew
Use the specified Bintray repository to automatically mirror stable URLs defined in the formulae (default: \fBmirror\fR)\.
.
.SS "\fBpr\-upload\fR [\fIoptions\fR]"
Apply the bottle commit and publish bottles to Bintray\.
Apply the bottle commit and publish bottles to Bintray or GitHub Releases\.
.
.TP
\fB\-\-no\-publish\fR