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.
@@ -7,6 +7,8 @@ class ReaPack::Index::CLI
7
7
  ].freeze
8
8
 
9
9
  DEFAULTS = {
10
+ check: false,
11
+ scan: [],
10
12
  verbose: false,
11
13
  warnings: true,
12
14
  progress: true,
@@ -41,28 +43,37 @@ class ReaPack::Index::CLI
41
43
  OptionParser.new do |op|
42
44
  op.program_name = PROGRAM_NAME
43
45
  op.version = ReaPack::Index::VERSION
44
- op.banner = "Package indexer for ReaPack-based repositories\n" +
46
+ op.banner = "Package indexer for git-based ReaPack repositories\n" +
45
47
  "Usage: #{PROGRAM_NAME} [options] [directory]"
46
48
 
47
- op.separator 'Options:'
48
-
49
- op.on '-a', '--[no-]amend', 'Reindex existing versions' do |bool|
50
- opts[:amend] = bool
51
- end
49
+ op.separator 'Modes:'
52
50
 
53
51
  op.on '-c', '--check', 'Test every package including uncommited changes and exit' do
54
52
  opts[:check] = true
55
53
  end
56
54
 
55
+ op.on '-s', '--scan [COMMIT]', 'Scan new commits (default) or specific commits' do |commit|
56
+ opts[:check] = false
57
+ opts[:scan] ||= []
58
+
59
+ if commit
60
+ opts[:scan] << commit.strip
61
+ else
62
+ opts[:scan].clear
63
+ end
64
+ end
65
+
66
+ op.separator 'Indexer options:'
67
+
68
+ op.on '-a', '--[no-]amend', 'Reindex existing versions' do |bool|
69
+ opts[:amend] = bool
70
+ end
71
+
57
72
  op.on '-i', '--ignore PATH', "Don't check or index any file starting with PATH" do |path|
58
73
  opts[:ignore] ||= []
59
74
  opts[:ignore] << expand_path(path)
60
75
  end
61
76
 
62
- op.on '-n', '--name NAME', 'Set the name shown in ReaPack for this repository' do |name|
63
- opts[:name] = name.strip
64
- end
65
-
66
77
  op.on '-U', "--url-template TEMPLATE=#{DEFAULTS[:url_template]}",
67
78
  'Set the template for implicit download links' do |tpl|
68
79
  opts[:url_template] = tpl.strip
@@ -73,6 +84,12 @@ class ReaPack::Index::CLI
73
84
  opts[:output] = file.strip
74
85
  end
75
86
 
87
+ op.separator 'Repository metadata:'
88
+
89
+ op.on '-n', '--name NAME', 'Set the name shown in ReaPack for this repository' do |name|
90
+ opts[:name] = name.strip
91
+ end
92
+
76
93
  op.on '-l', '--link LINK', 'Add or remove a website link' do |link|
77
94
  opts[:links] ||= Array.new
78
95
  opts[:links] << [:website, link.strip]
@@ -99,6 +116,8 @@ class ReaPack::Index::CLI
99
116
  opts[:dump_about] = true
100
117
  end
101
118
 
119
+ op.separator 'Misc options:'
120
+
102
121
  op.on '--[no-]progress', 'Enable or disable progress information' do |bool|
103
122
  opts[:progress] = bool
104
123
  end
@@ -1,5 +1,5 @@
1
1
  module ReaPack
2
2
  class Index
3
- VERSION = '1.0beta3'
3
+ VERSION = '1.0beta4'
4
4
  end
5
5
  end
@@ -0,0 +1,189 @@
1
+ class ReaPack::Index
2
+ class Git
3
+ def initialize(path)
4
+ @repo = Rugged::Repository.discover path
5
+ end
6
+
7
+ def empty?
8
+ @repo.empty?
9
+ end
10
+
11
+ def path
12
+ File.expand_path @repo.workdir
13
+ end
14
+
15
+ def commits_since(sha)
16
+ return [] if empty?
17
+
18
+ walker = Rugged::Walker.new @repo
19
+ walker.sorting Rugged::SORT_TOPO | Rugged::SORT_REVERSE
20
+ walker.push @repo.head.target_id
21
+
22
+ walker.hide sha if fetch_commit sha
23
+
24
+ walker.map {|c| Commit.new c, @repo }
25
+ end
26
+
27
+ def get_commit(sha)
28
+ c = fetch_commit sha
29
+ Commit.new c, @repo if c
30
+ end
31
+
32
+ def last_commit
33
+ c = @repo.last_commit
34
+ Commit.new c, @repo if c
35
+ end
36
+
37
+ def guess_url_template
38
+ remote = @repo.remotes['origin']
39
+ return unless remote
40
+
41
+ uri = Gitable::URI.parse remote.url
42
+ return unless uri.path =~ /\A\/?(?<user>[^\/]+)\/(?<repo>[^\/]+)\.git\Z/
43
+
44
+ tpl = uri.to_web_uri
45
+ tpl.path += '/raw/$commit/$path'
46
+
47
+ tpl.to_s
48
+ end
49
+
50
+ def relative_path(path)
51
+ root = Pathname.new @repo.workdir
52
+ file = Pathname.new path
53
+
54
+ file.relative_path_from(root).to_s
55
+ end
56
+
57
+ def create_commit(message, files)
58
+ old_index = @repo.index
59
+ target = empty? ? nil : @repo.head.target
60
+
61
+ if target
62
+ old_index.read_tree target.tree
63
+ else
64
+ old_index.clear
65
+ end
66
+
67
+ new_index = @repo.index
68
+ files.each {|f|
69
+ if File.exist? f
70
+ new_index.add relative_path(f)
71
+ else
72
+ new_index.remove relative_path(f)
73
+ end
74
+ }
75
+
76
+ c = Rugged::Commit.create @repo, \
77
+ tree: new_index.write_tree(@repo),
78
+ message: message,
79
+ parents: [target].compact,
80
+ update_ref: 'HEAD'
81
+
82
+ old_index.write
83
+
84
+ # force-reload the repository
85
+ @repo = Rugged::Repository.discover path
86
+
87
+ get_commit c
88
+ end
89
+
90
+ private
91
+ def fetch_commit(sha)
92
+ if sha && sha.size.between?(7, 40) && @repo.include?(sha)
93
+ object = @repo.lookup sha
94
+ object if object.is_a? Rugged::Commit
95
+ end
96
+ rescue Rugged::InvalidError
97
+ nil
98
+ end
99
+ end
100
+
101
+ class Git::Commit
102
+ def initialize(commit, repo)
103
+ @commit, @repo = commit, repo
104
+ @parent = commit.parents.first
105
+ end
106
+
107
+ def each_diff
108
+ return enum_for :each_diff unless block_given?
109
+
110
+ if @parent
111
+ diff = @parent.diff id
112
+ else
113
+ diff = @commit.diff
114
+ end
115
+
116
+ diff.each_delta {|delta| yield Git::Diff.new(delta, @parent.nil?, @repo) }
117
+ end
118
+
119
+ def id
120
+ @commit.oid
121
+ end
122
+
123
+ def short_id
124
+ id[0...7]
125
+ end
126
+
127
+ def message
128
+ @commit.message
129
+ end
130
+
131
+ def summary
132
+ message.lines.first.chomp
133
+ end
134
+
135
+ def time
136
+ @commit.time
137
+ end
138
+
139
+ def filelist
140
+ lsfiles @commit.tree
141
+ end
142
+
143
+ def ==(o)
144
+ id == o.id
145
+ end
146
+
147
+ private
148
+ def lsfiles(tree, base = String.new)
149
+ files = []
150
+
151
+ tree.each {|obj|
152
+ fullname = base.empty? ? obj[:name] : File.join(base, obj[:name])
153
+ case obj[:type]
154
+ when :blob
155
+ files << fullname
156
+ when :tree
157
+ files.concat lsfiles(@repo.lookup(obj[:oid]), fullname)
158
+ end
159
+ }
160
+
161
+ files
162
+ end
163
+ end
164
+
165
+ class Git::Diff
166
+ def initialize(delta, is_initial, repo)
167
+ @delta, @repo = delta, repo
168
+
169
+ if is_initial
170
+ @status = :added
171
+ @file = delta.old_file
172
+ else
173
+ @status = delta.status.to_sym
174
+ @file = delta.new_file
175
+ end
176
+ end
177
+
178
+ attr_reader :status
179
+
180
+ def file
181
+ @file[:path].force_encoding(Encoding::UTF_8)
182
+ end
183
+
184
+ def new_content
185
+ return if status == :deleted
186
+ @repo.lookup(@file[:oid]).content.force_encoding(Encoding::UTF_8)
187
+ end
188
+ end
189
+ end
@@ -24,7 +24,7 @@ class ReaPack::Index
24
24
  raise Addressable::URI::InvalidURIError
25
25
  end
26
26
  rescue Addressable::URI::InvalidURIError
27
- raise Error, "invalid link: #{url}"
27
+ raise Error, "invalid link '#{url}'"
28
28
  end
29
29
 
30
30
  make_root
@@ -60,7 +60,7 @@ class ReaPack::Index
60
60
  def remove_link(type, search)
61
61
  node = Link.find type, search, @root
62
62
 
63
- raise Error, "no such #{type} link: #{search}" unless node
63
+ raise Error, "no such #{type} link '#{search}'" unless node
64
64
 
65
65
  node.remove
66
66
  auto_remove
@@ -7,7 +7,7 @@ class ReaPack::Index
7
7
  @tag
8
8
  end
9
9
 
10
- def self.find_in(parent, name)
10
+ def self.find_one(name, parent)
11
11
  node = parent.element_children.find {|node|
12
12
  node.name == tag && node[NAME_ATTR] == name
13
13
  }
@@ -21,27 +21,32 @@ class ReaPack::Index
21
21
  .map {|node| self.new node }
22
22
  end
23
23
 
24
- def self.get(name, parent, create)
24
+ def self.fetch(name, parent, create)
25
25
  return unless parent
26
26
 
27
- node = self.find_in parent, name
27
+ instance = find_one name, parent
28
28
 
29
29
  if create
30
- node ||= self.new name, parent
30
+ instance ||= self.create name, parent
31
31
  end
32
32
 
33
- node
33
+ instance
34
34
  end
35
35
 
36
- def initialize(node, parent = nil)
37
- return @node = node if parent.nil?
36
+ def self.create(name, parent)
37
+ node = Nokogiri::XML::Node.new tag, parent.document
38
+ node[NAME_ATTR] = name
39
+ node.parent = parent
38
40
 
39
- @is_new = true
40
- @dirty = true
41
+ instance = new node
42
+ instance.instance_variable_set :@is_new, true
43
+ instance.instance_variable_set :@dirty, true
41
44
 
42
- @node = Nokogiri::XML::Node.new self.class.tag, parent.document
43
- @node[NAME_ATTR] = node
44
- @node.parent = parent
45
+ instance
46
+ end
47
+
48
+ def initialize(node)
49
+ @node = node
45
50
  end
46
51
 
47
52
  attr_reader :node
@@ -6,13 +6,10 @@ class ReaPack::Index
6
6
  class Package < NamedNode
7
7
  @tag = 'reapack'.freeze
8
8
 
9
- TYPE = 'type'.freeze
9
+ TYPE_ATTR = 'type'.freeze
10
10
 
11
- def initialize(node, parent = nil)
11
+ def initialize(node)
12
12
  super
13
-
14
- @versions = {}
15
-
16
13
  read_versions
17
14
  end
18
15
 
@@ -20,16 +17,24 @@ class ReaPack::Index
20
17
  super || @versions.values.any? {|ver| ver.modified? }
21
18
  end
22
19
 
20
+ def category
21
+ @node.parent[NAME_ATTR]
22
+ end
23
+
24
+ def path
25
+ @path ||= File.join category, name if category && name
26
+ end
27
+
23
28
  def type
24
- @node[TYPE].to_s
29
+ @node[TYPE_ATTR]&.to_sym
25
30
  end
26
31
 
27
32
  def type=(new_type)
28
- new_type ||= String.new
33
+ new_type = new_type.to_sym
29
34
 
30
35
  return if type == new_type
31
36
 
32
- @node[TYPE] = new_type
37
+ @node[TYPE_ATTR] = new_type
33
38
  @dirty = true
34
39
  end
35
40
 
@@ -45,7 +50,7 @@ class ReaPack::Index
45
50
  if has_version? name
46
51
  ver = @versions[name]
47
52
  else
48
- ver = @versions[name] = Version.new name, @node
53
+ ver = @versions[name] = Version.create name, @node
49
54
  end
50
55
 
51
56
  if block_given?
@@ -57,6 +62,8 @@ class ReaPack::Index
57
62
 
58
63
  private
59
64
  def read_versions
65
+ @versions ||= {}
66
+
60
67
  Version.find_all(@node).each {|ver|
61
68
  @versions[ver.name] = ver
62
69
  }
@@ -0,0 +1,46 @@
1
+ class ReaPack::Index
2
+ Provides = Struct.new :file_pattern, :url_template, :platform, :type do
3
+ PROVIDES_REGEX = /
4
+ \A
5
+ ( \[ \s* (?<options> .+? ) \s* \] )?
6
+ \s*
7
+ (?<file> .+?)
8
+ ( \s+ (?<url> (?:file|https?):\/\/.+ ) )?
9
+ \z
10
+ /x.freeze
11
+
12
+ class << self
13
+ def parse_each(input)
14
+ if block_given?
15
+ input.to_s.lines.map {|line| yield parse(line) }
16
+ else
17
+ enum_for :parse_each, input
18
+ end
19
+ end
20
+
21
+ def parse(line)
22
+ m = line.strip.match PROVIDES_REGEX
23
+ options, pattern, url_tpl = m[:options], m[:file], m[:url]
24
+
25
+ instance = self.new pattern, url_tpl
26
+
27
+ options and options.split(',').each {|user_opt|
28
+ user_opt.strip!
29
+ next if user_opt.empty?
30
+
31
+ opt = user_opt.downcase.to_sym
32
+
33
+ if Source.is_platform? opt
34
+ instance.platform = opt
35
+ elsif type = ReaPack::Index.resolve_type(opt)
36
+ instance.type = type
37
+ else
38
+ raise Error, "unknown option (platform or type) '#{user_opt}'"
39
+ end
40
+ }
41
+
42
+ instance
43
+ end
44
+ end
45
+ end
46
+ end