reapack-index 1.0beta4 → 1.0rc2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b08f343ff91fe1f1f543b9ac82de72f698404ce8
4
- data.tar.gz: 3b4f0c39955b88ba41f3bdf0759d7531d57e32ca
3
+ metadata.gz: 5cbcf419229c7231caca844f3b7f9e6800261b24
4
+ data.tar.gz: e3a8eabd17b759e7125f39276371383263130081
5
5
  SHA512:
6
- metadata.gz: 545316c4c749f62d19f68b1f3cb82f447a5d8b0b7d9aaaaba815258ee86424e8f6c0e06cfa2c9ef8876b4eb52074f5a5ac193a954070602d859d2c078a4803cc
7
- data.tar.gz: b0f6a34ed77612b71fe28a62cea6f0b6b0a7375506e8a2ecfc9ac90496172b2c30228a8212a064ae8c78e9d26103155ff493fab5d749b14ae4e04f2e4e17c5b7
6
+ metadata.gz: 7e04598f176942c766ade75dc7ec901793d3a58383e9e4e648d364f869d3798bdbc7b5239574ccc71b247bd115500c1c6c434de41353adc2611cbda46ed2bbf8
7
+ data.tar.gz: 42ebb6abbc5043e80b858d01ec64b94ec8c550e0a11a0c1daa964f3a7823b83a4282ef476d2c1e738b2242e781912d7cfe48bdcbba48bb943becb4bc3382a2f0
data/README.md CHANGED
@@ -10,9 +10,10 @@ Subproject: [https://github.com/cfillion/metaheader](https://github.com/cfillion
10
10
 
11
11
  ### Installation
12
12
 
13
- Ruby 2 need to be installed on your computer and ready to be used from a command
14
- prompt. Run the following command from a command prompt (cmd.exe, Terminal.app,
15
- XTerm) to install reapack-index on your computer:
13
+ Ruby v2.3 or newer must be installed on your computer in order to install
14
+ and use this software.
15
+ Run the following command from a command prompt (eg. cmd.exe, Terminal.app,
16
+ XTerm) to install reapack-index:
16
17
 
17
18
  ```
18
19
  gem install reapack-index
@@ -28,6 +29,7 @@ reapack-index [options] [path-to-your-repository]
28
29
  Modes:
29
30
  -c, --check Test every package including uncommited changes and exit
30
31
  -s, --scan [COMMIT] Scan new commits (default) or specific commits
32
+ --rebuild Clear the index and rescan the whole git history
31
33
  Indexer options:
32
34
  -a, --[no-]amend Reindex existing versions
33
35
  -i, --ignore PATH Don't check or index any file starting with PATH
@@ -12,6 +12,7 @@ require 'pandoc-ruby'
12
12
  require 'pathname'
13
13
  require 'rugged'
14
14
  require 'shellwords'
15
+ require 'stable_sort'
15
16
  require 'time'
16
17
 
17
18
  require 'reapack/index/cdetector'
@@ -23,6 +24,7 @@ require 'reapack/index/named_node'
23
24
  require 'reapack/index/package'
24
25
  require 'reapack/index/parsers'
25
26
  require 'reapack/index/provides'
27
+ require 'reapack/index/scanner'
26
28
  require 'reapack/index/source'
27
29
  require 'reapack/index/version'
28
30
 
@@ -36,31 +38,10 @@ class ReaPack::Index
36
38
  data: %w{data},
37
39
  }.freeze
38
40
 
39
- WITH_MAIN = [:script, :effect].freeze
40
-
41
- PROVIDES_VALIDATOR = proc {|value|
42
- begin
43
- Provides.parse_each(value).to_a and nil
44
- rescue Error => e
45
- e.message
46
- end
47
- }
48
-
49
- HEADER_RULES = {
50
- # package-wide tags
51
- :version => [
52
- MetaHeader::REQUIRED, MetaHeader::VALUE, MetaHeader::SINGLELINE, /\d/],
53
-
54
- # version-specific tags
55
- :author => [MetaHeader::VALUE, MetaHeader::SINGLELINE],
56
- :changelog => [MetaHeader::VALUE],
57
- :provides => [MetaHeader::VALUE, PROVIDES_VALIDATOR]
58
- }.freeze
59
-
60
41
  FS_ROOT = File.expand_path('/').freeze
61
42
 
62
- attr_reader :path, :url_template
63
- attr_accessor :amend, :files, :time
43
+ attr_reader :path, :url_template, :cdetector
44
+ attr_accessor :amend, :commit, :files, :time
64
45
 
65
46
  class << self
66
47
  def is_type?(input)
@@ -125,11 +106,6 @@ class ReaPack::Index
125
106
  type = self.class.type_of path
126
107
  return unless type
127
108
 
128
- # variables to restore if an error occur
129
- backups = Hash[[:@doc, :@cdetector].map {|var|
130
- [var, instance_variable_get(var).clone]
131
- }]
132
-
133
109
  mh = MetaHeader.new contents
134
110
 
135
111
  if mh[:noindex]
@@ -137,42 +113,21 @@ class ReaPack::Index
137
113
  return
138
114
  end
139
115
 
140
- if errors = mh.validate(HEADER_RULES)
141
- raise Error, errors.join("\n")
142
- end
116
+ # variables to restore if an error occur
117
+ backups = Hash[[:@doc, :@cdetector].map {|var|
118
+ [var, instance_variable_get(var).clone]
119
+ }]
143
120
 
144
121
  cat, pkg = package_for path
122
+ pkg.type = type
145
123
 
146
- cselector = @cdetector[pkg.type = type, path]
147
-
148
- pkg.version mh[:version] do |ver|
149
- next unless ver.is_new? || @amend
124
+ scanner = Scanner.new cat, pkg, mh, self
150
125
 
151
- # store the version name for make_url
152
- @currentVersion = ver.name
153
-
154
- ver.author = mh[:author]
155
- ver.time = @time if @time && ver.is_new?
156
- ver.changelog = mh[:changelog]
157
-
158
- ver.replace_sources do
159
- cselector.clear
160
- sources = parse_provides mh[:provides], pkg
161
-
162
- if WITH_MAIN.include?(type) && sources.none? {|src| src.file.nil? }
163
- # add the package itself as a source
164
- src = Source.new make_url(path)
165
- sources.unshift src
166
-
167
- cselector.push src.platform, path
168
- end
169
-
170
- sources.each {|src| ver.add_source src }
171
- end
172
- end
173
-
174
- if cons = cselector.resolve
175
- raise Error, cons.first
126
+ begin
127
+ scanner.run
128
+ rescue Error
129
+ backups.each {|var, value| instance_variable_set var, value }
130
+ raise
176
131
  end
177
132
 
178
133
  log_change 'new category', 'new categories' if cat.is_new?
@@ -190,16 +145,13 @@ class ReaPack::Index
190
145
  }
191
146
 
192
147
  bump_commit
193
- rescue Error
194
- backups.each {|var, value| instance_variable_set var, value }
195
- raise
196
148
  end
197
-
149
+
198
150
  def remove(path)
199
151
  cat, pkg = package_for path, false
200
152
  return unless pkg
201
153
 
202
- @cdetector[pkg.type, path].clear
154
+ @cdetector[path].clear
203
155
 
204
156
  pkg.remove
205
157
  cat.remove if cat.empty?
@@ -253,7 +205,7 @@ class ReaPack::Index
253
205
  raise Addressable::URI::InvalidURIError
254
206
  end
255
207
 
256
- @url_template = uri.to_s.freeze
208
+ @url_template = tpl
257
209
  rescue Addressable::URI::InvalidURIError
258
210
  raise Error, "invalid template '#{tpl}'"
259
211
  end
@@ -276,17 +228,15 @@ class ReaPack::Index
276
228
  log_change 'modified metadata' if oldName != newName
277
229
  end
278
230
 
279
- def commit
280
- @commit ||= @doc.root[:commit]
231
+ def last_commit
232
+ @doc.root[:commit]
281
233
  end
282
234
 
283
- attr_writer :commit
284
-
285
235
  def write(path)
286
236
  sort @doc.root
287
237
 
288
238
  FileUtils.mkdir_p File.dirname(path)
289
- File.write path, @doc.to_xml
239
+ File.binwrite path, @doc.to_xml
290
240
  end
291
241
 
292
242
  def write!
@@ -313,21 +263,9 @@ class ReaPack::Index
313
263
  list.join ', '
314
264
  end
315
265
 
316
- def make_url(path, template = nil)
317
- unless template
318
- unless @url_template
319
- raise Error, 'unable to generate download links: empty url template'
320
- end
321
-
322
- unless @files.include? path
323
- raise Error, "file not found '#{path}'"
324
- end
325
- end
326
-
327
- (template || @url_template)
328
- .sub('$path', path)
329
- .sub('$commit', commit || 'master')
330
- .sub('$version', @currentVersion.to_s)
266
+ def clear
267
+ Category.find_all(@doc.root).each {|cat| cat.remove }
268
+ @cdetector.clear
331
269
  end
332
270
 
333
271
  def self.expand(filepath, basedir)
@@ -354,56 +292,19 @@ private
354
292
  [cat, pkg]
355
293
  end
356
294
 
357
- def parse_provides(provides, pkg)
358
- pathdir = Pathname.new pkg.category
359
-
360
- Provides.parse_each(provides).map {|line|
361
- line.file_pattern = pkg.name if line.file_pattern == '.'
362
-
363
- expanded = self.class.expand line.file_pattern, pkg.category
364
- cselector = @cdetector[line.type || pkg.type, pkg.path]
365
-
366
- if expanded == pkg.path
367
- # always resolve path even when an url template is set
368
- files = [expanded]
369
- elsif line.url_template.nil?
370
- files = @files.select {|f| File.fnmatch expanded, f, File::FNM_PATHNAME }
371
- raise Error, "file not found '#{line.file_pattern}'" if files.empty?
372
- else
373
- # use the relative path for external urls
374
- files = [line.file_pattern]
375
- end
376
-
377
- files.map {|file|
378
- src = Source.new make_url(file, line.url_template)
379
- src.platform = line.platform
380
- src.type = line.type
381
-
382
- cselector.push src.platform, line.url_template ? expanded : file
383
-
384
- if file != pkg.path
385
- if line.url_template
386
- src.file = file
387
- else
388
- src.file = Pathname.new(file).relative_path_from(pathdir).to_s
389
- end
390
- end
391
-
392
- src
393
- }
394
- }.flatten
395
- end
396
-
397
295
  def sort(node)
398
- node.children.each {|n| sort n }
399
-
296
+ node.element_children.map {|n| sort n }
400
297
  return if node.name == Package.tag
401
- sorted = node.children.sort_by{|n| n[:name].to_s }.sort_by {|n| n.name }
298
+
299
+ sorted = node.element_children
300
+ .stable_sort_by {|n| n[:name].to_s.downcase }
301
+ .stable_sort_by {|n| n.name.downcase }
302
+
402
303
  sorted.each {|n| node << n }
403
304
  end
404
305
 
405
306
  def bump_commit
406
- sha1 = commit()
307
+ sha1 = @commit || last_commit
407
308
 
408
309
  if sha1.nil?
409
310
  @doc.root.remove_attribute 'commit'
@@ -3,21 +3,26 @@ class ReaPack::Index
3
3
  Entry = Struct.new :key, :platform, :file
4
4
 
5
5
  class Selector
6
- def initialize(bucket, key, cdetector)
7
- @bucket, @key, @cdetector = bucket, key, cdetector
8
- @entries = @cdetector.bucket bucket
6
+ def initialize(key, cdetector)
7
+ @key, @cdetector = key, cdetector
9
8
  end
10
9
 
11
- def push(platform, file)
12
- @entries << Entry.new(@key, platform, file).freeze
10
+ def push(bucket, platform, file)
11
+ @cdetector.bucket(bucket) << Entry.new(@key, platform, file).freeze
13
12
  end
14
13
 
15
14
  def clear
16
- @entries.reject! {|e| e.key == @key }
15
+ @cdetector.buckets.each_value do |b|
16
+ b.reject! {|e| e.key == @key }
17
+ end
17
18
  end
18
19
 
19
20
  def resolve
20
- @cdetector.resolve @bucket, @key
21
+ errors = @cdetector.buckets.map do |b, _|
22
+ @cdetector.resolve b, @key
23
+ end.compact.flatten
24
+
25
+ errors unless errors.empty?
21
26
  end
22
27
  end
23
28
 
@@ -31,28 +36,33 @@ class ReaPack::Index
31
36
  Hash[@buckets.map {|k, v| [k, v.clone] }]
32
37
  end
33
38
 
34
- def [](bucket, key)
35
- Selector.new bucket, key, self
39
+ def [](key)
40
+ Selector.new key, self
36
41
  end
37
42
 
43
+ attr_reader :buckets
44
+
38
45
  def bucket(name)
46
+ raise ArgumentError, 'bucket name is not a symbol' unless name.is_a? Symbol
39
47
  @buckets[name] ||= []
40
48
  end
41
49
 
42
- def resolve(bucket, key = nil)
43
- return unless bucket = @buckets[bucket]
50
+ def clear
51
+ @buckets.clear
52
+ end
44
53
 
45
- dups = bucket.group_by {|e| e.file }.values.select {|a| a.size > 1 }
54
+ def resolve(bname, key = nil)
55
+ dups = bucket(bname).group_by {|e| e.file }.values.select {|a| a.size > 1 }
46
56
 
47
57
  errors = dups.map {|a|
48
58
  packages = a.map {|e| e.key }.uniq
49
59
  next if key && !packages.include?(key)
50
60
 
51
61
  if packages.size == 1 || !key
52
- original = sort(a)[1]
62
+ original = sort_platforms(a)[1]
53
63
  msg = "duplicate file '#{original.file}'"
54
64
  else
55
- original = sort(a.select {|e| e.key != key }).first
65
+ original = sort_platforms(a.select {|e| e.key != key }).first
56
66
  msg = "'#{original.file}' conflicts with '#{original.key}'"
57
67
  end
58
68
 
@@ -61,6 +71,7 @@ class ReaPack::Index
61
71
  if platforms.size > 1
62
72
  # check platform inheritance
63
73
  platforms.any? {|p|
74
+ break true unless Source::PLATFORMS[p]
64
75
  loop do
65
76
  p = Source::PLATFORMS[p] or break false
66
77
  break true if platforms.include? p
@@ -81,7 +92,9 @@ class ReaPack::Index
81
92
  pkgroot = File.join(cat.name, pkg.name)
82
93
 
83
94
  pkg.versions.last&.children(Source::TAG)&.each {|src|
84
- entry = Entry.new pkgroot, src[:platform].to_sym
95
+ type = src[:type] || pkg.type
96
+ platform = src[:platform] || :all
97
+ entry = Entry.new pkgroot, platform.to_sym
85
98
 
86
99
  if src[:file]
87
100
  entry.file = ReaPack::Index.expand(src[:file], cat.name)
@@ -89,16 +102,17 @@ class ReaPack::Index
89
102
  entry.file = pkgroot
90
103
  end
91
104
 
92
- bucket(pkg.type) << entry
105
+ bucket(type.to_sym) << entry
93
106
  }
94
107
  }
95
108
  }
96
109
  end
97
110
 
98
111
  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 }
112
+ def sort_platforms(set)
113
+ set.group_by {|e| levels[e.platform] || 0 }.sort
114
+ .map {|_, a| a.sort_by {|e| Source::PLATFORMS.keys.index(e.platform) || 0 } }
115
+ .flatten
102
116
  end
103
117
 
104
118
  def levels
@@ -77,10 +77,13 @@ private
77
77
  return
78
78
  end
79
79
 
80
- if @opts[:scan].empty?
81
- commits = @git.commits_since @index.commit.to_s
80
+ commits = if @opts[:rebuild]
81
+ @index.clear
82
+ @git.commits
83
+ elsif @opts[:scan].empty?
84
+ @git.commits_since @index.last_commit
82
85
  else
83
- commits = @opts[:scan].map {|hash|
86
+ @opts[:scan].map {|hash|
84
87
  @git.get_commit hash or begin
85
88
  $stderr.puts '--scan: bad revision: %s' % @opts[:scan]
86
89
  @exit = false
@@ -105,7 +108,9 @@ private
105
108
  @index.time = commit.time
106
109
  @index.files = commit.filelist
107
110
 
108
- commit.each_diff {|diff| process_diff diff }
111
+ commit.each_diff
112
+ .sort_by {|diff| diff.status == :deleted ? 0 : 1 }
113
+ .each {|diff| process_diff diff }
109
114
  ensure
110
115
  bump_progress
111
116
  end
@@ -190,18 +195,19 @@ private
190
195
  def do_check
191
196
  check_name
192
197
 
193
- @index.amend = true # enable checks for released versions as well
198
+ @index.clear
194
199
  failures = []
195
200
 
196
201
  pkgs = Hash[Dir.glob("#{Regexp.quote(@git.path)}/**/*").sort.map {|abs|
197
202
  rel = @git.relative_path abs
198
203
  @index.files << rel
199
204
 
200
- next if !File.file?(abs) || ignored?(abs) || !ReaPack::Index.is_package?(abs)
205
+ next if !File.file?(abs) || ignored?(abs) || !ReaPack::Index.is_package?(rel)
201
206
 
202
207
  [abs, rel]
203
208
  }.compact]
204
209
 
210
+ # reiterate over the pkg list after registering every file
205
211
  pkgs.each_pair {|abs, rel|
206
212
  begin
207
213
  @index.scan rel, File.read(abs)
@@ -9,6 +9,7 @@ class ReaPack::Index::CLI
9
9
  DEFAULTS = {
10
10
  check: false,
11
11
  scan: [],
12
+ rebuild: false,
12
13
  verbose: false,
13
14
  warnings: true,
14
15
  progress: true,
@@ -53,7 +54,7 @@ class ReaPack::Index::CLI
53
54
  end
54
55
 
55
56
  op.on '-s', '--scan [COMMIT]', 'Scan new commits (default) or specific commits' do |commit|
56
- opts[:check] = false
57
+ opts[:check] = opts[:rebuild] = false
57
58
  opts[:scan] ||= []
58
59
 
59
60
  if commit
@@ -63,6 +64,11 @@ class ReaPack::Index::CLI
63
64
  end
64
65
  end
65
66
 
67
+ op.on '--rebuild', 'Clear the index and rescan the whole git history' do
68
+ opts[:check] = false
69
+ opts[:rebuild] = true
70
+ end
71
+
66
72
  op.separator 'Indexer options:'
67
73
 
68
74
  op.on '-a', '--[no-]amend', 'Reindex existing versions' do |bool|