ki-repo 0.1.0 → 0.1.1
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.
- data/README.md +26 -12
- data/VERSION +1 -1
- data/bin/ki +21 -0
- data/docs/backlog.md +35 -0
- data/docs/development.md +45 -0
- data/docs/development_setup.md +49 -0
- data/{lib/ki-repo.rb → docs/images/for_git.txt} +0 -0
- data/docs/ki_commands.md +202 -0
- data/docs/repository_basics.md +171 -0
- data/docs/writing_extensions.md +50 -0
- data/lib/cmd/cmd.rb +224 -0
- data/lib/cmd/user_pref_cmd.rb +122 -0
- data/lib/cmd/version_cmd.rb +483 -0
- data/lib/data_access/repository_finder.rb +200 -0
- data/lib/data_access/repository_info.rb +153 -0
- data/lib/data_access/version_helpers.rb +242 -0
- data/lib/data_access/version_iterators.rb +145 -0
- data/lib/data_access/version_operations.rb +80 -0
- data/lib/data_storage/dir_base.rb +106 -0
- data/lib/data_storage/ki_home.rb +44 -0
- data/lib/data_storage/ki_json.rb +153 -0
- data/lib/data_storage/repository.rb +91 -0
- data/lib/data_storage/version_metadata.rb +141 -0
- data/lib/ki_repo_all.rb +42 -0
- data/lib/util/attr_chain.rb +258 -0
- data/lib/util/exception_catcher.rb +118 -0
- data/lib/util/hash.rb +46 -0
- data/lib/util/hash_cache.rb +31 -0
- data/lib/util/ruby_extensions.rb +137 -0
- data/lib/util/service_registry.rb +88 -0
- data/lib/util/simple_optparse.rb +103 -0
- data/lib/util/test.rb +323 -0
- metadata +69 -13
- data/.document +0 -5
- data/.rspec +0 -1
- data/Gemfile +0 -14
- data/Gemfile.lock +0 -38
- data/Rakefile +0 -42
- data/spec/ki-repo_spec.rb +0 -6
- data/spec/spec_helper.rb +0 -12
@@ -0,0 +1,483 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
# Copyright 2012 Mikko Apo
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
|
17
|
+
module Ki
|
18
|
+
|
19
|
+
# Builds and updates ki-metada.json file based on parameters and added files
|
20
|
+
# @see VersionMetadataFile
|
21
|
+
class BuildVersionMetadataFile
|
22
|
+
attr_chain :input_dir, -> { Dir.pwd }
|
23
|
+
attr_chain :metadata_file, -> { VersionMetadataFile.new("ki-version.json") }
|
24
|
+
attr_chain :source_parameters, -> { Hash.new }
|
25
|
+
attr_chain :default_parameters, -> { {"hashes" => ["sha1"], "tags" => []} }
|
26
|
+
attr_chain :previous_dep, :require => "Define a dependency before -o or --operation"
|
27
|
+
attr_chain :shell_command, :require
|
28
|
+
|
29
|
+
def execute(ctx, args)
|
30
|
+
# opts.parse parses input parameters and fills in configuration parameters
|
31
|
+
files = opts.parse(args)
|
32
|
+
if source_parameters.size > 0
|
33
|
+
metadata_file.source(source_parameters)
|
34
|
+
end
|
35
|
+
# adds files to metadata and fills in parameters
|
36
|
+
metadata_file.add_files(input_dir, files, default_parameters)
|
37
|
+
metadata_file.save
|
38
|
+
end
|
39
|
+
|
40
|
+
def help
|
41
|
+
<<EOF
|
42
|
+
"#{shell_command}" can be used to generate version metadata files. Version metadata files
|
43
|
+
contain information about files (size, permission bits, hash checksums), version origins
|
44
|
+
and dependencies.
|
45
|
+
|
46
|
+
After version metadata file is ready, it can be imported to repository using version-import.
|
47
|
+
|
48
|
+
### Usage
|
49
|
+
|
50
|
+
#{shell_command} <parameters> file_pattern1*.* file_pattern2*.*
|
51
|
+
|
52
|
+
### Examples
|
53
|
+
|
54
|
+
#{shell_command} test.sh
|
55
|
+
#{shell_command} readme* -t doc
|
56
|
+
#{shell_command} -d my/component/1,name=comp,path=doc,internal -O "mv doc/test.sh helloworld.sh"
|
57
|
+
ki version-import
|
58
|
+
|
59
|
+
### Parameters
|
60
|
+
#{opts}
|
61
|
+
EOF
|
62
|
+
end
|
63
|
+
|
64
|
+
def summary
|
65
|
+
"Create version metadata file"
|
66
|
+
end
|
67
|
+
|
68
|
+
def opts
|
69
|
+
OptionParser.new do |opts|
|
70
|
+
opts.banner = ""
|
71
|
+
opts.on("-f", "--file FILE", "Version file target") do |v|
|
72
|
+
if !defined? @input_dir
|
73
|
+
input_dir(File.dirname(v))
|
74
|
+
end
|
75
|
+
metadata_file.init_from_path(v)
|
76
|
+
end
|
77
|
+
opts.on("-i", "--input-directory INPUT-DIR", "Input directory") do |v|
|
78
|
+
input_dir(v)
|
79
|
+
end
|
80
|
+
opts.on("-v", "--version-id VERSION-ID", "Version's id") do |v|
|
81
|
+
metadata_file.version_id=v
|
82
|
+
end
|
83
|
+
["url", "tag-url", "author", "repotype"].each do |source_param|
|
84
|
+
opts.on("--source-#{source_param} #{source_param.upcase}", "Build source parameter #{source_param}") do |v|
|
85
|
+
source_parameters[source_param]=v
|
86
|
+
end
|
87
|
+
end
|
88
|
+
opts.on("-t", "--tags TAGS", "Tag files with keywords") do |v|
|
89
|
+
default_parameters["tags"]= v.split(",").sort
|
90
|
+
end
|
91
|
+
hash_prefix = "/hashing"
|
92
|
+
hashes = KiCommand::KiExtensions.find(hash_prefix).map { |k, v| k[hash_prefix.size+1..-1] }
|
93
|
+
opts.on("--hashes HASHES", "Calculate checksums using defined hash algos. Default: sha1. Available: #{hashes.join(", ")}") do |v|
|
94
|
+
default_parameters["hashes"]= v.split(",").sort
|
95
|
+
end
|
96
|
+
opts.on("-d", "--dependency DEPENDENCY", "Dependency definition my/component/123[,name=AA][,path=aa][,internal]") do |v|
|
97
|
+
previous_dep(metadata_file.add_dependency(v))
|
98
|
+
end
|
99
|
+
opts.on("-o", "--operation OP", "Add operation to previous dependency") do |v|
|
100
|
+
previous_dep.add_operation(v.split(" "))
|
101
|
+
end
|
102
|
+
opts.on("-O", "--version-operation OP", "Add operation to version") do |v|
|
103
|
+
metadata_file.add_operation(v.split(" "))
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# Tests version from repository or metadata file
|
110
|
+
# @see VersionTester
|
111
|
+
class TestVersion
|
112
|
+
attr_chain :shell_command, :require
|
113
|
+
|
114
|
+
def execute(ctx, args)
|
115
|
+
@tester = VersionTester.new.recursive(false).print(true)
|
116
|
+
ver_strs = opts.parse(args)
|
117
|
+
if ver_strs.size > 0 || @tester.recursive
|
118
|
+
@tester.ki_home(ctx.ki_home)
|
119
|
+
versions = ver_strs.map { |v| ctx.ki_home.version(v) }
|
120
|
+
else
|
121
|
+
versions = []
|
122
|
+
end
|
123
|
+
if @file
|
124
|
+
versions.unshift Version.create_version(@file, @input_dir)
|
125
|
+
end
|
126
|
+
all_ok = true
|
127
|
+
versions.each do |v|
|
128
|
+
all_ok = all_ok && @tester.test_version(v)
|
129
|
+
end
|
130
|
+
if all_ok
|
131
|
+
puts "All files ok."
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def help
|
136
|
+
<<EOF
|
137
|
+
"#{shell_command}" tests versions, their files and their dependencies. Can also test version that has not been imported yet.
|
138
|
+
|
139
|
+
### Examples
|
140
|
+
|
141
|
+
#{shell_command} -r my/product other/product
|
142
|
+
#{shell_command} -f ki-version.json -i file-directory
|
143
|
+
|
144
|
+
### Parameters
|
145
|
+
#{opts}
|
146
|
+
EOF
|
147
|
+
end
|
148
|
+
|
149
|
+
def summary
|
150
|
+
"Tests versions and their dependencies"
|
151
|
+
end
|
152
|
+
|
153
|
+
def opts
|
154
|
+
OptionParser.new do |opts|
|
155
|
+
opts.banner = ""
|
156
|
+
opts.on("-f", "--file FILE", "Version source file. By default uses file's directory as source for binary files.'") do |v|
|
157
|
+
if @input_dir.nil?
|
158
|
+
dir = File.dirname(v)
|
159
|
+
@input_dir = dir != "." ? dir : Dir.pwd
|
160
|
+
end
|
161
|
+
@file = v
|
162
|
+
end
|
163
|
+
opts.on("-i", "--input-directory INPUT-DIR", "Binary file input directory") do |v|
|
164
|
+
@input_dir = v
|
165
|
+
end
|
166
|
+
opts.on("-r", "--recursive", "Tests version's dependencies also.'") do |v|
|
167
|
+
@tester.recursive = true
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
# Imports version and its files to repository
|
174
|
+
# @see VersionImporter
|
175
|
+
class ImportVersion
|
176
|
+
attr_chain :input_dir, -> { Dir.pwd }
|
177
|
+
attr_chain :file, -> { File.join(input_dir, "ki-version.json") }
|
178
|
+
attr_chain :importer, -> {}
|
179
|
+
attr_chain :shell_command, :require
|
180
|
+
|
181
|
+
def execute(ctx, args)
|
182
|
+
@importer = VersionImporter.new
|
183
|
+
opts.parse(args)
|
184
|
+
@importer.ki_home(ctx.ki_home).import(file, input_dir)
|
185
|
+
end
|
186
|
+
|
187
|
+
def help
|
188
|
+
<<EOF
|
189
|
+
"#{shell_command}" imports version and its files to repository.
|
190
|
+
|
191
|
+
Version name can be defined either during "version-build",
|
192
|
+
or generated automatically for component at import (with -c my/component) or defined to be a specific version (-v).
|
193
|
+
Can also move files (-m), test dependencies before import (-t).
|
194
|
+
|
195
|
+
### Examples
|
196
|
+
|
197
|
+
#{shell_command} -m -t -c my/product
|
198
|
+
#{shell_command} -f ki-version.json -i file-directory
|
199
|
+
|
200
|
+
### Parameters
|
201
|
+
#{opts}
|
202
|
+
EOF
|
203
|
+
end
|
204
|
+
|
205
|
+
def summary
|
206
|
+
"Imports version metadata and files to repository"
|
207
|
+
end
|
208
|
+
|
209
|
+
def opts
|
210
|
+
OptionParser.new do |opts|
|
211
|
+
opts.banner = ""
|
212
|
+
opts.on("-f", "--file FILE", "Version source file. By default uses file's directory as source for binary files.'") do |v|
|
213
|
+
if !defined? @input_dir
|
214
|
+
input_dir(File.dirname(v))
|
215
|
+
end
|
216
|
+
file(v)
|
217
|
+
end
|
218
|
+
opts.on("-i", "--input-directory INPUT-DIR", "Input directory") do |v|
|
219
|
+
input_dir(v)
|
220
|
+
end
|
221
|
+
opts.on("-t", "--test-recursive", "Tests version's dependencies before importing.'") do |v|
|
222
|
+
@importer.tester.recursive = true
|
223
|
+
end
|
224
|
+
opts.on("-m", "--move", "Moves files to repository'") do |v|
|
225
|
+
@importer.move_files = true
|
226
|
+
end
|
227
|
+
opts.on("-c", "--create-new-version COMPONENT", "Creates new version number for defined component'") do |c|
|
228
|
+
@importer.create_new_version = c
|
229
|
+
end
|
230
|
+
opts.on("-v", "--version-id VERSION", "Imports version with defined version id'") do |v|
|
231
|
+
@importer.specific_version_id = v
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
# Exports version from repository to target directory
|
238
|
+
# @see VersionExporter
|
239
|
+
class ExportVersion
|
240
|
+
attr_chain :out, -> { Dir.pwd }
|
241
|
+
attr_chain :shell_command, :require
|
242
|
+
|
243
|
+
def execute(ctx, args)
|
244
|
+
@exporter = VersionExporter.new
|
245
|
+
file_patterns = opts.parse(args)
|
246
|
+
version = file_patterns.delete_at(0)
|
247
|
+
@exporter.find_files.files(file_patterns)
|
248
|
+
@exporter.ki_home(ctx.ki_home).export(version, out)
|
249
|
+
end
|
250
|
+
|
251
|
+
def help
|
252
|
+
<<EOF
|
253
|
+
"#{shell_command}" exports version and its dependencies to target directory.
|
254
|
+
|
255
|
+
### Usage
|
256
|
+
|
257
|
+
#{shell_command} <parameters> <file_export_pattern*.*>
|
258
|
+
|
259
|
+
### Examples
|
260
|
+
|
261
|
+
#{shell_command} -o export-dir --tags -c bin my/product
|
262
|
+
#{shell_command} -o scripts -c -t my/admin-tools '*.sh'
|
263
|
+
|
264
|
+
### Parameters
|
265
|
+
#{opts}
|
266
|
+
EOF
|
267
|
+
end
|
268
|
+
|
269
|
+
def summary
|
270
|
+
"Export version to a directory"
|
271
|
+
end
|
272
|
+
|
273
|
+
def opts
|
274
|
+
OptionParser.new do |opts|
|
275
|
+
opts.banner = ""
|
276
|
+
opts.on("-o", "--output-directory INPUT-DIR", "Input directory") do |v|
|
277
|
+
out(v)
|
278
|
+
end
|
279
|
+
opts.on("--tags TAGS", "Select files with matching tag") do |v|
|
280
|
+
@exporter.find_files.tags(v.split(","))
|
281
|
+
end
|
282
|
+
opts.on("-t", "--test", "Test version before export") do |v|
|
283
|
+
@exporter.test_dependencies=true
|
284
|
+
end
|
285
|
+
opts.on("-c", "--copy", "Exported files are copied instead of linked") do |v|
|
286
|
+
@exporter.copy=true
|
287
|
+
end
|
288
|
+
end
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
# Sets status for version
|
293
|
+
class VersionStatus
|
294
|
+
attr_chain :shell_command, :require
|
295
|
+
|
296
|
+
def execute(ctx, args)
|
297
|
+
@repository = "local"
|
298
|
+
command = args.delete_at(0)
|
299
|
+
case command
|
300
|
+
when "add"
|
301
|
+
version, key_value, *rest = args
|
302
|
+
key, value = key_value.split("=")
|
303
|
+
flags = rest.to_h("=")
|
304
|
+
repository = ctx.ki_home.repository(@repository)
|
305
|
+
repository.version(version).statuses.add_status(key, value, flags)
|
306
|
+
when "order"
|
307
|
+
component, key, values_str = args
|
308
|
+
repository = ctx.ki_home.repository(@repository)
|
309
|
+
repository.component(component).status_info.edit_data do |info|
|
310
|
+
info.cached_data[key]=values_str.split(",")
|
311
|
+
end
|
312
|
+
else
|
313
|
+
raise "Not supported '#{command}'"
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
def help
|
318
|
+
<<EOF
|
319
|
+
"#{shell_command}" sets status values to versions and sets status value order to component.
|
320
|
+
|
321
|
+
Status value order is used to determine which statuses match version queries:
|
322
|
+
|
323
|
+
my/component:maturity>alpha
|
324
|
+
|
325
|
+
### Examples
|
326
|
+
|
327
|
+
#{shell_command} add my/component/1.2.3 Smoke=Green action=path/123
|
328
|
+
#{shell_command} order my/component maturity alpha,beta,gamma
|
329
|
+
EOF
|
330
|
+
end
|
331
|
+
|
332
|
+
def summary
|
333
|
+
"Add status values to version"
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
# Shows information about a version
|
338
|
+
class ShowVersion
|
339
|
+
attr_chain :shell_command, :require
|
340
|
+
|
341
|
+
def execute(ctx, args)
|
342
|
+
finder = ctx.ki_home.finder
|
343
|
+
versions = opts.parse(args).map { |v| finder.version(v) }
|
344
|
+
if @file
|
345
|
+
versions.unshift Version.create_version(@file, @input_dir)
|
346
|
+
end
|
347
|
+
versions.each do |ver|
|
348
|
+
VersionIterator.new.finder(finder).version(ver).iterate_versions do |version|
|
349
|
+
metadata = version.metadata
|
350
|
+
puts "Version: #{metadata.version_id}"
|
351
|
+
if metadata.source.size > 0
|
352
|
+
puts "Source: #{map_to_csl(metadata.source)}"
|
353
|
+
end
|
354
|
+
if metadata.dependencies.size > 0
|
355
|
+
puts "Dependencies(#{metadata.dependencies.size}):"
|
356
|
+
metadata.dependencies.each do |dep|
|
357
|
+
dep_data = dep.dup
|
358
|
+
dep_ops = dep_data.delete("operations")
|
359
|
+
puts "#{dep_data.delete("version_id")}: #{map_to_csl(dep_data)}"
|
360
|
+
if dep_ops && dep_ops.size > 0
|
361
|
+
puts "Depedency operations:"
|
362
|
+
dep_ops.each do |op|
|
363
|
+
puts op.join(" ")
|
364
|
+
end
|
365
|
+
end
|
366
|
+
end
|
367
|
+
end
|
368
|
+
if metadata.files.size > 0
|
369
|
+
puts "Files(#{metadata.files.size}):"
|
370
|
+
metadata.files.each do |file|
|
371
|
+
file_data = file.dup
|
372
|
+
puts "#{file_data.delete("path")} - size: #{file_data.delete("size")}, #{map_to_csl(file_data)}"
|
373
|
+
end
|
374
|
+
end
|
375
|
+
if metadata.operations.size > 0
|
376
|
+
puts "Version operations(#{metadata.operations.size}):"
|
377
|
+
metadata.operations.each do |op|
|
378
|
+
puts op.join(" ")
|
379
|
+
end
|
380
|
+
end
|
381
|
+
if @dirs
|
382
|
+
puts "Version directories: #{version.versions.map { |v| v.path }.join(", ")}"
|
383
|
+
end
|
384
|
+
if !@recursive
|
385
|
+
break
|
386
|
+
end
|
387
|
+
end
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
def map_to_csl(map)
|
392
|
+
map.sort.map { |k, v| "#{k}=#{Array.wrap(v).join(",")}" }.join(", ")
|
393
|
+
end
|
394
|
+
|
395
|
+
def help
|
396
|
+
<<EOF
|
397
|
+
"#{shell_command}" prints information about version or versions and their dependencies
|
398
|
+
|
399
|
+
### Examples
|
400
|
+
|
401
|
+
#{shell_command} -r -d my/component/23 my/product/127
|
402
|
+
#{shell_command} -f ki-version.json -i binary-dir
|
403
|
+
EOF
|
404
|
+
end
|
405
|
+
|
406
|
+
def summary
|
407
|
+
"Prints information about version or versions"
|
408
|
+
end
|
409
|
+
|
410
|
+
def opts
|
411
|
+
OptionParser.new do |opts|
|
412
|
+
opts.banner = ""
|
413
|
+
opts.on("-r", "--recursive", "Shows version's dependencies.") do |v|
|
414
|
+
@recursive = true
|
415
|
+
end
|
416
|
+
opts.on("-d", "--dirs", "Shows version's directories.") do |v|
|
417
|
+
@dirs = true
|
418
|
+
end
|
419
|
+
opts.on("-f", "--file FILE", "Version source file. By default uses file's directory as source for binary files.") do |v|
|
420
|
+
if @input_dir.nil?
|
421
|
+
dir = File.dirname(v)
|
422
|
+
@input_dir = dir != "." ? dir : Dir.pwd
|
423
|
+
end
|
424
|
+
@file = v
|
425
|
+
end
|
426
|
+
opts.on("-i", "--input-directory INPUT-DIR", "Binary file input directory") do |v|
|
427
|
+
@input_dir = v
|
428
|
+
end
|
429
|
+
end
|
430
|
+
end
|
431
|
+
end
|
432
|
+
|
433
|
+
# Sets status for version
|
434
|
+
class VersionSearch
|
435
|
+
attr_chain :shell_command, :require
|
436
|
+
|
437
|
+
def execute(ctx, args)
|
438
|
+
finder = ctx.ki_home.finder
|
439
|
+
args.each do |arg|
|
440
|
+
version = finder.version(arg)
|
441
|
+
if version
|
442
|
+
puts version.version_id
|
443
|
+
else
|
444
|
+
matcher = FileRegexp.matcher(arg)
|
445
|
+
found_components = finder.components.keys.select { |name| matcher.match(name) }
|
446
|
+
if found_components.size > 0
|
447
|
+
puts "Found components(#{found_components.size}):"
|
448
|
+
puts found_components.join("\n")
|
449
|
+
else
|
450
|
+
puts "'#{arg}' does not match versions or components"
|
451
|
+
end
|
452
|
+
end
|
453
|
+
end
|
454
|
+
end
|
455
|
+
|
456
|
+
def help
|
457
|
+
<<EOF
|
458
|
+
"#{shell_command}" searches for versions and components.
|
459
|
+
|
460
|
+
### Examples
|
461
|
+
|
462
|
+
#{shell_command} my/component
|
463
|
+
#{shell_command} my/*
|
464
|
+
EOF
|
465
|
+
end
|
466
|
+
|
467
|
+
def summary
|
468
|
+
"Searches for versions and components"
|
469
|
+
end
|
470
|
+
end
|
471
|
+
|
472
|
+
KiCommand.register_cmd("version-build", BuildVersionMetadataFile)
|
473
|
+
KiCommand.register_cmd("version-test", TestVersion)
|
474
|
+
KiCommand.register_cmd("version-import", ImportVersion)
|
475
|
+
KiCommand.register_cmd("version-export", ExportVersion)
|
476
|
+
KiCommand.register_cmd("version-status", VersionStatus)
|
477
|
+
KiCommand.register_cmd("version-show", ShowVersion)
|
478
|
+
KiCommand.register_cmd("version-search", VersionSearch)
|
479
|
+
KiCommand.register("/hashing/sha1", SHA1)
|
480
|
+
KiCommand.register("/hashing/sha2", SHA2)
|
481
|
+
KiCommand.register("/hashing/md5", MD5)
|
482
|
+
|
483
|
+
end
|
@@ -0,0 +1,200 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
# Copyright 2012 Mikko Apo
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
|
17
|
+
module Ki
|
18
|
+
# Collects components from multiple repositories and provides methods to find components and versions
|
19
|
+
# @see Component
|
20
|
+
# @see Version
|
21
|
+
class RepositoryFinder
|
22
|
+
attr_reader :versions
|
23
|
+
attr_reader :components
|
24
|
+
|
25
|
+
def initialize(source)
|
26
|
+
@source = source
|
27
|
+
@components = load_all_components
|
28
|
+
@versions = HashCache.new
|
29
|
+
end
|
30
|
+
|
31
|
+
# Finds matching component by name
|
32
|
+
# @return [Component] matching component
|
33
|
+
def component(component)
|
34
|
+
@components[component]
|
35
|
+
end
|
36
|
+
|
37
|
+
# Finds version matching arguments, goes through versions in chronological order from latest to oldest.
|
38
|
+
# * note: block form can be used to iterate through all available version
|
39
|
+
# Supported search formats:
|
40
|
+
# * version("test/comp") -> latest version
|
41
|
+
# * version("test/comp:Smoke=green"), version("test/comp","Smoke"=>"green") -> latest version with matching status
|
42
|
+
# * version("test/comp"){|version| ... } -> iterates all available versions and returns latest version where block returns true, block argument is a Version
|
43
|
+
# * version("test/comp:maturity!=alpha") ->
|
44
|
+
# Component can define ordering for status values: {"maturity": ["alpha","beta","gamma"]}
|
45
|
+
# * version("test/comp:maturity>alpha") -> returns first version where maturity is beta or gamma
|
46
|
+
# * version("test/comp:maturity>=alpha") -> returns first version where maturity is alpha, beta or gamma
|
47
|
+
# * version("test/comp:maturity<beta") -> returns first version where maturity is alpha
|
48
|
+
# * version("test/comp:maturity<=beta") -> returns first version where maturity is alpha or beta
|
49
|
+
# Version supports also Component and Version parameters:
|
50
|
+
# * version(my_version) -> returns my_version
|
51
|
+
# * version(my_component, "Smoke:green") -> finds Version matching other parameters
|
52
|
+
# @return [Version] matching version
|
53
|
+
def version(*args, &block)
|
54
|
+
if args.empty?
|
55
|
+
raise "no parameters!"
|
56
|
+
end
|
57
|
+
status_rules = []
|
58
|
+
component_or_version = nil
|
59
|
+
dep_navigation_arr = []
|
60
|
+
args.each do |str|
|
61
|
+
if str.kind_of?(String)
|
62
|
+
dep_nav_arr = str.split("->")
|
63
|
+
strings = dep_nav_arr.delete_at(0).split(":")
|
64
|
+
dep_navigation_arr.concat(dep_nav_arr)
|
65
|
+
if component_or_version.nil?
|
66
|
+
component_or_version = strings.delete_at(0)
|
67
|
+
end
|
68
|
+
strings.each do |s|
|
69
|
+
status_rules << s.match(/(.*?)([<=>!]+)(.*)/).captures
|
70
|
+
end
|
71
|
+
elsif str.kind_of?(Hash)
|
72
|
+
str.each_pair do |k, v|
|
73
|
+
status_rules << [k, "=", v]
|
74
|
+
end
|
75
|
+
elsif str.kind_of?(Version)
|
76
|
+
return str
|
77
|
+
elsif str.kind_of?(Component) && component_or_version.nil?
|
78
|
+
component_or_version = str.component_id
|
79
|
+
else
|
80
|
+
raise "Not supported '#{str.inspect}'"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
if component = @components[component_or_version]
|
84
|
+
if status_rules.size > 0 || block
|
85
|
+
component.versions.each do |v|
|
86
|
+
ver = component.version_by_id(v.name)
|
87
|
+
ok = has_statuses(ver.statuses, status_rules, component)
|
88
|
+
if ok && block
|
89
|
+
ok = block.call(ver)
|
90
|
+
end
|
91
|
+
if ok
|
92
|
+
return ver
|
93
|
+
end
|
94
|
+
end
|
95
|
+
else
|
96
|
+
# picking latest version
|
97
|
+
version_name = component.versions.first.name
|
98
|
+
end
|
99
|
+
else
|
100
|
+
# user has defined an exact version
|
101
|
+
version_arr = component_or_version.split("/")
|
102
|
+
version_name = version_arr.delete_at(-1)
|
103
|
+
component_str = version_arr.join("/")
|
104
|
+
component = @components[component_str]
|
105
|
+
end
|
106
|
+
if component && version_name
|
107
|
+
ver = component.version_by_id(version_name)
|
108
|
+
if dep_navigation_arr
|
109
|
+
dep_navigation_arr.each do |dep_str|
|
110
|
+
dep_version_str = find_dep_by_name(ver, dep_str)
|
111
|
+
if dep_version_str.nil?
|
112
|
+
raise "Could not locate dependency '#{dep_str}' from '#{ver.version_id}'"
|
113
|
+
end
|
114
|
+
ver = version(dep_version_str)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
ver
|
118
|
+
else
|
119
|
+
nil
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def find_dep_by_name(ver, dep_str)
|
124
|
+
ver.metadata.dependencies.each do |dep|
|
125
|
+
if dep["name"] == dep_str
|
126
|
+
return dep["version_id"]
|
127
|
+
end
|
128
|
+
end
|
129
|
+
nil
|
130
|
+
end
|
131
|
+
|
132
|
+
def all_repositories(source=@source)
|
133
|
+
node = source
|
134
|
+
repositories = []
|
135
|
+
while (node)
|
136
|
+
repositories.concat(node.repositories.to_a)
|
137
|
+
if node.root?
|
138
|
+
break
|
139
|
+
end
|
140
|
+
node = node.parent
|
141
|
+
end
|
142
|
+
repositories
|
143
|
+
end
|
144
|
+
|
145
|
+
# Loads all Component from all repositories
|
146
|
+
# @param [KiHome] source
|
147
|
+
def load_all_components(source=@source)
|
148
|
+
components = HashCache.new
|
149
|
+
all_repositories(source).each do |info|
|
150
|
+
info.components.each do |component_info|
|
151
|
+
component = components.cache(component_info.component_id) do
|
152
|
+
Component.new.component_id(component_info.component_id).finder(self).components([])
|
153
|
+
end
|
154
|
+
component.components << component_info
|
155
|
+
end
|
156
|
+
end
|
157
|
+
components
|
158
|
+
end
|
159
|
+
|
160
|
+
# locates first matching status for the key and checks if that is ok for the block
|
161
|
+
def check_status_value(version_statuses, key, &block)
|
162
|
+
version_statuses.each do |status_key, status_value|
|
163
|
+
if status_key == key
|
164
|
+
return block.call(status_value)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
false
|
168
|
+
end
|
169
|
+
|
170
|
+
# Checks if version's statuses match status_rules
|
171
|
+
def has_statuses(version_statuses_original, status_rules, component)
|
172
|
+
ret = true
|
173
|
+
if status_rules.size > 0
|
174
|
+
ret = false
|
175
|
+
version_statuses = version_statuses_original.reverse # latest first
|
176
|
+
status_info = component.status_info
|
177
|
+
# go through each rule and see if this version has matching status
|
178
|
+
status_rules.each do |key, op, value|
|
179
|
+
if order = status_info[key]
|
180
|
+
rule_value_index = order.index(value)
|
181
|
+
end
|
182
|
+
op_action = {
|
183
|
+
"=" => ->(status_value) { status_value == value },
|
184
|
+
"!=" => ->(status_value) { status_value != value },
|
185
|
+
"<" => ->(status_value) { order.index(status_value) < rule_value_index },
|
186
|
+
">" => ->(status_value) { order.index(status_value) > rule_value_index },
|
187
|
+
">=" => ->(status_value) { order.index(status_value) >= rule_value_index },
|
188
|
+
"<=" => ->(status_value) { order.index(status_value) <= rule_value_index }
|
189
|
+
}.fetch(op) do
|
190
|
+
raise "Not supported status operation: '#{key}#{op}#{value}'"
|
191
|
+
end
|
192
|
+
ret = check_status_value(version_statuses, key) do |status_value|
|
193
|
+
op_action.call(status_value)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
ret
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|