Merge pull request #20485 from SMillerDev/feat/audit/codeberg_audit

feat: audit codeberg repos
This commit is contained in:
Mike McQuaid 2025-08-18 07:50:25 +00:00 committed by GitHub
commit 24057cc9a5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 162 additions and 0 deletions

View File

@ -928,6 +928,20 @@ module Cask
add_error error, location: url.location if error add_error error, location: url.location if error
end end
sig { void }
def audit_forgejo_prerelease_version
return if (url = cask.url).nil?
odebug "Auditing Forgejo prerelease"
user, repo = get_repo_data(%r{https?://codeberg\.org/([^/]+)/([^/]+)/?.*}) if online?
return if user.nil? || repo.nil?
tag = SharedAudits.forgejo_tag_from_url(url.to_s)
tag ||= cask.version
error = SharedAudits.forgejo_release(user, repo, tag, cask:)
add_error error, location: url.location if error
end
sig { void } sig { void }
def audit_github_repository_archived def audit_github_repository_archived
# Deprecated/disabled casks may have an archived repository. # Deprecated/disabled casks may have an archived repository.
@ -960,6 +974,23 @@ module Cask
add_error "GitLab repo is archived", location: url.location if metadata["archived"] add_error "GitLab repo is archived", location: url.location if metadata["archived"]
end end
sig { void }
def audit_forgejo_repository_archived
return if cask.deprecated? || cask.disabled?
return if (url = cask.url).nil?
user, repo = get_repo_data(%r{https?://codeberg\.org/([^/]+)/([^/]+)/?.*}) if online?
return if user.nil? || repo.nil?
metadata = SharedAudits.forgejo_repo_data(user, repo)
return if metadata.nil?
return unless metadata["archived"]
add_error "Forgejo repository is archived since #{metadata["archived_at"]}",
location: url.location
end
sig { void } sig { void }
def audit_github_repository def audit_github_repository
return unless new_cask? return unless new_cask?
@ -1002,6 +1033,20 @@ module Cask
add_error error, location: url.location if error add_error error, location: url.location if error
end end
sig { void }
def audit_forgejo_repository
return unless new_cask?
return if (url = cask.url).nil?
user, repo = get_repo_data(%r{https?://codeberg\.org/([^/]+)/([^/]+)/?.*})
return if user.nil? || repo.nil?
odebug "Auditing Forgejo repo"
error = SharedAudits.forgejo(user, repo)
add_error error, location: url.location if error
end
sig { void } sig { void }
def audit_denylist def audit_denylist
return unless cask.tap return unless cask.tap

View File

@ -677,6 +677,19 @@ module Homebrew
problem "GitLab repository is archived" if metadata["archived"] problem "GitLab repository is archived" if metadata["archived"]
end end
sig { void }
def audit_forgejo_repository_archived
return if formula.deprecated? || formula.disabled?
user, repo = get_repo_data(%r{https?://codeberg\.org/([^/]+)/([^/]+)/?.*}) if @online
return if user.blank?
metadata = SharedAudits.forgejo_repo_data(user, repo)
return if metadata.nil?
problem "Forgejo repository is archived since #{metadata["archived_at"]}" if metadata["archived"]
end
def audit_github_repository def audit_github_repository
user, repo = get_repo_data(%r{https?://github\.com/([^/]+)/([^/]+)/?.*}) if @new_formula user, repo = get_repo_data(%r{https?://github\.com/([^/]+)/([^/]+)/?.*}) if @new_formula
@ -708,6 +721,17 @@ module Homebrew
new_formula_problem warning new_formula_problem warning
end end
sig { void }
def audit_forgejo_repository
user, repo = get_repo_data(%r{https?://codeberg\.org/([^/]+)/([^/]+)/?.*}) if @new_formula
return if user.blank?
warning = SharedAudits.forgejo(user, repo)
return if warning.nil?
new_formula_problem warning
end
def get_repo_data(regex) def get_repo_data(regex)
return unless @core_tap return unless @core_tap
return unless @online return unless @online
@ -839,6 +863,16 @@ module Homebrew
error = SharedAudits.github_release(owner, repo, tag, formula:) error = SharedAudits.github_release(owner, repo, tag, formula:)
problem error if error problem error if error
end end
when %r{^https://codeberg\.org/([\w-]+)/([\w-]+)}
owner = T.must(Regexp.last_match(1))
repo = T.must(Regexp.last_match(2))
tag = SharedAudits.forgejo_tag_from_url(url)
tag ||= formula.stable.specs[:tag]
if @online && !tag.nil?
error = SharedAudits.forgejo_release(owner, repo, tag, formula:)
problem error if error
end
end end
end end

View File

@ -96,4 +96,21 @@ RSpec.describe SharedAudits do
expect(described_class.gitlab_tag_from_url(url)).to eq("2.5") expect(described_class.gitlab_tag_from_url(url)).to eq("2.5")
end end
end end
describe "::forgejo_tag_from_url" do
it "finds tags in basic urls" do
url = "https://codeberg.org/Aviac/codeberg-cli/archive/v0.4.11.tar.gz"
expect(described_class.forgejo_tag_from_url(url)).to eq("v0.4.11")
end
it "finds tags in urls with subgroups" do
url = "https://codeberg.org/Aviac/codeberg-cli/archive/some/test/1.2.3.tar.gz"
expect(described_class.forgejo_tag_from_url(url)).to eq("some/test/1.2.3")
end
it "finds tags in orgs/repos with special characters" do
url = "https://codeberg.org/Aviaca-b_cv/codeberg-cli/archive/v0.4.11.tar.gz"
expect(described_class.forgejo_tag_from_url(url)).to eq("v0.4.11")
end
end
end end

View File

@ -90,6 +90,16 @@ module SharedAudits
end end
end end
sig { params(user: String, repo: String).returns(T.nilable(T::Hash[String, T.untyped])) }
def self.forgejo_repo_data(user, repo)
@forgejo_repo_data ||= T.let({}, T.nilable(T::Hash[String, T.untyped]))
@forgejo_repo_data["#{user}/#{repo}"] ||= begin
result = Utils::Curl.curl_output("https://codeberg.org/api/v1/repos/#{user}/#{repo}")
JSON.parse(result.stdout) if result.status.success?
end
end
sig { params(user: String, repo: String, tag: String).returns(T.nilable(T::Hash[String, T.untyped])) } sig { params(user: String, repo: String, tag: String).returns(T.nilable(T::Hash[String, T.untyped])) }
private_class_method def self.gitlab_release_data(user, repo, tag) private_class_method def self.gitlab_release_data(user, repo, tag)
id = "#{user}/#{repo}/#{tag}" id = "#{user}/#{repo}/#{tag}"
@ -125,6 +135,40 @@ module SharedAudits
"#{tag} is a GitLab pre-release." "#{tag} is a GitLab pre-release."
end end
sig { params(user: String, repo: String, tag: String).returns(T.nilable(T::Hash[String, T.untyped])) }
private_class_method def self.forgejo_release_data(user, repo, tag)
id = "#{user}/#{repo}/#{tag}"
@forgejo_release_data ||= T.let({}, T.nilable(T::Hash[String, T.untyped]))
@forgejo_release_data[id] ||= begin
result = Utils::Curl.curl_output(
"https://codeberg.org/api/v1/repos/#{user}/#{repo}/releases/tags/#{tag}", "--fail"
)
JSON.parse(result.stdout) if result.status.success?
end
end
sig {
params(
user: String, repo: String, tag: String, formula: T.nilable(Formula), cask: T.nilable(Cask::Cask),
).returns(
T.nilable(String),
)
}
def self.forgejo_release(user, repo, tag, formula: nil, cask: nil)
release = forgejo_release_data(user, repo, tag)
return unless release
return unless release["prerelease"]
exception, version = if formula
[formula.tap&.audit_exception(:forgejo_prerelease_allowlist, formula.name), formula.version]
elsif cask
[cask.tap&.audit_exception(:forgejo_prerelease_allowlist, cask.token), cask.version]
end
return if [version, "all"].include?(exception)
"#{tag} is a Forgejo pre-release."
end
sig { params(user: String, repo: String).returns(T.nilable(String)) } sig { params(user: String, repo: String).returns(T.nilable(String)) }
def self.github(user, repo) def self.github(user, repo)
metadata = github_repo_data(user, repo) metadata = github_repo_data(user, repo)
@ -191,6 +235,23 @@ module SharedAudits
"Bitbucket repository not notable enough (<30 forks and <75 watchers)" "Bitbucket repository not notable enough (<30 forks and <75 watchers)"
end end
sig { params(user: String, repo: String).returns(T.nilable(String)) }
def self.forgejo(user, repo)
metadata = forgejo_repo_data(user, repo)
return if metadata.nil?
return "Forgejo fork (not canonical repository)" if metadata["fork"]
if (metadata["forks_count"] < 30) && (metadata["watchers_count"] < 30) &&
(metadata["stars_count"] < 75)
return "Forgejo repository not notable enough (<30 forks, <30 watchers and <75 stars)"
end
return if Date.parse(metadata["created_at"]) <= (Date.today - 30)
"Forgejo repository too new (<30 days old)"
end
sig { params(url: String).returns(T.nilable(String)) } sig { params(url: String).returns(T.nilable(String)) }
def self.github_tag_from_url(url) def self.github_tag_from_url(url)
tag = url[%r{^https://github\.com/[\w-]+/[\w.-]+/archive/refs/tags/(.+)\.(tar\.gz|zip)$}, 1] tag = url[%r{^https://github\.com/[\w-]+/[\w.-]+/archive/refs/tags/(.+)\.(tar\.gz|zip)$}, 1]
@ -202,6 +263,11 @@ module SharedAudits
url[%r{^https://gitlab\.com/(?:\w[\w.-]*/){2,}-/archive/([^/]+)/}, 1] url[%r{^https://gitlab\.com/(?:\w[\w.-]*/){2,}-/archive/([^/]+)/}, 1]
end end
sig { params(url: String).returns(T.nilable(String)) }
def self.forgejo_tag_from_url(url)
url[%r{^https://codeberg\.org/[\w-]+/[\w.-]+/archive/(.+)\.(tar\.gz|zip)$}, 1]
end
sig { params(formula_or_cask: T.any(Formula, Cask::Cask)).returns(T.nilable(String)) } sig { params(formula_or_cask: T.any(Formula, Cask::Cask)).returns(T.nilable(String)) }
def self.check_deprecate_disable_reason(formula_or_cask) def self.check_deprecate_disable_reason(formula_or_cask)
return if !formula_or_cask.deprecated? && !formula_or_cask.disabled? return if !formula_or_cask.deprecated? && !formula_or_cask.disabled?