linecook 1.2.1 → 2.0.0
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/{History → History.rdoc} +3 -2
- data/README.rdoc +93 -0
- data/bin/linecook +32 -56
- data/bin/linecook_run +19 -6
- data/bin/linecook_scp +12 -4
- data/doc/vm_setup.rdoc +75 -0
- data/lib/linecook.rb +3 -2
- data/lib/linecook/attributes.rb +33 -8
- data/lib/linecook/command.rb +61 -0
- data/lib/linecook/command_set.rb +85 -0
- data/lib/linecook/command_utils.rb +20 -0
- data/lib/linecook/commands/build.rb +108 -57
- data/lib/linecook/commands/compile.rb +181 -0
- data/lib/linecook/commands/{helper.rb → compile_helper.rb} +123 -94
- data/lib/linecook/commands/run.rb +43 -39
- data/lib/linecook/commands/snapshot.rb +24 -24
- data/lib/linecook/commands/ssh.rb +7 -7
- data/lib/linecook/commands/start.rb +10 -10
- data/lib/linecook/commands/state.rb +7 -7
- data/lib/linecook/commands/stop.rb +3 -3
- data/lib/linecook/commands/{vbox_command.rb → virtual_box_command.rb} +31 -29
- data/lib/linecook/cookbook.rb +149 -131
- data/lib/linecook/executable.rb +28 -0
- data/lib/linecook/package.rb +177 -361
- data/lib/linecook/proxy.rb +4 -10
- data/lib/linecook/recipe.rb +289 -369
- data/lib/linecook/test.rb +114 -98
- data/lib/linecook/utils.rb +31 -41
- data/lib/linecook/version.rb +2 -6
- metadata +120 -68
- data/HowTo/Control Virtual Machines +0 -106
- data/HowTo/Generate Scripts +0 -268
- data/HowTo/Run Scripts +0 -87
- data/HowTo/Setup Virtual Machines +0 -76
- data/README +0 -117
- data/lib/linecook/commands.rb +0 -11
- data/lib/linecook/commands/command.rb +0 -58
- data/lib/linecook/commands/command_error.rb +0 -12
- data/lib/linecook/commands/env.rb +0 -89
- data/lib/linecook/commands/init.rb +0 -86
- data/lib/linecook/commands/package.rb +0 -57
- data/lib/linecook/template.rb +0 -17
- data/lib/linecook/test/command_parser.rb +0 -75
- data/lib/linecook/test/file_test.rb +0 -197
- data/lib/linecook/test/regexp_escape.rb +0 -86
- data/lib/linecook/test/shell_test.rb +0 -177
- data/lib/linecook/test/shim.rb +0 -71
- data/templates/Gemfile +0 -3
- data/templates/Rakefile +0 -146
- data/templates/_gitignore +0 -4
- data/templates/attributes/project_name.rb +0 -3
- data/templates/config/ssh +0 -14
- data/templates/cookbook +0 -10
- data/templates/files/example.txt +0 -1
- data/templates/helpers/project_name/echo.erb +0 -4
- data/templates/packages/abox.yml +0 -2
- data/templates/project_name.gemspec +0 -30
- data/templates/recipes/abox.rb +0 -16
- data/templates/templates/example.erb +0 -1
- data/templates/test/project_name_test.rb +0 -24
- data/templates/test/test_helper.rb +0 -14
@@ -1,26 +1,25 @@
|
|
1
|
-
require 'linecook/
|
1
|
+
require 'linecook/command'
|
2
2
|
require 'linecook/utils'
|
3
3
|
require 'fileutils'
|
4
4
|
require 'erb'
|
5
5
|
|
6
6
|
module Linecook
|
7
7
|
module Commands
|
8
|
-
|
9
|
-
# :startdoc::desc
|
8
|
+
|
9
|
+
# :startdoc::desc compile helper modules
|
10
10
|
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
# itself.
|
11
|
+
# Compiles a helper module from a set of source files. Each source file
|
12
|
+
# becomes a method in the module, named after the source file itself.
|
14
13
|
#
|
15
|
-
# The helper module will be generated under the
|
16
|
-
# corresponding to const_name (which can also be a constant path).
|
17
|
-
#
|
18
|
-
#
|
14
|
+
# The helper module will be generated under the output directory in a file
|
15
|
+
# corresponding to const_name (which can also be a constant path). Input
|
16
|
+
# directories may be specified to automatically search for source files
|
17
|
+
# based on constant path. These are all are equivalent and produce the
|
19
18
|
# Const::Name module in 'lib/const/name.rb':
|
20
19
|
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
20
|
+
# $ linecook compile-helper Const::Name helpers/const/name/*
|
21
|
+
# $ linecook compile-helper const/name helpers/const/name/*
|
22
|
+
# $ linecook compile-helper const/name -i helpers
|
24
23
|
#
|
25
24
|
# == Source Files
|
26
25
|
#
|
@@ -32,8 +31,7 @@ module Linecook
|
|
32
31
|
# .erb file defines an ERB template (compiled to ruby code)
|
33
32
|
#
|
34
33
|
# Source files can specify documenation and a method signature using a
|
35
|
-
#
|
36
|
-
# this:
|
34
|
+
# header separated from the body by a double-dash, like this:
|
37
35
|
#
|
38
36
|
# [echo.erb]
|
39
37
|
# Echo arguments out to the target.
|
@@ -41,34 +39,37 @@ module Linecook
|
|
41
39
|
# --
|
42
40
|
# echo <%= args.join(' ') %>
|
43
41
|
#
|
44
|
-
#
|
42
|
+
# Which produces something like:
|
45
43
|
#
|
46
44
|
# # Echo arguments out to the target.
|
47
45
|
# def echo(*args)
|
48
46
|
# eval ERB.new("echo <%= args.join(' ') %>").src
|
49
47
|
# end
|
50
48
|
#
|
51
|
-
# A second method is also generated to return the result without
|
52
|
-
# it to the target. The latter method is prefixed by
|
53
|
-
# like:
|
49
|
+
# A second 'capture' method is also generated to return the result without
|
50
|
+
# writing it to the target. The latter method is prefixed by an
|
51
|
+
# underscore like this:
|
54
52
|
#
|
55
53
|
# # Return the output of echo, without writing to the target
|
56
54
|
# def _echo(*args)
|
57
55
|
# ...
|
58
56
|
# end
|
59
57
|
#
|
60
|
-
#
|
61
|
-
#
|
58
|
+
# Special characters can be added to a method name by using a -extension
|
59
|
+
# to the file name. For example 'file-check.erb' defines the 'file?'
|
60
|
+
# method. These extensions are supported:
|
62
61
|
#
|
63
|
-
#
|
64
|
-
#
|
62
|
+
# extension character
|
63
|
+
# -check ?
|
64
|
+
# -bang !
|
65
|
+
# -eq =
|
65
66
|
#
|
66
|
-
# Otherwise the basename of the source file must be a
|
67
|
-
#
|
67
|
+
# Otherwise the basename of the source file must be a valid method name;
|
68
|
+
# invalid names raise an error.
|
68
69
|
#
|
69
70
|
# == Section Files
|
70
71
|
#
|
71
|
-
#
|
72
|
+
# Section files are source files that can be used to insert code in the
|
72
73
|
# following places:
|
73
74
|
#
|
74
75
|
# [:header]
|
@@ -82,112 +83,144 @@ module Linecook
|
|
82
83
|
# end
|
83
84
|
# [:footer]
|
84
85
|
#
|
85
|
-
# Section files are defined by prepending '
|
86
|
-
# path/to
|
87
|
-
#
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
86
|
+
# Section files are defined by prepending '_' to the file basename (like
|
87
|
+
# path/to/_header.rb) and are, like other source files, processed
|
88
|
+
# according to their extname:
|
89
|
+
#
|
90
|
+
# extname translation
|
91
|
+
# .rb file defines ruby (directly inserted)
|
92
|
+
# .rdoc file defines RDoc (lines commented out, then inserted)
|
93
|
+
#
|
94
|
+
# Note that section files prevent methods beginning with an underscore;
|
95
|
+
# this is intentional and prevents collisions with capture methods.
|
96
|
+
class CompileHelper < Command
|
97
|
+
config :output_dir, 'lib' # -o DIRECTORY : the output directory
|
98
|
+
config :search_dirs, [] # -i DIRECTORY : the input directory
|
99
|
+
config :force, false # -f, --force : force creation
|
100
|
+
config :quiet, false # -q, --quiet : print nothing
|
101
|
+
|
93
102
|
include Utils
|
94
|
-
|
103
|
+
|
95
104
|
def process(const_name, *sources)
|
96
105
|
const_path = underscore(const_name)
|
97
|
-
const_name = camelize(
|
98
|
-
|
106
|
+
const_name = camelize(const_name)
|
107
|
+
|
99
108
|
unless const_name?(const_name)
|
100
|
-
raise "invalid constant name: #{const_name.inspect}"
|
109
|
+
raise CommandError, "invalid constant name: #{const_name.inspect}"
|
101
110
|
end
|
102
|
-
|
103
|
-
sources =
|
104
|
-
target = File.expand_path(File.join(
|
105
|
-
|
111
|
+
|
112
|
+
sources = sources | search_sources(const_path)
|
113
|
+
target = File.expand_path(File.join(output_dir, "#{const_path}.rb"))
|
114
|
+
|
106
115
|
if sources.empty?
|
107
|
-
raise CommandError, "no sources specified
|
116
|
+
raise CommandError, "no sources specified"
|
108
117
|
end
|
109
|
-
|
118
|
+
|
110
119
|
if force || !FileUtils.uptodate?(target, sources)
|
111
120
|
content = build(const_name, sources)
|
112
|
-
|
121
|
+
|
113
122
|
target_dir = File.dirname(target)
|
114
123
|
unless File.exists?(target_dir)
|
115
124
|
FileUtils.mkdir_p(target_dir)
|
116
125
|
end
|
117
|
-
|
126
|
+
|
118
127
|
File.open(target, 'w') {|io| io << content }
|
119
128
|
$stdout.puts target unless quiet
|
120
129
|
end
|
121
|
-
|
130
|
+
|
122
131
|
target
|
123
132
|
end
|
124
|
-
|
125
|
-
# Returns
|
126
|
-
#
|
127
|
-
def
|
128
|
-
|
129
|
-
|
133
|
+
|
134
|
+
# Returns source files for a given constant path, which are all files
|
135
|
+
# under the 'search_dir/const_path' folder.
|
136
|
+
def search_sources(const_path)
|
137
|
+
sources = []
|
138
|
+
|
139
|
+
search_dirs.each do |search_dir|
|
140
|
+
pattern = File.join(search_dir, const_path, '*')
|
141
|
+
sources.concat Dir.glob(pattern)
|
142
|
+
end
|
143
|
+
|
130
144
|
sources.select {|path| File.file?(path) }
|
131
145
|
end
|
132
|
-
|
146
|
+
|
133
147
|
# returns true if const_name is a valid constant name.
|
134
148
|
def const_name?(const_name) # :nodoc:
|
135
149
|
const_name =~ /\A(?:::)?[A-Z]\w*(?:::[A-Z]\w*)*\z/
|
136
150
|
end
|
137
|
-
|
151
|
+
|
138
152
|
# helper to partition an array of source files into section and
|
139
153
|
# defintion files
|
140
154
|
def partition(sources) # :nodoc:
|
141
155
|
sources.partition do |path|
|
142
156
|
basename = File.basename(path)
|
143
157
|
extname = File.extname(path)
|
144
|
-
basename[0] ==
|
158
|
+
basename[0] == ?_ && basename.chomp(extname) != '_'
|
145
159
|
end
|
146
160
|
end
|
147
|
-
|
161
|
+
|
148
162
|
# helper to load each section path into a sections hash; removes the
|
149
163
|
# leading - from the path basename to determine the section key.
|
150
164
|
def load_sections(paths) # :nodoc:
|
151
165
|
sections = {}
|
152
|
-
|
166
|
+
|
153
167
|
paths.each do |path|
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
168
|
+
begin
|
169
|
+
basename = File.basename(path)
|
170
|
+
extname = File.extname(path)
|
171
|
+
key = basename[1, basename.length - extname.length - 1]
|
172
|
+
sections[key] = section_content(File.read(path), extname)
|
173
|
+
rescue CommandError
|
174
|
+
err = CommandError.new("invalid source file: #{path.inspect} (#{$!.message})")
|
175
|
+
err.set_backtrace($!.backtrace)
|
176
|
+
raise err
|
177
|
+
end
|
158
178
|
end
|
159
|
-
|
179
|
+
|
160
180
|
sections
|
161
181
|
end
|
162
|
-
|
182
|
+
|
183
|
+
def section_content(content, extname) # :nodoc:
|
184
|
+
case extname
|
185
|
+
when '.rdoc'
|
186
|
+
content.gsub(/^/, '# ')
|
187
|
+
|
188
|
+
when '.rb'
|
189
|
+
content
|
190
|
+
|
191
|
+
else
|
192
|
+
raise CommandError.new("unsupported section format #{extname.inspect}")
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
163
196
|
# helper to load and parse a definition file
|
164
197
|
def load_definition(path) # :nodoc:
|
165
198
|
extname = File.extname(path)
|
166
199
|
name = File.basename(path).chomp(extname)
|
167
200
|
desc, signature, body = parse_definition(File.read(path))
|
168
|
-
|
201
|
+
|
169
202
|
[desc, parse_method_name(name), signature, method_body(body, extname)]
|
170
203
|
rescue CommandError
|
171
|
-
err = CommandError.new("#{
|
204
|
+
err = CommandError.new("invalid source file: #{path.inspect} (#{$!.message})")
|
172
205
|
err.set_backtrace($!.backtrace)
|
173
206
|
raise err
|
174
207
|
end
|
175
|
-
|
208
|
+
|
176
209
|
# helper to reformat special basenames (in particular -check and -bang)
|
177
210
|
# to their corresponding method_name
|
178
211
|
def parse_definition(str) # :nodoc:
|
179
212
|
head, body = str.split(/^--.*\n/, 2)
|
180
213
|
head, body = '', head if body.nil?
|
181
|
-
|
214
|
+
|
182
215
|
found_signature = false
|
183
216
|
signature, desc = head.split("\n").partition do |line|
|
184
217
|
found_signature = true if line =~ /^\s*\(.*?\)/
|
185
218
|
found_signature
|
186
219
|
end
|
187
|
-
|
220
|
+
|
188
221
|
[desc.join("\n"), found_signature ? signature.join("\n") : '()', body.to_s]
|
189
222
|
end
|
190
|
-
|
223
|
+
|
191
224
|
# helper to reformat special basenames (in particular -check and -bang)
|
192
225
|
# to their corresponding method_name
|
193
226
|
def parse_method_name(basename) # :nodoc:
|
@@ -195,11 +228,11 @@ module Linecook
|
|
195
228
|
when /-check\z/ then basename.sub(/-check$/, '?')
|
196
229
|
when /-bang\z/ then basename.sub(/-bang$/, '!')
|
197
230
|
when /-eq\z/ then basename.sub(/-eq$/, '=')
|
198
|
-
when /\A\w
|
199
|
-
else raise CommandError.new("invalid method name
|
231
|
+
when /\A[A-Za-z]\w*\z/ then basename
|
232
|
+
else raise CommandError.new("invalid method name #{basename.inspect}")
|
200
233
|
end
|
201
234
|
end
|
202
|
-
|
235
|
+
|
203
236
|
# helper to reformat a definition body according to a given extname. rb
|
204
237
|
# content is rstripped to improve formatting. erb content is compiled
|
205
238
|
# and the source is placed as a comment before it (to improve
|
@@ -212,54 +245,57 @@ module Linecook
|
|
212
245
|
compiler.put_cmd = "write"
|
213
246
|
compiler.insert_cmd = "write"
|
214
247
|
code = [compiler.compile(body)].flatten.first
|
215
|
-
|
248
|
+
|
249
|
+
# remove encoding comment in 1.9 because it is not needed
|
250
|
+
code = code.sub(/\A#coding:.*?\n/, '')
|
251
|
+
|
216
252
|
"#{source}\n#{code}".gsub(/^(\s*)/) do |m|
|
217
253
|
indent = 2 + $1.length - ($1.length % 2)
|
218
254
|
' ' * indent
|
219
255
|
end
|
220
|
-
|
256
|
+
|
221
257
|
when '.rb'
|
222
258
|
body.rstrip
|
223
|
-
|
259
|
+
|
224
260
|
else
|
225
|
-
raise CommandError.new("
|
261
|
+
raise CommandError.new("unsupported format #{extname.inspect}")
|
226
262
|
end
|
227
263
|
end
|
228
|
-
|
264
|
+
|
229
265
|
# helper to nest a module body within a const_name. documentation
|
230
266
|
# can be provided for the innermost constant.
|
231
267
|
def module_nest(const_name, body, inner_doc=nil) # :nodoc:
|
232
268
|
body = body.strip.split("\n")
|
233
|
-
|
269
|
+
|
234
270
|
const_name.split(/::/).reverse_each do |name|
|
235
|
-
body.collect! {|line| " #{line}" }
|
236
|
-
|
271
|
+
body.collect! {|line| line.empty? ? line : " #{line}" }
|
272
|
+
|
237
273
|
body.unshift "module #{name}"
|
238
274
|
body.push "end"
|
239
|
-
|
275
|
+
|
240
276
|
# prepend the inner doc to the innermost const
|
241
277
|
if inner_doc
|
242
278
|
body = inner_doc.strip.split("\n") + body
|
243
279
|
inner_doc = nil
|
244
280
|
end
|
245
281
|
end
|
246
|
-
|
282
|
+
|
247
283
|
body.join("\n")
|
248
284
|
end
|
249
|
-
|
285
|
+
|
250
286
|
# Returns the code for a const_name module as defined by the source
|
251
287
|
# files.
|
252
288
|
def build(const_name, sources)
|
253
289
|
section_paths, definition_paths = partition(sources)
|
254
290
|
sections = load_sections(section_paths)
|
255
291
|
definitions = definition_paths.collect {|path| load_definition(path) }
|
256
|
-
|
292
|
+
|
257
293
|
body = eval DEFINITION_TEMPLATE, binding, __FILE__, DEFINITION_TEMPLATE_LINE
|
258
294
|
code = eval MODULE_TEMPLATE, binding, __FILE__, MODULE_TEMPLATE_LINE
|
259
|
-
|
295
|
+
|
260
296
|
code
|
261
297
|
end
|
262
|
-
|
298
|
+
|
263
299
|
# :stopdoc:
|
264
300
|
MODULE_TEMPLATE_LINE = __LINE__ + 2
|
265
301
|
MODULE_TEMPLATE = ERB.new(<<-DOC, nil, '<>').src
|
@@ -282,14 +318,7 @@ def <%= method_name %><%= signature %>
|
|
282
318
|
|
283
319
|
chain_proxy
|
284
320
|
end
|
285
|
-
|
286
|
-
def _<%= method_name %>(*args, &block) # :nodoc:
|
287
|
-
str = capture_str { <%= method_name %>(*args, &block) }
|
288
|
-
str.strip!
|
289
|
-
str
|
290
|
-
end
|
291
321
|
<% end %>
|
292
|
-
|
293
322
|
<%= sections['foot'] %>
|
294
323
|
DOC
|
295
324
|
# :startdoc:
|
@@ -1,48 +1,44 @@
|
|
1
|
-
require 'linecook/
|
2
|
-
require 'linecook/
|
1
|
+
require 'linecook/command'
|
2
|
+
require 'linecook/command_utils'
|
3
3
|
|
4
4
|
module Linecook
|
5
5
|
module Commands
|
6
|
-
|
6
|
+
|
7
7
|
# :startdoc::desc run packages
|
8
|
+
#
|
9
|
+
# Runs packages on remote hosts by transfering a package directory using
|
10
|
+
# scp, and then executing one or more scripts via ssh. Run is configured
|
11
|
+
# using an ssh config file, which should contain all necessary ssh
|
12
|
+
# options.
|
13
|
+
#
|
14
|
+
# Packages are transfered to hosts based on the basename of the package,
|
15
|
+
# hence 'path/to/abox' will run on the 'abox' host as configured in the
|
16
|
+
# ssh config file.
|
17
|
+
#
|
8
18
|
class Run < Command
|
19
|
+
include CommandUtils
|
20
|
+
|
9
21
|
RUN_SCRIPT = File.expand_path('../../../../bin/linecook_run', __FILE__)
|
10
22
|
SCP_SCRIPT = File.expand_path('../../../../bin/linecook_scp', __FILE__)
|
11
|
-
|
12
|
-
config :
|
13
|
-
config :
|
14
|
-
config :ssh_config_file, 'config/ssh'
|
15
|
-
config :
|
16
|
-
config :
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
def glob_package_dirs(package_names)
|
22
|
-
if package_names.empty?
|
23
|
-
pattern = File.expand_path('packages/*', project_dir)
|
24
|
-
Dir.glob(pattern).select {|path| File.directory?(path) }
|
25
|
-
else
|
26
|
-
package_names.collect do |package_name|
|
27
|
-
File.expand_path("packages/#{package_name}", project_dir)
|
28
|
-
end
|
29
|
-
end
|
23
|
+
|
24
|
+
config :remote_dir, 'linecook' # -D : the remote package dir
|
25
|
+
config :remote_scripts, ['run'] # -S : the remote script(s)
|
26
|
+
config :ssh_config_file, 'config/ssh' # -F : the ssh config file
|
27
|
+
config :xtrace, false # -x : xtrace local scripts
|
28
|
+
config :scp, true # transfer package or not
|
29
|
+
|
30
|
+
def full_path_to_remote_dir
|
31
|
+
(remote_dir[0] == ?/ ? remote_dir : "$(pwd)/#{remote_dir}").chomp('/')
|
30
32
|
end
|
31
|
-
|
32
|
-
def process(*
|
33
|
-
package_dirs = glob_package_dirs(package_names)
|
34
|
-
|
35
|
-
unless remote_dir[0] == ?/
|
36
|
-
self.remote_dir = "$(pwd)/#{remote_dir}"
|
37
|
-
self.remote_dir.chomp!('/')
|
38
|
-
end
|
39
|
-
|
33
|
+
|
34
|
+
def process(*package_dirs)
|
40
35
|
opts = {
|
41
|
-
'D' =>
|
42
|
-
'F' => ssh_config_file
|
36
|
+
'D' => full_path_to_remote_dir,
|
37
|
+
'F' => ssh_config_file,
|
38
|
+
'x' => xtrace
|
43
39
|
}
|
44
|
-
|
45
|
-
if
|
40
|
+
|
41
|
+
if scp
|
46
42
|
sh! "sh #{SCP_SCRIPT} #{format(opts)} #{package_dirs.join(' ')}"
|
47
43
|
end
|
48
44
|
|
@@ -51,14 +47,22 @@ module Linecook
|
|
51
47
|
sh! "sh #{RUN_SCRIPT} #{format(script_opts)} #{package_dirs.join(' ')}"
|
52
48
|
end
|
53
49
|
end
|
54
|
-
|
50
|
+
|
55
51
|
def format(opts)
|
56
52
|
options = []
|
57
|
-
|
53
|
+
|
58
54
|
opts.each_pair do |key, value|
|
59
|
-
|
55
|
+
next if value.to_s.strip.empty?
|
56
|
+
|
57
|
+
case value
|
58
|
+
when true
|
59
|
+
options << "-#{key}"
|
60
|
+
when false
|
61
|
+
else
|
62
|
+
options << "-#{key} '#{value}'"
|
63
|
+
end
|
60
64
|
end
|
61
|
-
|
65
|
+
|
62
66
|
options.sort.join(' ')
|
63
67
|
end
|
64
68
|
end
|