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
@@ -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
|
46
|
+
op.banner = "Package indexer for git-based ReaPack repositories\n" +
|
45
47
|
"Usage: #{PROGRAM_NAME} [options] [directory]"
|
46
48
|
|
47
|
-
op.separator '
|
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
|
@@ -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
|
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
|
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.
|
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.
|
24
|
+
def self.fetch(name, parent, create)
|
25
25
|
return unless parent
|
26
26
|
|
27
|
-
|
27
|
+
instance = find_one name, parent
|
28
28
|
|
29
29
|
if create
|
30
|
-
|
30
|
+
instance ||= self.create name, parent
|
31
31
|
end
|
32
32
|
|
33
|
-
|
33
|
+
instance
|
34
34
|
end
|
35
35
|
|
36
|
-
def
|
37
|
-
|
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
|
-
|
40
|
-
|
41
|
+
instance = new node
|
42
|
+
instance.instance_variable_set :@is_new, true
|
43
|
+
instance.instance_variable_set :@dirty, true
|
41
44
|
|
42
|
-
|
43
|
-
|
44
|
-
|
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
|
-
|
9
|
+
TYPE_ATTR = 'type'.freeze
|
10
10
|
|
11
|
-
def initialize(node
|
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[
|
29
|
+
@node[TYPE_ATTR]&.to_sym
|
25
30
|
end
|
26
31
|
|
27
32
|
def type=(new_type)
|
28
|
-
new_type
|
33
|
+
new_type = new_type.to_sym
|
29
34
|
|
30
35
|
return if type == new_type
|
31
36
|
|
32
|
-
@node[
|
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.
|
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
|