Merge pull request #10835 from MikeMcQuaid/pr_upload_github_packages
Support uploading and downloading bottles from GitHub packages
This commit is contained in:
commit
8726636ffe
@ -4,6 +4,7 @@
|
||||
require "cli/parser"
|
||||
require "archive"
|
||||
require "bintray"
|
||||
require "github_packages"
|
||||
require "github_releases"
|
||||
|
||||
module Homebrew
|
||||
@ -33,6 +34,8 @@ module Homebrew
|
||||
description: "Upload to the specified Internet Archive item (default: `homebrew`)."
|
||||
flag "--bintray-org=",
|
||||
description: "Upload to the specified Bintray organisation (default: `homebrew`)."
|
||||
flag "--github-org=",
|
||||
description: "Upload to the specified GitHub organisation's GitHub Packages (default: `homebrew`)."
|
||||
flag "--root-url=",
|
||||
description: "Use the specified <URL> as the root of the bottle's URL instead of Homebrew's default."
|
||||
|
||||
@ -73,6 +76,12 @@ module Homebrew
|
||||
end
|
||||
end
|
||||
|
||||
def github_packages?(bottles_hash)
|
||||
@github_packages ||= bottles_hash.values.all? do |bottle_hash|
|
||||
bottle_hash["bottle"]["root_url"].match? GitHubPackages::URL_REGEX
|
||||
end
|
||||
end
|
||||
|
||||
def pr_upload
|
||||
args = pr_upload_args.parse
|
||||
|
||||
@ -99,6 +108,8 @@ module Homebrew
|
||||
"Bintray"
|
||||
elsif github_releases?(bottles_hash)
|
||||
"GitHub Releases"
|
||||
elsif github_packages?(bottles_hash)
|
||||
"GitHub Packages"
|
||||
else
|
||||
odie "Service specified by root_url is not recognized"
|
||||
end
|
||||
@ -137,6 +148,10 @@ module Homebrew
|
||||
elsif github_releases?(bottles_hash)
|
||||
github_releases = GitHubReleases.new
|
||||
github_releases.upload_bottles(bottles_hash)
|
||||
elsif github_packages?(bottles_hash)
|
||||
github_org = args.github_org || "homebrew"
|
||||
github_packages = GitHubPackages.new(org: github_org)
|
||||
github_packages.upload_bottles(bottles_hash)
|
||||
else
|
||||
odie "Service specified by root_url is not recognized"
|
||||
end
|
||||
|
||||
@ -13,6 +13,8 @@ require "mechanize/http/content_disposition_parser"
|
||||
|
||||
require "utils/curl"
|
||||
|
||||
require "github_packages"
|
||||
|
||||
# @abstract Abstract superclass for all download strategies.
|
||||
#
|
||||
# @api private
|
||||
@ -527,6 +529,25 @@ class CurlDownloadStrategy < AbstractFileDownloadStrategy
|
||||
end
|
||||
end
|
||||
|
||||
# Strategy for downloading a file from an GitHub Packages URL.
|
||||
#
|
||||
# @api public
|
||||
class CurlGitHubPackagesDownloadStrategy < CurlDownloadStrategy
|
||||
attr_accessor :checksum, :name
|
||||
|
||||
private
|
||||
|
||||
def _fetch(url:, resolved_url:)
|
||||
raise "Empty checksum" if checksum.blank?
|
||||
raise "Empty name" if name.blank?
|
||||
|
||||
_, org, repo, = *url.match(GitHubPackages::URL_REGEX)
|
||||
|
||||
blob_url = "https://ghcr.io/v2/#{org}/#{repo}/#{name}/blobs/sha256:#{checksum}"
|
||||
curl_download(blob_url, "--header", "Authorization: Bearer", to: temporary_path)
|
||||
end
|
||||
end
|
||||
|
||||
# Strategy for downloading a file from an Apache Mirror URL.
|
||||
#
|
||||
# @api public
|
||||
@ -1243,6 +1264,8 @@ class DownloadStrategyDetector
|
||||
|
||||
def self.detect_from_url(url)
|
||||
case url
|
||||
when GitHubPackages::URL_REGEX
|
||||
CurlGitHubPackagesDownloadStrategy
|
||||
when %r{^https?://github\.com/[^/]+/[^/]+\.git$}
|
||||
GitHubGitDownloadStrategy
|
||||
when %r{^https?://.+\.git$},
|
||||
|
||||
@ -172,6 +172,13 @@ module Homebrew
|
||||
"\n\n *Note:* Homebrew doesn't require permissions for any of the scopes, but some " \
|
||||
"developer commands may require additional permissions.",
|
||||
},
|
||||
HOMEBREW_GITHUB_PACKAGES_TOKEN: {
|
||||
description: "Use this GitHub personal access token when accessing the GitHub Packages Registry "\
|
||||
"(where bottles may be stored).",
|
||||
},
|
||||
HOMEBREW_GITHUB_PACKAGES_USER: {
|
||||
description: "Use this username when accessing the GitHub Packages Registry (where bottles may be stored).",
|
||||
},
|
||||
HOMEBREW_GIT_EMAIL: {
|
||||
description: "Set the Git author and committer email to this value.",
|
||||
},
|
||||
|
||||
71
Library/Homebrew/github_packages.rb
Normal file
71
Library/Homebrew/github_packages.rb
Normal file
@ -0,0 +1,71 @@
|
||||
# typed: false
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "utils/curl"
|
||||
require "json"
|
||||
|
||||
# GitHub Packages client.
|
||||
#
|
||||
# @api private
|
||||
class GitHubPackages
|
||||
extend T::Sig
|
||||
|
||||
include Context
|
||||
include Utils::Curl
|
||||
|
||||
URL_REGEX = %r{https://ghcr.io/v2/([\w-]+)/([\w-]+)}.freeze
|
||||
|
||||
sig { returns(String) }
|
||||
def inspect
|
||||
"#<GitHubPackages: org=#{@github_org}>"
|
||||
end
|
||||
|
||||
sig { params(org: T.nilable(String)).void }
|
||||
def initialize(org: "homebrew")
|
||||
@github_org = org
|
||||
|
||||
raise UsageError, "Must set a GitHub organisation!" unless @github_org
|
||||
|
||||
ENV["HOMEBREW_FORCE_HOMEBREW_ON_LINUX"] = "1" if @github_org == "homebrew" && !OS.mac?
|
||||
end
|
||||
|
||||
sig { params(bottles_hash: T::Hash[String, T.untyped]).void }
|
||||
def upload_bottles(bottles_hash)
|
||||
user = Homebrew::EnvConfig.github_packages_user
|
||||
token = Homebrew::EnvConfig.github_packages_token
|
||||
|
||||
raise UsageError, "HOMEBREW_GITHUB_PACKAGES_USER is unset." if user.blank?
|
||||
raise UsageError, "HOMEBREW_GITHUB_PACKAGES_TOKEN is unset." if token.blank?
|
||||
|
||||
oras = Formula["oras"].opt_bin/"oras" if Formula["oras"].any_version_installed?
|
||||
oras ||= begin
|
||||
ohai "Installing `oras` for upload..."
|
||||
safe_system HOMEBREW_BREW_FILE, "install", "oras"
|
||||
Formula["oras"].opt_bin/"oras"
|
||||
end
|
||||
|
||||
bottles_hash.each do |formula_name, bottle_hash|
|
||||
_, org, repo, = *bottle_hash["bottle"]["root_url"].match(URL_REGEX)
|
||||
version = bottle_hash["formula"]["pkg_version"]
|
||||
rebuild = bottle_hash["bottle"]["rebuild"]
|
||||
|
||||
bottle_hash["bottle"]["tags"].each do |bottle_tag, tag_hash|
|
||||
local_file = tag_hash["local_filename"]
|
||||
odebug "Uploading #{local_file}"
|
||||
|
||||
tag = "#{version}.#{bottle_tag}"
|
||||
tag = "#{tag}.#{rebuild}" if rebuild.positive?
|
||||
|
||||
system_command!(oras, verbose: true, print_stdout: true, args: [
|
||||
"push", "ghcr.io/#{org}/#{repo}/#{formula_name}:#{tag}",
|
||||
"--username", user,
|
||||
"--password", token,
|
||||
"#{local_file}:application/vnd.oci.image.layer.v1.tar+gzip"
|
||||
])
|
||||
|
||||
# TODO: make this package public?
|
||||
# TODO: make this latest?
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -316,12 +316,16 @@ class Bottle
|
||||
|
||||
def fetch(verify_download_integrity: true)
|
||||
# add the default bottle domain as a fallback mirror
|
||||
# TODO: this may need adjusted when if we use GitHub Packages by default
|
||||
if @resource.download_strategy == CurlDownloadStrategy &&
|
||||
@resource.url.start_with?(Homebrew::EnvConfig.bottle_domain)
|
||||
fallback_url = @resource.url
|
||||
.sub(/^#{Regexp.escape(Homebrew::EnvConfig.bottle_domain)}/,
|
||||
HOMEBREW_BOTTLE_DEFAULT_DOMAIN)
|
||||
@resource.mirror(fallback_url) if [@resource.url, *@resource.mirrors].exclude?(fallback_url)
|
||||
elsif @resource.download_strategy == CurlGitHubPackagesDownloadStrategy
|
||||
@resource.downloader.name = @name
|
||||
@resource.downloader.checksum = @resource.checksum.hexdigest
|
||||
end
|
||||
@resource.fetch(verify_download_integrity: verify_download_integrity)
|
||||
end
|
||||
|
||||
@ -1483,6 +1483,7 @@ _brew_pr_upload() {
|
||||
--bintray-org
|
||||
--debug
|
||||
--dry-run
|
||||
--github-org
|
||||
--help
|
||||
--keep-old
|
||||
--no-commit
|
||||
|
||||
@ -1069,6 +1069,7 @@ __fish_brew_complete_arg 'pr-upload' -l archive-item -d 'Upload to the specified
|
||||
__fish_brew_complete_arg 'pr-upload' -l bintray-org -d 'Upload to the specified Bintray organisation (default: `homebrew`)'
|
||||
__fish_brew_complete_arg 'pr-upload' -l debug -d 'Display any debugging information'
|
||||
__fish_brew_complete_arg 'pr-upload' -l dry-run -d 'Print what would be done rather than doing it'
|
||||
__fish_brew_complete_arg 'pr-upload' -l github-org -d 'Upload to the specified GitHub organisation\'s GitHub Packages (default: `homebrew`)'
|
||||
__fish_brew_complete_arg 'pr-upload' -l help -d 'Show this message'
|
||||
__fish_brew_complete_arg 'pr-upload' -l keep-old -d 'If the formula specifies a rebuild version, attempt to preserve its value in the generated DSL'
|
||||
__fish_brew_complete_arg 'pr-upload' -l no-commit -d 'Do not generate a new commit before uploading'
|
||||
|
||||
@ -1243,6 +1243,7 @@ _brew_pr_upload() {
|
||||
'--bintray-org[Upload to the specified Bintray organisation (default: `homebrew`)]' \
|
||||
'--debug[Display any debugging information]' \
|
||||
'--dry-run[Print what would be done rather than doing it]' \
|
||||
'--github-org[Upload to the specified GitHub organisation'\''s GitHub Packages (default: `homebrew`)]' \
|
||||
'--help[Show this message]' \
|
||||
'--keep-old[If the formula specifies a rebuild version, attempt to preserve its value in the generated DSL]' \
|
||||
'--no-commit[Do not generate a new commit before uploading]' \
|
||||
|
||||
@ -1214,6 +1214,8 @@ Apply the bottle commit and publish bottles to a host.
|
||||
Upload to the specified Internet Archive item (default: `homebrew`).
|
||||
* `--bintray-org`:
|
||||
Upload to the specified Bintray organisation (default: `homebrew`).
|
||||
* `--github-org`:
|
||||
Upload to the specified GitHub organisation's GitHub Packages (default: `homebrew`).
|
||||
* `--root-url`:
|
||||
Use the specified *`URL`* as the root of the bottle's URL instead of Homebrew's default.
|
||||
|
||||
@ -1829,6 +1831,12 @@ example, run `export HOMEBREW_NO_INSECURE_REDIRECT=1` rather than just
|
||||
|
||||
*Note:* Homebrew doesn't require permissions for any of the scopes, but some developer commands may require additional permissions.
|
||||
|
||||
- `HOMEBREW_GITHUB_PACKAGES_TOKEN`
|
||||
<br>Use this GitHub personal access token when accessing the GitHub Packages Registry (where bottles may be stored).
|
||||
|
||||
- `HOMEBREW_GITHUB_PACKAGES_USER`
|
||||
<br>Use this username when accessing the GitHub Packages Registry (where bottles may be stored).
|
||||
|
||||
- `HOMEBREW_GIT_EMAIL`
|
||||
<br>Set the Git author and committer email to this value.
|
||||
|
||||
|
||||
@ -1708,6 +1708,10 @@ Upload to the specified Internet Archive item (default: \fBhomebrew\fR)\.
|
||||
Upload to the specified Bintray organisation (default: \fBhomebrew\fR)\.
|
||||
.
|
||||
.TP
|
||||
\fB\-\-github\-org\fR
|
||||
Upload to the specified GitHub organisation\'s GitHub Packages (default: \fBhomebrew\fR)\.
|
||||
.
|
||||
.TP
|
||||
\fB\-\-root\-url\fR
|
||||
Use the specified \fIURL\fR as the root of the bottle\'s URL instead of Homebrew\'s default\.
|
||||
.
|
||||
@ -2602,6 +2606,18 @@ Use this personal access token for the GitHub API, for features such as \fBbrew
|
||||
\fINote:\fR Homebrew doesn\'t require permissions for any of the scopes, but some developer commands may require additional permissions\.
|
||||
.
|
||||
.TP
|
||||
\fBHOMEBREW_GITHUB_PACKAGES_TOKEN\fR
|
||||
.
|
||||
.br
|
||||
Use this GitHub personal access token when accessing the GitHub Packages Registry (where bottles may be stored)\.
|
||||
.
|
||||
.TP
|
||||
\fBHOMEBREW_GITHUB_PACKAGES_USER\fR
|
||||
.
|
||||
.br
|
||||
Use this username when accessing the GitHub Packages Registry (where bottles may be stored)\.
|
||||
.
|
||||
.TP
|
||||
\fBHOMEBREW_GIT_EMAIL\fR
|
||||
.
|
||||
.br
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user