brew extract from all taps

This commit is contained in:
kiendang 2019-03-17 18:39:47 +08:00
parent db42dd0420
commit 6be6bba0be
3 changed files with 116 additions and 21 deletions

View File

@ -97,47 +97,64 @@ module Homebrew
# Expect exactly two named arguments: formula and tap
raise UsageError if ARGV.named.length != 2
if ARGV.named.first !~ HOMEBREW_TAP_FORMULA_REGEX
name = ARGV.named.first.downcase
source_tap = CoreTap.instance
else
name = Regexp.last_match(3).downcase
source_tap = Tap.fetch(Regexp.last_match(1), Regexp.last_match(2))
raise TapFormulaUnavailableError.new(source_tap, name) unless source_tap.installed?
end
destination_tap = Tap.fetch(ARGV.named.second)
odie "Cannot extract formula to homebrew/core!" if destination_tap.core_tap?
odie "Cannot extract formula to the same tap!" if destination_tap == source_tap
destination_tap.install unless destination_tap.installed?
name = ARGV.named.first.downcase
repo = CoreTap.instance.path
# Formulae can technically live in "<repo>/<formula>.rb" or
# "<repo>/Formula/<formula>.rb", but explicitly use the latter for now
# since that is how the core tap is structured.
file = repo/"Formula/#{name}.rb"
repo = source_tap.path
pattern = source_tap.core_tap? ? repo/"Formula/#{name}.rb" : repo/"{,**/}#{name}.rb"
if args.version
ohai "Searching repository history"
version = args.version
rev = "HEAD"
test_formula = nil
result = ""
loop do
loop do
rev = Git.last_revision_commit_of_file(repo, file, before_commit: "#{rev}~1")
break if rev.empty?
break unless Git.last_revision_of_file(repo, file, before_commit: rev).empty?
rev, (path,) = Git.last_revision_commit_of_files(repo, pattern, before_commit: "#{rev}~1")
odie "Could not find #{name}! The formula or version may not have existed." if rev.nil?
file = repo/path
result = Git.last_revision_of_file(repo, file, before_commit: rev)
if result.empty?
ohai "Skipping revision #{rev} - file is empty at this revision" if ARGV.debug?
next
end
test_formula = formula_at_revision(repo, name, file, rev)
break if test_formula.nil? || test_formula.version == version
ohai "Trying #{test_formula.version} from revision #{rev} against desired #{version}" if ARGV.debug?
end
odie "Could not find #{name}! The formula or version may not have existed." if test_formula.nil?
result = Git.last_revision_of_file(repo, file, before_commit: rev)
elsif File.exist?(file)
rev = "HEAD"
version = Formulary.factory(file).version
result = File.read(file)
else
ohai "Searching repository history"
rev = Git.last_revision_commit_of_file(repo, file)
version = formula_at_revision(repo, name, file, rev).version
odie "Could not find #{name}! The formula or version may not have existed." if rev.empty?
result = Git.last_revision_of_file(repo, file)
files = Dir[repo/"{,**/}"].map do |dir|
Pathname.glob(["#{dir}/#{name}.rb"]).find(&:file?)
end.compact
if files.empty?
ohai "Searching repository history"
rev, (path,) = Git.last_revision_commit_of_files(repo, pattern)
odie "Could not find #{name}! The formula or version may not have existed." if rev.nil?
file = repo/path
version = formula_at_revision(repo, name, file, rev).version
result = Git.last_revision_of_file(repo, file)
else
file = files.first.realpath
rev = "HEAD"
version = Formulary.factory(file).version
result = File.read(file)
end
end
# The class name has to be renamed to match the new filename,

View File

@ -1,5 +1,5 @@
describe "brew extract", :integration_test do
it "retrieves the specified version of formula, defaulting to most recent" do
it "retrieves the specified version of formula from core tap, defaulting to most recent" do
path = Tap::TAP_DIRECTORY/"homebrew/homebrew-foo"
(path/"Formula").mkpath
target = Tap.from_path(path)
@ -29,4 +29,69 @@ describe "brew extract", :integration_test do
expect(Formulary.factory(path/"Formula/testball@0.1.rb").version).to be == "0.1"
end
it "retrieves the specified version of formula from a tap other than core, defaulting to most recent" do
destination = Tap::TAP_DIRECTORY/"homebrew/homebrew-foo"
(destination/"Formula").mkpath
destination_tap = Tap.from_path(destination)
source = Tap::TAP_DIRECTORY/"homebrew/homebrew-bar"
source.mkpath
source_tap = Tap.from_path(source)
tarball = if OS.linux?
TEST_FIXTURE_DIR/"tarballs/testball-0.1-linux.tbz"
else
TEST_FIXTURE_DIR/"tarballs/testball-0.1.tbz"
end
content = <<~RUBY
desc "Some test"
homepage "https://brew.sh/testball"
url "file://#{tarball}"
sha256 "#{tarball.sha256}"
option "with-foo", "Build with foo"
def install
(prefix/"foo"/"test").write("test") if build.with? "foo"
prefix.install Dir["*"]
(buildpath/"test.c").write \
"#include <stdio.h>\\nint main(){return printf(\\"test\\");}"
bin.mkpath
system ENV.cc, "test.c", "-o", bin/"test"
end
RUBY
formula_file = source_tap.path/"testball.rb"
formula_file.write <<~RUBY
class Testball < Formula
#{content}
end
RUBY
source_tap.path.cd do
system "git", "init"
system "git", "add", "--all"
system "git", "commit", "-m", "testball 0.1"
contents = File.read(formula_file)
contents.gsub!("testball-0.1", "testball-0.2")
File.write(formula_file, contents)
system "git", "add", "--all"
system "git", "commit", "-m", "testball 0.2"
end
expect { brew "extract", "homebrew/bar/testball", destination_tap.name }
.to be_a_success
expect(destination/"Formula/testball@0.2.rb").to exist
expect(Formulary.factory(destination/"Formula/testball@0.2.rb").version).to be == "0.2"
expect { brew "extract", "homebrew/bar/testball", destination_tap.name, "--version=0.1" }
.to be_a_success
expect(destination/"Formula/testball@0.1.rb").to exist
expect(Formulary.factory(destination/"Formula/testball@0.1.rb").version).to be == "0.1"
end
end

View File

@ -14,6 +14,19 @@ module Git
out.chomp
end
def last_revision_commit_of_files(repo, file, before_commit: nil)
args = [before_commit.nil? ? "--skip=1" : before_commit.split("..").first]
cmd = [
HOMEBREW_SHIMS_PATH/"scm/git", "-C", repo,
"log", "--format=%h", "--abbrev=7", "--max-count=1", "--name-only",
*args, "--", file
]
out, = Open3.capture3(cmd.join(" "))
rev, *files = out.chomp.split(/\n/).reject(&:empty?)
[rev, files]
end
def last_revision_of_file(repo, file, before_commit: nil)
relative_file = Pathname(file).relative_path_from(repo)