-
-
Notifications
You must be signed in to change notification settings - Fork 277
/
brew_dumper.rb
138 lines (123 loc) 路 3.75 KB
/
brew_dumper.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
require "json"
require "tsort"
module Bundle
class BrewDumper
def self.reset!
@formulae = nil
@formula_aliases = nil
end
def self.formulae
@formulae ||= begin
@formulae = formulae_info
sort!
end
end
def self.dump
formulae.map do |f|
if f[:args].empty?
"brew '#{f[:full_name]}'"
else
args = f[:args].map { |arg| "'#{arg}'" }.sort.join(", ")
"brew '#{f[:full_name]}', args: [#{args}]"
end
end.join("\n")
end
def self.cask_requirements
formulae.map { |f| f[:requirements].map { |req| req["cask"] } }.flatten.compact.uniq
end
def self.formula_names
formulae.map { |f| f[:name] }
end
def self.formula_aliases
return @formula_aliases if @formula_aliases
@formula_aliases = {}
formulae.each do |f|
aliases = f[:aliases]
next if !aliases || aliases.empty?
if f[:full_name].include? "/" # tap formula
aliases.each do |a|
@formula_aliases[a] = f[:full_name]
@formula_aliases["#{f[:full_name].rpartition("/").first}/#{a}"] = f[:full_name]
end
else
aliases.each { |a| @formula_aliases[a] = f[:full_name] }
end
end
@formula_aliases
end
def self.formula_info(name)
@formula_info_name ||= {}
@formula_info_name[name] ||= begin
require "formula"
formula = Formula[name]
return {} unless formula
formula_inspector formula.to_hash
end
end
private
def self.formulae_info
require "formula"
Formula.installed.map { |f| formula_inspector f.to_hash }
end
def self.formula_inspector(f)
installed = f["installed"]
if f["linked_keg"].nil?
keg = installed.last
else
keg = installed.detect { |k| f["linked_keg"] == k["version"] }
end
if keg
args = keg["used_options"].to_a.map { |option| option.gsub(/^--/, "") }
args << "HEAD" if keg["version"].to_s.start_with?("HEAD")
args << "devel" if keg["version"].to_s.gsub(/_\d+$/, "") == f["versions"]["devel"]
args.uniq!
version = keg["version"]
else
args = []
version = nil
end
{
:name => f["name"],
:full_name => f["full_name"],
:aliases => f["aliases"],
:args => args,
:version => version,
:dependencies => f["dependencies"],
:requirements => f["requirements"],
:conflicts_with => f["conflicts_with"],
:pinned? => !!f["pinned"],
:outdated? => !!f["outdated"],
}
end
class Topo < Hash
include TSort
alias_method :tsort_each_node, :each_key
def tsort_each_child(node, &block)
fetch(node).each(&block)
end
end
def self.sort!
# Step 1: Sort by formula full name while putting tap formulae behind core formulae.
# So we can have a nicer output.
@formulae.sort! do |a, b|
if !a[:full_name].include?("/") && b[:full_name].include?("/")
-1
elsif a[:full_name].include?("/") && !b[:full_name].include?("/")
1
else
a[:full_name] <=> b[:full_name]
end
end
# Step 2: Sort by formula dependency topology.
topo = Topo.new
@formulae.each do |f|
deps = (f[:dependencies] + f[:requirements].map { |req| req["default_formula"] }.compact).uniq
topo[f[:full_name]] = deps.map do |dep|
ff = @formulae.detect { |formula| formula[:name] == dep || formula[:full_name] == dep }
ff[:full_name] if ff
end.compact
end
@formulae = topo.tsort.map { |name| @formulae.detect { |formula| formula[:full_name] == name } }
end
end
end