Move migrations into UpdateMigrator module.
This commit is contained in:
parent
fbcaa8c85a
commit
516a39f5a4
@ -14,6 +14,8 @@ end
|
|||||||
|
|
||||||
require_relative "global"
|
require_relative "global"
|
||||||
|
|
||||||
|
require "update_migrator"
|
||||||
|
|
||||||
begin
|
begin
|
||||||
trap("INT", std_trap) # restore default CTRL-C handler
|
trap("INT", std_trap) # restore default CTRL-C handler
|
||||||
|
|
||||||
@ -76,7 +78,7 @@ begin
|
|||||||
end
|
end
|
||||||
|
|
||||||
# Migrate LinkedKegs/PinnedKegs if update didn't already do so
|
# Migrate LinkedKegs/PinnedKegs if update didn't already do so
|
||||||
migrate_legacy_keg_symlinks_if_necessary
|
UpdateMigrator.migrate_legacy_keg_symlinks_if_necessary
|
||||||
|
|
||||||
# Uninstall old brew-cask if it's still around; we just use the tap now.
|
# Uninstall old brew-cask if it's still around; we just use the tap now.
|
||||||
if cmd == "cask" && (HOMEBREW_CELLAR/"brew-cask").exist?
|
if cmd == "cask" && (HOMEBREW_CELLAR/"brew-cask").exist?
|
||||||
|
|||||||
@ -7,7 +7,7 @@ require "migrator"
|
|||||||
require "formulary"
|
require "formulary"
|
||||||
require "descriptions"
|
require "descriptions"
|
||||||
require "cleanup"
|
require "cleanup"
|
||||||
require "hbc/download"
|
require "update_migrator"
|
||||||
|
|
||||||
module Homebrew
|
module Homebrew
|
||||||
module_function
|
module_function
|
||||||
@ -111,10 +111,10 @@ module Homebrew
|
|||||||
updated = true
|
updated = true
|
||||||
end
|
end
|
||||||
|
|
||||||
migrate_legacy_cache_if_necessary
|
UpdateMigrator.migrate_legacy_cache_if_necessary
|
||||||
migrate_cache_entries_to_double_dashes(initial_version)
|
UpdateMigrator.migrate_cache_entries_to_double_dashes(initial_version)
|
||||||
migrate_cache_entries_to_symlinks(initial_version)
|
UpdateMigrator.migrate_cache_entries_to_symlinks(initial_version)
|
||||||
migrate_legacy_keg_symlinks_if_necessary
|
UpdateMigrator.migrate_legacy_keg_symlinks_if_necessary
|
||||||
|
|
||||||
if !updated
|
if !updated
|
||||||
if !ARGV.include?("--preinstall") && !ENV["HOMEBREW_UPDATE_FAILED"]
|
if !ARGV.include?("--preinstall") && !ENV["HOMEBREW_UPDATE_FAILED"]
|
||||||
@ -140,7 +140,7 @@ module Homebrew
|
|||||||
# This should always be the last thing to run (but skip on auto-update).
|
# This should always be the last thing to run (but skip on auto-update).
|
||||||
if !ARGV.include?("--preinstall") ||
|
if !ARGV.include?("--preinstall") ||
|
||||||
ENV["HOMEBREW_ENABLE_AUTO_UPDATE_MIGRATION"]
|
ENV["HOMEBREW_ENABLE_AUTO_UPDATE_MIGRATION"]
|
||||||
migrate_legacy_repository_if_necessary
|
UpdateMigrator.migrate_legacy_repository_if_necessary
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -158,323 +158,6 @@ module Homebrew
|
|||||||
ENV["HOMEBREW_UPDATE_AFTER_HOMEBREW_HOMEBREW_CORE"] = revision
|
ENV["HOMEBREW_UPDATE_AFTER_HOMEBREW_HOMEBREW_CORE"] = revision
|
||||||
end
|
end
|
||||||
|
|
||||||
def migrate_legacy_cache_if_necessary
|
|
||||||
legacy_cache = Pathname.new "/Library/Caches/Homebrew"
|
|
||||||
return if HOMEBREW_CACHE.to_s == legacy_cache.to_s
|
|
||||||
return unless legacy_cache.directory?
|
|
||||||
return unless legacy_cache.readable_real?
|
|
||||||
|
|
||||||
migration_attempted_file = legacy_cache/".migration_attempted"
|
|
||||||
return if migration_attempted_file.exist?
|
|
||||||
|
|
||||||
return unless legacy_cache.writable_real?
|
|
||||||
FileUtils.touch migration_attempted_file
|
|
||||||
|
|
||||||
# This directory could have been compromised if it's world-writable/
|
|
||||||
# a symlink/owned by another user so don't copy files in those cases.
|
|
||||||
world_writable = legacy_cache.stat.mode & 0777 == 0777
|
|
||||||
return if world_writable
|
|
||||||
return if legacy_cache.symlink?
|
|
||||||
return if !legacy_cache.owned? && legacy_cache.lstat.uid.nonzero?
|
|
||||||
|
|
||||||
ohai "Migrating #{legacy_cache} to #{HOMEBREW_CACHE}..."
|
|
||||||
HOMEBREW_CACHE.mkpath
|
|
||||||
legacy_cache.cd do
|
|
||||||
legacy_cache.entries.each do |f|
|
|
||||||
next if [".", "..", ".migration_attempted"].include? f.to_s
|
|
||||||
begin
|
|
||||||
FileUtils.cp_r f, HOMEBREW_CACHE
|
|
||||||
rescue
|
|
||||||
@migration_failed ||= true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if @migration_failed
|
|
||||||
opoo <<~EOS
|
|
||||||
Failed to migrate #{legacy_cache} to
|
|
||||||
#{HOMEBREW_CACHE}. Please do so manually.
|
|
||||||
EOS
|
|
||||||
else
|
|
||||||
ohai "Deleting #{legacy_cache}..."
|
|
||||||
FileUtils.rm_rf legacy_cache
|
|
||||||
if legacy_cache.exist?
|
|
||||||
FileUtils.touch migration_attempted_file
|
|
||||||
opoo <<~EOS
|
|
||||||
Failed to delete #{legacy_cache}.
|
|
||||||
Please do so manually.
|
|
||||||
EOS
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def formula_resources(formula)
|
|
||||||
specs = [formula.stable, formula.devel, formula.head].compact
|
|
||||||
|
|
||||||
[*formula.bottle&.resource] + specs.flat_map do |spec|
|
|
||||||
[
|
|
||||||
spec,
|
|
||||||
*spec.resources.values,
|
|
||||||
*spec.patches.select(&:external?).map(&:resource),
|
|
||||||
]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def parse_extname(url)
|
|
||||||
uri_path = if URI::DEFAULT_PARSER.make_regexp =~ url
|
|
||||||
uri = URI(url)
|
|
||||||
uri.query ? "#{uri.path}?#{uri.query}" : uri.path
|
|
||||||
else
|
|
||||||
url
|
|
||||||
end
|
|
||||||
|
|
||||||
# Given a URL like https://example.com/download.php?file=foo-1.0.tar.gz
|
|
||||||
# the extension we want is ".tar.gz", not ".php".
|
|
||||||
Pathname.new(uri_path).ascend do |path|
|
|
||||||
ext = path.extname[/[^?&]+/]
|
|
||||||
return ext if ext
|
|
||||||
end
|
|
||||||
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
|
|
||||||
def migrate_cache_entries_to_double_dashes(initial_version)
|
|
||||||
return if initial_version && initial_version > "1.7.1"
|
|
||||||
|
|
||||||
return if ENV.key?("HOMEBREW_DISABLE_LOAD_FORMULA")
|
|
||||||
|
|
||||||
ohai "Migrating cache entries..."
|
|
||||||
|
|
||||||
Formula.each do |formula|
|
|
||||||
formula_resources(formula).each do |resource|
|
|
||||||
downloader = resource.downloader
|
|
||||||
|
|
||||||
url = downloader.url
|
|
||||||
name = resource.download_name
|
|
||||||
version = resource.version
|
|
||||||
|
|
||||||
extname = parse_extname(url)
|
|
||||||
old_location = downloader.cache/"#{name}-#{version}#{extname}"
|
|
||||||
new_location = downloader.cache/"#{name}--#{version}#{extname}"
|
|
||||||
|
|
||||||
next unless old_location.file?
|
|
||||||
|
|
||||||
if new_location.exist?
|
|
||||||
begin
|
|
||||||
FileUtils.rm_rf old_location
|
|
||||||
rescue Errno::EACCES
|
|
||||||
opoo "Could not remove #{old_location}, please do so manually."
|
|
||||||
end
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
FileUtils.mv old_location, new_location
|
|
||||||
rescue Errno::EACCES
|
|
||||||
opoo "Could not move #{old_location} to #{new_location}, please do so manually."
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def migrate_cache_entries_to_symlinks(initial_version)
|
|
||||||
return if initial_version && initial_version > "1.7.2"
|
|
||||||
|
|
||||||
return if ENV.key?("HOMEBREW_DISABLE_LOAD_FORMULA")
|
|
||||||
|
|
||||||
ohai "Migrating cache entries..."
|
|
||||||
|
|
||||||
load_formula = lambda do |formula|
|
|
||||||
begin
|
|
||||||
Formula[formula]
|
|
||||||
rescue FormulaUnavailableError
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
load_cask = lambda do |cask|
|
|
||||||
begin
|
|
||||||
Hbc::CaskLoader.load(cask)
|
|
||||||
rescue Hbc::CaskUnavailableError
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
formula_downloaders = if HOMEBREW_CACHE.directory?
|
|
||||||
HOMEBREW_CACHE.children
|
|
||||||
.select(&:file?)
|
|
||||||
.map { |child| child.basename.to_s.sub(/\-\-.*/, "") }
|
|
||||||
.uniq
|
|
||||||
.map(&load_formula)
|
|
||||||
.compact
|
|
||||||
.flat_map { |formula| formula_resources(formula) }
|
|
||||||
.map { |resource| [resource.downloader, resource.download_name, resource.version] }
|
|
||||||
else
|
|
||||||
[]
|
|
||||||
end
|
|
||||||
|
|
||||||
cask_downloaders = if (HOMEBREW_CACHE/"Cask").directory?
|
|
||||||
(HOMEBREW_CACHE/"Cask").children
|
|
||||||
.map { |child| child.basename.to_s.sub(/\-\-.*/, "") }
|
|
||||||
.uniq
|
|
||||||
.map(&load_cask)
|
|
||||||
.compact
|
|
||||||
.map { |cask| [Hbc::Download.new(cask).downloader, cask.token, cask.version] }
|
|
||||||
else
|
|
||||||
[]
|
|
||||||
end
|
|
||||||
|
|
||||||
downloaders = formula_downloaders + cask_downloaders
|
|
||||||
|
|
||||||
downloaders.each do |downloader, name, version|
|
|
||||||
next unless downloader.respond_to?(:symlink_location)
|
|
||||||
|
|
||||||
url = downloader.url
|
|
||||||
extname = parse_extname(url)
|
|
||||||
old_location = downloader.cache/"#{name}--#{version}#{extname}"
|
|
||||||
next unless old_location.file?
|
|
||||||
|
|
||||||
new_symlink_location = downloader.symlink_location
|
|
||||||
new_location = downloader.cached_location
|
|
||||||
|
|
||||||
if new_location.exist? && new_symlink_location.symlink?
|
|
||||||
begin
|
|
||||||
FileUtils.rm_rf old_location unless old_location == new_symlink_location
|
|
||||||
rescue Errno::EACCES
|
|
||||||
opoo "Could not remove #{old_location}, please do so manually."
|
|
||||||
end
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
new_location.dirname.mkpath
|
|
||||||
FileUtils.mv old_location, new_location unless new_location.exist?
|
|
||||||
symlink_target = new_location.relative_path_from(new_symlink_location.dirname)
|
|
||||||
new_symlink_location.dirname.mkpath
|
|
||||||
FileUtils.ln_s symlink_target, new_symlink_location, force: true
|
|
||||||
rescue Errno::EACCES
|
|
||||||
opoo "Could not move #{old_location} to #{new_location}, please do so manually."
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def migrate_legacy_repository_if_necessary
|
|
||||||
return unless HOMEBREW_PREFIX.to_s == "/usr/local"
|
|
||||||
return unless HOMEBREW_REPOSITORY.to_s == "/usr/local"
|
|
||||||
|
|
||||||
ohai "Migrating HOMEBREW_REPOSITORY (please wait)..."
|
|
||||||
|
|
||||||
unless HOMEBREW_PREFIX.writable_real?
|
|
||||||
ofail <<~EOS
|
|
||||||
#{HOMEBREW_PREFIX} is not writable.
|
|
||||||
|
|
||||||
You should change the ownership and permissions of #{HOMEBREW_PREFIX}
|
|
||||||
temporarily back to your user account so we can complete the Homebrew
|
|
||||||
repository migration:
|
|
||||||
sudo chown -R $(whoami) #{HOMEBREW_PREFIX}
|
|
||||||
EOS
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
new_homebrew_repository = Pathname.new "/usr/local/Homebrew"
|
|
||||||
new_homebrew_repository.rmdir_if_possible
|
|
||||||
if new_homebrew_repository.exist?
|
|
||||||
ofail <<~EOS
|
|
||||||
#{new_homebrew_repository} already exists.
|
|
||||||
Please remove it manually or uninstall and reinstall Homebrew into a new
|
|
||||||
location as the migration cannot be done automatically.
|
|
||||||
EOS
|
|
||||||
return
|
|
||||||
end
|
|
||||||
new_homebrew_repository.mkpath
|
|
||||||
|
|
||||||
repo_files = HOMEBREW_REPOSITORY.cd do
|
|
||||||
Utils.popen_read("git ls-files").lines.map(&:chomp)
|
|
||||||
end
|
|
||||||
|
|
||||||
unless Utils.popen_read("git status --untracked-files=all --porcelain").empty?
|
|
||||||
HOMEBREW_REPOSITORY.cd do
|
|
||||||
quiet_system "git", "merge", "--abort"
|
|
||||||
quiet_system "git", "rebase", "--abort"
|
|
||||||
quiet_system "git", "reset", "--mixed"
|
|
||||||
safe_system "git", "-c", "user.email=brew-update@localhost",
|
|
||||||
"-c", "user.name=brew update",
|
|
||||||
"stash", "save", "--include-untracked"
|
|
||||||
end
|
|
||||||
stashed = true
|
|
||||||
end
|
|
||||||
|
|
||||||
FileUtils.cp_r "#{HOMEBREW_REPOSITORY}/.git", "#{new_homebrew_repository}/.git"
|
|
||||||
new_homebrew_repository.cd do
|
|
||||||
safe_system "git", "checkout", "--force", "."
|
|
||||||
safe_system "git", "stash", "pop" if stashed
|
|
||||||
end
|
|
||||||
|
|
||||||
if (HOMEBREW_REPOSITORY/"Library/Locks").exist?
|
|
||||||
FileUtils.cp_r "#{HOMEBREW_REPOSITORY}/Library/Locks", "#{new_homebrew_repository}/Library/Locks"
|
|
||||||
end
|
|
||||||
|
|
||||||
if (HOMEBREW_REPOSITORY/"Library/Taps").exist?
|
|
||||||
FileUtils.cp_r "#{HOMEBREW_REPOSITORY}/Library/Taps", "#{new_homebrew_repository}/Library/Taps"
|
|
||||||
end
|
|
||||||
|
|
||||||
unremovable_paths = []
|
|
||||||
extra_remove_paths = [".git", "Library/Locks", "Library/Taps",
|
|
||||||
"Library/Homebrew/cask", "Library/Homebrew/test"]
|
|
||||||
(repo_files + extra_remove_paths).each do |file|
|
|
||||||
path = Pathname.new "#{HOMEBREW_REPOSITORY}/#{file}"
|
|
||||||
begin
|
|
||||||
FileUtils.rm_rf path
|
|
||||||
rescue Errno::EACCES
|
|
||||||
unremovable_paths << path
|
|
||||||
end
|
|
||||||
quiet_system "rmdir", "-p", path.parent if path.parent.exist?
|
|
||||||
end
|
|
||||||
|
|
||||||
unless unremovable_paths.empty?
|
|
||||||
ofail <<~EOS
|
|
||||||
Could not remove old HOMEBREW_REPOSITORY paths!
|
|
||||||
Please do this manually with:
|
|
||||||
sudo rm -rf #{unremovable_paths.join " "}
|
|
||||||
EOS
|
|
||||||
end
|
|
||||||
|
|
||||||
(Keg::ALL_TOP_LEVEL_DIRECTORIES + ["Cellar"]).each do |dir|
|
|
||||||
FileUtils.mkdir_p "#{HOMEBREW_PREFIX}/#{dir}"
|
|
||||||
end
|
|
||||||
|
|
||||||
src = Pathname.new("#{new_homebrew_repository}/bin/brew")
|
|
||||||
dst = Pathname.new("#{HOMEBREW_PREFIX}/bin/brew")
|
|
||||||
begin
|
|
||||||
FileUtils.ln_s(src.relative_path_from(dst.parent), dst)
|
|
||||||
rescue Errno::EACCES, Errno::ENOENT
|
|
||||||
ofail <<~EOS
|
|
||||||
Could not create symlink at #{dst}!
|
|
||||||
Please do this manually with:
|
|
||||||
sudo ln -sf #{src} #{dst}
|
|
||||||
sudo chown $(whoami) #{dst}
|
|
||||||
EOS
|
|
||||||
end
|
|
||||||
|
|
||||||
link_completions_manpages_and_docs(new_homebrew_repository)
|
|
||||||
|
|
||||||
ohai "Migrated HOMEBREW_REPOSITORY to #{new_homebrew_repository}!"
|
|
||||||
puts <<~EOS
|
|
||||||
Homebrew no longer needs to have ownership of /usr/local. If you wish you can
|
|
||||||
return /usr/local to its default ownership with:
|
|
||||||
sudo chown root:wheel #{HOMEBREW_PREFIX}
|
|
||||||
EOS
|
|
||||||
rescue => e
|
|
||||||
ofail <<~EOS
|
|
||||||
#{Tty.bold}Failed to migrate HOMEBREW_REPOSITORY to #{new_homebrew_repository}!#{Tty.reset}
|
|
||||||
The error was:
|
|
||||||
#{e}
|
|
||||||
Please try to resolve this error yourself and then run `brew update` again to
|
|
||||||
complete the migration. If you need help please +1 an existing error or comment
|
|
||||||
with your new error in issue:
|
|
||||||
#{Formatter.url("https://github.com/Homebrew/brew/issues/987")}
|
|
||||||
EOS
|
|
||||||
$stderr.puts e.backtrace
|
|
||||||
end
|
|
||||||
|
|
||||||
def link_completions_manpages_and_docs(repository = HOMEBREW_REPOSITORY)
|
def link_completions_manpages_and_docs(repository = HOMEBREW_REPOSITORY)
|
||||||
command = "brew update"
|
command = "brew update"
|
||||||
Utils::Link.link_completions(repository, command)
|
Utils::Link.link_completions(repository, command)
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
require "cmd/update-report"
|
require "update_migrator"
|
||||||
|
|
||||||
describe "brew update-report" do
|
describe UpdateMigrator do
|
||||||
describe "::migrate_cache_entries_to_double_dashes" do
|
describe "::migrate_cache_entries_to_double_dashes" do
|
||||||
let(:formula_name) { "foo" }
|
let(:formula_name) { "foo" }
|
||||||
let(:f) {
|
let(:f) {
|
||||||
@ -18,7 +18,7 @@ describe "brew update-report" do
|
|||||||
end
|
end
|
||||||
|
|
||||||
it "moves old files to use double dashes when upgrading from <= 1.7.1" do
|
it "moves old files to use double dashes when upgrading from <= 1.7.1" do
|
||||||
Homebrew.migrate_cache_entries_to_double_dashes(Version.new("1.7.1"))
|
described_class.migrate_cache_entries_to_double_dashes(Version.new("1.7.1"))
|
||||||
|
|
||||||
expect(old_cache_file).not_to exist
|
expect(old_cache_file).not_to exist
|
||||||
expect(new_cache_file).to exist
|
expect(new_cache_file).to exist
|
||||||
@ -28,8 +28,8 @@ describe "brew update-report" do
|
|||||||
let(:formula_name) { "foo-bar" }
|
let(:formula_name) { "foo-bar" }
|
||||||
|
|
||||||
it "does not introduce extra double dashes when called multiple times" do
|
it "does not introduce extra double dashes when called multiple times" do
|
||||||
Homebrew.migrate_cache_entries_to_double_dashes(Version.new("1.7.1"))
|
described_class.migrate_cache_entries_to_double_dashes(Version.new("1.7.1"))
|
||||||
Homebrew.migrate_cache_entries_to_double_dashes(Version.new("1.7.1"))
|
described_class.migrate_cache_entries_to_double_dashes(Version.new("1.7.1"))
|
||||||
|
|
||||||
expect(old_cache_file).not_to exist
|
expect(old_cache_file).not_to exist
|
||||||
expect(new_cache_file).to exist
|
expect(new_cache_file).to exist
|
||||||
@ -37,7 +37,7 @@ describe "brew update-report" do
|
|||||||
end
|
end
|
||||||
|
|
||||||
it "does not move files if upgrading from > 1.7.1" do
|
it "does not move files if upgrading from > 1.7.1" do
|
||||||
Homebrew.migrate_cache_entries_to_double_dashes(Version.new("1.7.2"))
|
described_class.migrate_cache_entries_to_double_dashes(Version.new("1.7.2"))
|
||||||
|
|
||||||
expect(old_cache_file).to exist
|
expect(old_cache_file).to exist
|
||||||
expect(new_cache_file).not_to exist
|
expect(new_cache_file).not_to exist
|
||||||
@ -63,7 +63,7 @@ describe "brew update-report" do
|
|||||||
end
|
end
|
||||||
|
|
||||||
it "moves old files to use symlinks when upgrading from <= 1.7.2" do
|
it "moves old files to use symlinks when upgrading from <= 1.7.2" do
|
||||||
Homebrew.migrate_cache_entries_to_symlinks(Version.new("1.7.2"))
|
described_class.migrate_cache_entries_to_symlinks(Version.new("1.7.2"))
|
||||||
|
|
||||||
expect(old_cache_file).to eq(new_cache_symlink)
|
expect(old_cache_file).to eq(new_cache_symlink)
|
||||||
expect(new_cache_symlink).to be_a_symlink
|
expect(new_cache_symlink).to be_a_symlink
|
||||||
@ -74,7 +74,7 @@ describe "brew update-report" do
|
|||||||
end
|
end
|
||||||
|
|
||||||
it "does not move files if upgrading from > 1.7.2" do
|
it "does not move files if upgrading from > 1.7.2" do
|
||||||
Homebrew.migrate_cache_entries_to_symlinks(Version.new("1.7.3"))
|
described_class.migrate_cache_entries_to_symlinks(Version.new("1.7.3"))
|
||||||
|
|
||||||
expect(old_cache_file).to exist
|
expect(old_cache_file).to exist
|
||||||
expect(new_cache_file).not_to exist
|
expect(new_cache_file).not_to exist
|
||||||
39
Library/Homebrew/update_migrator.rb
Normal file
39
Library/Homebrew/update_migrator.rb
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
require "update_migrator/cache_entries_to_double_dashes"
|
||||||
|
require "update_migrator/cache_entries_to_symlinks"
|
||||||
|
require "update_migrator/legacy_cache"
|
||||||
|
require "update_migrator/legacy_keg_symlinks"
|
||||||
|
require "update_migrator/legacy_repository"
|
||||||
|
|
||||||
|
module UpdateMigrator
|
||||||
|
module_function
|
||||||
|
|
||||||
|
def formula_resources(formula)
|
||||||
|
specs = [formula.stable, formula.devel, formula.head].compact
|
||||||
|
|
||||||
|
[*formula.bottle&.resource] + specs.flat_map do |spec|
|
||||||
|
[
|
||||||
|
spec,
|
||||||
|
*spec.resources.values,
|
||||||
|
*spec.patches.select(&:external?).map(&:resource),
|
||||||
|
]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def parse_extname(url)
|
||||||
|
uri_path = if URI::DEFAULT_PARSER.make_regexp =~ url
|
||||||
|
uri = URI(url)
|
||||||
|
uri.query ? "#{uri.path}?#{uri.query}" : uri.path
|
||||||
|
else
|
||||||
|
url
|
||||||
|
end
|
||||||
|
|
||||||
|
# Given a URL like https://example.com/download.php?file=foo-1.0.tar.gz
|
||||||
|
# the extension we want is ".tar.gz", not ".php".
|
||||||
|
Pathname.new(uri_path).ascend do |path|
|
||||||
|
ext = path.extname[/[^?&]+/]
|
||||||
|
return ext if ext
|
||||||
|
end
|
||||||
|
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
@ -0,0 +1,41 @@
|
|||||||
|
module UpdateMigrator
|
||||||
|
module_function
|
||||||
|
|
||||||
|
def migrate_cache_entries_to_double_dashes(initial_version)
|
||||||
|
return if initial_version && initial_version > "1.7.1"
|
||||||
|
|
||||||
|
return if ENV.key?("HOMEBREW_DISABLE_LOAD_FORMULA")
|
||||||
|
|
||||||
|
ohai "Migrating cache entries..."
|
||||||
|
|
||||||
|
Formula.each do |formula|
|
||||||
|
formula_resources(formula).each do |resource|
|
||||||
|
downloader = resource.downloader
|
||||||
|
|
||||||
|
url = downloader.url
|
||||||
|
name = resource.download_name
|
||||||
|
version = resource.version
|
||||||
|
|
||||||
|
extname = parse_extname(url)
|
||||||
|
old_location = downloader.cache/"#{name}-#{version}#{extname}"
|
||||||
|
new_location = downloader.cache/"#{name}--#{version}#{extname}"
|
||||||
|
|
||||||
|
next unless old_location.file?
|
||||||
|
|
||||||
|
if new_location.exist?
|
||||||
|
begin
|
||||||
|
FileUtils.rm_rf old_location
|
||||||
|
rescue Errno::EACCES
|
||||||
|
opoo "Could not remove #{old_location}, please do so manually."
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
FileUtils.mv old_location, new_location
|
||||||
|
rescue Errno::EACCES
|
||||||
|
opoo "Could not move #{old_location} to #{new_location}, please do so manually."
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@ -0,0 +1,90 @@
|
|||||||
|
require "hbc/cask_loader"
|
||||||
|
require "hbc/download"
|
||||||
|
|
||||||
|
module UpdateMigrator
|
||||||
|
module_function
|
||||||
|
|
||||||
|
def migrate_cache_entries_to_symlinks(initial_version)
|
||||||
|
return if initial_version && initial_version > "1.7.2"
|
||||||
|
|
||||||
|
return if ENV.key?("HOMEBREW_DISABLE_LOAD_FORMULA")
|
||||||
|
|
||||||
|
ohai "Migrating cache entries..."
|
||||||
|
|
||||||
|
load_formula = lambda do |formula|
|
||||||
|
begin
|
||||||
|
Formula[formula]
|
||||||
|
rescue FormulaUnavailableError
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
load_cask = lambda do |cask|
|
||||||
|
begin
|
||||||
|
Hbc::CaskLoader.load(cask)
|
||||||
|
rescue Hbc::CaskUnavailableError
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
formula_downloaders = if HOMEBREW_CACHE.directory?
|
||||||
|
HOMEBREW_CACHE.children
|
||||||
|
.select(&:file?)
|
||||||
|
.map { |child| child.basename.to_s.sub(/\-\-.*/, "") }
|
||||||
|
.uniq
|
||||||
|
.map(&load_formula)
|
||||||
|
.compact
|
||||||
|
.flat_map { |formula| formula_resources(formula) }
|
||||||
|
.map { |resource| [resource.downloader, resource.download_name, resource.version] }
|
||||||
|
else
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
|
||||||
|
cask_downloaders = if (HOMEBREW_CACHE/"Cask").directory?
|
||||||
|
(HOMEBREW_CACHE/"Cask").children
|
||||||
|
.map { |child| child.basename.to_s.sub(/\-\-.*/, "") }
|
||||||
|
.uniq
|
||||||
|
.map(&load_cask)
|
||||||
|
.compact
|
||||||
|
.map { |cask| [Hbc::Download.new(cask).downloader, cask.token, cask.version] }
|
||||||
|
else
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
|
||||||
|
downloaders = formula_downloaders + cask_downloaders
|
||||||
|
|
||||||
|
downloaders.each do |downloader, name, version|
|
||||||
|
next unless downloader.respond_to?(:symlink_location)
|
||||||
|
|
||||||
|
url = downloader.url
|
||||||
|
extname = parse_extname(url)
|
||||||
|
old_location = downloader.cache/"#{name}--#{version}#{extname}"
|
||||||
|
next unless old_location.file?
|
||||||
|
|
||||||
|
new_symlink_location = downloader.symlink_location
|
||||||
|
new_location = downloader.cached_location
|
||||||
|
|
||||||
|
if new_location.exist? && new_symlink_location.symlink?
|
||||||
|
begin
|
||||||
|
FileUtils.rm_rf old_location unless old_location == new_symlink_location
|
||||||
|
rescue Errno::EACCES
|
||||||
|
opoo "Could not remove #{old_location}, please do so manually."
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
new_location.dirname.mkpath
|
||||||
|
if new_location.exist?
|
||||||
|
FileUtils.rm_rf old_location
|
||||||
|
else
|
||||||
|
FileUtils.mv old_location, new_location
|
||||||
|
end
|
||||||
|
symlink_target = new_location.relative_path_from(new_symlink_location.dirname)
|
||||||
|
new_symlink_location.dirname.mkpath
|
||||||
|
FileUtils.ln_s symlink_target, new_symlink_location, force: true
|
||||||
|
rescue Errno::EACCES
|
||||||
|
opoo "Could not move #{old_location} to #{new_location}, please do so manually."
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
53
Library/Homebrew/update_migrator/legacy_cache.rb
Normal file
53
Library/Homebrew/update_migrator/legacy_cache.rb
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
module UpdateMigrator
|
||||||
|
module_function
|
||||||
|
|
||||||
|
def migrate_legacy_cache_if_necessary
|
||||||
|
legacy_cache = Pathname.new "/Library/Caches/Homebrew"
|
||||||
|
return if HOMEBREW_CACHE.to_s == legacy_cache.to_s
|
||||||
|
return unless legacy_cache.directory?
|
||||||
|
return unless legacy_cache.readable_real?
|
||||||
|
|
||||||
|
migration_attempted_file = legacy_cache/".migration_attempted"
|
||||||
|
return if migration_attempted_file.exist?
|
||||||
|
|
||||||
|
return unless legacy_cache.writable_real?
|
||||||
|
FileUtils.touch migration_attempted_file
|
||||||
|
|
||||||
|
# This directory could have been compromised if it's world-writable/
|
||||||
|
# a symlink/owned by another user so don't copy files in those cases.
|
||||||
|
world_writable = legacy_cache.stat.mode & 0777 == 0777
|
||||||
|
return if world_writable
|
||||||
|
return if legacy_cache.symlink?
|
||||||
|
return if !legacy_cache.owned? && legacy_cache.lstat.uid.nonzero?
|
||||||
|
|
||||||
|
ohai "Migrating #{legacy_cache} to #{HOMEBREW_CACHE}..."
|
||||||
|
HOMEBREW_CACHE.mkpath
|
||||||
|
legacy_cache.cd do
|
||||||
|
legacy_cache.entries.each do |f|
|
||||||
|
next if [".", "..", ".migration_attempted"].include? f.to_s
|
||||||
|
begin
|
||||||
|
FileUtils.cp_r f, HOMEBREW_CACHE
|
||||||
|
rescue
|
||||||
|
@migration_failed ||= true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if @migration_failed
|
||||||
|
opoo <<~EOS
|
||||||
|
Failed to migrate #{legacy_cache} to
|
||||||
|
#{HOMEBREW_CACHE}. Please do so manually.
|
||||||
|
EOS
|
||||||
|
else
|
||||||
|
ohai "Deleting #{legacy_cache}..."
|
||||||
|
FileUtils.rm_rf legacy_cache
|
||||||
|
if legacy_cache.exist?
|
||||||
|
FileUtils.touch migration_attempted_file
|
||||||
|
opoo <<~EOS
|
||||||
|
Failed to delete #{legacy_cache}.
|
||||||
|
Please do so manually.
|
||||||
|
EOS
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
42
Library/Homebrew/update_migrator/legacy_keg_symlinks.rb
Normal file
42
Library/Homebrew/update_migrator/legacy_keg_symlinks.rb
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
module UpdateMigrator
|
||||||
|
module_function
|
||||||
|
|
||||||
|
def migrate_legacy_keg_symlinks_if_necessary
|
||||||
|
legacy_linked_kegs = HOMEBREW_LIBRARY/"LinkedKegs"
|
||||||
|
return unless legacy_linked_kegs.directory?
|
||||||
|
|
||||||
|
HOMEBREW_LINKED_KEGS.mkpath unless legacy_linked_kegs.children.empty?
|
||||||
|
legacy_linked_kegs.children.each do |link|
|
||||||
|
name = link.basename.to_s
|
||||||
|
src = begin
|
||||||
|
link.realpath
|
||||||
|
rescue Errno::ENOENT
|
||||||
|
begin
|
||||||
|
(HOMEBREW_PREFIX/"opt/#{name}").realpath
|
||||||
|
rescue Errno::ENOENT
|
||||||
|
begin
|
||||||
|
Formulary.factory(name).installed_prefix
|
||||||
|
rescue
|
||||||
|
next
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
dst = HOMEBREW_LINKED_KEGS/name
|
||||||
|
dst.unlink if dst.exist?
|
||||||
|
FileUtils.ln_sf(src.relative_path_from(dst.parent), dst)
|
||||||
|
end
|
||||||
|
FileUtils.rm_rf legacy_linked_kegs
|
||||||
|
|
||||||
|
legacy_pinned_kegs = HOMEBREW_LIBRARY/"PinnedKegs"
|
||||||
|
return unless legacy_pinned_kegs.directory?
|
||||||
|
|
||||||
|
HOMEBREW_PINNED_KEGS.mkpath unless legacy_pinned_kegs.children.empty?
|
||||||
|
legacy_pinned_kegs.children.each do |link|
|
||||||
|
name = link.basename.to_s
|
||||||
|
src = link.realpath
|
||||||
|
dst = HOMEBREW_PINNED_KEGS/name
|
||||||
|
FileUtils.ln_sf(src.relative_path_from(dst.parent), dst)
|
||||||
|
end
|
||||||
|
FileUtils.rm_rf legacy_pinned_kegs
|
||||||
|
end
|
||||||
|
end
|
||||||
122
Library/Homebrew/update_migrator/legacy_repository.rb
Normal file
122
Library/Homebrew/update_migrator/legacy_repository.rb
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
module UpdateMigrator
|
||||||
|
module_function
|
||||||
|
|
||||||
|
def migrate_legacy_repository_if_necessary
|
||||||
|
return unless HOMEBREW_PREFIX.to_s == "/usr/local"
|
||||||
|
return unless HOMEBREW_REPOSITORY.to_s == "/usr/local"
|
||||||
|
|
||||||
|
ohai "Migrating HOMEBREW_REPOSITORY (please wait)..."
|
||||||
|
|
||||||
|
unless HOMEBREW_PREFIX.writable_real?
|
||||||
|
ofail <<~EOS
|
||||||
|
#{HOMEBREW_PREFIX} is not writable.
|
||||||
|
|
||||||
|
You should change the ownership and permissions of #{HOMEBREW_PREFIX}
|
||||||
|
temporarily back to your user account so we can complete the Homebrew
|
||||||
|
repository migration:
|
||||||
|
sudo chown -R $(whoami) #{HOMEBREW_PREFIX}
|
||||||
|
EOS
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
new_homebrew_repository = Pathname.new "/usr/local/Homebrew"
|
||||||
|
new_homebrew_repository.rmdir_if_possible
|
||||||
|
if new_homebrew_repository.exist?
|
||||||
|
ofail <<~EOS
|
||||||
|
#{new_homebrew_repository} already exists.
|
||||||
|
Please remove it manually or uninstall and reinstall Homebrew into a new
|
||||||
|
location as the migration cannot be done automatically.
|
||||||
|
EOS
|
||||||
|
return
|
||||||
|
end
|
||||||
|
new_homebrew_repository.mkpath
|
||||||
|
|
||||||
|
repo_files = HOMEBREW_REPOSITORY.cd do
|
||||||
|
Utils.popen_read("git ls-files").lines.map(&:chomp)
|
||||||
|
end
|
||||||
|
|
||||||
|
unless Utils.popen_read("git status --untracked-files=all --porcelain").empty?
|
||||||
|
HOMEBREW_REPOSITORY.cd do
|
||||||
|
quiet_system "git", "merge", "--abort"
|
||||||
|
quiet_system "git", "rebase", "--abort"
|
||||||
|
quiet_system "git", "reset", "--mixed"
|
||||||
|
safe_system "git", "-c", "user.email=brew-update@localhost",
|
||||||
|
"-c", "user.name=brew update",
|
||||||
|
"stash", "save", "--include-untracked"
|
||||||
|
end
|
||||||
|
stashed = true
|
||||||
|
end
|
||||||
|
|
||||||
|
FileUtils.cp_r "#{HOMEBREW_REPOSITORY}/.git", "#{new_homebrew_repository}/.git"
|
||||||
|
new_homebrew_repository.cd do
|
||||||
|
safe_system "git", "checkout", "--force", "."
|
||||||
|
safe_system "git", "stash", "pop" if stashed
|
||||||
|
end
|
||||||
|
|
||||||
|
if (HOMEBREW_REPOSITORY/"Library/Locks").exist?
|
||||||
|
FileUtils.cp_r "#{HOMEBREW_REPOSITORY}/Library/Locks", "#{new_homebrew_repository}/Library/Locks"
|
||||||
|
end
|
||||||
|
|
||||||
|
if (HOMEBREW_REPOSITORY/"Library/Taps").exist?
|
||||||
|
FileUtils.cp_r "#{HOMEBREW_REPOSITORY}/Library/Taps", "#{new_homebrew_repository}/Library/Taps"
|
||||||
|
end
|
||||||
|
|
||||||
|
unremovable_paths = []
|
||||||
|
extra_remove_paths = [".git", "Library/Locks", "Library/Taps",
|
||||||
|
"Library/Homebrew/cask", "Library/Homebrew/test"]
|
||||||
|
(repo_files + extra_remove_paths).each do |file|
|
||||||
|
path = Pathname.new "#{HOMEBREW_REPOSITORY}/#{file}"
|
||||||
|
begin
|
||||||
|
FileUtils.rm_rf path
|
||||||
|
rescue Errno::EACCES
|
||||||
|
unremovable_paths << path
|
||||||
|
end
|
||||||
|
quiet_system "rmdir", "-p", path.parent if path.parent.exist?
|
||||||
|
end
|
||||||
|
|
||||||
|
unless unremovable_paths.empty?
|
||||||
|
ofail <<~EOS
|
||||||
|
Could not remove old HOMEBREW_REPOSITORY paths!
|
||||||
|
Please do this manually with:
|
||||||
|
sudo rm -rf #{unremovable_paths.join " "}
|
||||||
|
EOS
|
||||||
|
end
|
||||||
|
|
||||||
|
(Keg::ALL_TOP_LEVEL_DIRECTORIES + ["Cellar"]).each do |dir|
|
||||||
|
FileUtils.mkdir_p "#{HOMEBREW_PREFIX}/#{dir}"
|
||||||
|
end
|
||||||
|
|
||||||
|
src = Pathname.new("#{new_homebrew_repository}/bin/brew")
|
||||||
|
dst = Pathname.new("#{HOMEBREW_PREFIX}/bin/brew")
|
||||||
|
begin
|
||||||
|
FileUtils.ln_s(src.relative_path_from(dst.parent), dst)
|
||||||
|
rescue Errno::EACCES, Errno::ENOENT
|
||||||
|
ofail <<~EOS
|
||||||
|
Could not create symlink at #{dst}!
|
||||||
|
Please do this manually with:
|
||||||
|
sudo ln -sf #{src} #{dst}
|
||||||
|
sudo chown $(whoami) #{dst}
|
||||||
|
EOS
|
||||||
|
end
|
||||||
|
|
||||||
|
link_completions_manpages_and_docs(new_homebrew_repository)
|
||||||
|
|
||||||
|
ohai "Migrated HOMEBREW_REPOSITORY to #{new_homebrew_repository}!"
|
||||||
|
puts <<~EOS
|
||||||
|
Homebrew no longer needs to have ownership of /usr/local. If you wish you can
|
||||||
|
return /usr/local to its default ownership with:
|
||||||
|
sudo chown root:wheel #{HOMEBREW_PREFIX}
|
||||||
|
EOS
|
||||||
|
rescue => e
|
||||||
|
ofail <<~EOS
|
||||||
|
#{Tty.bold}Failed to migrate HOMEBREW_REPOSITORY to #{new_homebrew_repository}!#{Tty.reset}
|
||||||
|
The error was:
|
||||||
|
#{e}
|
||||||
|
Please try to resolve this error yourself and then run `brew update` again to
|
||||||
|
complete the migration. If you need help please +1 an existing error or comment
|
||||||
|
with your new error in issue:
|
||||||
|
#{Formatter.url("https://github.com/Homebrew/brew/issues/987")}
|
||||||
|
EOS
|
||||||
|
$stderr.puts e.backtrace
|
||||||
|
end
|
||||||
|
end
|
||||||
@ -498,45 +498,6 @@ def truncate_text_to_approximate_size(s, max_bytes, options = {})
|
|||||||
out
|
out
|
||||||
end
|
end
|
||||||
|
|
||||||
def migrate_legacy_keg_symlinks_if_necessary
|
|
||||||
legacy_linked_kegs = HOMEBREW_LIBRARY/"LinkedKegs"
|
|
||||||
return unless legacy_linked_kegs.directory?
|
|
||||||
|
|
||||||
HOMEBREW_LINKED_KEGS.mkpath unless legacy_linked_kegs.children.empty?
|
|
||||||
legacy_linked_kegs.children.each do |link|
|
|
||||||
name = link.basename.to_s
|
|
||||||
src = begin
|
|
||||||
link.realpath
|
|
||||||
rescue Errno::ENOENT
|
|
||||||
begin
|
|
||||||
(HOMEBREW_PREFIX/"opt/#{name}").realpath
|
|
||||||
rescue Errno::ENOENT
|
|
||||||
begin
|
|
||||||
Formulary.factory(name).installed_prefix
|
|
||||||
rescue
|
|
||||||
next
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
dst = HOMEBREW_LINKED_KEGS/name
|
|
||||||
dst.unlink if dst.exist?
|
|
||||||
FileUtils.ln_sf(src.relative_path_from(dst.parent), dst)
|
|
||||||
end
|
|
||||||
FileUtils.rm_rf legacy_linked_kegs
|
|
||||||
|
|
||||||
legacy_pinned_kegs = HOMEBREW_LIBRARY/"PinnedKegs"
|
|
||||||
return unless legacy_pinned_kegs.directory?
|
|
||||||
|
|
||||||
HOMEBREW_PINNED_KEGS.mkpath unless legacy_pinned_kegs.children.empty?
|
|
||||||
legacy_pinned_kegs.children.each do |link|
|
|
||||||
name = link.basename.to_s
|
|
||||||
src = link.realpath
|
|
||||||
dst = HOMEBREW_PINNED_KEGS/name
|
|
||||||
FileUtils.ln_sf(src.relative_path_from(dst.parent), dst)
|
|
||||||
end
|
|
||||||
FileUtils.rm_rf legacy_pinned_kegs
|
|
||||||
end
|
|
||||||
|
|
||||||
# Calls the given block with the passed environment variables
|
# Calls the given block with the passed environment variables
|
||||||
# added to ENV, then restores ENV afterwards.
|
# added to ENV, then restores ENV afterwards.
|
||||||
# Example:
|
# Example:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user