update-sponsors: don't require admin token.
Instead, use a different API to query these with a lower scope. This should be usable by GitHub Actions.
This commit is contained in:
parent
93ea8cb2c4
commit
bc295f7947
@ -9,8 +9,8 @@ module Homebrew
|
|||||||
|
|
||||||
module_function
|
module_function
|
||||||
|
|
||||||
NAMED_TIER_AMOUNT = 100
|
NAMED_MONTHLY_AMOUNT = 100
|
||||||
URL_TIER_AMOUNT = 1000
|
URL_MONTHLY_AMOUNT = 1000
|
||||||
|
|
||||||
sig { returns(CLI::Parser) }
|
sig { returns(CLI::Parser) }
|
||||||
def update_sponsors_args
|
def update_sponsors_args
|
||||||
@ -23,16 +23,16 @@ module Homebrew
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def sponsor_name(s)
|
def sponsor_name(sponsor)
|
||||||
s["name"] || s["login"]
|
sponsor[:name] || sponsor[:login]
|
||||||
end
|
end
|
||||||
|
|
||||||
def sponsor_logo(s)
|
def sponsor_logo(sponsor)
|
||||||
"https://github.com/#{s["login"]}.png?size=64"
|
"https://github.com/#{sponsor[:login]}.png?size=64"
|
||||||
end
|
end
|
||||||
|
|
||||||
def sponsor_url(s)
|
def sponsor_url(sponsor)
|
||||||
"https://github.com/#{s["login"]}"
|
"https://github.com/#{sponsor[:login]}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_sponsors
|
def update_sponsors
|
||||||
@ -41,18 +41,13 @@ module Homebrew
|
|||||||
named_sponsors = []
|
named_sponsors = []
|
||||||
logo_sponsors = []
|
logo_sponsors = []
|
||||||
|
|
||||||
GitHub.sponsors_by_tier("Homebrew").each do |tier|
|
GitHub.sponsorships("Homebrew").each do |s|
|
||||||
if tier["tier"] >= NAMED_TIER_AMOUNT
|
largest_monthly_amount = [s[:monthly_amount], s[:closest_tier_monthly_amount]].compact.max
|
||||||
named_sponsors += tier["sponsors"].map do |s|
|
named_sponsors << "[#{sponsor_name(s)}](#{sponsor_url(s)})" if largest_monthly_amount >= NAMED_MONTHLY_AMOUNT
|
||||||
"[#{sponsor_name(s)}](#{sponsor_url(s)})"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
next if tier["tier"] < URL_TIER_AMOUNT
|
next if largest_monthly_amount < URL_MONTHLY_AMOUNT
|
||||||
|
|
||||||
logo_sponsors += tier["sponsors"].map do |s|
|
logo_sponsors << "[})](#{sponsor_url(s)})"
|
||||||
"[})](#{sponsor_url(s)})"
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
named_sponsors << "many other users and organisations via [GitHub Sponsors](https://github.com/sponsors/Homebrew)"
|
named_sponsors << "many other users and organisations via [GitHub Sponsors](https://github.com/sponsors/Homebrew)"
|
||||||
|
@ -57,14 +57,6 @@ describe GitHub do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "::sponsors_by_tier", :needs_network do
|
|
||||||
it "errors on an unauthenticated token" do
|
|
||||||
expect {
|
|
||||||
described_class.sponsors_by_tier("Homebrew")
|
|
||||||
}.to raise_error(/INSUFFICIENT_SCOPES|FORBIDDEN|token needs the 'admin:org' scope/)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "::get_artifact_url", :needs_network do
|
describe "::get_artifact_url", :needs_network do
|
||||||
it "fails to find a nonexistent workflow" do
|
it "fails to find a nonexistent workflow" do
|
||||||
expect {
|
expect {
|
||||||
|
@ -403,59 +403,75 @@ module GitHub
|
|||||||
result["organization"]["team"]["members"]["nodes"].to_h { |member| [member["login"], member["name"]] }
|
result["organization"]["team"]["members"]["nodes"].to_h { |member| [member["login"], member["name"]] }
|
||||||
end
|
end
|
||||||
|
|
||||||
def sponsors_by_tier(user)
|
def sponsorships(user)
|
||||||
query = <<~EOS
|
has_next_page = true
|
||||||
{ organization(login: "#{user}") {
|
after = ""
|
||||||
sponsorsListing {
|
sponsorships = []
|
||||||
tiers(first: 10, orderBy: {field: MONTHLY_PRICE_IN_CENTS, direction: DESC}) {
|
errors = []
|
||||||
|
while has_next_page
|
||||||
|
query = <<~EOS
|
||||||
|
{ organization(login: "#{user}") {
|
||||||
|
sponsorshipsAsMaintainer(first: 100 #{after}) {
|
||||||
|
pageInfo {
|
||||||
|
startCursor
|
||||||
|
hasNextPage
|
||||||
|
endCursor
|
||||||
|
}
|
||||||
|
totalCount
|
||||||
nodes {
|
nodes {
|
||||||
monthlyPriceInDollars
|
tier {
|
||||||
adminInfo {
|
monthlyPriceInDollars
|
||||||
sponsorships(first: 100, includePrivate: true) {
|
closestLesserValueTier {
|
||||||
totalCount
|
monthlyPriceInDollars
|
||||||
nodes {
|
|
||||||
privacyLevel
|
|
||||||
sponsorEntity {
|
|
||||||
__typename
|
|
||||||
... on Organization { login name }
|
|
||||||
... on User { login name }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
sponsorEntity {
|
||||||
|
__typename
|
||||||
|
... on Organization { login name }
|
||||||
|
... on User { login name }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
EOS
|
||||||
EOS
|
# Some organisations do not permit themselves to be queried through the
|
||||||
result = API.open_graphql(query, scopes: ["admin:org", "user"])
|
# API like this and raise an error so handle these errors later.
|
||||||
|
# This has been reported to GitHub.
|
||||||
|
result = API.open_graphql(query, scopes: ["user"], raise_errors: false)
|
||||||
|
errors += result["errors"] if result["errors"].present?
|
||||||
|
|
||||||
tiers = result["organization"]["sponsorsListing"]["tiers"]["nodes"]
|
current_sponsorships = result["data"]["organization"]["sponsorshipsAsMaintainer"]
|
||||||
|
|
||||||
tiers.map do |t|
|
# The organisations mentioned above will show up as nil nodes.
|
||||||
tier = t["monthlyPriceInDollars"]
|
sponsorships += current_sponsorships["nodes"].compact
|
||||||
raise API::Error, "Your token needs the 'admin:org' scope to access this API" if t["adminInfo"].nil?
|
|
||||||
|
|
||||||
sponsorships = t["adminInfo"]["sponsorships"]
|
if (page_info = current_sponsorships["pageInfo"].presence) &&
|
||||||
count = sponsorships["totalCount"]
|
page_info["hasNextPage"].presence
|
||||||
sponsors = sponsorships["nodes"].map do |sponsor|
|
after = %Q(, after: "#{page_info["endCursor"]}")
|
||||||
next unless sponsor["privacyLevel"] == "PUBLIC"
|
else
|
||||||
|
has_next_page = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
se = sponsor["sponsorEntity"]
|
# Only raise errors if we didn't get any sponsorships.
|
||||||
{
|
if sponsorships.blank? && errors.present?
|
||||||
"name" => se["name"].presence || sponsor["login"],
|
raise API::Error, errors.map { |e| "#{e["type"]}: #{e["message"]}" }.join("\n")
|
||||||
"login" => se["login"],
|
end
|
||||||
"type" => se["__typename"].downcase,
|
|
||||||
}
|
sponsorships.map do |sponsorship|
|
||||||
end.compact
|
closest_tier_monthly_amount = sponsorship["tier"].fetch("closestLesserValueTier", nil)
|
||||||
|
&.fetch("monthlyPriceInDollars", nil)
|
||||||
|
monthly_amount = sponsorship["tier"]["monthlyPriceInDollars"]
|
||||||
|
sponsor = sponsorship["sponsorEntity"]
|
||||||
|
|
||||||
{
|
{
|
||||||
"tier" => tier,
|
name: sponsor["name"].presence || sponsor["login"],
|
||||||
"count" => count,
|
login: sponsor["login"],
|
||||||
"sponsors" => sponsors,
|
monthly_amount: monthly_amount,
|
||||||
|
closest_tier_monthly_amount: closest_tier_monthly_amount || 0,
|
||||||
}
|
}
|
||||||
end.compact
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_repo_license(user, repo)
|
def get_repo_license(user, repo)
|
||||||
|
@ -252,17 +252,19 @@ module GitHub
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def open_graphql(query, variables: nil, scopes: [].freeze)
|
def open_graphql(query, variables: nil, scopes: [].freeze, raise_errors: true)
|
||||||
data = { query: query, variables: variables }
|
data = { query: query, variables: variables }
|
||||||
result = open_rest("#{API_URL}/graphql", scopes: scopes, data: data, request_method: "POST")
|
result = open_rest("#{API_URL}/graphql", scopes: scopes, data: data, request_method: "POST")
|
||||||
|
|
||||||
if result["errors"].present?
|
if raise_errors
|
||||||
raise Error, result["errors"].map { |e|
|
if result["errors"].present?
|
||||||
"#{e["type"]}: #{e["message"]}"
|
raise Error, result["errors"].map { |e| "#{e["type"]}: #{e["message"]}" }.join("\n")
|
||||||
}.join("\n")
|
end
|
||||||
end
|
|
||||||
|
|
||||||
result["data"]
|
result["data"]
|
||||||
|
else
|
||||||
|
result
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def raise_error(output, errors, http_code, headers, scopes)
|
def raise_error(output, errors, http_code, headers, scopes)
|
||||||
|
@ -85,4 +85,6 @@ Flaky test detection and tracking is provided by [BuildPulse](https://buildpulse
|
|||||||
[](https://dnsimple.com/resolving/homebrew#gh-light-mode-only)
|
[](https://dnsimple.com/resolving/homebrew#gh-light-mode-only)
|
||||||
[](https://dnsimple.com/resolving/homebrew#gh-dark-mode-only)
|
[](https://dnsimple.com/resolving/homebrew#gh-dark-mode-only)
|
||||||
|
|
||||||
Homebrew is generously supported by [Randy Reddig](https://github.com/ydnar), [Codecademy](https://github.com/Codecademy), [Appwrite](https://github.com/appwrite), [embark-studios](https://github.com/embark-studios) and many other users and organisations via [GitHub Sponsors](https://github.com/sponsors/Homebrew).
|
Homebrew is generously supported by [GitHub](https://github.com/github), [Custom Ink](https://github.com/customink), [Randy Reddig](https://github.com/ydnar), [Christian (Xian) M. Lilley](https://github.com/xml), [David Fernandez](https://github.com/lidstromberg), [embark-studios](https://github.com/embark-studios) and many other users and organisations via [GitHub Sponsors](https://github.com/sponsors/Homebrew).
|
||||||
|
|
||||||
|
[](https://github.com/github)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user