From 19f27f9a20665df72efb5d6cd4dcf308d1c26801 Mon Sep 17 00:00:00 2001 From: Bo Anderson Date: Fri, 10 Nov 2023 17:17:24 +0000 Subject: [PATCH] Store and use revision, where possible, in tab runtime dependencies. Let's start storing `revision` and `pkg_version` for tab runtime dependencies and use them when available. When the `revision` is not available, use a conservative approach to deciding whether dependencies need to be upgrade. Co-authored-by: Mike McQuaid --- Library/Homebrew/dependency.rb | 29 +++++++++++++++++++------ Library/Homebrew/formula_installer.rb | 7 ++++-- Library/Homebrew/tab.rb | 2 ++ Library/Homebrew/test/tab_spec.rb | 10 ++++++--- Library/Homebrew/upgrade.rb | 31 +++++++++++++++++++++------ 5 files changed, 61 insertions(+), 18 deletions(-) diff --git a/Library/Homebrew/dependency.rb b/Library/Homebrew/dependency.rb index 9551cded23..15e50ab335 100644 --- a/Library/Homebrew/dependency.rb +++ b/Library/Homebrew/dependency.rb @@ -46,7 +46,8 @@ class Dependency formula end - def installed?(minimum_version: nil) + sig { params(minimum_version: T.nilable(Version), minimum_revision: T.nilable(Integer)).returns(T::Boolean) } + def installed?(minimum_version: nil, minimum_revision: nil) formula = begin to_formula rescue FormulaUnavailableError @@ -55,14 +56,29 @@ class Dependency return false unless formula if minimum_version.present? - formula.any_version_installed? && (formula.any_installed_version.version >= minimum_version) + installed_version = formula.any_installed_version + return false unless installed_version + + # Tabs prior to 4.1.18 did not have revision or pkg_version fields. + # As a result, we have to be more conversative when we do not have + # a minimum revision from the tab and assume that if the formula has a + # the same version and a non-zero revision that it needs upgraded. + if minimum_revision.present? + minimum_pkg_version = PkgVersion.new(minimum_version, minimum_revision) + installed_version >= minimum_pkg_version + elsif installed_version.version == minimum_version + formula.revision.zero? + else + installed_version.version > minimum_version + end else formula.latest_version_installed? end end - def satisfied?(inherited_options = [], minimum_version: nil) - installed?(minimum_version: minimum_version) && missing_options(inherited_options).empty? + def satisfied?(inherited_options = [], minimum_version: nil, minimum_revision: nil) + installed?(minimum_version: minimum_version, minimum_revision: minimum_revision) && + missing_options(inherited_options).empty? end def missing_options(inherited_options) @@ -248,8 +264,9 @@ class UsesFromMacOSDependency < Dependency [name, tags, bounds].hash end - def installed?(minimum_version: nil) - use_macos_install? || super(minimum_version: minimum_version) + sig { params(minimum_version: T.nilable(Version), minimum_revision: T.nilable(Integer)).returns(T::Boolean) } + def installed?(minimum_version: nil, minimum_revision: nil) + use_macos_install? || super(minimum_version: minimum_version, minimum_revision: minimum_revision) end sig { returns(T::Boolean) } diff --git a/Library/Homebrew/formula_installer.rb b/Library/Homebrew/formula_installer.rb index 41ec3200b5..a5972f45db 100644 --- a/Library/Homebrew/formula_installer.rb +++ b/Library/Homebrew/formula_installer.rb @@ -616,11 +616,14 @@ on_request: installed_on_request?, options: options) keep_build_test ||= dep.build? && !install_bottle_for?(dependent, build) && (formula.head? || !dependent.latest_version_installed?) - bottle_runtime_version = @bottle_tab_runtime_dependencies.dig(dep.name, "version") + bottle_runtime_version = @bottle_tab_runtime_dependencies.dig(dep.name, "version").presence + bottle_runtime_version = Version.new(bottle_runtime_version) if bottle_runtime_version + bottle_runtime_revision = @bottle_tab_runtime_dependencies.dig(dep.name, "revision") if dep.prune_from_option?(build) || ((dep.build? || dep.test?) && !keep_build_test) Dependency.prune - elsif dep.satisfied?(inherited_options[dep.name], minimum_version: bottle_runtime_version) + elsif dep.satisfied?(inherited_options[dep.name], minimum_version: bottle_runtime_version, + minimum_revision: bottle_runtime_revision) Dependency.skip end end diff --git a/Library/Homebrew/tab.rb b/Library/Homebrew/tab.rb index 40866bb215..4070738df6 100644 --- a/Library/Homebrew/tab.rb +++ b/Library/Homebrew/tab.rb @@ -226,6 +226,8 @@ class Tab { "full_name" => f.full_name, "version" => f.version.to_s, + "revision" => f.revision, + "pkg_version" => f.pkg_version.to_s, "declared_directly" => formula.deps.include?(dep), } end diff --git a/Library/Homebrew/test/tab_spec.rb b/Library/Homebrew/test/tab_spec.rb index d31bf0d27b..f7f25ab499 100644 --- a/Library/Homebrew/test/tab_spec.rb +++ b/Library/Homebrew/test/tab_spec.rb @@ -141,7 +141,8 @@ describe Tab do tab.homebrew_version = "1.1.6" tab.runtime_dependencies = runtime_deps_hash expect(tab.runtime_dependencies).to eql( - [{ "full_name" => "foo", "version" => "1.0", "declared_directly" => false }], + [{ "full_name" => "foo", "version" => "1.0", "revision" => 0, "pkg_version" => "1.0", +"declared_directly" => false }], ) end @@ -255,6 +256,7 @@ describe Tab do tap = Tap.new("user", "repo") from_tap = formula("from_tap", path: tap.path/"Formula/from_tap.rb") do url "from_tap-1.0" + revision 1 end stub_formula_loader from_tap @@ -266,8 +268,10 @@ describe Tab do tab = described_class.create(f, compiler, stdlib) runtime_dependencies = [ - { "full_name" => "bar", "version" => "2.0", "declared_directly" => true }, - { "full_name" => "user/repo/from_tap", "version" => "1.0", "declared_directly" => true }, + { "full_name" => "bar", "version" => "2.0", "revision" => 0, "pkg_version" => "2.0", +"declared_directly" => true }, + { "full_name" => "user/repo/from_tap", "version" => "1.0", "revision" => 1, "pkg_version" => "1.0_1", +"declared_directly" => true }, ] expect(tab.runtime_dependencies).to eq(runtime_dependencies) diff --git a/Library/Homebrew/upgrade.rb b/Library/Homebrew/upgrade.rb index 1f29b0c487..04c9bee2fb 100644 --- a/Library/Homebrew/upgrade.rb +++ b/Library/Homebrew/upgrade.rb @@ -72,17 +72,34 @@ module Homebrew unless dry_run fi.prelude - # Don't need to install this bottle if all the runtime dependencies - # are already satisfied. - next if dependents && fi.bottle_tab_runtime_dependencies.presence&.none? do |dependency, hash| - installed_version = begin - Formula[dependency].any_installed_version + # Don't need to install this bottle if all of the runtime + # dependencies have the same or newer version already installed. + next if dependents && fi.bottle_tab_runtime_dependencies.presence&.all? do |dependency, hash| + dependency_formula = begin + Formula[dependency] rescue FormulaUnavailableError nil end - next true unless installed_version + next false if dependency_formula.nil? - Version.new(hash["version"]) > installed_version.version + installed_version = dependency_formula.any_installed_version + next false unless installed_version + + next false if hash["version"].blank? + + # Tabs prior to 4.1.18 did not have revision or pkg_version fields. + # As a result, we have to be more conversative when we do not have + # a revision in the tab and assume that if the formula has a + # the same version and a non-zero revision that it needs upgraded. + tab_version = Version.new(hash["version"]) + if hash["revision"].present? + tab_pkg_version = PkgVersion.new(tab_version, hash["revision"]) + installed_version >= tab_pkg_version + elsif installed_version.version == tab_version + dependency_formula.revision.zero? + else + installed_version.version > tab_version + end end fi.fetch