Merge pull request #4316 from reitermarkus/merge-search
Merge `brew cask search` into `brew search`.
This commit is contained in:
commit
25542d7398
@ -16,14 +16,15 @@ require "pathname"
|
||||
HOMEBREW_LIBRARY_PATH = Pathname.new(__FILE__).realpath.parent
|
||||
|
||||
require "English"
|
||||
unless $LOAD_PATH.include?(HOMEBREW_LIBRARY_PATH.to_s)
|
||||
$LOAD_PATH.unshift(HOMEBREW_LIBRARY_PATH.to_s)
|
||||
end
|
||||
|
||||
unless $LOAD_PATH.include?("#{HOMEBREW_LIBRARY_PATH}/cask/lib")
|
||||
$LOAD_PATH.unshift("#{HOMEBREW_LIBRARY_PATH}/cask/lib")
|
||||
end
|
||||
|
||||
unless $LOAD_PATH.include?(HOMEBREW_LIBRARY_PATH.to_s)
|
||||
$LOAD_PATH.unshift(HOMEBREW_LIBRARY_PATH.to_s)
|
||||
end
|
||||
|
||||
require "global"
|
||||
|
||||
begin
|
||||
|
||||
@ -15,13 +15,11 @@ require "hbc/download"
|
||||
require "hbc/download_strategy"
|
||||
require "hbc/exceptions"
|
||||
require "hbc/installer"
|
||||
require "hbc/config"
|
||||
require "hbc/macos"
|
||||
require "hbc/pkg"
|
||||
require "hbc/staged"
|
||||
require "hbc/system_command"
|
||||
require "hbc/topological_hash"
|
||||
require "hbc/url"
|
||||
require "hbc/utils"
|
||||
require "hbc/verify"
|
||||
require "hbc/version"
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
require "hbc/cask_loader"
|
||||
require "hbc/config"
|
||||
require "hbc/dsl"
|
||||
require "hbc/metadata"
|
||||
require "searchable"
|
||||
|
||||
@ -2,6 +2,9 @@ require "optparse"
|
||||
require "shellwords"
|
||||
|
||||
require "extend/optparse"
|
||||
|
||||
require "hbc/config"
|
||||
|
||||
require "hbc/cli/options"
|
||||
|
||||
require "hbc/cli/abstract_command"
|
||||
@ -18,7 +21,6 @@ require "hbc/cli/install"
|
||||
require "hbc/cli/list"
|
||||
require "hbc/cli/outdated"
|
||||
require "hbc/cli/reinstall"
|
||||
require "hbc/cli/search"
|
||||
require "hbc/cli/style"
|
||||
require "hbc/cli/uninstall"
|
||||
require "hbc/cli/upgrade"
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
require_relative "options"
|
||||
require "search"
|
||||
|
||||
module Hbc
|
||||
class CLI
|
||||
class AbstractCommand
|
||||
include Options
|
||||
include Homebrew::Search
|
||||
|
||||
option "--[no-]binaries", :binaries, true
|
||||
option "--debug", :debug, false
|
||||
@ -50,7 +52,7 @@ module Hbc
|
||||
end
|
||||
|
||||
def suggestion_message(cask_token)
|
||||
matches, = Search.search(cask_token)
|
||||
matches = search_casks(cask_token)
|
||||
|
||||
if matches.one?
|
||||
"Did you mean “#{matches.first}”?"
|
||||
|
||||
@ -1,53 +0,0 @@
|
||||
require "search"
|
||||
|
||||
module Hbc
|
||||
class CLI
|
||||
class Search < AbstractCommand
|
||||
extend Homebrew::Search
|
||||
|
||||
def run
|
||||
if args.empty?
|
||||
puts Formatter.columns(CLI.nice_listing(Cask.map(&:qualified_token)))
|
||||
else
|
||||
results = self.class.search(*args)
|
||||
self.class.render_results(*results)
|
||||
end
|
||||
end
|
||||
|
||||
def self.search(*arguments)
|
||||
query = arguments.join(" ")
|
||||
string_or_regex = query_regexp(query)
|
||||
local_results = search_casks(string_or_regex)
|
||||
|
||||
remote_matches = search_taps(query, silent: true)[:casks]
|
||||
|
||||
[local_results, remote_matches, query]
|
||||
end
|
||||
|
||||
def self.render_results(partial_matches, remote_matches, search_term)
|
||||
unless $stdout.tty?
|
||||
puts [*partial_matches, *remote_matches]
|
||||
return
|
||||
end
|
||||
|
||||
if partial_matches.empty? && remote_matches.empty?
|
||||
puts "No Cask found for \"#{search_term}\"."
|
||||
return
|
||||
end
|
||||
|
||||
unless partial_matches.empty?
|
||||
ohai "Matches"
|
||||
puts Formatter.columns(partial_matches)
|
||||
end
|
||||
|
||||
return if remote_matches.empty?
|
||||
ohai "Remote Matches"
|
||||
puts Formatter.columns(remote_matches)
|
||||
end
|
||||
|
||||
def self.help
|
||||
"searches all known Casks"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -1,3 +1,5 @@
|
||||
require "hbc/utils"
|
||||
|
||||
require "hbc/container/base"
|
||||
require "hbc/container/air"
|
||||
require "hbc/container/bzip2"
|
||||
|
||||
@ -2,6 +2,9 @@ require "locale"
|
||||
|
||||
require "hbc/artifact"
|
||||
|
||||
require "hbc/caskroom"
|
||||
require "hbc/exceptions"
|
||||
|
||||
require "hbc/dsl/appcast"
|
||||
require "hbc/dsl/base"
|
||||
require "hbc/dsl/caveats"
|
||||
@ -16,6 +19,8 @@ require "hbc/dsl/uninstall_postflight"
|
||||
require "hbc/dsl/uninstall_preflight"
|
||||
require "hbc/dsl/version"
|
||||
|
||||
require "hbc/url"
|
||||
|
||||
module Hbc
|
||||
class DSL
|
||||
ORDINARY_ARTIFACT_CLASSES = [
|
||||
|
||||
@ -52,38 +52,41 @@ module Homebrew
|
||||
return
|
||||
end
|
||||
|
||||
if args.remaining.empty?
|
||||
if args.remaining.empty? && !args.desc?
|
||||
puts Formatter.columns(Formula.full_names.sort)
|
||||
elsif args.desc?
|
||||
query = args.remaining.join(" ")
|
||||
string_or_regex = query_regexp(query)
|
||||
Descriptions.search(string_or_regex, :desc).print
|
||||
elsif args.remaining.first =~ HOMEBREW_TAP_FORMULA_REGEX
|
||||
query = args.remaining.first
|
||||
return
|
||||
end
|
||||
|
||||
results = begin
|
||||
[Formulary.factory(query).name]
|
||||
rescue FormulaUnavailableError
|
||||
_, _, name = query.split("/", 3)
|
||||
remote_results = search_taps(name)
|
||||
[*remote_results[:formulae], *remote_results[:casks]].sort
|
||||
query = args.remaining.join(" ")
|
||||
string_or_regex = query_regexp(query)
|
||||
|
||||
if args.desc?
|
||||
search_descriptions(string_or_regex)
|
||||
else
|
||||
remote_results = search_taps(query, silent: true)
|
||||
|
||||
local_formulae = search_formulae(string_or_regex)
|
||||
remote_formulae = remote_results[:formulae]
|
||||
all_formulae = local_formulae + remote_formulae
|
||||
|
||||
local_casks = search_casks(string_or_regex)
|
||||
remote_casks = remote_results[:casks]
|
||||
all_casks = local_casks + remote_casks
|
||||
|
||||
if all_formulae.any?
|
||||
ohai "Formulae"
|
||||
puts Formatter.columns(all_formulae)
|
||||
end
|
||||
|
||||
puts Formatter.columns(results) unless results.empty?
|
||||
else
|
||||
query = args.remaining.join(" ")
|
||||
string_or_regex = query_regexp(query)
|
||||
local_results = search_formulae(string_or_regex)
|
||||
puts Formatter.columns(local_results.sort) unless local_results.empty?
|
||||
|
||||
remote_results = search_taps(query)
|
||||
tap_results = [*remote_results[:formulae], *remote_results[:casks]].sort
|
||||
puts Formatter.columns(tap_results) unless tap_results.empty?
|
||||
if all_casks.any?
|
||||
puts if all_formulae.any?
|
||||
ohai "Casks"
|
||||
puts Formatter.columns(all_casks)
|
||||
end
|
||||
|
||||
if $stdout.tty?
|
||||
count = local_results.length + tap_results.length
|
||||
count = all_formulae.count + all_casks.count
|
||||
|
||||
ohai "Searching blacklisted, migrated and deleted formulae..."
|
||||
if reason = MissingFormula.reason(query, silent: true)
|
||||
if count.positive?
|
||||
puts
|
||||
@ -91,7 +94,7 @@ module Homebrew
|
||||
end
|
||||
puts reason
|
||||
elsif count.zero?
|
||||
puts "No formula found for #{query.inspect}."
|
||||
puts "No formula or cask found for #{query.inspect}."
|
||||
GitHub.print_pull_requests_matching(query)
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
require "compat/hbc/cask_loader"
|
||||
require "compat/hbc/cli/search"
|
||||
require "compat/hbc/cli/update"
|
||||
require "compat/hbc/cache"
|
||||
require "compat/hbc/caskroom"
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
require "cask/lib/hbc/cli/options"
|
||||
require "hbc/cli/options"
|
||||
|
||||
module Hbc
|
||||
class CLI
|
||||
|
||||
21
Library/Homebrew/compat/hbc/cli/search.rb
Normal file
21
Library/Homebrew/compat/hbc/cli/search.rb
Normal file
@ -0,0 +1,21 @@
|
||||
require "hbc/cli/abstract_command"
|
||||
require "cmd/search"
|
||||
|
||||
module Hbc
|
||||
class CLI
|
||||
module Compat
|
||||
class Search < AbstractCommand
|
||||
def run
|
||||
odeprecated "`brew cask search`", "`brew search`"
|
||||
Homebrew.search(args)
|
||||
end
|
||||
|
||||
def self.visible
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
prepend Compat
|
||||
end
|
||||
end
|
||||
@ -1,10 +1,10 @@
|
||||
require "cask/lib/hbc/cli/abstract_command"
|
||||
require "hbc/cli/abstract_command"
|
||||
|
||||
module Hbc
|
||||
class CLI
|
||||
module Compat
|
||||
class Update < AbstractCommand
|
||||
def self.run(*_ignored)
|
||||
def run
|
||||
odisabled "`brew cask update`", "`brew update`"
|
||||
end
|
||||
|
||||
|
||||
@ -47,4 +47,7 @@ unless defined? HOMEBREW_LIBRARY_PATH
|
||||
end
|
||||
|
||||
# Load path used by standalone scripts to access the Homebrew code base
|
||||
HOMEBREW_LOAD_PATH = HOMEBREW_LIBRARY_PATH
|
||||
HOMEBREW_LOAD_PATH = [
|
||||
HOMEBREW_LIBRARY_PATH,
|
||||
HOMEBREW_LIBRARY_PATH/"cask/lib",
|
||||
].join(File::PATH_SEPARATOR)
|
||||
|
||||
@ -8,6 +8,6 @@ module Homebrew
|
||||
module_function
|
||||
|
||||
def ruby
|
||||
exec ENV["HOMEBREW_RUBY_PATH"], "-I#{HOMEBREW_LIBRARY_PATH}", "-rglobal", "-rdev-cmd/irb", *ARGV
|
||||
exec ENV["HOMEBREW_RUBY_PATH"], "-I", HOMEBREW_LOAD_PATH, "-rglobal", "-rdev-cmd/irb", *ARGV
|
||||
end
|
||||
end
|
||||
|
||||
43
Library/Homebrew/extend/os/mac/search.rb
Normal file
43
Library/Homebrew/extend/os/mac/search.rb
Normal file
@ -0,0 +1,43 @@
|
||||
require "hbc/cask"
|
||||
require "hbc/cask_loader"
|
||||
|
||||
module Homebrew
|
||||
module Search
|
||||
module Extension
|
||||
def search_descriptions(string_or_regex)
|
||||
super
|
||||
|
||||
puts
|
||||
|
||||
ohai "Casks"
|
||||
Hbc::Cask.to_a.extend(Searchable)
|
||||
.search(string_or_regex, &:name)
|
||||
.each do |cask|
|
||||
puts "#{Tty.bold}#{cask.token}:#{Tty.reset} #{cask.name.join(", ")}"
|
||||
end
|
||||
end
|
||||
|
||||
def search_casks(string_or_regex)
|
||||
if string_or_regex.is_a?(String) && string_or_regex.match?(HOMEBREW_TAP_CASK_REGEX)
|
||||
return begin
|
||||
[Hbc::CaskLoader.load(string_or_regex).token]
|
||||
rescue Hbc::CaskUnavailableError
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
results = Hbc::Cask.search(string_or_regex, &:token).sort_by(&:token)
|
||||
|
||||
results.map do |cask|
|
||||
if cask.installed?
|
||||
pretty_installed(cask.token)
|
||||
else
|
||||
cask.token
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
prepend Extension
|
||||
end
|
||||
end
|
||||
1
Library/Homebrew/extend/os/search.rb
Normal file
1
Library/Homebrew/extend/os/search.rb
Normal file
@ -0,0 +1 @@
|
||||
require "extend/os/mac/search" if OS.mac?
|
||||
@ -1,5 +1,4 @@
|
||||
require "searchable"
|
||||
require "hbc/cask"
|
||||
|
||||
module Homebrew
|
||||
module Search
|
||||
@ -13,7 +12,16 @@ module Homebrew
|
||||
raise "#{query} is not a valid regex."
|
||||
end
|
||||
|
||||
def search_descriptions(string_or_regex)
|
||||
ohai "Formulae"
|
||||
Descriptions.search(string_or_regex, :desc).print
|
||||
end
|
||||
|
||||
def search_taps(query, silent: false)
|
||||
if query.match?(Regexp.union(HOMEBREW_TAP_FORMULA_REGEX, HOMEBREW_TAP_CASK_REGEX))
|
||||
_, _, query = query.split("/", 3)
|
||||
end
|
||||
|
||||
results = { formulae: [], casks: [] }
|
||||
|
||||
return results if ENV["HOMEBREW_NO_GITHUB_API"]
|
||||
@ -40,7 +48,7 @@ module Homebrew
|
||||
tap = Tap.fetch(match["repository"]["full_name"])
|
||||
full_name = "#{tap.name}/#{name}"
|
||||
|
||||
next if tap.installed? && !match["path"].start_with?("Casks/")
|
||||
next if tap.installed?
|
||||
|
||||
if match["path"].start_with?("Casks/")
|
||||
results[:casks] = [*results[:casks], full_name].sort
|
||||
@ -53,8 +61,13 @@ module Homebrew
|
||||
end
|
||||
|
||||
def search_formulae(string_or_regex)
|
||||
# Use stderr to avoid breaking parsed output
|
||||
$stderr.puts Formatter.headline("Searching local taps...", color: :blue)
|
||||
if string_or_regex.is_a?(String) && string_or_regex.match?(HOMEBREW_TAP_FORMULA_REGEX)
|
||||
return begin
|
||||
[Formulary.factory(string_or_regex).name]
|
||||
rescue FormulaUnavailableError
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
aliases = Formula.alias_full_names
|
||||
results = (Formula.full_names + aliases)
|
||||
@ -81,16 +94,10 @@ module Homebrew
|
||||
end.compact
|
||||
end
|
||||
|
||||
def search_casks(string_or_regex)
|
||||
results = Hbc::Cask.search(string_or_regex, &:token).sort_by(&:token)
|
||||
|
||||
results.map do |cask|
|
||||
if cask.installed?
|
||||
pretty_installed(cask.token)
|
||||
else
|
||||
cask.token
|
||||
end
|
||||
end
|
||||
def search_casks(_string_or_regex)
|
||||
[]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
require "extend/os/search"
|
||||
|
||||
@ -1,123 +0,0 @@
|
||||
require_relative "shared_examples/invalid_option"
|
||||
|
||||
describe Hbc::CLI::Search, :cask do
|
||||
before do
|
||||
allow(Tty).to receive(:width).and_return(0)
|
||||
end
|
||||
|
||||
it_behaves_like "a command that handles invalid options"
|
||||
|
||||
it "lists the available Casks that match the search term" do
|
||||
allow(GitHub).to receive(:search_code).and_return([])
|
||||
|
||||
expect {
|
||||
Hbc::CLI::Search.run("local")
|
||||
}.to output(<<~EOS).to_stdout.as_tty
|
||||
==> Matches
|
||||
local-caffeine
|
||||
local-transmission
|
||||
EOS
|
||||
end
|
||||
|
||||
it "outputs a plain list when stdout is not a TTY" do
|
||||
allow(GitHub).to receive(:search_code).and_return([])
|
||||
|
||||
expect {
|
||||
Hbc::CLI::Search.run("local")
|
||||
}.to output(<<~EOS).to_stdout
|
||||
local-caffeine
|
||||
local-transmission
|
||||
EOS
|
||||
end
|
||||
|
||||
it "returns matches even when online search failed" do
|
||||
allow(GitHub).to receive(:search_code).and_raise(GitHub::Error.new("reason"))
|
||||
|
||||
expect {
|
||||
Hbc::CLI::Search.run("local")
|
||||
}.to output(<<~EOS).to_stdout
|
||||
local-caffeine
|
||||
local-transmission
|
||||
EOS
|
||||
.and output(/^Warning: Error searching on GitHub: reason/).to_stderr
|
||||
end
|
||||
|
||||
it "shows that there are no Casks matching a search term that did not result in anything" do
|
||||
expect {
|
||||
Hbc::CLI::Search.run("foo-bar-baz")
|
||||
}.to output(<<~EOS).to_stdout.as_tty
|
||||
No Cask found for "foo-bar-baz".
|
||||
EOS
|
||||
end
|
||||
|
||||
it "doesn't output anything to non-TTY stdout when there are no matches" do
|
||||
allow(GitHub).to receive(:search_code).and_return([])
|
||||
|
||||
expect { Hbc::CLI::Search.run("foo-bar-baz") }
|
||||
.to not_to_output.to_stdout
|
||||
.and not_to_output.to_stderr
|
||||
end
|
||||
|
||||
it "lists all Casks available offline with no search term" do
|
||||
allow(GitHub).to receive(:search_code).and_raise(GitHub::Error.new("reason"))
|
||||
expect { Hbc::CLI::Search.run }
|
||||
.to output(/local-caffeine/).to_stdout.as_tty
|
||||
.and not_to_output.to_stderr
|
||||
end
|
||||
|
||||
it "ignores hyphens in search terms" do
|
||||
expect {
|
||||
Hbc::CLI::Search.run("lo-cal-caffeine")
|
||||
}.to output(/local-caffeine/).to_stdout.as_tty
|
||||
end
|
||||
|
||||
it "ignores hyphens in Cask tokens" do
|
||||
expect {
|
||||
Hbc::CLI::Search.run("localcaffeine")
|
||||
}.to output(/local-caffeine/).to_stdout.as_tty
|
||||
end
|
||||
|
||||
it "accepts multiple arguments" do
|
||||
expect {
|
||||
Hbc::CLI::Search.run("local caffeine")
|
||||
}.to output(/local-caffeine/).to_stdout.as_tty
|
||||
end
|
||||
|
||||
it "accepts a regexp argument" do
|
||||
expect {
|
||||
Hbc::CLI::Search.run("/^local-c[a-z]ffeine$/")
|
||||
}.to output(<<~EOS).to_stdout.as_tty
|
||||
==> Matches
|
||||
local-caffeine
|
||||
EOS
|
||||
end
|
||||
|
||||
it "returns both exact and partial matches" do
|
||||
expect {
|
||||
Hbc::CLI::Search.run("test-opera")
|
||||
}.to output(<<~EOS).to_stdout.as_tty
|
||||
==> Matches
|
||||
test-opera
|
||||
test-opera-mail
|
||||
EOS
|
||||
end
|
||||
|
||||
it "does not search the Tap name" do
|
||||
expect {
|
||||
Hbc::CLI::Search.run("caskroom")
|
||||
}.to output(<<~EOS).to_stdout.as_tty
|
||||
No Cask found for "caskroom".
|
||||
EOS
|
||||
end
|
||||
|
||||
it "highlights installed packages" do
|
||||
Hbc::CLI::Install.run("local-caffeine")
|
||||
|
||||
expect {
|
||||
Hbc::CLI::Search.run("local-caffeine")
|
||||
}.to output(<<~EOS).to_stdout.as_tty
|
||||
==> Matches
|
||||
local-caffeine ✔
|
||||
EOS
|
||||
end
|
||||
end
|
||||
@ -15,7 +15,6 @@ describe "brew search", :integration_test do
|
||||
it "supports searching by name" do
|
||||
expect { brew "search", "testball" }
|
||||
.to output(/testball/).to_stdout
|
||||
.and output(/Searching/).to_stderr
|
||||
.and be_a_success
|
||||
end
|
||||
|
||||
@ -31,7 +30,6 @@ describe "brew search", :integration_test do
|
||||
|
||||
expect { brew "search", "homebrew/cask/firefox" }
|
||||
.to output(/firefox/).to_stdout
|
||||
.and output(/Searching/).to_stderr
|
||||
.and be_a_success
|
||||
end
|
||||
|
||||
|
||||
@ -14,8 +14,8 @@ require "rubocop"
|
||||
require "rubocop/rspec/support"
|
||||
require "find"
|
||||
|
||||
$LOAD_PATH.unshift(File.expand_path("#{ENV["HOMEBREW_LIBRARY"]}/Homebrew"))
|
||||
$LOAD_PATH.unshift(File.expand_path("#{ENV["HOMEBREW_LIBRARY"]}/Homebrew/cask/lib"))
|
||||
$LOAD_PATH.unshift(File.expand_path("#{ENV["HOMEBREW_LIBRARY"]}/Homebrew"))
|
||||
$LOAD_PATH.unshift(File.expand_path("#{ENV["HOMEBREW_LIBRARY"]}/Homebrew/test/support/lib"))
|
||||
|
||||
require "global"
|
||||
|
||||
@ -83,9 +83,7 @@ RSpec.shared_context "integration test" do
|
||||
@ruby_args ||= begin
|
||||
ruby_args = [
|
||||
"-W0",
|
||||
"-I", "#{HOMEBREW_LIBRARY_PATH}/test/support/lib",
|
||||
"-I", HOMEBREW_LIBRARY_PATH.to_s,
|
||||
"-I", "#{HOMEBREW_LIBRARY_PATH}/cask/lib",
|
||||
"-I", HOMEBREW_LOAD_PATH,
|
||||
"-rconfig"
|
||||
]
|
||||
if ENV["HOMEBREW_TESTS_COVERAGE"]
|
||||
|
||||
@ -15,7 +15,11 @@ end
|
||||
# Paths pointing into the Homebrew code base that persist across test runs
|
||||
HOMEBREW_LIBRARY_PATH = Pathname.new(File.expand_path("../../..", __dir__))
|
||||
HOMEBREW_SHIMS_PATH = HOMEBREW_LIBRARY_PATH.parent+"Homebrew/shims"
|
||||
HOMEBREW_LOAD_PATH = [File.expand_path(__dir__), HOMEBREW_LIBRARY_PATH].join(":")
|
||||
HOMEBREW_LOAD_PATH = [
|
||||
File.expand_path(__dir__),
|
||||
HOMEBREW_LIBRARY_PATH,
|
||||
HOMEBREW_LIBRARY_PATH.join("cask/lib"),
|
||||
].join(File::PATH_SEPARATOR)
|
||||
|
||||
# Paths redirected to a temporary directory and wiped at the end of the test run
|
||||
HOMEBREW_PREFIX = Pathname.new(TEST_TMPDIR).join("prefix")
|
||||
|
||||
@ -16,6 +16,7 @@ require "time"
|
||||
def require?(path)
|
||||
return false if path.nil?
|
||||
require path
|
||||
true
|
||||
rescue LoadError => e
|
||||
# we should raise on syntax errors but not if the file doesn't exist.
|
||||
raise unless e.message.include?(path)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user