ki-repo 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/LICENSE.txt +1 -1
- data/README.md +21 -0
- data/VERSION +1 -1
- data/bin/ki +1 -1
- data/docs/backlog.md +10 -10
- data/docs/development.md +3 -1
- data/docs/ki_commands.md +35 -6
- data/docs/repository_basics.md +12 -1
- data/docs/writing_extensions.md +25 -7
- data/lib/cmd/cmd.rb +69 -54
- data/lib/cmd/user_pref_cmd.rb +47 -37
- data/lib/cmd/version_cmd.rb +116 -113
- data/lib/data_access/repository_finder.rb +1 -1
- data/lib/data_access/repository_info.rb +1 -1
- data/lib/data_access/version_helpers.rb +11 -11
- data/lib/data_access/version_iterators.rb +6 -6
- data/lib/data_access/version_operations.rb +1 -1
- data/lib/data_storage/dir_base.rb +20 -18
- data/lib/data_storage/ki_home.rb +1 -1
- data/lib/data_storage/ki_json.rb +4 -2
- data/lib/data_storage/repository.rb +1 -1
- data/lib/data_storage/version_metadata.rb +54 -39
- data/lib/ki_repo_all.rb +7 -1
- data/lib/util/attr_chain.rb +2 -2
- data/lib/util/exception_catcher.rb +1 -1
- data/lib/util/hash.rb +1 -1
- data/lib/util/hash_cache.rb +1 -1
- data/lib/util/hash_log.rb +93 -0
- data/lib/util/ruby_extensions.rb +58 -21
- data/lib/util/service_registry.rb +1 -1
- data/lib/util/shell.rb +96 -0
- data/lib/util/simple_optparse.rb +6 -4
- data/lib/util/test.rb +13 -1
- data/lib/web/default_rack_handler.rb +43 -0
- data/lib/web/rack_cmd.rb +161 -0
- data/lib/web/test_browser.rb +69 -0
- metadata +80 -24
@@ -1,6 +1,6 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
|
3
|
-
# Copyright 2012 Mikko Apo
|
3
|
+
# Copyright 2012-2013 Mikko Apo
|
4
4
|
#
|
5
5
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
6
|
# you may not use this file except in compliance with the License.
|
@@ -104,14 +104,14 @@ module Ki
|
|
104
104
|
end
|
105
105
|
|
106
106
|
def import_from_metadata(metadata, source=nil)
|
107
|
-
if
|
107
|
+
if specific_version_id && create_new_version
|
108
108
|
raise "Can't define both specific_version_id '#{specific_version_id}' and create_new_version '#{create_new_version}'!"
|
109
109
|
end
|
110
110
|
|
111
|
-
if
|
112
|
-
version_id =
|
113
|
-
elsif
|
114
|
-
component_id =
|
111
|
+
if specific_version_id
|
112
|
+
version_id = specific_version_id
|
113
|
+
elsif create_new_version
|
114
|
+
component_id = create_new_version
|
115
115
|
version = finder.version(component_id)
|
116
116
|
if version
|
117
117
|
id = version.version_id.split("/").last
|
@@ -140,7 +140,7 @@ module Ki
|
|
140
140
|
metadata_dir.metadata.cached_data = metadata.cached_data
|
141
141
|
metadata_dir.metadata.version_id = version_id
|
142
142
|
metadata_dir.metadata.save
|
143
|
-
if
|
143
|
+
if move_files
|
144
144
|
FileUtils.rm(metadata.path)
|
145
145
|
end
|
146
146
|
source_dirs = []
|
@@ -158,7 +158,7 @@ module Ki
|
|
158
158
|
|
159
159
|
|
160
160
|
def delete_empty_source_dirs(source, source_dirs)
|
161
|
-
if
|
161
|
+
if move_files
|
162
162
|
expanded_source_dirs = {}
|
163
163
|
source_dirs.each do |d|
|
164
164
|
dir_entries(d).each do |expanded|
|
@@ -179,7 +179,7 @@ module Ki
|
|
179
179
|
arr = str.split("/")
|
180
180
|
ret = []
|
181
181
|
c = arr.size
|
182
|
-
while
|
182
|
+
while c > 0
|
183
183
|
ret << File.join(arr[0..c])
|
184
184
|
c-=1
|
185
185
|
end
|
@@ -187,7 +187,7 @@ module Ki
|
|
187
187
|
end
|
188
188
|
|
189
189
|
def to_repo(src, dest)
|
190
|
-
if
|
190
|
+
if move_files
|
191
191
|
FileUtils.mv(src, dest)
|
192
192
|
else
|
193
193
|
FileUtils.cp(src, dest)
|
@@ -223,7 +223,7 @@ module Ki
|
|
223
223
|
if dir != "."
|
224
224
|
FileUtils.mkdir_p File.join(out, dir)
|
225
225
|
end
|
226
|
-
if
|
226
|
+
if copy
|
227
227
|
FileUtils.cp(full_path, File.join(out, file_path))
|
228
228
|
else
|
229
229
|
FileUtils.ln_sf(full_path, File.join(out, file_path))
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
|
3
|
-
# Copyright 2012 Mikko Apo
|
3
|
+
# Copyright 2012-2013 Mikko Apo
|
4
4
|
#
|
5
5
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
6
|
# you may not use this file except in compliance with the License.
|
@@ -25,7 +25,7 @@ module Ki
|
|
25
25
|
attr_chain :finder, -> { version.component.finder }
|
26
26
|
attr_chain :block
|
27
27
|
attr_chain :internals
|
28
|
-
attr_chain :exclude_dependencies, -> { [] }, :convert => lambda { |list| Array
|
28
|
+
attr_chain :exclude_dependencies, -> { [] }, :convert => lambda { |list| Array(list).map { |s| /#{s}/ } }
|
29
29
|
|
30
30
|
def iterate_versions(&block)
|
31
31
|
start_iteration do |version_iterator|
|
@@ -80,10 +80,10 @@ module Ki
|
|
80
80
|
end
|
81
81
|
|
82
82
|
class FileFinder < VersionIterator
|
83
|
-
attr_chain :files, -> { [] }, :convert => lambda { |list| Array
|
84
|
-
attr_chain :exclude_files, -> { [] }, :convert => lambda { |list| Array
|
85
|
-
attr_chain :tags, -> { [] }, :convert => lambda { |list| Array
|
86
|
-
attr_chain :exclude_tags, -> { [] }, :convert => lambda { |list| Array
|
83
|
+
attr_chain :files, -> { [] }, :convert => lambda { |list| Array(list).map { |s| FileRegexp.matcher(s) } }
|
84
|
+
attr_chain :exclude_files, -> { [] }, :convert => lambda { |list| Array(list).map { |s| FileRegexp.matcher(s) } }
|
85
|
+
attr_chain :tags, -> { [] }, :convert => lambda { |list| Array(list)}
|
86
|
+
attr_chain :exclude_tags, -> { [] }, :convert => lambda { |list| Array(list)}
|
87
87
|
|
88
88
|
def file_map
|
89
89
|
start_iteration do |ver_iterator|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
|
3
|
-
# Copyright 2012 Mikko Apo
|
3
|
+
# Copyright 2012-2013 Mikko Apo
|
4
4
|
#
|
5
5
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
6
|
# you may not use this file except in compliance with the License.
|
@@ -15,9 +15,25 @@
|
|
15
15
|
# limitations under the License.
|
16
16
|
|
17
17
|
module Ki
|
18
|
-
|
18
|
+
|
19
|
+
class DirectoryBase
|
19
20
|
attr_chain :parent, :require
|
20
21
|
|
22
|
+
def initialize(path)
|
23
|
+
init_from_path(path)
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.find!(path, *locations)
|
27
|
+
locations.each do |loc|
|
28
|
+
dest = loc.go(path)
|
29
|
+
if dest.exists?
|
30
|
+
return dest
|
31
|
+
end
|
32
|
+
end
|
33
|
+
raise "Could not find '#{path}' from '#{locations.map { |l| l.path }.join("', '")}'"
|
34
|
+
end
|
35
|
+
|
36
|
+
|
21
37
|
def init_from_path(path)
|
22
38
|
@path = path
|
23
39
|
end
|
@@ -84,23 +100,9 @@ module Ki
|
|
84
100
|
def child(name)
|
85
101
|
DirectoryBase.new(name)
|
86
102
|
end
|
87
|
-
end
|
88
|
-
|
89
|
-
class DirectoryBase
|
90
|
-
include DirectoryBaseModule
|
91
|
-
|
92
|
-
def initialize(path)
|
93
|
-
init_from_path(path)
|
94
|
-
end
|
95
103
|
|
96
|
-
def
|
97
|
-
|
98
|
-
dest = loc.go(path)
|
99
|
-
if dest.exists?
|
100
|
-
return dest
|
101
|
-
end
|
102
|
-
end
|
103
|
-
raise "Could not find '#{path}' from '#{locations.map { |l| l.path }.join("', '")}'"
|
104
|
+
def empty?(*sub_path)
|
105
|
+
Dir.entries(go(*sub_path).path).size == 2
|
104
106
|
end
|
105
107
|
end
|
106
108
|
end
|
data/lib/data_storage/ki_home.rb
CHANGED
data/lib/data_storage/ki_json.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
|
3
|
-
# Copyright 2012 Mikko Apo
|
3
|
+
# Copyright 2012-2013 Mikko Apo
|
4
4
|
#
|
5
5
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
6
|
# you may not use this file except in compliance with the License.
|
@@ -71,7 +71,9 @@ module Ki
|
|
71
71
|
|
72
72
|
def add_item(obj)
|
73
73
|
edit_data do
|
74
|
-
|
74
|
+
if !@cached_data.include?(obj)
|
75
|
+
@cached_data << obj
|
76
|
+
end
|
75
77
|
end
|
76
78
|
create_list_item(obj)
|
77
79
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
|
3
|
-
# Copyright 2012 Mikko Apo
|
3
|
+
# Copyright 2012-2013 Mikko Apo
|
4
4
|
#
|
5
5
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
6
|
# you may not use this file except in compliance with the License.
|
@@ -29,16 +29,6 @@ module Ki
|
|
29
29
|
attr_chain :operations, -> { Array.new }, :accessor => CachedData
|
30
30
|
attr_chain :dependencies, -> { Array.new }, :accessor => CachedData
|
31
31
|
|
32
|
-
def add_file_info(name, size, *args)
|
33
|
-
extra = (args.select { |arg| arg.kind_of?(Hash) }.size!(0..1).first or {})
|
34
|
-
tags = (args - [extra]).flatten.uniq
|
35
|
-
file_hash = {"path" => name, "size" => size}.merge(extra)
|
36
|
-
if tags.size > 0
|
37
|
-
file_hash["tags"]=tags
|
38
|
-
end
|
39
|
-
files << file_hash
|
40
|
-
end
|
41
|
-
|
42
32
|
# Comma separated list of dependency arguments
|
43
33
|
# * dependency parameters can be given in the hash
|
44
34
|
# TODO: version_id should be resolved through Version
|
@@ -58,41 +48,27 @@ module Ki
|
|
58
48
|
operations << args
|
59
49
|
end
|
60
50
|
|
61
|
-
def VersionMetadataFile.calculate_hashes(full_path, digester_ids)
|
62
|
-
digesters = {}
|
63
|
-
digester_ids.each do |h|
|
64
|
-
digesters[h] = KiCommand::KiExtensions.find!(File.join("/hashing", h)).digest
|
65
|
-
end
|
66
|
-
algos = digesters.values
|
67
|
-
File.open(full_path, "r") do |io|
|
68
|
-
while (!io.eof)
|
69
|
-
buf = io.readpartial(1024)
|
70
|
-
algos.each do |digester|
|
71
|
-
digester.update(buf)
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
75
|
-
digesters.each_pair do |h, digester|
|
76
|
-
digesters[h]=digester.hexdigest
|
77
|
-
end
|
78
|
-
digesters
|
79
|
-
end
|
80
|
-
|
81
51
|
# Processes all files from source that match patterns and for each file calculates hashes and stores tags based on default_parameters
|
82
52
|
def add_files(source, patterns, default_parameters={})
|
83
|
-
files_or_dirs = Array
|
53
|
+
files_or_dirs = Array(patterns).map do |pattern|
|
84
54
|
Dir.glob(File.join(source, pattern))
|
85
55
|
end.flatten
|
86
56
|
|
87
|
-
files =
|
88
|
-
|
89
|
-
|
57
|
+
files = []
|
58
|
+
|
59
|
+
files_or_dirs.each do |file_or_dir|
|
60
|
+
if File.file?(file_or_dir)
|
61
|
+
files << file_or_dir
|
90
62
|
else
|
91
|
-
file_or_dir
|
63
|
+
Dir.glob(File.join(file_or_dir, "**/*")).each do |file|
|
64
|
+
if File.file?(file)
|
65
|
+
files << file
|
66
|
+
end
|
67
|
+
end
|
92
68
|
end
|
93
|
-
end
|
69
|
+
end
|
94
70
|
|
95
|
-
files.each do |file|
|
71
|
+
files.sort.each do |file|
|
96
72
|
add_file(source, file, default_parameters)
|
97
73
|
end
|
98
74
|
self
|
@@ -106,7 +82,7 @@ module Ki
|
|
106
82
|
extra["executable"]=true
|
107
83
|
end
|
108
84
|
if parameters["tags"]
|
109
|
-
tags = Array
|
85
|
+
tags = Array(parameters["tags"])
|
110
86
|
if tags && tags.size > 0
|
111
87
|
extra["tags"]= tags
|
112
88
|
end
|
@@ -119,6 +95,45 @@ module Ki
|
|
119
95
|
end
|
120
96
|
add_file_info(full_path[root.size+1..-1], size, extra)
|
121
97
|
end
|
98
|
+
|
99
|
+
def add_file_info(name, size, *args)
|
100
|
+
extra = (args.select { |arg| arg.kind_of?(Hash) }.size!(0..1).first or {})
|
101
|
+
tags = (args - [extra]).flatten.uniq
|
102
|
+
file_hash = {"path" => name, "size" => size}.merge(extra)
|
103
|
+
if tags.size > 0
|
104
|
+
file_hash["tags"]=tags
|
105
|
+
end
|
106
|
+
files.each do |f|
|
107
|
+
if f["path"] == name
|
108
|
+
if f == file_hash
|
109
|
+
return
|
110
|
+
else
|
111
|
+
raise "'#{name}' has already been added to version, but with different attributes:\n- old: #{f.inspect}\n- new: #{file_hash.inspect}"
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
files << file_hash
|
116
|
+
end
|
117
|
+
|
118
|
+
def VersionMetadataFile.calculate_hashes(full_path, digester_ids)
|
119
|
+
digesters = {}
|
120
|
+
digester_ids.each do |h|
|
121
|
+
digesters[h] = KiCommand::KiExtensions.find!(File.join("/hashing", h)).digest
|
122
|
+
end
|
123
|
+
algos = digesters.values
|
124
|
+
File.open(full_path, "r") do |io|
|
125
|
+
while (!io.eof)
|
126
|
+
buf = io.readpartial(1024)
|
127
|
+
algos.each do |digester|
|
128
|
+
digester.update(buf)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
digesters.each_pair do |h, digester|
|
133
|
+
digesters[h]=digester.hexdigest
|
134
|
+
end
|
135
|
+
digesters
|
136
|
+
end
|
122
137
|
end
|
123
138
|
|
124
139
|
module DependencyMethods
|
data/lib/ki_repo_all.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
|
3
|
-
# Copyright 2012 Mikko Apo
|
3
|
+
# Copyright 2012-2013 Mikko Apo
|
4
4
|
#
|
5
5
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
6
|
# you may not use this file except in compliance with the License.
|
@@ -23,7 +23,9 @@ require_relative 'util/test'
|
|
23
23
|
require_relative 'util/service_registry'
|
24
24
|
require_relative 'util/hash'
|
25
25
|
require_relative 'util/hash_cache'
|
26
|
+
require_relative 'util/hash_log'
|
26
27
|
require_relative 'util/simple_optparse'
|
28
|
+
require_relative 'util/shell'
|
27
29
|
|
28
30
|
require_relative 'data_storage/dir_base'
|
29
31
|
require_relative 'data_storage/ki_json'
|
@@ -40,3 +42,7 @@ require_relative 'data_access/version_iterators'
|
|
40
42
|
require_relative 'cmd/cmd'
|
41
43
|
require_relative 'cmd/version_cmd'
|
42
44
|
require_relative 'cmd/user_pref_cmd'
|
45
|
+
|
46
|
+
require_relative 'web/default_rack_handler'
|
47
|
+
require_relative 'web/rack_cmd'
|
48
|
+
require_relative 'web/test_browser'
|
data/lib/util/attr_chain.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
|
3
|
-
# Copyright 2012 Mikko Apo
|
3
|
+
# Copyright 2012-2013 Mikko Apo
|
4
4
|
#
|
5
5
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
6
|
# you may not use this file except in compliance with the License.
|
@@ -45,7 +45,7 @@
|
|
45
45
|
# * :default makes it easy to isolate functionality to a default value while still making it easy to override the default behaviour
|
46
46
|
# * :default adds easy lazy evalution and memoization to the attribute, default value is evaluated only if needed
|
47
47
|
# * Testing becomes easier when objects have more exposed fields
|
48
|
-
# * :require converts tricky nil exceptions in to useful errors. Instead of the "undefined method
|
48
|
+
# * :require converts tricky nil exceptions in to useful errors. Instead of the "undefined method 'bar' for nil:NilClass" you get a good error message that states which field was not defined
|
49
49
|
# foo.name.bar # if name has not been defined, raises "'name' has not been set" exception
|
50
50
|
# * :immutable, :valid and :convert make complex validations and converts easy
|
51
51
|
#
|
data/lib/util/hash.rb
CHANGED
data/lib/util/hash_cache.rb
CHANGED
@@ -0,0 +1,93 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
# Copyright 2012-2013 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
|
+
# Generate hash object based log entries.
|
19
|
+
# @see log
|
20
|
+
module HashLog
|
21
|
+
HashLogThreadCurrentKey = :hash_log
|
22
|
+
HashLogMutex = Mutex.new
|
23
|
+
|
24
|
+
# Create a hash log entry or return current hash log entry
|
25
|
+
# * Supports hierarchic logs, where log entry can contain any number of sub entries
|
26
|
+
# * Supports parallel execution, threads can log in to one log structure
|
27
|
+
# * Logs start and stop time of execution
|
28
|
+
#
|
29
|
+
# Hierarchic logging works by keeping a stack of log entries in Thread local store
|
30
|
+
#
|
31
|
+
# @see set_hash_log_root_for_thread
|
32
|
+
# @see hash_log_current
|
33
|
+
def log(*args, &block)
|
34
|
+
current_log_entry = hash_log_current
|
35
|
+
# return
|
36
|
+
if args.empty? && block.nil?
|
37
|
+
current_log_entry
|
38
|
+
else
|
39
|
+
new_entry = {"start" => Time.now}
|
40
|
+
if args.first.kind_of?(String)
|
41
|
+
new_entry["name"] = args.delete_at(0)
|
42
|
+
end
|
43
|
+
if args.first.kind_of?(Hash)
|
44
|
+
new_entry.merge!(args.first)
|
45
|
+
end
|
46
|
+
log_list = nil
|
47
|
+
if current_log_entry
|
48
|
+
# there is current log entry, create a new sub log entry
|
49
|
+
HashLogMutex.synchronize do
|
50
|
+
log_list = current_log_entry["logs"] ||= []
|
51
|
+
end
|
52
|
+
else
|
53
|
+
# append new_entry to end of log list
|
54
|
+
log_list = Thread.current[HashLogThreadCurrentKey]
|
55
|
+
end
|
56
|
+
HashLogMutex.synchronize do
|
57
|
+
log_list << new_entry
|
58
|
+
end
|
59
|
+
if block
|
60
|
+
HashLogMutex.synchronize do
|
61
|
+
Thread.current[HashLogThreadCurrentKey] << new_entry
|
62
|
+
end
|
63
|
+
begin
|
64
|
+
block.call new_entry
|
65
|
+
rescue Exception => e
|
66
|
+
new_entry["exception"] = e.message
|
67
|
+
new_entry["backtrace"] = e.backtrace.join("\n")
|
68
|
+
raise
|
69
|
+
ensure
|
70
|
+
HashLogMutex.synchronize do
|
71
|
+
Thread.current[HashLogThreadCurrentKey].delete(new_entry)
|
72
|
+
end
|
73
|
+
new_entry["end"] = Time.now
|
74
|
+
end
|
75
|
+
else
|
76
|
+
new_entry
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def set_hash_log_root_for_thread(root)
|
82
|
+
HashLogMutex.synchronize do
|
83
|
+
(Thread.current[HashLogThreadCurrentKey] ||= []) << root
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def hash_log_current
|
88
|
+
HashLogMutex.synchronize do
|
89
|
+
(Thread.current[HashLogThreadCurrentKey] ||= []).last
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|