ki-repo 0.1.1 → 0.1.2
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 +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
data/lib/util/ruby_extensions.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.
|
@@ -82,14 +82,6 @@ end
|
|
82
82
|
|
83
83
|
class Array
|
84
84
|
include Ki::KiEnumerable
|
85
|
-
|
86
|
-
def Array.wrap(maybe_arr)
|
87
|
-
if maybe_arr.kind_of?(Array)
|
88
|
-
maybe_arr
|
89
|
-
else
|
90
|
-
[maybe_arr]
|
91
|
-
end
|
92
|
-
end
|
93
85
|
end
|
94
86
|
|
95
87
|
module Enumerable
|
@@ -109,7 +101,7 @@ class File
|
|
109
101
|
end
|
110
102
|
end
|
111
103
|
FileUtils.mv(tmp, dest)
|
112
|
-
rescue Exception
|
104
|
+
rescue Exception
|
113
105
|
FileUtils.remove_entry_secure(tmp)
|
114
106
|
raise
|
115
107
|
end
|
@@ -117,17 +109,6 @@ class File
|
|
117
109
|
end
|
118
110
|
|
119
111
|
class Hash
|
120
|
-
original_get = self.instance_method(:[])
|
121
|
-
|
122
|
-
define_method(:[]) do |key, default=nil|
|
123
|
-
value = original_get.bind(self).call(key)
|
124
|
-
if value || include?(key)
|
125
|
-
value
|
126
|
-
else
|
127
|
-
default
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
131
112
|
def require(key)
|
132
113
|
if !include?(key)
|
133
114
|
raise "'#{key}' is not defined!"
|
@@ -135,3 +116,59 @@ class Hash
|
|
135
116
|
self[key]
|
136
117
|
end
|
137
118
|
end
|
119
|
+
|
120
|
+
class Object
|
121
|
+
# if block raises an exception outputs the error message. returns block's exit value or reraises the exception
|
122
|
+
def show_errors(&block)
|
123
|
+
begin
|
124
|
+
block.call
|
125
|
+
rescue Exception => e
|
126
|
+
puts "Exception '#{e.message}':\n#{e.backtrace.join("\n")}"
|
127
|
+
raise
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
# Resolves fully qualified class named including modules: Ki::KiCommand
|
132
|
+
def Object.const_get_full(full_class_name)
|
133
|
+
class_or_module = self
|
134
|
+
full_class_name.split("::").each do |name|
|
135
|
+
class_or_module = class_or_module.const_get(name)
|
136
|
+
end
|
137
|
+
class_or_module
|
138
|
+
end
|
139
|
+
|
140
|
+
def try(retries, retry_sleep, &block)
|
141
|
+
c = 0
|
142
|
+
start = Time.now
|
143
|
+
while c < retries
|
144
|
+
begin
|
145
|
+
return block.call(c+1)
|
146
|
+
rescue Exception => e
|
147
|
+
c += 1
|
148
|
+
if c < retries
|
149
|
+
sleep retry_sleep
|
150
|
+
else
|
151
|
+
raise e.class, e.message + " (tried #{c} times, waited #{sprintf("%.2f", Time.now - start)} seconds)", e.backtrace
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
class String
|
159
|
+
def split_strip(separator=",")
|
160
|
+
split(separator).map{|s| s.strip}
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
module ObjectSpace
|
165
|
+
def ObjectSpace.all_classes
|
166
|
+
arr = []
|
167
|
+
each_object do |o|
|
168
|
+
if o.kind_of?(Class)
|
169
|
+
arr << o
|
170
|
+
end
|
171
|
+
end
|
172
|
+
arr
|
173
|
+
end
|
174
|
+
end
|
data/lib/util/shell.rb
ADDED
@@ -0,0 +1,96 @@
|
|
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
|
+
# Executes a command and logs using HashLog
|
19
|
+
class ShellCommandExecution
|
20
|
+
attr_chain :cmd, :require
|
21
|
+
attr_chain :exitstatus, :require
|
22
|
+
attr_chain :pid, :require
|
23
|
+
attr_chain :env, :require
|
24
|
+
attr_chain :options, :require
|
25
|
+
attr_chain :out, :require
|
26
|
+
attr_chain :err, :require
|
27
|
+
end
|
28
|
+
class DummyHashLog
|
29
|
+
def log(*arr, &block)
|
30
|
+
block.call
|
31
|
+
end
|
32
|
+
end
|
33
|
+
class HashLogShell
|
34
|
+
attr_chain :env
|
35
|
+
attr_chain :chdir
|
36
|
+
attr_chain :ignore_error
|
37
|
+
attr_reader :previous
|
38
|
+
attr_chain :root_log, -> { DummyHashLog.new }
|
39
|
+
|
40
|
+
def spawn(*arr)
|
41
|
+
run_env = {}
|
42
|
+
run_options = {}
|
43
|
+
if (env)
|
44
|
+
run_env.merge!(env)
|
45
|
+
end
|
46
|
+
if (arr.first.kind_of?(Hash))
|
47
|
+
run_env.merge!(arr.delete_at(0))
|
48
|
+
end
|
49
|
+
if (arr.last.kind_of?(Hash))
|
50
|
+
run_options.merge!(arr.delete_at(-1))
|
51
|
+
end
|
52
|
+
if (chdir && !run_options[:chdir])
|
53
|
+
run_options[:chdir] = chdir
|
54
|
+
end
|
55
|
+
rout = wout = rerr = werr = nil
|
56
|
+
if (!run_options[:out])
|
57
|
+
rout, wout = IO.pipe
|
58
|
+
run_options[:out]=wout
|
59
|
+
end
|
60
|
+
if (!run_options[:err])
|
61
|
+
rerr, werr = IO.pipe
|
62
|
+
run_options[:err]=werr
|
63
|
+
end
|
64
|
+
cmd = arr.first
|
65
|
+
root_log.log("Shell command '#{cmd}'") do
|
66
|
+
pid = system_spawn(run_env, cmd, run_options)
|
67
|
+
pid, status = Process.waitpid2(pid)
|
68
|
+
exitstatus = status.exitstatus
|
69
|
+
@previous = ShellCommandExecution.new.
|
70
|
+
cmd(cmd).
|
71
|
+
exitstatus(exitstatus).
|
72
|
+
pid(pid).
|
73
|
+
env(run_env).
|
74
|
+
options(run_options)
|
75
|
+
if rout
|
76
|
+
wout.close
|
77
|
+
@previous.out(rout.readlines.join("\n"))
|
78
|
+
rout.close
|
79
|
+
end
|
80
|
+
if rerr
|
81
|
+
werr.close
|
82
|
+
@previous.err(rerr.readlines.join("\n"))
|
83
|
+
rerr.close
|
84
|
+
end
|
85
|
+
if (exitstatus != 0 && !ignore_error)
|
86
|
+
raise "Shell command '#{cmd}' failed with exit code #{exitstatus}"
|
87
|
+
end
|
88
|
+
@previous
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def system_spawn(run_env, cmd, run_options)
|
93
|
+
Process.spawn(run_env, cmd, run_options)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
data/lib/util/simple_optparse.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.
|
@@ -36,12 +36,14 @@ module Ki
|
|
36
36
|
if block.nil?
|
37
37
|
raise "Option without parser block: " + args.join(", ")
|
38
38
|
end
|
39
|
-
if args.size == 3
|
40
|
-
short = args.delete_at(0)
|
39
|
+
if args.size == 2 || args.size == 3
|
40
|
+
short = args.size == 3 ? args.delete_at(0) : nil
|
41
41
|
long, *params = args.delete_at(0).split(" ")
|
42
42
|
comment = args.delete_at(0)
|
43
43
|
options_for_to_s << {short: short, long: long, comment: comment, params: params, block: block }
|
44
|
-
|
44
|
+
if short
|
45
|
+
options << {opt: short, comment: comment, params: params, block: block }
|
46
|
+
end
|
45
47
|
options << {opt: long, comment: comment, params: params, block: block }
|
46
48
|
else
|
47
49
|
raise "unsupported option configuration size: " + args.join(", ")
|
data/lib/util/test.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.
|
@@ -318,6 +318,18 @@ module Ki
|
|
318
318
|
def write(s)
|
319
319
|
self.<< s
|
320
320
|
end
|
321
|
+
def flush
|
322
|
+
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
def restore_extensions
|
327
|
+
original_commands = KiCommand::KiExtensions.dup
|
328
|
+
cleaners << lambda do
|
329
|
+
KiCommand::KiExtensions.clear
|
330
|
+
KiCommand::KiExtensions.register(original_commands)
|
331
|
+
end
|
321
332
|
end
|
333
|
+
|
322
334
|
end
|
323
335
|
end
|
@@ -0,0 +1,43 @@
|
|
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
|
+
|
19
|
+
# Tries to launch Rack handlers in default order
|
20
|
+
# @see RackCommand
|
21
|
+
class DefaultRackHandler
|
22
|
+
def run(rack_app, config={})
|
23
|
+
detect_rack_handler.run(rack_app, config) do |server|
|
24
|
+
@server = server
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def stop
|
29
|
+
@server.stop
|
30
|
+
end
|
31
|
+
|
32
|
+
def detect_rack_handler
|
33
|
+
servers = %W(thin mongrel webrick)
|
34
|
+
servers.each do |server_name|
|
35
|
+
begin
|
36
|
+
return Rack::Handler.get(server_name.to_s)
|
37
|
+
rescue Exception
|
38
|
+
end
|
39
|
+
end
|
40
|
+
fail "Could not resolve server handlers for any of '#{servers.join(', ')}'."
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/lib/web/rack_cmd.rb
ADDED
@@ -0,0 +1,161 @@
|
|
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
|
+
require 'socket'
|
18
|
+
require 'net/http'
|
19
|
+
|
20
|
+
require 'sinatra/base'
|
21
|
+
require 'sass'
|
22
|
+
require 'coffee-script'
|
23
|
+
|
24
|
+
module Ki
|
25
|
+
|
26
|
+
class WebContext
|
27
|
+
attr_accessor :ki_home
|
28
|
+
attr_accessor :development
|
29
|
+
attr_chain :started, -> { Time.now.to_i }
|
30
|
+
attr_chain :resource_hash, -> { started.to_s(16) }
|
31
|
+
end
|
32
|
+
|
33
|
+
module KiWebBase
|
34
|
+
def web_ctx
|
35
|
+
RackCommand.web_ctx
|
36
|
+
end
|
37
|
+
|
38
|
+
def ki_home
|
39
|
+
web_ctx.ki_home
|
40
|
+
end
|
41
|
+
|
42
|
+
def res_url(path)
|
43
|
+
if path.include?("..")
|
44
|
+
raise "File '#{path}' cannot reference parent directories with '..'!"
|
45
|
+
end
|
46
|
+
"/file/web/#{ RackCommand.web_ctx.resource_hash}/#{self.class.name}:#{path}"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# When starting up, looks for /web extension classes loaded from ki-scripts and starts up a web site
|
51
|
+
# class MyApp2 < Sinatra::Base
|
52
|
+
# get '/' do
|
53
|
+
# "MyApp2"
|
54
|
+
# end
|
55
|
+
# end
|
56
|
+
# KiCommand.register("/web/test", MyApp2)
|
57
|
+
#
|
58
|
+
# @see DefaultRackHandler
|
59
|
+
class RackCommand
|
60
|
+
@@web_ctx = WebContext.new
|
61
|
+
|
62
|
+
attr_chain :shell_command, :require
|
63
|
+
attr_chain :handler, -> { DefaultRackHandler }
|
64
|
+
|
65
|
+
def ki_app
|
66
|
+
extensions = KiCommand::KiExtensions.by_parent["/web"]
|
67
|
+
if extensions.nil? || extensions.empty?
|
68
|
+
raise "No /web extensions defined!"
|
69
|
+
end
|
70
|
+
RackCommand.build_app(extensions.map{|p,c| [p[4..-1], c]})
|
71
|
+
end
|
72
|
+
|
73
|
+
def RackCommand.build_app(path_class_list)
|
74
|
+
Rack::Builder.new do
|
75
|
+
path_class_list.each do |path, clazz|
|
76
|
+
map(path) do
|
77
|
+
run(clazz)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def RackCommand.find_free_tcp_port
|
84
|
+
socket = Socket.new(:INET, :STREAM, 0)
|
85
|
+
socket.bind(Addrinfo.tcp("127.0.0.1", 0))
|
86
|
+
begin
|
87
|
+
socket.local_address.ip_port
|
88
|
+
ensure
|
89
|
+
socket.close
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def RackCommand.wait_until_url_responds(url, &block)
|
94
|
+
try(20, 0.1) do
|
95
|
+
response = Net::HTTP.get_response(URI(url))
|
96
|
+
if block
|
97
|
+
block.call(response)
|
98
|
+
else
|
99
|
+
if (code = response.code) == "200"
|
100
|
+
return response
|
101
|
+
else
|
102
|
+
raise "Response code from #{url} was #{code}"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def start_server
|
109
|
+
@server = handler.new
|
110
|
+
[:INT, :TERM].each { |sig| trap(sig) { stop_server } }
|
111
|
+
@server.run(ki_app, :Port => (@port || 8290))
|
112
|
+
end
|
113
|
+
|
114
|
+
def stop_server
|
115
|
+
@server.stop
|
116
|
+
end
|
117
|
+
|
118
|
+
def execute(ctx, args)
|
119
|
+
RackCommand.web_ctx.ki_home=ctx.ki_home
|
120
|
+
@port = nil
|
121
|
+
opts.parse(args)
|
122
|
+
start_server
|
123
|
+
end
|
124
|
+
|
125
|
+
def opts
|
126
|
+
OptionParser.new do |opts|
|
127
|
+
opts.banner = ""
|
128
|
+
opts.on("--handler HANDLER", "Use specified Rack Handler") do |v|
|
129
|
+
handler(Object.const_get_full(v))
|
130
|
+
end
|
131
|
+
opts.on("--development", "Development mode, resource urls are reloaded") do |v|
|
132
|
+
RackCommand.web_ctx.development=true
|
133
|
+
end
|
134
|
+
opts.on("-p", "--port PORT", "Use specified port") do |v|
|
135
|
+
@port = Integer(v)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def self.web_ctx
|
141
|
+
@@web_ctx
|
142
|
+
end
|
143
|
+
|
144
|
+
attr_chain :summary, -> { "Starts Ki web server and uses code from Ki packages" }
|
145
|
+
|
146
|
+
def help
|
147
|
+
<<EOF
|
148
|
+
ki-repo has a built in web server.
|
149
|
+
|
150
|
+
### Usage
|
151
|
+
|
152
|
+
#{shell_command} - Starts Ki web server
|
153
|
+
|
154
|
+
### Parameters
|
155
|
+
#{opts}
|
156
|
+
EOF
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
KiCommand.register_cmd("web", RackCommand)
|
161
|
+
end
|