Skip to content

Commit d515525

Browse files
committed
Fix audit version_scheme and revision checks.
Another attempt at fixing `brew audit` issues around detecting `revision` and `version_scheme` changes correctly. First done in #1754 and #2086 (reverted in #2099 and #2100). To ease future debugging a `ph` helper has been added to print a hash and a series of RSpec tests to verify that the `revision`, `version_scheme` and `version` formula version audits behave as expected. Fixes #1731.
1 parent f3dc06a commit d515525

File tree

6 files changed

+297
-45
lines changed

6 files changed

+297
-45
lines changed

Library/Homebrew/dev-cmd/audit.rb

Lines changed: 52 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -827,51 +827,71 @@ def audit_revision_and_version_scheme
827827
return unless formula.tap.git? # git log is required
828828
return if @new_formula
829829

830-
fv = FormulaVersions.new(formula, max_depth: 1)
830+
fv = FormulaVersions.new(formula)
831831
attributes = [:revision, :version_scheme]
832-
833832
attributes_map = fv.version_attributes_map(attributes, "origin/master")
834833

835-
attributes.each do |attribute|
836-
stable_attribute_map = attributes_map[attribute][:stable]
837-
next if stable_attribute_map.nil? || stable_attribute_map.empty?
838-
839-
attributes_for_version = stable_attribute_map[formula.version]
840-
next if attributes_for_version.nil? || attributes_for_version.empty?
841-
842-
old_attribute = formula.send(attribute)
843-
max_attribute = attributes_for_version.max
844-
if max_attribute && old_attribute < max_attribute
845-
problem "#{attribute} should not decrease (from #{max_attribute} to #{old_attribute})"
846-
end
847-
end
848-
834+
current_version_scheme = formula.version_scheme
849835
[:stable, :devel].each do |spec|
850836
spec_version_scheme_map = attributes_map[:version_scheme][spec]
851837
next if spec_version_scheme_map.nil? || spec_version_scheme_map.empty?
852838

853-
max_version_scheme = spec_version_scheme_map.values.flatten.max
839+
version_schemes = spec_version_scheme_map.values.flatten
840+
max_version_scheme = version_schemes.max
854841
max_version = spec_version_scheme_map.select do |_, version_scheme|
855842
version_scheme.first == max_version_scheme
856843
end.keys.max
857844

858-
formula_spec = formula.send(spec)
859-
next if formula_spec.nil?
845+
if max_version_scheme && current_version_scheme < max_version_scheme
846+
problem "version_scheme should not decrease (from #{max_version_scheme} to #{current_version_scheme})"
847+
end
860848

861-
if max_version && formula_spec.version < max_version
862-
problem "#{spec} version should not decrease (from #{max_version} to #{formula_spec.version})"
849+
if max_version_scheme && current_version_scheme >= max_version_scheme &&
850+
current_version_scheme > 1 &&
851+
!version_schemes.include?(current_version_scheme - 1)
852+
problem "version_schemes should only increment by 1"
863853
end
854+
855+
formula_spec = formula.send(spec)
856+
next unless formula_spec
857+
858+
spec_version = formula_spec.version
859+
next unless max_version
860+
next if spec_version >= max_version
861+
862+
above_max_version_scheme = current_version_scheme > max_version_scheme
863+
map_includes_version = spec_version_scheme_map.keys.include?(spec_version)
864+
next if !current_version_scheme.zero? &&
865+
(above_max_version_scheme || map_includes_version)
866+
problem "#{spec} version should not decrease (from #{max_version} to #{spec_version})"
864867
end
865868

866-
return if formula.revision.zero?
869+
current_revision = formula.revision
867870
if formula.stable
868-
revision_map = attributes_map[:revision][:stable]
869-
stable_revisions = revision_map[formula.stable.version] if revision_map
870-
if !stable_revisions || stable_revisions.empty?
871-
problem "'revision #{formula.revision}' should be removed"
871+
if revision_map = attributes_map[:revision][:stable]
872+
if !revision_map.nil? && !revision_map.empty?
873+
stable_revisions = revision_map[formula.stable.version]
874+
stable_revisions ||= []
875+
current_revision = formula.revision
876+
max_revision = stable_revisions.max || 0
877+
878+
if current_revision < max_revision
879+
problem "revision should not decrease (from #{max_revision} to #{current_revision})"
880+
end
881+
882+
stable_revisions -= [formula.revision]
883+
if !current_revision.zero? && stable_revisions.empty? &&
884+
revision_map.keys.length > 1
885+
problem "'revision #{formula.revision}' should be removed"
886+
elsif current_revision > 1 &&
887+
current_revision != max_revision &&
888+
!stable_revisions.include?(current_revision - 1)
889+
problem "revisions should only increment by 1"
890+
end
891+
end
872892
end
873-
else # head/devel-only formula
874-
problem "'revision #{formula.revision}' should be removed"
893+
elsif !current_revision.zero? # head/devel-only formula
894+
problem "'revision #{current_revision}' should be removed"
875895
end
876896
end
877897

@@ -1211,6 +1231,10 @@ def line_problems(line, _lineno)
12111231
end
12121232
end
12131233

1234+
if line =~ /((revision|version_scheme)\s+0)/
1235+
problem "'#{$1}' should be removed"
1236+
end
1237+
12141238
return unless @strict
12151239

12161240
problem "`#{$1}` in formulae is deprecated" if line =~ /(env :(std|userpaths))/

Library/Homebrew/formula_versions.rb

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,22 @@ class FormulaVersions
77
ErrorDuringExecution, LoadError, MethodDeprecatedError
88
].freeze
99

10+
MAX_VERSIONS_DEPTH = 2
11+
1012
attr_reader :name, :path, :repository, :entry_name
1113

12-
def initialize(formula, options = {})
14+
def initialize(formula)
1315
@name = formula.name
1416
@path = formula.path
1517
@repository = formula.tap.path
1618
@entry_name = @path.relative_path_from(repository).to_s
17-
@max_depth = options[:max_depth]
19+
@current_formula = formula
1820
end
1921

2022
def rev_list(branch)
2123
repository.cd do
22-
depth = 0
2324
Utils.popen_read("git", "rev-list", "--abbrev-commit", "--remove-empty", branch, "--", entry_name) do |io|
24-
yield io.readline.chomp until io.eof? || (@max_depth && (depth += 1) > @max_depth)
25+
yield io.readline.chomp until io.eof?
2526
end
2627
end
2728
end
@@ -49,11 +50,15 @@ def formula_at_revision(rev)
4950

5051
def bottle_version_map(branch)
5152
map = Hash.new { |h, k| h[k] = [] }
53+
54+
versions_seen = 0
5255
rev_list(branch) do |rev|
5356
formula_at_revision(rev) do |f|
5457
bottle = f.bottle_specification
5558
map[f.pkg_version] << bottle.rebuild unless bottle.checksums.empty?
59+
versions_seen = (map.keys + [f.pkg_version]).uniq.length
5660
end
61+
return map if versions_seen > MAX_VERSIONS_DEPTH
5762
end
5863
map
5964
end
@@ -62,27 +67,35 @@ def version_attributes_map(attributes, branch)
6267
attributes_map = {}
6368
return attributes_map if attributes.empty?
6469

65-
attributes.each do |attribute|
66-
attributes_map[attribute] ||= {}
67-
end
68-
70+
stable_versions_seen = 0
6971
rev_list(branch) do |rev|
7072
formula_at_revision(rev) do |f|
7173
attributes.each do |attribute|
74+
attributes_map[attribute] ||= {}
7275
map = attributes_map[attribute]
73-
if f.stable
74-
map[:stable] ||= {}
75-
map[:stable][f.stable.version] ||= []
76-
map[:stable][f.stable.version] << f.send(attribute)
77-
end
78-
next unless f.devel
79-
map[:devel] ||= {}
80-
map[:devel][f.devel.version] ||= []
81-
map[:devel][f.devel.version] << f.send(attribute)
76+
set_attribute_map(map, f, attribute)
77+
78+
stable_keys_length = (map[:stable].keys + [f.version]).uniq.length
79+
stable_versions_seen = [stable_versions_seen, stable_keys_length].max
8280
end
8381
end
82+
break if stable_versions_seen > MAX_VERSIONS_DEPTH
8483
end
8584

8685
attributes_map
8786
end
87+
88+
private
89+
90+
def set_attribute_map(map, f, attribute)
91+
if f.stable
92+
map[:stable] ||= {}
93+
map[:stable][f.stable.version] ||= []
94+
map[:stable][f.stable.version] << f.send(attribute)
95+
end
96+
return unless f.devel
97+
map[:devel] ||= {}
98+
map[:devel][f.devel.version] ||= []
99+
map[:devel][f.devel.version] << f.send(attribute)
100+
end
88101
end

0 commit comments

Comments
 (0)