Merge branch 'master' into license

This commit is contained in:
Lionell 2020-06-28 02:07:53 +08:00
commit 5f6917ae7c
32 changed files with 458 additions and 154 deletions

View File

@ -91,7 +91,7 @@ GEM
rubocop-ast (>= 0.0.3, < 1.0) rubocop-ast (>= 0.0.3, < 1.0)
ruby-progressbar (~> 1.7) ruby-progressbar (~> 1.7)
unicode-display_width (>= 1.4.0, < 2.0) unicode-display_width (>= 1.4.0, < 2.0)
rubocop-ast (0.0.3) rubocop-ast (0.1.0)
parser (>= 2.7.0.1) parser (>= 2.7.0.1)
rubocop-performance (1.6.1) rubocop-performance (1.6.1)
rubocop (>= 0.71.0) rubocop (>= 0.71.0)

View File

@ -22,9 +22,9 @@ module Homebrew
switch "--force-bottle", switch "--force-bottle",
description: "Show the cache file used when pouring a bottle." description: "Show the cache file used when pouring a bottle."
switch "--formula", switch "--formula",
description: "Show cache files for only formulae" description: "Only show cache files for formulae."
switch "--cask", switch "--cask",
description: "Show cache files for only casks" description: "Only show cache files for casks."
conflicts "--build-from-source", "--force-bottle" conflicts "--build-from-source", "--force-bottle"
conflicts "--formula", "--cask" conflicts "--formula", "--cask"
end end

View File

@ -267,6 +267,7 @@ module Homebrew
# Need to rescue before `FormulaUnavailableError` (superclass of this) # Need to rescue before `FormulaUnavailableError` (superclass of this)
# is handled, as searching for a formula doesn't make sense here (the # is handled, as searching for a formula doesn't make sense here (the
# formula was found, but there's a problem with its implementation). # formula was found, but there's a problem with its implementation).
$stderr.puts e.backtrace if Homebrew::EnvConfig.developer?
ofail e.message ofail e.message
rescue FormulaUnavailableError => e rescue FormulaUnavailableError => e
if e.name == "updog" if e.name == "updog"

View File

@ -172,14 +172,6 @@ module Homebrew
@text.split("\n__END__").first @text.split("\n__END__").first
end end
def data?
/^[^#]*\bDATA\b/ =~ @text
end
def end?
/^__END__$/ =~ @text
end
def trailing_newline? def trailing_newline?
/\Z\n/ =~ @text /\Z\n/ =~ @text
end end
@ -239,12 +231,6 @@ module Homebrew
end end
def audit_file def audit_file
# TODO: check could be in RuboCop
problem "'DATA' was found, but no '__END__'" if text.data? && !text.end?
# TODO: check could be in RuboCop
problem "'__END__' was found, but 'DATA' is not used" if text.end? && !text.data?
# TODO: check could be in RuboCop # TODO: check could be in RuboCop
if text.to_s.match?(/inreplace [^\n]* do [^\n]*\n[^\n]*\.gsub![^\n]*\n\ *end/m) if text.to_s.match?(/inreplace [^\n]* do [^\n]*\n[^\n]*\.gsub![^\n]*\n\ *end/m)
problem "'inreplace ... do' was used for a single substitution (use the non-block form instead)." problem "'inreplace ... do' was used for a single substitution (use the non-block form instead)."

View File

@ -23,6 +23,8 @@ module Homebrew
description: "Create a basic template for an Autotools-style build." description: "Create a basic template for an Autotools-style build."
switch "--cmake", switch "--cmake",
description: "Create a basic template for a CMake-style build." description: "Create a basic template for a CMake-style build."
switch "--crystal",
description: "Create a basic template for a Crystal build."
switch "--go", switch "--go",
description: "Create a basic template for a Go build." description: "Create a basic template for a Go build."
switch "--meson", switch "--meson",
@ -52,7 +54,7 @@ module Homebrew
switch :force switch :force
switch :verbose switch :verbose
switch :debug switch :debug
conflicts "--autotools", "--cmake", "--go", "--meson", "--perl", "--python", "--rust" conflicts "--autotools", "--cmake", "--crystal", "--go", "--meson", "--perl", "--python", "--rust"
named 1 named 1
end end
end end
@ -86,6 +88,8 @@ module Homebrew
:autotools :autotools
elsif args.meson? elsif args.meson?
:meson :meson
elsif args.crystal?
:crystal
elsif args.go? elsif args.go?
:go :go
elsif args.perl? elsif args.perl?

View File

@ -14,9 +14,9 @@ module Homebrew
Reupload the stable URL of a formula to Bintray for use as a mirror. Reupload the stable URL of a formula to Bintray for use as a mirror.
EOS EOS
flag "--bintray-org=", flag "--bintray-org=",
description: "Upload to the specified Bintray organisation (default: homebrew)." description: "Upload to the specified Bintray organisation (default: `homebrew`)."
flag "--bintray-repo=", flag "--bintray-repo=",
description: "Upload to the specified Bintray repository (default: mirror)." description: "Upload to the specified Bintray repository (default: `mirror`)."
switch "--no-publish", switch "--no-publish",
description: "Upload to Bintray, but don't publish." description: "Upload to Bintray, but don't publish."
switch :verbose switch :verbose

View File

@ -34,18 +34,18 @@ module Homebrew
description: "When a patch fails to apply, leave in progress and allow user to resolve, "\ description: "When a patch fails to apply, leave in progress and allow user to resolve, "\
"instead of aborting." "instead of aborting."
flag "--workflow=", flag "--workflow=",
description: "Retrieve artifacts from the specified workflow (default: tests.yml)." description: "Retrieve artifacts from the specified workflow (default: `tests.yml`)."
flag "--artifact=", flag "--artifact=",
description: "Download artifacts with the specified name (default: bottles)." description: "Download artifacts with the specified name (default: `bottles`)."
flag "--bintray-org=", flag "--bintray-org=",
description: "Upload to the specified Bintray organisation (default: homebrew)." description: "Upload to the specified Bintray organisation (default: `homebrew`)."
flag "--tap=", flag "--tap=",
description: "Target tap repository (default: homebrew/core)." description: "Target tap repository (default: `homebrew/core`)."
flag "--root-url=", flag "--root-url=",
description: "Use the specified <URL> as the root of the bottle's URL instead of Homebrew's default." description: "Use the specified <URL> as the root of the bottle's URL instead of Homebrew's default."
flag "--bintray-mirror=", flag "--bintray-mirror=",
description: "Use the specified Bintray repository to automatically mirror stable URLs "\ description: "Use the specified Bintray repository to automatically mirror stable URLs "\
"defined in the formulae (default: mirror)" "defined in the formulae (default: `mirror`)."
switch :verbose switch :verbose
switch :debug switch :debug
min_named 1 min_named 1

View File

@ -15,10 +15,10 @@ module Homebrew
EOS EOS
switch "--no-publish", switch "--no-publish",
description: "Apply the bottle commit and upload the bottles, but don't publish them." description: "Apply the bottle commit and upload the bottles, but don't publish them."
switch "--dry-run", "-n", switch "-n", "--dry-run",
description: "Print what would be done rather than doing it." description: "Print what would be done rather than doing it."
flag "--bintray-org=", flag "--bintray-org=",
description: "Upload to the specified Bintray organisation (default: homebrew)." description: "Upload to the specified Bintray organisation (default: `homebrew`)."
flag "--root-url=", flag "--root-url=",
description: "Use the specified <URL> as the root of the bottle's URL instead of Homebrew's default." description: "Use the specified <URL> as the root of the bottle's URL instead of Homebrew's default."
switch :verbose switch :verbose

View File

@ -27,6 +27,7 @@ class Keg
# patchelf requires that the ELF file have a .dynstr section. # patchelf requires that the ELF file have a .dynstr section.
# Skip ELF files that do not have a .dynstr section. # Skip ELF files that do not have a .dynstr section.
return if ["cannot find section .dynstr", "strange: no string table"].include?(old_rpath) return if ["cannot find section .dynstr", "strange: no string table"].include?(old_rpath)
unless $CHILD_STATUS.success? unless $CHILD_STATUS.success?
raise ErrorDuringExecution.new(cmd_rpath, status: $CHILD_STATUS, output: [[:stderr, old_rpath]]) raise ErrorDuringExecution.new(cmd_rpath, status: $CHILD_STATUS, output: [[:stderr, old_rpath]])
end end
@ -41,15 +42,15 @@ class Keg
new_rpath = rpath.join(":") new_rpath = rpath.join(":")
cmd = [patchelf, "--force-rpath", "--set-rpath", new_rpath] cmd = [patchelf, "--force-rpath", "--set-rpath", new_rpath]
if file.with_interpreter? old_interpreter = file.interpreter
old_interpreter = Utils.safe_popen_read(patchelf, "--print-interpreter", file).strip new_interpreter = if old_interpreter.nil?
new_interpreter = if File.readable? "#{new_prefix}/lib/ld.so" nil
elsif File.readable? "#{new_prefix}/lib/ld.so"
"#{new_prefix}/lib/ld.so" "#{new_prefix}/lib/ld.so"
else else
old_interpreter.sub old_prefix, new_prefix old_interpreter.sub old_prefix, new_prefix
end end
cmd << "--set-interpreter" << new_interpreter if old_interpreter != new_interpreter cmd << "--set-interpreter" << new_interpreter if old_interpreter != new_interpreter
end
return if old_rpath == new_rpath && old_interpreter == new_interpreter return if old_rpath == new_rpath && old_interpreter == new_interpreter

View File

@ -104,6 +104,8 @@ module Homebrew
<% if mode == :cmake %> <% if mode == :cmake %>
depends_on "cmake" => :build depends_on "cmake" => :build
<% elsif mode == :crystal %>
depends_on "crystal" => :build
<% elsif mode == :go %> <% elsif mode == :go %>
depends_on "go" => :build depends_on "go" => :build
<% elsif mode == :meson %> <% elsif mode == :meson %>
@ -139,6 +141,9 @@ module Homebrew
"--disable-dependency-tracking", "--disable-dependency-tracking",
"--disable-silent-rules", "--disable-silent-rules",
"--prefix=\#{prefix}" "--prefix=\#{prefix}"
<% elsif mode == :crystal %>
system "shards", "build", "--release"
bin.install "bin/#{name}"
<% elsif mode == :go %> <% elsif mode == :go %>
system "go", "build", *std_go_args system "go", "build", *std_go_args
<% elsif mode == :meson %> <% elsif mode == :meson %>

View File

@ -35,6 +35,7 @@ module Formulary
begin begin
mod.module_eval(contents, path) mod.module_eval(contents, path)
rescue NameError, ArgumentError, ScriptError => e rescue NameError, ArgumentError, ScriptError => e
$stderr.puts e.backtrace if Homebrew::EnvConfig.developer?
raise FormulaUnreadableError.new(name, e) raise FormulaUnreadableError.new(name, e)
end end
class_name = class_s(name) class_name = class_s(name)

View File

@ -103,7 +103,7 @@ module Homebrew
help_lines = command_help_lines(path) help_lines = command_help_lines(path)
return if help_lines.blank? return if help_lines.blank?
Formatter.wrap(help_lines.join.delete_prefix(" "), COMMAND_DESC_WIDTH) Formatter.wrap(help_lines.join, COMMAND_DESC_WIDTH)
.sub("@hide_from_man_page ", "") .sub("@hide_from_man_page ", "")
.sub(/^\* /, "#{Tty.bold}Usage: brew#{Tty.reset} ") .sub(/^\* /, "#{Tty.bold}Usage: brew#{Tty.reset} ")
.gsub(/`(.*?)`/m, "#{Tty.bold}\\1#{Tty.reset}") .gsub(/`(.*?)`/m, "#{Tty.bold}\\1#{Tty.reset}")

View File

@ -68,28 +68,24 @@ module ELFShim
elf_type == :executable elf_type == :executable
end end
def with_interpreter? def interpreter
return @with_interpreter if defined? @with_interpreter return @interpreter if defined? @interpreter
@with_interpreter = if binary_executable? @interpreter = if HOMEBREW_PATCHELF_RB
true
elsif dylib?
if HOMEBREW_PATCHELF_RB
begin begin
patchelf_patcher.interpreter.present? patchelf_patcher.interpreter
rescue PatchELF::PatchError => e rescue PatchELF::PatchError => e
opoo e unless e.to_s.start_with? "No interpreter found" opoo e unless e.to_s.start_with? "No interpreter found"
false nil
end end
elsif which "readelf" elsif (patchelf = DevelopmentTools.locate "patchelf")
Utils.popen_read("readelf", "-l", to_path).include?(" INTERP ") interp = Utils.popen_read(patchelf, "--print-interpreter", to_s, err: :out).strip
elsif which "file" $CHILD_STATUS.success? ? interp : nil
Utils.popen_read("file", "-L", "-b", to_path).include?(" interpreter ") elsif (file = DevelopmentTools.locate("file"))
output = Utils.popen_read(file, "-L", "-b", to_s, err: :out).strip
output[/^ELF.*, interpreter (.+?), /, 1]
else else
raise "Please install either readelf (from binutils) or file." raise "Please install either patchelf or file."
end
else
false
end end
end end

View File

@ -22,5 +22,6 @@ require "rubocops/uses_from_macos"
require "rubocops/files" require "rubocops/files"
require "rubocops/keg_only" require "rubocops/keg_only"
require "rubocops/version" require "rubocops/version"
require "rubocops/deprecate"
require "rubocops/rubocop-cask" require "rubocops/rubocop-cask"

View File

@ -0,0 +1,35 @@
# frozen_string_literal: true
require "rubocops/extend/formula"
module RuboCop
module Cop
module FormulaAudit
# This cop audits deprecate!
class Deprecate < FormulaCop
def audit_formula(_node, _class_node, _parent_class_node, body_node)
deprecate_node = find_node_method_by_name(body_node, :deprecate!)
return if deprecate_node.nil? || deprecate_node.children.length < 3
date_node = find_strings(deprecate_node).first
begin
Date.iso8601(string_content(date_node))
rescue ArgumentError
fixed_date_string = Date.parse(string_content(date_node)).iso8601
offending_node(date_node)
problem "Use `#{fixed_date_string}` to comply with ISO 8601"
end
end
def autocorrect(node)
lambda do |corrector|
fixed_fixed_date_string = Date.parse(string_content(node)).iso8601
corrector.replace(node.source_range, "\"#{fixed_fixed_date_string}\"")
end
end
end
end
end
end

View File

@ -495,7 +495,15 @@ module RuboCop
when :str when :str
node.str_content node.str_content
when :dstr when :dstr
node.each_child_node(:str).map(&:str_content).join content = ""
node.each_child_node(:str, :begin) do |child|
content += if child.begin_type?
child.source
else
child.str_content
end
end
content
when :const when :const
node.const_name node.const_name
when :sym when :sym

View File

@ -195,7 +195,7 @@ module RuboCop
end end
end end
class ShellCmd < FormulaCop class SafePopenCommands < FormulaCop
def audit_formula(_node, _class_node, _parent_class_node, body_node) def audit_formula(_node, _class_node, _parent_class_node, body_node)
test = find_block(body_node, :test) test = find_block(body_node, :test)
@ -223,6 +223,35 @@ module RuboCop
end end
end end
class ShellVariables < FormulaCop
def audit_formula(_node, _class_node, _parent_class_node, body_node)
popen_commands = [
:popen,
:popen_read,
:safe_popen_read,
:popen_write,
:safe_popen_write,
]
popen_commands.each do |command|
find_instance_method_call(body_node, "Utils", command) do |method|
next unless match = regex_match_group(parameters(method).first, /^([^"' ]+)=([^"' ]+)(?: (.*))?$/)
good_args = "Utils.#{command}({ \"#{match[1]}\" => \"#{match[2]}\" }, \"#{match[3]}\")"
problem "Use `#{good_args}` instead of `#{method.source}`"
end
end
end
def autocorrect(node)
lambda do |corrector|
match = regex_match_group(node, /^([^"' ]+)=([^"' ]+)(?: (.*))?$/)
corrector.replace(node.source_range, "{ \"#{match[1]}\" => \"#{match[2]}\" }, \"#{match[3]}\"")
end
end
end
class Miscellaneous < FormulaCop class Miscellaneous < FormulaCop
def audit_formula(_node, _class_node, _parent_class_node, body_node) def audit_formula(_node, _class_node, _parent_class_node, body_node)
# FileUtils is included in Formula # FileUtils is included in Formula

View File

@ -8,7 +8,9 @@ module RuboCop
module FormulaAudit module FormulaAudit
# This cop audits patches in Formulae. # This cop audits patches in Formulae.
class Patches < FormulaCop class Patches < FormulaCop
def audit_formula(_node, _class_node, _parent_class_node, body) def audit_formula(node, _class_node, _parent_class_node, body)
@full_source_content = source_buffer(node).source
external_patches = find_all_blocks(body, :patch) external_patches = find_all_blocks(body, :patch)
external_patches.each do |patch_block| external_patches.each do |patch_block|
url_node = find_every_method_call_by_name(patch_block, :url).first url_node = find_every_method_call_by_name(patch_block, :url).first
@ -16,6 +18,14 @@ module RuboCop
patch_problems(url_string) patch_problems(url_string)
end end
inline_patches = find_every_method_call_by_name(body, :patch)
inline_patches.each { |patch| inline_patch_problems(patch) }
if inline_patches.empty? && patch_end?
offending_patch_end_node(node)
problem "patch is missing 'DATA'"
end
patches_node = find_method_def(body, :patches) patches_node = find_method_def(body, :patches)
return if patches_node.nil? return if patches_node.nil?
@ -84,6 +94,30 @@ module RuboCop
#{patch_url} #{patch_url}
EOS EOS
end end
def inline_patch_problems(patch)
return unless patch_data?(patch) && !patch_end?
offending_node(patch)
problem "patch is missing '__END__'"
end
def_node_search :patch_data?, <<~AST
(send nil? :patch (:sym :DATA))
AST
def patch_end?
/^__END__$/.match?(@full_source_content)
end
def offending_patch_end_node(node)
@offensive_node = node
@source_buf = source_buffer(node)
@line_no = node.loc.last_line + 1
@column = 0
@length = 7 # "__END__".size
@offense_source_range = source_range(@source_buf, @line_no, @column, @length)
end
end end
end end
end end

View File

@ -41,8 +41,6 @@ module Homebrew
url "https://www.brew.sh/valid-1.0.tar.gz" url "https://www.brew.sh/valid-1.0.tar.gz"
RUBY RUBY
expect(ft).not_to have_data
expect(ft).not_to have_end
expect(ft).to have_trailing_newline expect(ft).to have_trailing_newline
expect(ft =~ /\burl\b/).to be_truthy expect(ft =~ /\burl\b/).to be_truthy
@ -55,20 +53,6 @@ module Homebrew
ft = formula_text "newline" ft = formula_text "newline"
expect(ft).to have_trailing_newline expect(ft).to have_trailing_newline
end end
specify "#data?" do
ft = formula_text "data", <<~RUBY
patch :DATA
RUBY
expect(ft).to have_data
end
specify "#end?" do
ft = formula_text "end", "", patch: "__END__\na patch here"
expect(ft).to have_end
expect(ft.without_patch).to eq("class End < Formula\n \nend")
end
end end
describe FormulaAuditor do describe FormulaAuditor do
@ -186,31 +170,6 @@ module Homebrew
end end
describe "#audit_file" do describe "#audit_file" do
specify "DATA but no __END__" do
fa = formula_auditor "foo", <<~RUBY
class Foo < Formula
url "https://brew.sh/foo-1.0.tgz"
patch :DATA
end
RUBY
fa.audit_file
expect(fa.problems).to eq(["'DATA' was found, but no '__END__'"])
end
specify "__END__ but no DATA" do
fa = formula_auditor "foo", <<~RUBY
class Foo < Formula
url "https://brew.sh/foo-1.0.tgz"
end
__END__
a patch goes here
RUBY
fa.audit_file
expect(fa.problems).to eq(["'__END__' was found, but 'DATA' is not used"])
end
specify "no issue" do specify "no issue" do
fa = formula_auditor "foo", <<~RUBY fa = formula_auditor "foo", <<~RUBY
class Foo < Formula class Foo < Formula

View File

@ -0,0 +1,56 @@
# frozen_string_literal: true
require "rubocops/deprecate"
describe RuboCop::Cop::FormulaAudit::Deprecate do
subject(:cop) { described_class.new }
context "When auditing formula for deprecate!" do
it "deprecation date is not ISO 8601 compliant" do
expect_offense(<<~RUBY)
class Foo < Formula
url 'https://brew.sh/foo-1.0.tgz'
deprecate! :date => "June 25, 2020"
^^^^^^^^^^^^^^^ Use `2020-06-25` to comply with ISO 8601
end
RUBY
end
it "deprecation date is ISO 8601 compliant" do
expect_no_offenses(<<~RUBY)
class Foo < Formula
url 'https://brew.sh/foo-1.0.tgz'
deprecate! :date => "2020-06-25"
end
RUBY
end
it "no deprecation date" do
expect_no_offenses(<<~RUBY)
class Foo < Formula
url 'https://brew.sh/foo-1.0.tgz'
deprecate!
end
RUBY
end
it "auto corrects to ISO 8601 format" do
source = <<~RUBY
class Foo < Formula
url 'https://brew.sh/foo-1.0.tgz'
deprecate! :date => "June 25, 2020"
end
RUBY
corrected_source = <<~RUBY
class Foo < Formula
url 'https://brew.sh/foo-1.0.tgz'
deprecate! :date => "2020-06-25"
end
RUBY
new_source = autocorrect_source(source)
expect(new_source).to eq(corrected_source)
end
end
end

View File

@ -345,10 +345,10 @@ describe RuboCop::Cop::FormulaAudit::MpiCheck do
end end
end end
describe RuboCop::Cop::FormulaAudit::ShellCmd do describe RuboCop::Cop::FormulaAudit::SafePopenCommands do
subject(:cop) { described_class.new } subject(:cop) { described_class.new }
context "When auditing shell commands" do context "When auditing popen commands" do
it "Utils.popen_read should become Utils.safe_popen_read" do it "Utils.popen_read should become Utils.safe_popen_read" do
expect_offense(<<~RUBY) expect_offense(<<~RUBY)
class Foo < Formula class Foo < Formula
@ -440,6 +440,140 @@ describe RuboCop::Cop::FormulaAudit::ShellCmd do
end end
end end
describe RuboCop::Cop::FormulaAudit::ShellVariables do
subject(:cop) { described_class.new }
context "When auditing shell variables" do
it "Shell variables should be expanded in Utils.popen" do
expect_offense(<<~RUBY)
class Foo < Formula
def install
Utils.popen "SHELL=bash foo"
^^^^^^^^^^^^^^ Use `Utils.popen({ "SHELL" => "bash" }, "foo")` instead of `Utils.popen "SHELL=bash foo"`
end
end
RUBY
end
it "Shell variables should be expanded in Utils.safe_popen_read" do
expect_offense(<<~RUBY)
class Foo < Formula
def install
Utils.safe_popen_read "SHELL=bash foo"
^^^^^^^^^^^^^^ Use `Utils.safe_popen_read({ "SHELL" => "bash" }, "foo")` instead of `Utils.safe_popen_read "SHELL=bash foo"`
end
end
RUBY
end
it "Shell variables should be expanded in Utils.safe_popen_write" do
expect_offense(<<~RUBY)
class Foo < Formula
def install
Utils.safe_popen_write "SHELL=bash foo"
^^^^^^^^^^^^^^ Use `Utils.safe_popen_write({ "SHELL" => "bash" }, "foo")` instead of `Utils.safe_popen_write "SHELL=bash foo"`
end
end
RUBY
end
it "Shell variables should be expanded and keep inline string variables in the arguments" do
expect_offense(<<~RUBY)
class Foo < Formula
def install
Utils.popen "SHELL=bash \#{bin}/foo"
^^^^^^^^^^^^^^^^^^^^^ Use `Utils.popen({ "SHELL" => "bash" }, "\#{bin}/foo")` instead of `Utils.popen "SHELL=bash \#{bin}/foo"`
end
end
RUBY
end
it "corrects shell variables in Utils.popen" do
source = <<~RUBY
class Foo < Formula
def install
Utils.popen("SHELL=bash foo")
end
end
RUBY
corrected_source = <<~RUBY
class Foo < Formula
def install
Utils.popen({ "SHELL" => "bash" }, "foo")
end
end
RUBY
new_source = autocorrect_source(source)
expect(new_source).to eq(corrected_source)
end
it "corrects shell variables in Utils.safe_popen_read" do
source = <<~RUBY
class Foo < Formula
def install
Utils.safe_popen_read("SHELL=bash foo")
end
end
RUBY
corrected_source = <<~RUBY
class Foo < Formula
def install
Utils.safe_popen_read({ "SHELL" => "bash" }, "foo")
end
end
RUBY
new_source = autocorrect_source(source)
expect(new_source).to eq(corrected_source)
end
it "corrects shell variables in Utils.safe_popen_write" do
source = <<~RUBY
class Foo < Formula
def install
Utils.safe_popen_write("SHELL=bash foo")
end
end
RUBY
corrected_source = <<~RUBY
class Foo < Formula
def install
Utils.safe_popen_write({ "SHELL" => "bash" }, "foo")
end
end
RUBY
new_source = autocorrect_source(source)
expect(new_source).to eq(corrected_source)
end
it "corrects shell variables with inline string variable in arguments" do
source = <<~RUBY
class Foo < Formula
def install
Utils.popen("SHELL=bash \#{bin}/foo")
end
end
RUBY
corrected_source = <<~RUBY
class Foo < Formula
def install
Utils.popen({ "SHELL" => "bash" }, "\#{bin}/foo")
end
end
RUBY
new_source = autocorrect_source(source)
expect(new_source).to eq(corrected_source)
end
end
end
describe RuboCop::Cop::FormulaAudit::Miscellaneous do describe RuboCop::Cop::FormulaAudit::Miscellaneous do
subject(:cop) { described_class.new } subject(:cop) { described_class.new }

View File

@ -163,6 +163,53 @@ describe RuboCop::Cop::FormulaAudit::Patches do
end end
end end
context "When auditing inline patches" do
it "reports no offenses for valid inline patches" do
expect_no_offenses(<<~RUBY)
class Foo < Formula
url 'https://brew.sh/foo-1.0.tgz'
patch :DATA
end
__END__
patch content here
RUBY
end
it "reports no offenses for valid nested inline patches" do
expect_no_offenses(<<~RUBY)
class Foo < Formula
url 'https://brew.sh/foo-1.0.tgz'
stable do
patch :DATA
end
end
__END__
patch content here
RUBY
end
it "reports an offense when DATA is found with no __END__" do
expect_offense(<<~RUBY)
class Foo < Formula
url 'https://brew.sh/foo-1.0.tgz'
patch :DATA
^^^^^^^^^^^ patch is missing '__END__'
end
RUBY
end
it "reports an offense when __END__ is found with no DATA" do
expect_offense(<<~RUBY)
class Foo < Formula
url 'https://brew.sh/foo-1.0.tgz'
end
__END__
^^^^^^^ patch is missing 'DATA'
patch content here
RUBY
end
end
context "When auditing external patches" do context "When auditing external patches" do
it "Patch URLs" do it "Patch URLs" do
patch_urls = [ patch_urls = [

View File

@ -497,7 +497,7 @@ module Kernel
path.read path.read
.lines .lines
.grep(/^#:/) .grep(/^#:/)
.map { |line| line.slice(2..-1) } .map { |line| line.slice(2..-1).delete_prefix(" ") }
end end
def redact_secrets(input, secrets) def redact_secrets(input, secrets)

View File

@ -162,6 +162,7 @@ _brew_create() {
--HEAD --HEAD
--autotools --autotools
--cmake --cmake
--crystal
--debug --debug
--force --force
--go --go

View File

@ -45,20 +45,19 @@ __brew_completion_caching_policy() {
tmp=( $1(mw-2N) ) tmp=( $1(mw-2N) )
(( $#tmp )) || return 0 (( $#tmp )) || return 0
# otherwise, invalidate if latest tap index file is missing or newer than # otherwise, invalidate if latest tap index file is missing or newer than cache file
# cache file
tmp=( ${HOMEBREW_REPOSITORY:-/usr/local/Homebrew}/Library/Taps/*/*/.git/index(om[1]N) ) tmp=( ${HOMEBREW_REPOSITORY:-/usr/local/Homebrew}/Library/Taps/*/*/.git/index(om[1]N) )
[[ -z $tmp || $tmp -nt $1 ]] [[ -z $tmp || $tmp -nt $1 ]]
} }
__brew_formulae() { __brew_formulae() {
local -a formulae local -a list
local comp_cachename=brew_formulae local comp_cachename=brew_formulae
if _cache_invalid $comp_cachename || ! _retrieve_cache $comp_cachename; then if ! _retrieve_cache $comp_cachename; then
formulae=($(brew search)) list=( $(brew search) )
_store_cache $comp_cachename formulae _store_cache $comp_cachename list
fi fi
_describe -t formulae 'all formulae' formulae _describe -t formulae 'all formulae' list
} }
__brew_installed_formulae() { __brew_installed_formulae() {
@ -145,18 +144,17 @@ __brew_common_commands() {
} }
__brew_all_commands() { __brew_all_commands() {
local -a commands local -a list
local comp_cachename=brew_all_commands local comp_cachename=brew_all_commands
if _cache_invalid $comp_cachename || ! _retrieve_cache $comp_cachename; then if ! _retrieve_cache $comp_cachename; then
HOMEBREW_CACHE=$(brew --cache) local cache_dir=$(brew --cache)
HOMEBREW_REPOSITORY=$(brew --repo) [[ -f $cache_dir/all_commands_list.txt ]] &&
[[ -f "$HOMEBREW_CACHE/all_commands_list.txt" ]] && list=( $(<$cache_dir/all_commands_list.txt) ) ||
commands=($(cat "$HOMEBREW_CACHE/all_commands_list.txt")) || list=( $(<$(brew --repo)/completions/internal_commands_list.txt) )
commands=($(cat "$HOMEBREW_REPOSITORY/completions/internal_commands_list.txt")) list=( ${list:#*instal} ) # Exclude instal, uninstal, etc.
commands=(${commands:#*instal}) # Exclude instal, uninstal, etc. _store_cache $comp_cachename list
_store_cache $comp_cachename commands
fi fi
_describe -t all-commands 'all commands' commands _describe -t all-commands 'all commands' list
} }
__brew_commands() { __brew_commands() {
@ -857,10 +855,10 @@ _brew() {
case "$state" in case "$state" in
command) command)
# set default cache policy # set default cache policy
zstyle -s ":completion:${curcontext%:*}:*" cache-policy tmp zstyle -s ":completion:${curcontext%:*}:*" cache-policy tmp ||
[[ -n $tmp ]] || zstyle ":completion:${curcontext%:*}:*" cache-policy __brew_completion_caching_policy
zstyle ":completion:${curcontext%:*}:*" cache-policy \ zstyle -s ":completion:${curcontext%:*}:*" use-cache tmp ||
__brew_completion_caching_policy zstyle ":completion:${curcontext%:*}:*" use-cache true
__brew_commands && return 0 __brew_commands && return 0
;; ;;
@ -878,10 +876,10 @@ _brew() {
# set default cache policy (we repeat this dance because the context # set default cache policy (we repeat this dance because the context
# service differs from above) # service differs from above)
zstyle -s ":completion:${curcontext%:*}:*" cache-policy tmp zstyle -s ":completion:${curcontext%:*}:*" cache-policy tmp ||
[[ -n $tmp ]] || zstyle ":completion:${curcontext%:*}:*" cache-policy __brew_completion_caching_policy
zstyle ":completion:${curcontext%:*}:*" cache-policy \ zstyle -s ":completion:${curcontext%:*}:*" use-cache tmp ||
__brew_completion_caching_policy zstyle ":completion:${curcontext%:*}:*" use-cache true
# call completion for named command e.g. _brew_list # call completion for named command e.g. _brew_list
local completion_func="_brew_${command//-/_}" local completion_func="_brew_${command//-/_}"

View File

@ -21,7 +21,7 @@ __brew_all_casks() {
local expl local expl
local comp_cachename=brew_casks local comp_cachename=brew_casks
if _cache_invalid $comp_cachename || ! _retrieve_cache $comp_cachename; then if ! _retrieve_cache $comp_cachename; then
list=( $(brew search --casks) ) list=( $(brew search --casks) )
_store_cache $comp_cachename list _store_cache $comp_cachename list
fi fi

View File

@ -8,7 +8,7 @@ If a bottle is available and usable it will be downloaded and poured automatical
Bottles will not be used if the user requests it (see above), if the formula requests it (with `pour_bottle?`), if any options are specified during installation (bottles are all compiled with default options), if the bottle is not up to date (e.g. lacking a checksum) or if the bottle's `cellar` is not `:any` nor equal to the current `HOMEBREW_CELLAR`. Bottles will not be used if the user requests it (see above), if the formula requests it (with `pour_bottle?`), if any options are specified during installation (bottles are all compiled with default options), if the bottle is not up to date (e.g. lacking a checksum) or if the bottle's `cellar` is not `:any` nor equal to the current `HOMEBREW_CELLAR`.
## Creation ## Creation
Bottles are created using the [Brew Test Bot](Brew-Test-Bot.md). This happens mostly when people submit pull requests to Homebrew and the `bottle do` block is updated by maintainers when they `brew pr-publish` or `brew pr-pull` the contents of a pull request. For the Homebrew organisations' taps they are uploaded to and downloaded from [Bintray](https://bintray.com/homebrew). Bottles are created using the [Brew Test Bot](Brew-Test-Bot.md), usually when people submit pull requests to Homebrew. The `bottle do` block is updated by maintainers when they merge a pull request. For the Homebrew organisations' taps they are uploaded to and downloaded from [Bintray](https://bintray.com/homebrew).
By default, bottles will be built for the oldest CPU supported by the OS/architecture you're building for (Core 2 for 64-bit OSs). This ensures that bottles are compatible with all computers you might distribute them to. If you *really* want your bottles to be optimised for something else, you can pass the `--bottle-arch=` option to build for another architecture; for example, `brew install foo --build-bottle --bottle-arch=penryn`. Just remember that if you build for a newer architecture some of your users might get binaries they can't run and that would be sad! By default, bottles will be built for the oldest CPU supported by the OS/architecture you're building for (Core 2 for 64-bit OSs). This ensures that bottles are compatible with all computers you might distribute them to. If you *really* want your bottles to be optimised for something else, you can pass the `--bottle-arch=` option to build for another architecture; for example, `brew install foo --build-bottle --bottle-arch=penryn`. Just remember that if you build for a newer architecture some of your users might get binaries they can't run and that would be sad!

View File

@ -8,6 +8,7 @@ If a pull request is correct and doesn't need any modifications to commit messag
1. Ensure the job has already completed successfully. 1. Ensure the job has already completed successfully.
2. Run `brew pr-publish 12345` where `12345` is the pull request number (or URL). 2. Run `brew pr-publish 12345` where `12345` is the pull request number (or URL).
- Approving a PR for an existing formula will automatically publish the bottles and close the PR, taking care of this step.
3. Watch the [actions queue](https://github.com/Homebrew/homebrew-core/actions) to ensure your job finishes. BrewTestBot will usually notify you of failures with a ping as well. 3. Watch the [actions queue](https://github.com/Homebrew/homebrew-core/actions) to ensure your job finishes. BrewTestBot will usually notify you of failures with a ping as well.
If a pull request needs changes to the commit messages: If a pull request needs changes to the commit messages:

View File

@ -54,7 +54,7 @@ Check for:
- if CI is green and... - if CI is green and...
- formula `bottle :unneeded`, you can merge it through GitHub UI - formula `bottle :unneeded`, you can merge it through GitHub UI
- bottles need to be pulled, and... - bottles need to be pulled, and...
- the commits are correct and don't need changes, use: `brew pr-publish $PR_ID` - the commits are correct and don't need changes, approve the PR to trigger an automatic merge (use `brew pr-publish $PR_ID` to trigger manually in case of a new formula)
- the commits need to be amended, use `brew pr-pull $PR_ID`, make changes, and `git push` - the commits need to be amended, use `brew pr-pull $PR_ID`, make changes, and `git push`
- don't forget to thank the contributor - don't forget to thank the contributor
- celebrate the first-time contributors - celebrate the first-time contributors

View File

@ -588,9 +588,9 @@ If *`formula`* is provided, display the file or directory used to cache *`formul
* `--force-bottle`: * `--force-bottle`:
Show the cache file used when pouring a bottle. Show the cache file used when pouring a bottle.
* `--formula`: * `--formula`:
Show cache files for only formulae Only show cache files for formulae.
* `--cask`: * `--cask`:
Show cache files for only casks Only show cache files for casks.
### `--cellar` [*`formula`*] ### `--cellar` [*`formula`*]
@ -769,6 +769,8 @@ a simple example. For the complete API, see:
Create a basic template for an Autotools-style build. Create a basic template for an Autotools-style build.
* `--cmake`: * `--cmake`:
Create a basic template for a CMake-style build. Create a basic template for a CMake-style build.
* `--crystal`:
Create a basic template for a Crystal build.
* `--go`: * `--go`:
Create a basic template for a Go build. Create a basic template for a Go build.
* `--meson`: * `--meson`:
@ -900,17 +902,17 @@ repository.
* `--resolve`: * `--resolve`:
When a patch fails to apply, leave in progress and allow user to resolve, instead of aborting. When a patch fails to apply, leave in progress and allow user to resolve, instead of aborting.
* `--workflow`: * `--workflow`:
Retrieve artifacts from the specified workflow (default: tests.yml). Retrieve artifacts from the specified workflow (default: `tests.yml`).
* `--artifact`: * `--artifact`:
Download artifacts with the specified name (default: bottles). Download artifacts with the specified name (default: `bottles`).
* `--bintray-org`: * `--bintray-org`:
Upload to the specified Bintray organisation (default: homebrew). Upload to the specified Bintray organisation (default: `homebrew`).
* `--tap`: * `--tap`:
Target tap repository (default: homebrew/core). Target tap repository (default: `homebrew/core`).
* `--root-url`: * `--root-url`:
Use the specified *`URL`* as the root of the bottle's URL instead of Homebrew's default. Use the specified *`URL`* as the root of the bottle's URL instead of Homebrew's default.
* `--bintray-mirror`: * `--bintray-mirror`:
Use the specified Bintray repository to automatically mirror stable URLs defined in the formulae (default: mirror) Use the specified Bintray repository to automatically mirror stable URLs defined in the formulae (default: `mirror`).
### `pr-upload` [*`options`*] ### `pr-upload` [*`options`*]
@ -921,7 +923,7 @@ Apply the bottle commit and publish bottles to Bintray.
* `-n`, `--dry-run`: * `-n`, `--dry-run`:
Print what would be done rather than doing it. Print what would be done rather than doing it.
* `--bintray-org`: * `--bintray-org`:
Upload to the specified Bintray organisation (default: homebrew). Upload to the specified Bintray organisation (default: `homebrew`).
* `--root-url`: * `--root-url`:
Use the specified *`URL`* as the root of the bottle's URL instead of Homebrew's default. Use the specified *`URL`* as the root of the bottle's URL instead of Homebrew's default.

View File

@ -33,9 +33,10 @@ A few requests:
- In Homebrew/brew, close pull requests using GitHub's "Merge pull request" - In Homebrew/brew, close pull requests using GitHub's "Merge pull request"
button in "Create a merge commit" mode. button in "Create a merge commit" mode.
- In Homebrew/homebrew-core, use `brew pr-publish` to close pull requests - In Homebrew/homebrew-core, use `brew pr-publish` to close pull requests
that require new bottles or change multiple formulae. If commits need to that require new bottles or change multiple formulae. Note that an approving
be amended use `brew pr-pull` instead. Let these commands auto-close review on a pull request for an existing formula will trigger this automatically.
issues whenever possible (it may take up to 5 minutes). If in doubt, If commits need to be amended use `brew pr-pull` instead. Let these commands
auto-close issues whenever possible (it may take up to 5 minutes). If in doubt,
check with e.g. Fork.app that you've not accidentally added merge commits. check with e.g. Fork.app that you've not accidentally added merge commits.
If bottles are unnecessary, use GitHub's "Merge pull request" button in If bottles are unnecessary, use GitHub's "Merge pull request" button in
"Squash and merge" mode for a single formula change. "Squash and merge" mode for a single formula change.

View File

@ -765,11 +765,11 @@ Show the cache file used when pouring a bottle\.
. .
.TP .TP
\fB\-\-formula\fR \fB\-\-formula\fR
Show cache files for only formulae Only show cache files for formulae\.
. .
.TP .TP
\fB\-\-cask\fR \fB\-\-cask\fR
Show cache files for only casks Only show cache files for casks\.
. .
.SS "\fB\-\-cellar\fR [\fIformula\fR]" .SS "\fB\-\-cellar\fR [\fIformula\fR]"
Display Homebrew\'s Cellar path\. \fIDefault:\fR \fB$(brew \-\-prefix)/Cellar\fR, or if that directory doesn\'t exist, \fB$(brew \-\-repository)/Cellar\fR\. Display Homebrew\'s Cellar path\. \fIDefault:\fR \fB$(brew \-\-prefix)/Cellar\fR, or if that directory doesn\'t exist, \fB$(brew \-\-repository)/Cellar\fR\.
@ -999,6 +999,10 @@ Create a basic template for an Autotools\-style build\.
Create a basic template for a CMake\-style build\. Create a basic template for a CMake\-style build\.
. .
.TP .TP
\fB\-\-crystal\fR
Create a basic template for a Crystal build\.
.
.TP
\fB\-\-go\fR \fB\-\-go\fR
Create a basic template for a Go build\. Create a basic template for a Go build\.
. .
@ -1169,19 +1173,19 @@ When a patch fails to apply, leave in progress and allow user to resolve, instea
. .
.TP .TP
\fB\-\-workflow\fR \fB\-\-workflow\fR
Retrieve artifacts from the specified workflow (default: tests\.yml)\. Retrieve artifacts from the specified workflow (default: \fBtests\.yml\fR)\.
. .
.TP .TP
\fB\-\-artifact\fR \fB\-\-artifact\fR
Download artifacts with the specified name (default: bottles)\. Download artifacts with the specified name (default: \fBbottles\fR)\.
. .
.TP .TP
\fB\-\-bintray\-org\fR \fB\-\-bintray\-org\fR
Upload to the specified Bintray organisation (default: homebrew)\. Upload to the specified Bintray organisation (default: \fBhomebrew\fR)\.
. .
.TP .TP
\fB\-\-tap\fR \fB\-\-tap\fR
Target tap repository (default: homebrew/core)\. Target tap repository (default: \fBhomebrew/core\fR)\.
. .
.TP .TP
\fB\-\-root\-url\fR \fB\-\-root\-url\fR
@ -1189,7 +1193,7 @@ Use the specified \fIURL\fR as the root of the bottle\'s URL instead of Homebrew
. .
.TP .TP
\fB\-\-bintray\-mirror\fR \fB\-\-bintray\-mirror\fR
Use the specified Bintray repository to automatically mirror stable URLs defined in the formulae (default: mirror) Use the specified Bintray repository to automatically mirror stable URLs defined in the formulae (default: \fBmirror\fR)\.
. .
.SS "\fBpr\-upload\fR [\fIoptions\fR]" .SS "\fBpr\-upload\fR [\fIoptions\fR]"
Apply the bottle commit and publish bottles to Bintray\. Apply the bottle commit and publish bottles to Bintray\.
@ -1204,7 +1208,7 @@ Print what would be done rather than doing it\.
. .
.TP .TP
\fB\-\-bintray\-org\fR \fB\-\-bintray\-org\fR
Upload to the specified Bintray organisation (default: homebrew)\. Upload to the specified Bintray organisation (default: \fBhomebrew\fR)\.
. .
.TP .TP
\fB\-\-root\-url\fR \fB\-\-root\-url\fR