Merge pull request #17097 from ZhongRuoyu/artifact-actions-v4

pr-pull: support globbing artifacts
This commit is contained in:
Ruoyu Zhong 2024-04-18 00:58:44 +08:00 committed by GitHub
commit 84d731888e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 46 additions and 32 deletions

View File

@ -55,8 +55,8 @@ module Homebrew
flag "--message=", flag "--message=",
depends_on: "--autosquash", depends_on: "--autosquash",
description: "Message to include when autosquashing revision bumps, deletions and rebuilds." description: "Message to include when autosquashing revision bumps, deletions and rebuilds."
flag "--artifact=", flag "--artifact-pattern=", "--artifact=",
description: "Download artifacts with the specified name (default: `bottles`)." description: "Download artifacts with the specified pattern (default: `bottles{,_*}`)."
flag "--tap=", flag "--tap=",
description: "Target tap repository (default: `homebrew/core`)." description: "Target tap repository (default: `homebrew/core`)."
flag "--root-url=", flag "--root-url=",
@ -81,7 +81,7 @@ module Homebrew
ensure_executable!("unzip", reason: "extracting CI artifacts") ensure_executable!("unzip", reason: "extracting CI artifacts")
workflows = args.workflows.presence || ["tests.yml"] workflows = args.workflows.presence || ["tests.yml"]
artifact = args.artifact || "bottles" artifact_pattern = args.artifact_pattern || "bottles{,_*}"
tap = Tap.fetch(args.tap || CoreTap.instance.name) tap = Tap.fetch(args.tap || CoreTap.instance.name)
raise TapUnavailableError, tap.name unless tap.installed? raise TapUnavailableError, tap.name unless tap.installed?
@ -143,7 +143,7 @@ module Homebrew
workflows.each do |workflow| workflows.each do |workflow|
workflow_run = GitHub.get_workflow_run( workflow_run = GitHub.get_workflow_run(
user, repo, pr, workflow_id: workflow, artifact_name: artifact user, repo, pr, workflow_id: workflow, artifact_pattern:
) )
if args.ignore_missing_artifacts.present? && if args.ignore_missing_artifacts.present? &&
T.must(args.ignore_missing_artifacts).include?(workflow) && T.must(args.ignore_missing_artifacts).include?(workflow) &&
@ -155,8 +155,9 @@ module Homebrew
end end
ohai "Downloading bottles for workflow: #{workflow}" ohai "Downloading bottles for workflow: #{workflow}"
url = GitHub.get_artifact_url(workflow_run)
GitHub.download_artifact(url, pr) urls = GitHub.get_artifact_urls(workflow_run)
urls.each { |url| GitHub.download_artifact(url, pr) }
end end
next if args.no_upload? next if args.no_upload?

View File

@ -8,6 +8,9 @@ class Homebrew::CLI::Args
sig { returns(T.nilable(String)) } sig { returns(T.nilable(String)) }
def artifact; end def artifact; end
sig { returns(T.nilable(String)) }
def artifact_pattern; end
sig { returns(T::Boolean) } sig { returns(T::Boolean) }
def autosquash?; end def autosquash?; end

View File

@ -46,10 +46,10 @@ RSpec.describe GitHub do
end end
end end
describe "::get_artifact_url", :needs_network do describe "::get_artifact_urls", :needs_network do
it "fails to find a nonexistent workflow" do it "fails to find a nonexistent workflow" do
expect do expect do
described_class.get_artifact_url( described_class.get_artifact_urls(
described_class.get_workflow_run("Homebrew", "homebrew-core", "1"), described_class.get_workflow_run("Homebrew", "homebrew-core", "1"),
) )
end.to raise_error(/No matching check suite found/) end.to raise_error(/No matching check suite found/)
@ -57,19 +57,27 @@ RSpec.describe GitHub do
it "fails to find artifacts that don't exist" do it "fails to find artifacts that don't exist" do
expect do expect do
described_class.get_artifact_url( described_class.get_artifact_urls(
described_class.get_workflow_run("Homebrew", "homebrew-core", "135608", described_class.get_workflow_run("Homebrew", "homebrew-core", "135608",
workflow_id: "triage.yml", artifact_name: "false_artifact"), workflow_id: "triage.yml", artifact_pattern: "false_artifact"),
) )
end.to raise_error(/No artifact .+ was found/) end.to raise_error(/No artifacts with the pattern .+ were found/)
end end
it "gets an artifact link" do it "gets artifact URLs" do
url = described_class.get_artifact_url( urls = described_class.get_artifact_urls(
described_class.get_workflow_run("Homebrew", "homebrew-core", "135608", described_class.get_workflow_run("Homebrew", "homebrew-core", "135608",
workflow_id: "triage.yml", artifact_name: "event_payload"), workflow_id: "triage.yml", artifact_pattern: "event_payload"),
) )
expect(url).to eq("https://api.github.com/repos/Homebrew/homebrew-core/actions/artifacts/781984175/zip") expect(urls).to eq(["https://api.github.com/repos/Homebrew/homebrew-core/actions/artifacts/781984175/zip"])
end
it "supports pattern matching" do
urls = described_class.get_artifact_urls(
described_class.get_workflow_run("Homebrew", "brew", "17068",
workflow_id: "pkg-installer.yml", artifact_pattern: "Homebrew-*.pkg"),
)
expect(urls).to eq(["https://api.github.com/repos/Homebrew/brew/actions/artifacts/1405050842/zip"])
end end
end end

View File

@ -283,7 +283,7 @@ module GitHub
API.open_rest(url, data_binary_path: local_file, request_method: :POST, scopes: CREATE_ISSUE_FORK_OR_PR_SCOPES) API.open_rest(url, data_binary_path: local_file, request_method: :POST, scopes: CREATE_ISSUE_FORK_OR_PR_SCOPES)
end end
def self.get_workflow_run(user, repo, pull_request, workflow_id: "tests.yml", artifact_name: "bottles") def self.get_workflow_run(user, repo, pull_request, workflow_id: "tests.yml", artifact_pattern: "bottles{,_*}")
scopes = CREATE_ISSUE_FORK_OR_PR_SCOPES scopes = CREATE_ISSUE_FORK_OR_PR_SCOPES
# GraphQL unfortunately has no way to get the workflow yml name, so we need an extra REST call. # GraphQL unfortunately has no way to get the workflow yml name, so we need an extra REST call.
@ -333,11 +333,11 @@ module GitHub
[] []
end end
[check_suite, user, repo, pull_request, workflow_id, scopes, artifact_name] [check_suite, user, repo, pull_request, workflow_id, scopes, artifact_pattern]
end end
def self.get_artifact_url(workflow_array) def self.get_artifact_urls(workflow_array)
check_suite, user, repo, pr, workflow_id, scopes, artifact_name = *workflow_array check_suite, user, repo, pr, workflow_id, scopes, artifact_pattern = *workflow_array
if check_suite.empty? if check_suite.empty?
raise API::Error, <<~EOS raise API::Error, <<~EOS
No matching check suite found for these criteria! No matching check suite found for these criteria!
@ -357,18 +357,20 @@ module GitHub
run_id = check_suite.last["workflowRun"]["databaseId"] run_id = check_suite.last["workflowRun"]["databaseId"]
artifacts = API.open_rest("#{API_URL}/repos/#{user}/#{repo}/actions/runs/#{run_id}/artifacts", scopes:) artifacts = API.open_rest("#{API_URL}/repos/#{user}/#{repo}/actions/runs/#{run_id}/artifacts", scopes:)
artifact = artifacts["artifacts"].select do |art| matching_artifacts =
art["name"] == artifact_name artifacts["artifacts"]
end .group_by { |art| art["name"] }
.select { |name| File.fnmatch?(artifact_pattern, name, File::FNM_EXTGLOB) }
.map { |_, arts| arts.last }
if artifact.empty? if matching_artifacts.empty?
raise API::Error, <<~EOS raise API::Error, <<~EOS
No artifact with the name `#{artifact_name}` was found! No artifacts with the pattern `#{artifact_pattern}` were found!
#{Formatter.url check_suite.last["workflowRun"]["url"]} #{Formatter.url check_suite.last["workflowRun"]["url"]}
EOS EOS
end end
artifact.last["archive_download_url"] matching_artifacts.map { |art| art["archive_download_url"] }
end end
def self.public_member_usernames(org, per_page: 100) def self.public_member_usernames(org, per_page: 100)

View File

@ -1760,7 +1760,7 @@ _brew_pr_pull() {
case "${cur}" in case "${cur}" in
-*) -*)
__brewcomp " __brewcomp "
--artifact --artifact-pattern
--autosquash --autosquash
--branch-okay --branch-okay
--clean --clean

View File

@ -1191,7 +1191,7 @@ __fish_brew_complete_arg 'pr-publish' -l workflow -d 'Target workflow filename (
__fish_brew_complete_cmd 'pr-pull' 'Download and publish bottles, and apply the bottle commit from a pull request with artifacts generated by GitHub Actions' __fish_brew_complete_cmd 'pr-pull' 'Download and publish bottles, and apply the bottle commit from a pull request with artifacts generated by GitHub Actions'
__fish_brew_complete_arg 'pr-pull' -l artifact -d 'Download artifacts with the specified name (default: `bottles`)' __fish_brew_complete_arg 'pr-pull' -l artifact-pattern -d 'Download artifacts with the specified pattern (default: `bottles{,_*}`)'
__fish_brew_complete_arg 'pr-pull' -l autosquash -d 'Automatically reformat and reword commits in the pull request to our preferred format' __fish_brew_complete_arg 'pr-pull' -l autosquash -d 'Automatically reformat and reword commits in the pull request to our preferred format'
__fish_brew_complete_arg 'pr-pull' -l branch-okay -d 'Do not warn if pulling to a branch besides the repository default (useful for testing)' __fish_brew_complete_arg 'pr-pull' -l branch-okay -d 'Do not warn if pulling to a branch besides the repository default (useful for testing)'
__fish_brew_complete_arg 'pr-pull' -l clean -d 'Do not amend the commits from pull requests' __fish_brew_complete_arg 'pr-pull' -l clean -d 'Do not amend the commits from pull requests'

View File

@ -1482,7 +1482,7 @@ _brew_pr_publish() {
# brew pr-pull # brew pr-pull
_brew_pr_pull() { _brew_pr_pull() {
_arguments \ _arguments \
'--artifact[Download artifacts with the specified name (default: `bottles`)]' \ '--artifact-pattern[Download artifacts with the specified pattern (default: `bottles{,_*}`)]' \
'(--clean)--autosquash[Automatically reformat and reword commits in the pull request to our preferred format]' \ '(--clean)--autosquash[Automatically reformat and reword commits in the pull request to our preferred format]' \
'--branch-okay[Do not warn if pulling to a branch besides the repository default (useful for testing)]' \ '--branch-okay[Do not warn if pulling to a branch besides the repository default (useful for testing)]' \
'(--autosquash)--clean[Do not amend the commits from pull requests]' \ '(--autosquash)--clean[Do not amend the commits from pull requests]' \

View File

@ -2423,9 +2423,9 @@ repository.
: Message to include when autosquashing revision bumps, deletions and rebuilds. : Message to include when autosquashing revision bumps, deletions and rebuilds.
`--artifact` `--artifact-pattern`
: Download artifacts with the specified name (default: `bottles`). : Download artifacts with the specified pattern (default: `bottles{,_*}`).
`--tap` `--tap`

View File

@ -1543,8 +1543,8 @@ Specify a committer name and email in \fBgit\fP\[u2019]s standard author format\
\fB\-\-message\fP \fB\-\-message\fP
Message to include when autosquashing revision bumps, deletions and rebuilds\. Message to include when autosquashing revision bumps, deletions and rebuilds\.
.TP .TP
\fB\-\-artifact\fP \fB\-\-artifact\-pattern\fP
Download artifacts with the specified name (default: \fBbottles\fP)\. Download artifacts with the specified pattern (default: \fBbottles{,_*}\fP)\.
.TP .TP
\fB\-\-tap\fP \fB\-\-tap\fP
Target tap repository (default: \fBhomebrew/core\fP)\. Target tap repository (default: \fBhomebrew/core\fP)\.