reapack-index 1.0beta3 → 1.0beta4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|