diff --git a/Library/Homebrew/dev-cmd/pr-upload.rb b/Library/Homebrew/dev-cmd/pr-upload.rb index 03c2512ca4..9a4846e35d 100644 --- a/Library/Homebrew/dev-cmd/pr-upload.rb +++ b/Library/Homebrew/dev-cmd/pr-upload.rb @@ -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 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 diff --git a/Library/Homebrew/env_config.rb b/Library/Homebrew/env_config.rb index 391d7e38c9..cd8ff0a1a5 100644 --- a/Library/Homebrew/env_config.rb +++ b/Library/Homebrew/env_config.rb @@ -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.", }, diff --git a/Library/Homebrew/github_packages.rb b/Library/Homebrew/github_packages.rb new file mode 100644 index 0000000000..daaad08bba --- /dev/null +++ b/Library/Homebrew/github_packages.rb @@ -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 + "#" + 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 diff --git a/completions/bash/brew b/completions/bash/brew index c08a2f4f01..e9aa2d4cfb 100644 --- a/completions/bash/brew +++ b/completions/bash/brew @@ -1483,6 +1483,7 @@ _brew_pr_upload() { --bintray-org --debug --dry-run + --github-org --help --keep-old --no-commit diff --git a/completions/fish/brew.fish b/completions/fish/brew.fish index 827d0e60e6..842c424a88 100644 --- a/completions/fish/brew.fish +++ b/completions/fish/brew.fish @@ -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' diff --git a/completions/zsh/_brew b/completions/zsh/_brew index bffa4d86a6..8142d84e7b 100644 --- a/completions/zsh/_brew +++ b/completions/zsh/_brew @@ -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]' \ diff --git a/docs/Manpage.md b/docs/Manpage.md index 507b314185..627cc6e371 100644 --- a/docs/Manpage.md +++ b/docs/Manpage.md @@ -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` +
Use this GitHub personal access token when accessing the GitHub Packages Registry (where bottles may be stored). + +- `HOMEBREW_GITHUB_PACKAGES_USER` +
Use this username when accessing the GitHub Packages Registry (where bottles may be stored). + - `HOMEBREW_GIT_EMAIL`
Set the Git author and committer email to this value. diff --git a/manpages/brew.1 b/manpages/brew.1 index d218cd8c79..827c8e5721 100644 --- a/manpages/brew.1 +++ b/manpages/brew.1 @@ -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