From 849e62c7368cf9d927448548b52387dc0e96c9ce Mon Sep 17 00:00:00 2001 From: Xu Cheng Date: Thu, 25 Feb 2016 15:34:11 +0800 Subject: [PATCH] update-report: use tap inside Reporter * Avoid tons of unnecessary file path manipulation. Use abstraction offered by Tap class if possible. * Handle formula rename/tap migration inside reporter in per tap basis. * Avoid duplicated computation. * Remove redundant/dead code. --- Library/Homebrew/cmd/update-report.rb | 222 ++++++++++++++------------ 1 file changed, 123 insertions(+), 99 deletions(-) diff --git a/Library/Homebrew/cmd/update-report.rb b/Library/Homebrew/cmd/update-report.rb index 38e6164b4b..1aea227e23 100644 --- a/Library/Homebrew/cmd/update-report.rb +++ b/Library/Homebrew/cmd/update-report.rb @@ -115,119 +115,143 @@ module Homebrew end class Reporter - attr_reader :initial_revision, :current_revision, :repository + class ReporterRevisionUnsetError < RuntimeError + def initialize(var_name) + super "#{var_name} is unset!" + end + end - def self.repository_variable(repository) - if repository == HOMEBREW_REPOSITORY + attr_reader :tap, :initial_revision, :current_revision + + def initialize(tap) + @tap = tap + + initial_revision_var = "HOMEBREW_UPDATE_BEFORE#{repo_var}" + @initial_revision = ENV[initial_revision_var].to_s + raise ReporterRevisionUnsetError, initial_revision_var if @initial_revision.empty? + + current_revision_var = "HOMEBREW_UPDATE_AFTER#{repo_var}" + @current_revision = ENV[current_revision_var].to_s + raise ReporterRevisionUnsetError, current_revision_var if @current_revision.empty? + end + + def report + return @report if @report + + @report = Hash.new { |h, k| h[k] = [] } + return @report unless updated? + + diff.each_line do |line| + status, *paths = line.split + src = Pathname.new paths.first + dst = Pathname.new paths.last + + next unless dst.extname == ".rb" + next unless paths.any? { |p| tap.formula_file?(p) } + + case status + when "A", "D" + @report[status.to_sym] << tap.formula_file_to_name(src) + when "M" + begin + formula = Formulary.factory(tap.path/src) + new_version = formula.pkg_version + old_version = FormulaVersions.new(formula).formula_at_revision(@initial_revision, &:pkg_version) + next if new_version == old_version + rescue Exception => e + onoe e if ARGV.homebrew_developer? + end + @report[:M] << tap.formula_file_to_name(src) + when /^R\d{0,3}/ + @report[:D] << tap.formula_file_to_name(src) if tap.formula_file?(src) + @report[:A] << tap.formula_file_to_name(dst) if tap.formula_file?(dst) + end + end + + renamed_formulae = [] + @report[:D].each do |old_full_name| + old_name = old_full_name.split("/").last + new_name = tap.formula_renames[old_name] + next unless new_name + + if tap.core_formula_repository? + new_full_name = new_name + else + new_full_name = "#{tap}/#{new_full_name}" + end + + renamed_formulae << [old_full_name, new_full_name] if @report[:A].include? new_full_name + end + + unless renamed_formulae.empty? + @report[:A] -= renamed_formulae.map(&:last) + @report[:D] -= renamed_formulae.map(&:first) + @report[:R] = renamed_formulae + end + + @report + end + + def updated? + initial_revision != current_revision + end + + def migrate_tap_migration + report[:D].each do |full_name| + name = full_name.split("/").last + next unless (dir = HOMEBREW_CELLAR/name).exist? # skip if formula is not installed. + next unless new_tap_name = tap.tap_migrations[name] # skip if formula is not in tap_migrations list. + tabs = dir.subdirs.map { |d| Tab.for_keg(Keg.new(d)) } + next unless tabs.first.tap == tap # skip if installed formula is not from this tap. + new_tap = Tap.fetch(new_tap_name) + new_tap.install unless new_tap.installed? + # update tap for each Tab + tabs.each { |tab| tab.tap = new_tap } + tabs.each(&:write) + end + end + + def migrate_formula_rename + report[:R].each do |old_full_name, new_full_name| + old_name = old_full_name.split("/").last + next unless (dir = HOMEBREW_CELLAR/old_name).directory? && !dir.subdirs.empty? + + begin + f = Formulary.factory(new_full_name) + rescue Exception => e + onoe e if ARGV.homebrew_developer? + next + end + + begin + migrator = Migrator.new(f) + migrator.migrate + rescue Migrator::MigratorDifferentTapsError + rescue Exception => e + onoe e + end + end + end + + private + + def repo_var + @repo_var ||= if tap.path == HOMEBREW_REPOSITORY "" else - repository.to_s. + tap.path.to_s. strip_prefix(Tap::TAP_DIRECTORY.to_s). tr("^A-Za-z0-9", "_"). upcase end end - def initialize(repository) - @repository = repository - - repo_var = Reporter.repository_variable(@repository) - initial_revision_var = "HOMEBREW_UPDATE_BEFORE#{repo_var}" - @initial_revision = ENV[initial_revision_var].to_s - if @initial_revision.empty? - raise "#{initial_revision_var} is unset!" if ARGV.homebrew_developer? - raise "update-report should not be called directly!" - end - - current_revision_var = "HOMEBREW_UPDATE_AFTER#{repo_var}" - @current_revision = ENV[current_revision_var].to_s - if @current_revision.empty? - raise "#{current_revision_var} is unset!" if ARGV.homebrew_developer? - raise "update-report should not be called directly!" - end - end - - def report - map = Hash.new { |h, k| h[k] = [] } - - if initial_revision && initial_revision != current_revision - wc_revision = read_current_revision - - diff.each_line do |line| - status, *paths = line.split - src = paths.first - dst = paths.last - - next unless File.extname(dst) == ".rb" - next unless paths.any? { |p| File.dirname(p) == formula_directory } - - case status - when "A", "D" - map[status.to_sym] << repository.join(src) - when "M" - file = repository.join(src) - begin - formula = Formulary.factory(file) - new_version = if wc_revision == current_revision - formula.pkg_version - else - FormulaVersions.new(formula).formula_at_revision(@current_revision, &:pkg_version) - end - old_version = FormulaVersions.new(formula).formula_at_revision(@initial_revision, &:pkg_version) - next if new_version == old_version - # short term fix to prevent situation like https://github.com/Homebrew/homebrew/issues/45616 - rescue Exception => e - onoe e if ARGV.homebrew_developer? - end - map[:M] << file - when /^R\d{0,3}/ - map[:D] << repository.join(src) if File.dirname(src) == formula_directory - map[:A] << repository.join(dst) if File.dirname(dst) == formula_directory - end - end - end - - map - end - - def updated? - initial_revision && initial_revision != current_revision - end - - private - - def formula_directory - if repository == HOMEBREW_REPOSITORY - "Library/Formula" - elsif repository.join("Formula").directory? - "Formula" - elsif repository.join("HomebrewFormula").directory? - "HomebrewFormula" - else - "." - end - end - - def read_current_revision - `git rev-parse -q --verify HEAD`.chomp - end - def diff Utils.popen_read( - "git", "diff-tree", "-r", "--name-status", "--diff-filter=AMDR", + "git", "-C", tap.path, "diff-tree", "-r", "--name-status", "--diff-filter=AMDR", "-M85%", initial_revision, current_revision ) end - - def `(cmd) - out = super - unless $?.success? - $stderr.puts(out) unless out.empty? - raise ErrorDuringExecution.new(cmd) - end - ohai(cmd, out) if ARGV.verbose? - out - end end class Report