reapack-index 1.0beta3 → 1.0beta4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +1 -1
- data/README.md +11 -113
- data/Rakefile +1 -1
- data/lib/reapack/index.rb +159 -109
- data/lib/reapack/index/cdetector.rb +120 -0
- data/lib/reapack/index/cli.rb +127 -161
- data/lib/reapack/index/cli/options.rb +29 -10
- data/lib/reapack/index/gem_version.rb +1 -1
- data/lib/reapack/index/git.rb +189 -0
- data/lib/reapack/index/metadata.rb +2 -2
- data/lib/reapack/index/named_node.rb +17 -12
- data/lib/reapack/index/package.rb +16 -9
- data/lib/reapack/index/provides.rb +46 -0
- data/lib/reapack/index/source.rb +59 -0
- data/lib/reapack/index/version.rb +3 -52
- data/reapack-index.gemspec +7 -6
- data/setup/reapack-index.nsi +9 -5
- data/test/cli/test_check.rb +190 -0
- data/test/cli/test_metadata.rb +194 -0
- data/test/cli/test_scan.rb +326 -0
- data/test/data/index.xml +1 -1
- data/test/helper.rb +90 -1
- data/test/index/test_metadata.rb +111 -0
- data/test/index/test_provides.rb +302 -0
- data/test/index/test_scan.rb +333 -0
- data/test/test_cdetector.rb +214 -0
- data/test/test_cli.rb +69 -797
- data/test/test_git.rb +139 -0
- data/test/test_index.rb +109 -703
- data/test/test_metadata.rb +2 -2
- data/test/test_named_node.rb +50 -42
- data/test/test_package.rb +15 -26
- data/test/test_provides.rb +68 -0
- data/test/test_source.rb +115 -0
- data/test/test_version.rb +10 -63
- metadata +33 -22
@@ -0,0 +1,120 @@
|
|
1
|
+
class ReaPack::Index
|
2
|
+
class ConflictDetector
|
3
|
+
Entry = Struct.new :key, :platform, :file
|
4
|
+
|
5
|
+
class Selector
|
6
|
+
def initialize(bucket, key, cdetector)
|
7
|
+
@bucket, @key, @cdetector = bucket, key, cdetector
|
8
|
+
@entries = @cdetector.bucket bucket
|
9
|
+
end
|
10
|
+
|
11
|
+
def push(platform, file)
|
12
|
+
@entries << Entry.new(@key, platform, file).freeze
|
13
|
+
end
|
14
|
+
|
15
|
+
def clear
|
16
|
+
@entries.reject! {|e| e.key == @key }
|
17
|
+
end
|
18
|
+
|
19
|
+
def resolve
|
20
|
+
@cdetector.resolve @bucket, @key
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize
|
25
|
+
@buckets = {}
|
26
|
+
end
|
27
|
+
|
28
|
+
def initialize_clone(other)
|
29
|
+
super
|
30
|
+
other.instance_variable_set :@buckets,
|
31
|
+
Hash[@buckets.map {|k, v| [k, v.clone] }]
|
32
|
+
end
|
33
|
+
|
34
|
+
def [](bucket, key)
|
35
|
+
Selector.new bucket, key, self
|
36
|
+
end
|
37
|
+
|
38
|
+
def bucket(name)
|
39
|
+
@buckets[name] ||= []
|
40
|
+
end
|
41
|
+
|
42
|
+
def resolve(bucket, key = nil)
|
43
|
+
return unless bucket = @buckets[bucket]
|
44
|
+
|
45
|
+
dups = bucket.group_by {|e| e.file }.values.select {|a| a.size > 1 }
|
46
|
+
|
47
|
+
errors = dups.map {|a|
|
48
|
+
packages = a.map {|e| e.key }.uniq
|
49
|
+
next if key && !packages.include?(key)
|
50
|
+
|
51
|
+
if packages.size == 1 || !key
|
52
|
+
original = sort(a)[1]
|
53
|
+
msg = "duplicate file '#{original.file}'"
|
54
|
+
else
|
55
|
+
original = sort(a.select {|e| e.key != key }).first
|
56
|
+
msg = "'#{original.file}' conflicts with '#{original.key}'"
|
57
|
+
end
|
58
|
+
|
59
|
+
platforms = a.map {|e| e.platform }.uniq
|
60
|
+
|
61
|
+
if platforms.size > 1
|
62
|
+
# check platform inheritance
|
63
|
+
platforms.any? {|p|
|
64
|
+
loop do
|
65
|
+
p = Source::PLATFORMS[p] or break false
|
66
|
+
break true if platforms.include? p
|
67
|
+
end
|
68
|
+
} or next
|
69
|
+
end
|
70
|
+
|
71
|
+
platform = original.platform
|
72
|
+
platform == :all ? msg : "#{msg} on #{platform}"
|
73
|
+
}.compact
|
74
|
+
|
75
|
+
errors unless errors.empty?
|
76
|
+
end
|
77
|
+
|
78
|
+
def load_xml(node)
|
79
|
+
Category.find_all(node).each {|cat|
|
80
|
+
Package.find_all(cat.node).each {|pkg|
|
81
|
+
pkgroot = File.join(cat.name, pkg.name)
|
82
|
+
|
83
|
+
pkg.versions.last&.children(Source::TAG)&.each {|src|
|
84
|
+
entry = Entry.new pkgroot, src[:platform].to_sym
|
85
|
+
|
86
|
+
if src[:file]
|
87
|
+
entry.file = ReaPack::Index.expand(src[:file], cat.name)
|
88
|
+
else
|
89
|
+
entry.file = pkgroot
|
90
|
+
end
|
91
|
+
|
92
|
+
bucket(pkg.type) << entry
|
93
|
+
}
|
94
|
+
}
|
95
|
+
}
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
def sort(set)
|
100
|
+
sorted = set.sort_by {|e| levels[e.platform] }
|
101
|
+
sorted.sort_by! {|e| Source::PLATFORMS.keys.index e.platform }
|
102
|
+
end
|
103
|
+
|
104
|
+
def levels
|
105
|
+
@@levels ||= begin
|
106
|
+
Hash[Source::PLATFORMS.map {|name, parent|
|
107
|
+
levels = 0
|
108
|
+
|
109
|
+
loop do
|
110
|
+
break unless parent
|
111
|
+
levels += 1
|
112
|
+
parent = Source::PLATFORMS[parent]
|
113
|
+
end
|
114
|
+
|
115
|
+
[name, levels]
|
116
|
+
}]
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
data/lib/reapack/index/cli.rb
CHANGED
@@ -1,17 +1,24 @@
|
|
1
1
|
class ReaPack::Index::CLI
|
2
|
+
attr_reader :index
|
3
|
+
|
2
4
|
def initialize(argv = [])
|
3
5
|
@opts = parse_options(argv)
|
4
6
|
path = argv.last || Dir.pwd
|
5
7
|
|
6
8
|
return unless @exit.nil?
|
7
9
|
|
8
|
-
@git =
|
10
|
+
@git = ReaPack::Index::Git.new path
|
9
11
|
@opts = parse_options(read_config).merge @opts unless @opts[:noconfig]
|
10
12
|
|
11
13
|
@opts = DEFAULTS.merge @opts
|
14
|
+
return unless @exit.nil?
|
15
|
+
|
16
|
+
log Hash[@opts.sort].inspect
|
12
17
|
|
13
|
-
|
14
|
-
|
18
|
+
@index = ReaPack::Index.new expand_path(@opts[:output])
|
19
|
+
@index.amend = @opts[:amend]
|
20
|
+
set_url_template
|
21
|
+
rescue Rugged::OSError, Rugged::RepositoryError, ReaPack::Index::Error => e
|
15
22
|
$stderr.puts e.message
|
16
23
|
@exit = false
|
17
24
|
end
|
@@ -19,11 +26,8 @@ class ReaPack::Index::CLI
|
|
19
26
|
def run
|
20
27
|
return @exit unless @exit.nil?
|
21
28
|
|
22
|
-
@db = ReaPack::Index.new expand_path(@opts[:output])
|
23
|
-
@db.amend = @opts[:amend]
|
24
|
-
|
25
29
|
if @opts[:check]
|
26
|
-
return
|
30
|
+
return do_check
|
27
31
|
end
|
28
32
|
|
29
33
|
if @opts[:lslinks]
|
@@ -32,135 +36,101 @@ class ReaPack::Index::CLI
|
|
32
36
|
end
|
33
37
|
|
34
38
|
if @opts[:dump_about]
|
35
|
-
print @
|
39
|
+
print @index.description
|
36
40
|
return true
|
37
41
|
end
|
38
42
|
|
39
|
-
|
40
|
-
tpl = @opts[:url_template]
|
41
|
-
is_custom = tpl != DEFAULTS[:url_template]
|
43
|
+
do_name; do_about; eval_links; do_scan
|
42
44
|
|
43
|
-
|
44
|
-
rescue ReaPack::Index::Error => e
|
45
|
-
warn '--url-template: ' + e.message if is_custom
|
46
|
-
end
|
47
|
-
|
48
|
-
do_name; do_about; eval_links; scan_commits
|
49
|
-
|
50
|
-
unless @db.modified?
|
45
|
+
unless @index.modified?
|
51
46
|
$stderr.puts 'Nothing to do!' unless @opts[:quiet]
|
52
|
-
return
|
47
|
+
return success_code
|
53
48
|
end
|
54
49
|
|
55
50
|
# changelog will be cleared by Index#write!
|
56
|
-
changelog = @
|
51
|
+
changelog = @index.changelog
|
57
52
|
puts changelog unless @opts[:quiet]
|
58
53
|
|
59
|
-
@
|
54
|
+
@index.write!
|
60
55
|
commit changelog
|
61
56
|
|
62
|
-
|
57
|
+
success_code
|
63
58
|
end
|
64
59
|
|
65
60
|
private
|
66
|
-
def
|
61
|
+
def success_code
|
62
|
+
@exit.nil? ? true : @exit
|
63
|
+
end
|
64
|
+
|
65
|
+
def set_url_template
|
66
|
+
tpl = @opts[:url_template]
|
67
|
+
is_custom = tpl != DEFAULTS[:url_template]
|
68
|
+
|
69
|
+
@index.url_template = is_custom ? tpl : @git.guess_url_template
|
70
|
+
rescue ReaPack::Index::Error => e
|
71
|
+
warn '--url-template: ' + e.message if is_custom
|
72
|
+
end
|
73
|
+
|
74
|
+
def do_scan
|
67
75
|
if @git.empty?
|
68
76
|
warn 'The current branch does not contains any commit.'
|
69
77
|
return
|
70
78
|
end
|
71
79
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
80
|
+
if @opts[:scan].empty?
|
81
|
+
commits = @git.commits_since @index.commit.to_s
|
82
|
+
else
|
83
|
+
commits = @opts[:scan].map {|hash|
|
84
|
+
@git.get_commit hash or begin
|
85
|
+
$stderr.puts '--scan: bad revision: %s' % @opts[:scan]
|
86
|
+
@exit = false
|
87
|
+
nil
|
88
|
+
end
|
89
|
+
}.compact
|
79
90
|
end
|
80
91
|
|
81
|
-
commits = walker.each.to_a
|
82
|
-
|
83
|
-
@done, @total = 0, commits.size
|
84
|
-
|
85
92
|
unless commits.empty?
|
86
|
-
|
87
|
-
|
88
|
-
|
93
|
+
progress_wrapper commits.size do
|
94
|
+
commits.each {|commit| process_commit commit }
|
95
|
+
end
|
89
96
|
end
|
90
97
|
end
|
91
98
|
|
92
|
-
def
|
99
|
+
def process_commit(commit)
|
93
100
|
if @opts[:verbose]
|
94
|
-
|
95
|
-
message = commit.message.lines.first.chomp
|
96
|
-
log "processing %s: %s" % [sha, message]
|
101
|
+
log 'processing %s: %s' % [commit.short_id, commit.summary]
|
97
102
|
end
|
98
103
|
|
99
|
-
@
|
100
|
-
@
|
101
|
-
@
|
104
|
+
@index.commit = commit.id
|
105
|
+
@index.time = commit.time
|
106
|
+
@index.files = commit.filelist
|
102
107
|
|
103
|
-
|
104
|
-
|
105
|
-
if parent
|
106
|
-
diff = parent.diff commit.oid
|
107
|
-
else
|
108
|
-
diff = commit.diff
|
109
|
-
end
|
110
|
-
|
111
|
-
diff.each_delta {|delta| index delta, parent.nil? }
|
108
|
+
commit.each_diff {|diff| process_diff diff }
|
112
109
|
ensure
|
113
|
-
|
114
|
-
print_progress
|
110
|
+
bump_progress
|
115
111
|
end
|
116
112
|
|
117
|
-
def
|
118
|
-
if
|
119
|
-
|
120
|
-
file = delta.old_file
|
121
|
-
else
|
122
|
-
status = delta.status
|
123
|
-
file = delta.new_file
|
124
|
-
end
|
113
|
+
def process_diff(diff)
|
114
|
+
return if ignored? expand_path(diff.file)
|
115
|
+
return unless ReaPack::Index.type_of diff.file
|
125
116
|
|
126
|
-
|
127
|
-
return unless ReaPack::Index.type_of file[:path]
|
117
|
+
log "-> indexing #{diff.status} file #{diff.file}"
|
128
118
|
|
129
|
-
|
130
|
-
|
131
|
-
if status == :deleted
|
132
|
-
@db.remove file[:path]
|
119
|
+
if diff.status == :deleted
|
120
|
+
@index.remove diff.file
|
133
121
|
else
|
134
|
-
blob = @git.lookup file[:oid]
|
135
|
-
|
136
122
|
begin
|
137
|
-
@
|
123
|
+
@index.scan diff.file, diff.new_content
|
138
124
|
rescue ReaPack::Index::Error => e
|
139
|
-
warn "#{file
|
125
|
+
warn "#{diff.file}:\n#{indent e.message}"
|
140
126
|
end
|
141
127
|
end
|
142
128
|
end
|
143
129
|
|
144
|
-
def lsfiles(tree, base = String.new)
|
145
|
-
files = []
|
146
|
-
|
147
|
-
tree.each {|obj|
|
148
|
-
fullname = base.empty? ? obj[:name] : File.join(base, obj[:name])
|
149
|
-
case obj[:type]
|
150
|
-
when :blob
|
151
|
-
files << fullname
|
152
|
-
when :tree
|
153
|
-
files.concat lsfiles(@git.lookup(obj[:oid]), fullname)
|
154
|
-
end
|
155
|
-
}
|
156
|
-
|
157
|
-
files
|
158
|
-
end
|
159
|
-
|
160
130
|
def eval_links
|
161
131
|
Array(@opts[:links]).each {|link|
|
162
132
|
begin
|
163
|
-
@
|
133
|
+
@index.eval_link *link
|
164
134
|
rescue ReaPack::Index::Error => e
|
165
135
|
opt = case link.first
|
166
136
|
when :website
|
@@ -177,7 +147,7 @@ private
|
|
177
147
|
def print_links
|
178
148
|
ReaPack::Index::Link::VALID_TYPES.each {|type|
|
179
149
|
prefix = "[#{type}]".bold.light_black
|
180
|
-
@
|
150
|
+
@index.links(type).each {|link|
|
181
151
|
display = link.name == link.url ? link.url : '%s (%s)' % [link.name, link.url]
|
182
152
|
puts '%s %s' % [prefix, display]
|
183
153
|
}
|
@@ -185,74 +155,85 @@ private
|
|
185
155
|
end
|
186
156
|
|
187
157
|
def do_name
|
188
|
-
@
|
189
|
-
|
190
|
-
if @db.name.empty?
|
191
|
-
warn "The name of this index is unset. " \
|
192
|
-
"Run the following command with a name of your choice:" \
|
193
|
-
"\n #{$0} --name 'FooBar Scripts'"
|
194
|
-
end
|
158
|
+
@index.name = @opts[:name] if @opts[:name]
|
159
|
+
check_name
|
195
160
|
rescue ReaPack::Index::Error => e
|
196
161
|
warn '--name: ' + e.message
|
197
162
|
end
|
198
163
|
|
164
|
+
def check_name
|
165
|
+
if @index.name.empty?
|
166
|
+
warn 'This index is unnamed. ' \
|
167
|
+
'Run the following command to set a name of your choice:' \
|
168
|
+
"\n #{File.basename $0} --name 'FooBar Scripts'"
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
199
172
|
def do_about
|
200
173
|
path = @opts[:about]
|
201
174
|
|
202
175
|
unless path
|
203
|
-
@
|
176
|
+
@index.description = String.new if @opts[:rmabout]
|
204
177
|
return
|
205
178
|
end
|
206
179
|
|
207
180
|
log "converting #{path} into RTF..."
|
208
181
|
|
209
182
|
# look for the file in the working directory, not on the repository root
|
210
|
-
@
|
183
|
+
@index.description = File.read(path)
|
211
184
|
rescue Errno::ENOENT => e
|
212
185
|
warn '--about: ' + e.message.sub(' @ rb_sysopen', '')
|
213
186
|
rescue ReaPack::Index::Error => e
|
214
187
|
warn e.message
|
215
188
|
end
|
216
189
|
|
217
|
-
def
|
190
|
+
def do_check
|
191
|
+
check_name
|
192
|
+
|
193
|
+
@index.amend = true # enable checks for released versions as well
|
218
194
|
failures = []
|
219
|
-
root = @git.workdir
|
220
195
|
|
221
|
-
|
222
|
-
|
223
|
-
|
196
|
+
pkgs = Hash[Dir.glob("#{Regexp.quote(@git.path)}/**/*").sort.map {|abs|
|
197
|
+
rel = @git.relative_path abs
|
198
|
+
@index.files << rel
|
224
199
|
|
225
|
-
|
226
|
-
next if ignored? file
|
200
|
+
next if !File.file?(abs) || ignored?(abs) || !ReaPack::Index.is_package?(abs)
|
227
201
|
|
228
|
-
|
229
|
-
|
202
|
+
[abs, rel]
|
203
|
+
}.compact]
|
230
204
|
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
file[0..root.size-1] = ''
|
205
|
+
pkgs.each_pair {|abs, rel|
|
206
|
+
begin
|
207
|
+
@index.scan rel, File.read(abs)
|
235
208
|
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
209
|
+
if @opts[:verbose]
|
210
|
+
$stderr.puts '%s: passed' % rel
|
211
|
+
elsif !@opts[:quiet]
|
212
|
+
$stderr.print '.'
|
213
|
+
end
|
214
|
+
rescue ReaPack::Index::Error => e
|
215
|
+
if @opts[:verbose]
|
216
|
+
$stderr.puts '%s: failed' % rel
|
217
|
+
elsif !@opts[:quiet]
|
218
|
+
$stderr.print 'F'
|
219
|
+
end
|
220
|
+
|
221
|
+
failures << "%s failed:\n%s" % [rel, indent(e.message).yellow]
|
240
222
|
end
|
241
223
|
}
|
242
224
|
|
243
|
-
$stderr.puts "\n" unless @opts[:quiet]
|
225
|
+
$stderr.puts "\n" unless @opts[:quiet] || @opts[:verbose]
|
244
226
|
|
245
227
|
failures.each_with_index {|msg, index|
|
246
228
|
$stderr.puts unless @opts[:quiet] && index == 0
|
247
|
-
$stderr.puts msg
|
229
|
+
$stderr.puts '%d) %s' % [index + 1, msg]
|
248
230
|
}
|
249
231
|
|
250
232
|
unless @opts[:quiet]
|
251
233
|
$stderr.puts "\n"
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
failures.size, failures.size == 1 ? '' : 's'
|
234
|
+
$stderr.puts 'Finished checks for %d package%s with %d failure%s' % [
|
235
|
+
pkgs.size, pkgs.size == 1 ? '' : 's',
|
236
|
+
failures.size, failures.size == 1 ? '' : 's',
|
256
237
|
]
|
257
238
|
end
|
258
239
|
|
@@ -267,29 +248,7 @@ private
|
|
267
248
|
prompt 'Commit the new index?'
|
268
249
|
end
|
269
250
|
|
270
|
-
|
271
|
-
target = @git.empty? ? nil : @git.head.target
|
272
|
-
|
273
|
-
if target
|
274
|
-
old_index.read_tree target.tree
|
275
|
-
else
|
276
|
-
old_index.clear
|
277
|
-
end
|
278
|
-
|
279
|
-
root = Pathname.new @git.workdir
|
280
|
-
file = Pathname.new @db.path
|
281
|
-
|
282
|
-
index = @git.index
|
283
|
-
index.add file.relative_path_from(root).to_s
|
284
|
-
|
285
|
-
Rugged::Commit.create @git, \
|
286
|
-
tree: index.write_tree(@git),
|
287
|
-
message: "index: #{changelog}",
|
288
|
-
parents: [target].compact,
|
289
|
-
update_ref: 'HEAD'
|
290
|
-
|
291
|
-
old_index.write
|
292
|
-
|
251
|
+
@git.create_commit "index: #{changelog}", [@index.path]
|
293
252
|
$stderr.puts 'commit created'
|
294
253
|
end
|
295
254
|
|
@@ -316,7 +275,19 @@ private
|
|
316
275
|
@add_nl = false
|
317
276
|
end
|
318
277
|
|
319
|
-
$stderr.puts "
|
278
|
+
$stderr.puts "warning: #{line}".yellow
|
279
|
+
end
|
280
|
+
|
281
|
+
def progress_wrapper(total, &block)
|
282
|
+
@done, @total = 0, total
|
283
|
+
print_progress
|
284
|
+
block[]
|
285
|
+
$stderr.print "\n" if @add_nl
|
286
|
+
end
|
287
|
+
|
288
|
+
def bump_progress
|
289
|
+
@done += 1
|
290
|
+
print_progress
|
320
291
|
end
|
321
292
|
|
322
293
|
def print_progress
|
@@ -330,8 +301,10 @@ private
|
|
330
301
|
end
|
331
302
|
|
332
303
|
def ignored?(path)
|
304
|
+
path = path + '/'
|
305
|
+
|
333
306
|
@opts[:ignore].each {|pattern|
|
334
|
-
return true if path.start_with? pattern
|
307
|
+
return true if path.start_with? pattern + '/'
|
335
308
|
}
|
336
309
|
|
337
310
|
false
|
@@ -340,19 +313,12 @@ private
|
|
340
313
|
def expand_path(path)
|
341
314
|
# expand from the repository root or from the current directory if
|
342
315
|
# the repository is not yet initialized
|
343
|
-
File.expand_path path, @git ? @git.
|
316
|
+
File.expand_path path, @git ? @git.path : Dir.pwd
|
344
317
|
end
|
345
318
|
|
346
|
-
def
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
uri = Gitable::URI.parse remote.url
|
351
|
-
return unless uri.path =~ /\A\/?(?<user>[^\/]+)\/(?<repo>[^\/]+)\.git\Z/
|
352
|
-
|
353
|
-
tpl = uri.to_web_uri
|
354
|
-
tpl.path += '/raw/$commit/$path'
|
355
|
-
|
356
|
-
tpl.to_s
|
319
|
+
def indent(input)
|
320
|
+
output = String.new
|
321
|
+
input.lines {|l| output += "\x20\x20#{l}" }
|
322
|
+
output
|
357
323
|
end
|
358
324
|
end
|