nutella_framework 0.2.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +1 -0
- data/VERSION +1 -1
- data/actors/main_interface/main_interface_bot.rb +17 -5
- data/actors/main_interface/public/index.html +5 -1
- data/data/index.html +21 -0
- data/data/startup +13 -0
- data/lib/core/commands/compile.rb +21 -0
- data/lib/core/commands/dependencies.rb +21 -0
- data/lib/core/commands/help.rb +9 -3
- data/lib/core/commands/install.rb +2 -24
- data/lib/core/commands/runs.rb +4 -4
- data/lib/core/commands/start.rb +112 -32
- data/lib/core/commands/stop.rb +16 -0
- data/lib/core/commands/template.rb +132 -0
- data/lib/core/run_command.rb +59 -5
- data/lib/core/template_command.rb +46 -0
- data/lib/core/tmux.rb +1 -1
- data/nutella_framework.gemspec +14 -4
- data/test/test_run_command.rb +54 -0
- metadata +29 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 25372f33a5d356902733e9c5c56e632cf7d03f00
|
4
|
+
data.tar.gz: 615a93c90ef6cba3206ee4f10e819bd3179c58fe
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 12dd8ec3456123428114c3c9b1c32eb611ff51c46346a7e1d2cb834ce76bc9fcbcd0efa3b65423c73fe2ba9f4359a2e58df056cd0454a1254a6368771e210495
|
7
|
+
data.tar.gz: 605229b6c2f71386d74b7930d23b80ab89e55e52d0eb35293348029c036923d71f196cf4c4bb60166d272607bed4cfa9c96a22d8d0fb5a3bb819603836e5a2a7
|
data/Gemfile
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.3.0
|
@@ -28,8 +28,14 @@ get '/' do
|
|
28
28
|
end
|
29
29
|
|
30
30
|
|
31
|
+
# Redirect if there is no slash after the run_id
|
32
|
+
get '/:run_id' do
|
33
|
+
redirect "#{request.url}/"
|
34
|
+
end
|
35
|
+
|
36
|
+
|
31
37
|
# Renders the run template
|
32
|
-
get '/:run_id
|
38
|
+
get '/:run_id/' do
|
33
39
|
|
34
40
|
# Parse the run_id from URL and extract the run path from runlist.json
|
35
41
|
@run_id = params[:run_id]
|
@@ -53,6 +59,12 @@ get '/:run_id/?' do
|
|
53
59
|
end
|
54
60
|
|
55
61
|
|
62
|
+
# Redirect if there is a slash after the interface
|
63
|
+
get '/:run_id/:interface/' do
|
64
|
+
redirect "#{request.url[0..-2]}"
|
65
|
+
end
|
66
|
+
|
67
|
+
|
56
68
|
# Serves the index.html file for each individual interface augmented with nutella query string parameters
|
57
69
|
get '/:run_id/:interface' do
|
58
70
|
|
@@ -128,7 +140,7 @@ def extract_interface_info( interfaces_path, iface_dir )
|
|
128
140
|
|
129
141
|
unless File.exist? index_path
|
130
142
|
iface_props[:name] = iface_dir
|
131
|
-
iface_props[:description] = 'My designer was a lazy
|
143
|
+
iface_props[:description] = 'My designer was a bit lazy and didn\'t include an index.html file in the main interface directory :('
|
132
144
|
return iface_props
|
133
145
|
end
|
134
146
|
|
@@ -138,14 +150,14 @@ def extract_interface_info( interfaces_path, iface_dir )
|
|
138
150
|
f.close
|
139
151
|
iface_props[:name] = doc.css('title').empty? ? iface_dir : doc.css('title').text
|
140
152
|
if doc.css("meta[name='description']").empty?
|
141
|
-
iface_props[:description] = 'My designer was a lazy
|
153
|
+
iface_props[:description] = 'My designer was a bit lazy and didn\'t include a <meta name="description" content="Description of this interface"> tag in the index.html file :('
|
142
154
|
else
|
143
155
|
if doc.css("meta[name='description']").attribute('content').nil?
|
144
|
-
iface_props[:description] = 'There was no attribute content in <meta name="description" content="Description of this interface"> tag in the index.html file'
|
156
|
+
iface_props[:description] = 'There was no attribute content in <meta name="description" content="Description of this interface"> tag in the index.html file :('
|
145
157
|
else
|
146
158
|
iface_props[:description] = doc.css("meta[name='description']").attribute('content').text
|
147
159
|
end
|
148
160
|
end
|
149
|
-
iface_props[:url] = "#{
|
161
|
+
iface_props[:url] = "#{iface_dir}"
|
150
162
|
iface_props
|
151
163
|
end
|
@@ -40,7 +40,11 @@
|
|
40
40
|
<script>
|
41
41
|
el = document.getElementById('run_id_input');
|
42
42
|
el.addEventListener('change', function() {
|
43
|
-
|
43
|
+
url = location.href
|
44
|
+
if (url.substr(url.length - 1)=="/")
|
45
|
+
window.location = url + this.value + '/';
|
46
|
+
else
|
47
|
+
window.location = url + '/' + this.value + '/';
|
44
48
|
});
|
45
49
|
</script>
|
46
50
|
</body>
|
data/data/index.html
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
<!doctype html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<meta charset="utf-8">
|
5
|
+
<!-- Your interface name here-->
|
6
|
+
<title></title>
|
7
|
+
<!-- The description of this interfaces here -->
|
8
|
+
<meta name="description" content="">
|
9
|
+
|
10
|
+
<!-- Your CSS here -->
|
11
|
+
</head>
|
12
|
+
|
13
|
+
<body>
|
14
|
+
|
15
|
+
<!-- Your markup here -->
|
16
|
+
|
17
|
+
|
18
|
+
<!-- Your scripts here -->
|
19
|
+
|
20
|
+
</body>
|
21
|
+
</html>
|
data/data/startup
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
|
3
|
+
# Extract the path of the current directory.
|
4
|
+
# From now on you can refer to any file in the bot directory as:
|
5
|
+
# $BASEDIR/<my_file>
|
6
|
+
BASEDIR=$(dirname $0)
|
7
|
+
|
8
|
+
|
9
|
+
# Remember that nutella passes the run_id and the broker name as
|
10
|
+
# command line parameters that you can access via $1 and $2 respectively.
|
11
|
+
|
12
|
+
# Start your bot here with something like (example in ruby):
|
13
|
+
# ruby $BASEDIR/my_bot.rb $1 $2
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'core/run_command'
|
2
|
+
require 'core/tmux'
|
3
|
+
|
4
|
+
module Nutella
|
5
|
+
class Compile < RunCommand
|
6
|
+
@description = 'Compiles all the actors that need compilation in the project'
|
7
|
+
|
8
|
+
def run(args=nil)
|
9
|
+
|
10
|
+
# If the current directory is not a nutella project, return
|
11
|
+
return unless Nutella.current_project.exist?
|
12
|
+
|
13
|
+
# Compile all actors
|
14
|
+
return unless prepare_bot( Nutella.current_project.dir, 'compile', 'Compiling' )
|
15
|
+
|
16
|
+
# Output success message
|
17
|
+
console.success "All actors compiled for #{Nutella.current_project.config['name']}"
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'core/run_command'
|
2
|
+
require 'core/tmux'
|
3
|
+
|
4
|
+
module Nutella
|
5
|
+
class Dependencies < RunCommand
|
6
|
+
@description = 'Installs the dependencies for all actors in the project'
|
7
|
+
|
8
|
+
def run(args=nil)
|
9
|
+
|
10
|
+
# If the current directory is not a nutella project, return
|
11
|
+
return unless Nutella.current_project.exist?
|
12
|
+
|
13
|
+
# Install all dependencies
|
14
|
+
return unless prepare_bot( Nutella.current_project.dir, 'dependencies', 'Installing dependencies for' )
|
15
|
+
|
16
|
+
# Output success message
|
17
|
+
console.success "All dependencies installed for #{Nutella.current_project.config['name']}"
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
data/lib/core/commands/help.rb
CHANGED
@@ -8,11 +8,17 @@ module Nutella
|
|
8
8
|
def run(args=nil)
|
9
9
|
message=''
|
10
10
|
Dir["#{File.dirname(__FILE__)}/*.rb"].each do |file|
|
11
|
-
|
12
|
-
|
11
|
+
command = File.basename(file, File.extname(file))
|
12
|
+
if command.length > 7
|
13
|
+
message += "#{command}\t"
|
14
|
+
else
|
15
|
+
message += "#{command}\t\t"
|
16
|
+
end
|
17
|
+
message += Object::const_get("Nutella::#{command.capitalize}").description
|
13
18
|
message += "\n"
|
14
19
|
end
|
15
|
-
console.info message
|
20
|
+
console.info message
|
21
|
+
console.info 'For more details on individual commands, see https://github.com/nutella-framework/nutella_framework/wiki/Nutella-Command-Line-Interface'
|
16
22
|
end
|
17
23
|
|
18
24
|
end
|
@@ -1,11 +1,10 @@
|
|
1
|
-
require 'core/
|
2
|
-
require 'json'
|
1
|
+
require 'core/template_command'
|
3
2
|
require 'git'
|
4
3
|
require 'net/http'
|
5
4
|
|
6
5
|
module Nutella
|
7
6
|
|
8
|
-
class Install <
|
7
|
+
class Install < TemplateCommand
|
9
8
|
@description = 'Copies an arbitrary template (from central DB, directory or URL) into the current project'
|
10
9
|
|
11
10
|
def run(args=nil)
|
@@ -118,27 +117,6 @@ module Nutella
|
|
118
117
|
Dir.mkdir Nutella.config['tmp_dir'] unless Dir.exists? Nutella.config['tmp_dir']
|
119
118
|
Git.clone(template, dest_dir, :path => Nutella.config['tmp_dir'])
|
120
119
|
end
|
121
|
-
|
122
|
-
|
123
|
-
def validate_template( dir )
|
124
|
-
# Parse the template's nutella.json file
|
125
|
-
begin
|
126
|
-
template_nutella_file_json = JSON.parse(IO.read("#{dir}/nutella.json"))
|
127
|
-
rescue
|
128
|
-
return false
|
129
|
-
end
|
130
|
-
# If template is a bot, perform the appropriate checks
|
131
|
-
if template_nutella_file_json['type']=='bot'
|
132
|
-
# Is there a mandatory 'startup' script and is it executable
|
133
|
-
return false unless File.executable? "#{dir}/startup"
|
134
|
-
end
|
135
|
-
# If template is an interface, perform the appropriate checks
|
136
|
-
if template_nutella_file_json['type']=='interface'
|
137
|
-
# Is there the mandatory index.html file
|
138
|
-
return false unless File.exist? "#{dir}/index.html"
|
139
|
-
end
|
140
|
-
true
|
141
|
-
end
|
142
120
|
|
143
121
|
|
144
122
|
def extract_remote_repo_url( template_name )
|
data/lib/core/commands/runs.rb
CHANGED
@@ -8,7 +8,7 @@ module Nutella
|
|
8
8
|
def run(args=nil)
|
9
9
|
|
10
10
|
# If invoked with "all" it will show all the runs under this instance of nutella
|
11
|
-
if args[0]=='all'
|
11
|
+
if args[0]=='--all' || args[0]=='-a'
|
12
12
|
display_all_runs
|
13
13
|
else
|
14
14
|
# If current dir is not a nutella project, return
|
@@ -38,10 +38,10 @@ module Nutella
|
|
38
38
|
runs.each do |run|
|
39
39
|
run_id = run.dup
|
40
40
|
run_id.slice! "#{project_name}_"
|
41
|
-
if
|
42
|
-
console.info " #{project_name}"
|
41
|
+
if run==project_name
|
42
|
+
console.info " #{project_name} (default instance)"
|
43
43
|
else
|
44
|
-
console.info " #{
|
44
|
+
console.info " #{run_id}"
|
45
45
|
end
|
46
46
|
end
|
47
47
|
end
|
data/lib/core/commands/start.rb
CHANGED
@@ -12,9 +12,37 @@ module Nutella
|
|
12
12
|
|
13
13
|
# Extract run (passed run name) and run_id
|
14
14
|
run, run_id = extract_names args
|
15
|
-
|
15
|
+
|
16
|
+
# Check that the run name is different than the project name
|
17
|
+
if run==Nutella.current_project.config['name']
|
18
|
+
console.warn 'Your run name should be different than your project name'
|
19
|
+
return
|
20
|
+
end
|
21
|
+
|
22
|
+
# Extract parameters
|
23
|
+
begin
|
24
|
+
params = extract_parameters args
|
25
|
+
rescue
|
26
|
+
console.warn 'The only supported parameters are --with (-w) and --without (-wo)'
|
27
|
+
return
|
28
|
+
end
|
29
|
+
|
30
|
+
# Check that we are not using 'with' and 'without' options at the same time
|
31
|
+
unless params[:with].empty? || params[:without].empty?
|
32
|
+
console.warn 'You can\'t use both --with and --without at the same time'
|
33
|
+
return
|
34
|
+
end
|
35
|
+
|
36
|
+
# Extract project directory
|
16
37
|
cur_prj_dir = Nutella.current_project.dir
|
17
38
|
|
39
|
+
# Check that there is at least a regular bot that will be started,
|
40
|
+
# otherwise it makes no sense to create a run
|
41
|
+
if run_bots_list_minus_project_bots(cur_prj_dir, params).empty? && project_bots_started?
|
42
|
+
console.warn "Run #{run} not created: your project bots are already started and you specified no regular bots exclusively for this run"
|
43
|
+
return
|
44
|
+
end
|
45
|
+
|
18
46
|
# Check that the run_id is unique and add it to the list of runs
|
19
47
|
# If it's not, return (without adding the run_id to the list of course)
|
20
48
|
return unless add_to_run_list( run_id, cur_prj_dir )
|
@@ -27,24 +55,49 @@ module Nutella
|
|
27
55
|
# Start all nutella internal actors, if needed
|
28
56
|
return unless start_nutella_actors
|
29
57
|
|
30
|
-
#
|
31
|
-
return unless
|
32
|
-
|
33
|
-
|
58
|
+
# Start all project level actors, if any
|
59
|
+
return unless start_project_bots cur_prj_dir
|
60
|
+
|
61
|
+
# Start all bots
|
62
|
+
return unless start_bots( cur_prj_dir, run_id, params )
|
34
63
|
|
35
64
|
# Output success message
|
36
65
|
output_success_message( run_id, run, 'started' )
|
37
|
-
output_monitoring_details run_id
|
66
|
+
output_monitoring_details( run_id, cur_prj_dir, params)
|
38
67
|
end
|
39
68
|
|
40
69
|
|
41
70
|
private
|
42
71
|
|
43
72
|
|
73
|
+
def run_bots_list_minus_project_bots( cur_prj_dir, params )
|
74
|
+
# Fetch the list of project bots
|
75
|
+
project_bots_list = Nutella.current_project.config['project_bots']
|
76
|
+
run_bots_list = run_actors_list("#{cur_prj_dir}/bots/")
|
77
|
+
# Depending on the mode we are in we want to start only some bots, exclude only some bots, start all bots
|
78
|
+
unless params[:with].empty?
|
79
|
+
return project_bots_list.nil? ? params[:with] : params[:with] - project_bots_list
|
80
|
+
end
|
81
|
+
unless params[:without].empty?
|
82
|
+
return project_bots_list.nil? ? run_bots_list - params[:without] : run_bots_list - params[:without] - project_bots_list
|
83
|
+
end
|
84
|
+
if params[:with].empty? && params[:without].empty?
|
85
|
+
return project_bots_list.nil? ? run_bots_list : run_bots_list - project_bots_list
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
|
90
|
+
def project_bots_started?
|
91
|
+
project_name = Nutella.current_project.config['name']
|
92
|
+
tmux_session_name = "#{project_name}-project-bots"
|
93
|
+
return Tmux.session_exist? tmux_session_name
|
94
|
+
end
|
95
|
+
|
96
|
+
|
44
97
|
def add_to_run_list(run_id, prj_dir)
|
45
98
|
unless Nutella.runlist.add?( run_id, prj_dir )
|
46
99
|
# If the run_id is already in the list, check that it's actually live
|
47
|
-
if Tmux.
|
100
|
+
if Tmux.session_exist? run_id
|
48
101
|
console.error 'Impossible to start project: an instance of this project with the same run_id is already running!'
|
49
102
|
console.error "You might want to kill it with 'nutella stop #{run_id}'"
|
50
103
|
return false
|
@@ -126,6 +179,7 @@ module Nutella
|
|
126
179
|
true
|
127
180
|
end
|
128
181
|
|
182
|
+
|
129
183
|
def start_nutella_actor( actor_dir )
|
130
184
|
pid_file_path = "#{actor_dir}/.pid"
|
131
185
|
return true if sanitize_pid_file pid_file_path
|
@@ -145,43 +199,69 @@ module Nutella
|
|
145
199
|
end
|
146
200
|
|
147
201
|
|
148
|
-
def
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
202
|
+
def start_project_bots( cur_prj_dir )
|
203
|
+
project_bots_list = Nutella.current_project.config['project_bots']
|
204
|
+
project_name = Nutella.current_project.config['name']
|
205
|
+
tmux_session_name = "#{project_name}-project-bots"
|
206
|
+
bots_dir = "#{cur_prj_dir}/bots/"
|
207
|
+
# If project bots have been started already, then do nothing
|
208
|
+
unless Tmux.session_exist? tmux_session_name
|
209
|
+
# Start all project bots in the list into a new tmux session
|
210
|
+
tmux = Tmux.new tmux_session_name
|
211
|
+
for_each_actor_in_dir bots_dir do |bot|
|
212
|
+
unless project_bots_list.nil? || !project_bots_list.include?( bot )
|
213
|
+
# If there is no 'startup' script output a warning (because
|
214
|
+
# startup is mandatory) and skip the bot
|
215
|
+
unless File.exist?("#{bots_dir}#{bot}/startup")
|
216
|
+
console.warn "Impossible to start bot #{bot}. Couldn't locate 'startup' script."
|
217
|
+
next
|
218
|
+
end
|
219
|
+
# Create a new window in the session for this run
|
220
|
+
tmux.new_bot_window bot
|
221
|
+
end
|
222
|
+
end
|
159
223
|
end
|
160
224
|
true
|
161
225
|
end
|
162
226
|
|
163
227
|
|
164
|
-
def start_bots( cur_prj_dir, run_id )
|
165
|
-
bots_dir = "#{cur_prj_dir}/bots/"
|
228
|
+
def start_bots( cur_prj_dir, run_id, params )
|
166
229
|
# Create a new tmux instance for this run
|
167
230
|
tmux = Tmux.new run_id
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
console.warn "Impossible to start bot #{bot}. Couldn't locate 'startup' script."
|
173
|
-
next
|
174
|
-
end
|
175
|
-
# Create a new window in the session for this run
|
176
|
-
tmux.new_bot_window bot
|
177
|
-
end
|
231
|
+
# Fetch bots dir
|
232
|
+
bots_dir = "#{cur_prj_dir}/bots/"
|
233
|
+
# Start the appropriate bots
|
234
|
+
run_bots_list_minus_project_bots( cur_prj_dir, params ).each { |bot| start_bot(bots_dir, bot, tmux) }
|
178
235
|
true
|
179
236
|
end
|
180
237
|
|
181
238
|
|
182
|
-
def
|
183
|
-
|
239
|
+
def start_bot( bots_dir, bot, tmux )
|
240
|
+
# If there is no 'startup' script output a warning (because
|
241
|
+
# startup is mandatory) and skip the bot
|
242
|
+
unless File.exist?("#{bots_dir}#{bot}/startup")
|
243
|
+
console.warn "Impossible to start bot #{bot}. Couldn't locate 'startup' script."
|
244
|
+
return
|
245
|
+
end
|
246
|
+
# Create a new window in the session for this run
|
247
|
+
tmux.new_bot_window bot
|
248
|
+
end
|
249
|
+
|
250
|
+
|
251
|
+
def output_monitoring_details( run_id, cur_prj_dir, params )
|
252
|
+
project_bots_list = Nutella.current_project.config['project_bots']
|
253
|
+
project_name = Nutella.current_project.config['name']
|
254
|
+
tmux_session_name = "#{project_name}-project-bots"
|
255
|
+
unless project_bots_list.nil? || project_bots_list.empty?
|
256
|
+
console.success "Do `tmux attach-session -t #{tmux_session_name}` to monitor your project bots."
|
257
|
+
end
|
258
|
+
if run_bots_list_minus_project_bots(cur_prj_dir, params).empty?
|
259
|
+
console.success 'No tmux session was created for this run because you specified no regular bots exclusively for this run'
|
260
|
+
else
|
261
|
+
console.success "Do `tmux attach-session -t #{run_id}` to monitor your bots."
|
262
|
+
end
|
184
263
|
console.success "Go to http://localhost:#{Nutella.config['main_interface_port']}/#{run_id} to access your interfaces"
|
264
|
+
|
185
265
|
end
|
186
266
|
|
187
267
|
|
data/lib/core/commands/stop.rb
CHANGED
@@ -20,6 +20,9 @@ module Nutella
|
|
20
20
|
# Stops all the bots
|
21
21
|
Tmux.kill_session run_id
|
22
22
|
|
23
|
+
# Stop all project level actors (if any) if needed
|
24
|
+
stop_project_bots
|
25
|
+
|
23
26
|
# Stop all nutella internal actors, if needed
|
24
27
|
if Nutella.runlist.empty?
|
25
28
|
stop_nutella_actors
|
@@ -47,6 +50,19 @@ module Nutella
|
|
47
50
|
end
|
48
51
|
|
49
52
|
|
53
|
+
def stop_project_bots
|
54
|
+
project_name = Nutella.current_project.config['name']
|
55
|
+
tmux_session_name = "#{project_name}-project-bots"
|
56
|
+
if Tmux.session_exist? tmux_session_name
|
57
|
+
# Are there any runs of this project hinging on the project bots?
|
58
|
+
if Nutella.runlist.runs_by_project(project_name).empty?
|
59
|
+
Tmux.kill_session tmux_session_name
|
60
|
+
end
|
61
|
+
end
|
62
|
+
true
|
63
|
+
end
|
64
|
+
|
65
|
+
|
50
66
|
def stop_nutella_actors
|
51
67
|
nutella_actors_dir = "#{Nutella.config['nutella_home']}actors"
|
52
68
|
for_each_actor_in_dir nutella_actors_dir do |actor|
|
@@ -0,0 +1,132 @@
|
|
1
|
+
require 'core/template_command'
|
2
|
+
|
3
|
+
module Nutella
|
4
|
+
|
5
|
+
class Template < TemplateCommand
|
6
|
+
@description = 'Helps create and validate templates'
|
7
|
+
|
8
|
+
def run(args=nil)
|
9
|
+
|
10
|
+
# If no argument then we just display info about the command
|
11
|
+
if args==nil || args.length < 2
|
12
|
+
display_help
|
13
|
+
return
|
14
|
+
end
|
15
|
+
|
16
|
+
# Extract sub command
|
17
|
+
sub_command = args[0]
|
18
|
+
param = args[1]
|
19
|
+
|
20
|
+
if sub_command == 'validate'
|
21
|
+
validate_template_sub_command param
|
22
|
+
end
|
23
|
+
|
24
|
+
if sub_command == 'create'
|
25
|
+
create_template_sub_command param
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def display_help
|
33
|
+
console.info 'You need to specify a sub command.'
|
34
|
+
console.info 'create <template_name> creates a template skeleton in the folder with the same name'
|
35
|
+
console.info 'validate <temple_dir> validates a template that already exists'
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
def validate_template_sub_command( dir )
|
40
|
+
if validate_template dir
|
41
|
+
console.success 'This directory appears to be a valid nutella template'
|
42
|
+
else
|
43
|
+
console.warn 'Looks like this directory is not a valid nutella template'
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
def create_template_sub_command( name_d )
|
49
|
+
ver_d = '0.1.0'
|
50
|
+
type_d = 'bot'
|
51
|
+
|
52
|
+
# Name
|
53
|
+
puts 'What is the name of your template?'
|
54
|
+
print "(#{name_d}) "
|
55
|
+
c = $stdin.gets.chomp!
|
56
|
+
name = c.empty? ? name_d : c
|
57
|
+
# Version
|
58
|
+
puts 'What is the version of your template?'
|
59
|
+
print "(#{ver_d}) "
|
60
|
+
c = $stdin.gets.chomp!
|
61
|
+
version = c.empty? ? ver_d : c
|
62
|
+
# Type
|
63
|
+
puts 'Are you creating a template for a "bot" or "interface"?'
|
64
|
+
print "(#{type_d}) "
|
65
|
+
c = $stdin.gets.chomp!
|
66
|
+
type = c.empty? ? type_d : c
|
67
|
+
# Description
|
68
|
+
puts "Do you want to provide a short description for your #{type} template?"
|
69
|
+
print '(optional, hit enter if no description) '
|
70
|
+
description = $stdin.gets.chomp!
|
71
|
+
# Repo
|
72
|
+
puts 'Do you want to provide a git repository for your template?'
|
73
|
+
print '(optional, hit enter if no repo) '
|
74
|
+
repo = $stdin.gets.chomp!
|
75
|
+
|
76
|
+
# Build JSON and show confirmation
|
77
|
+
puts 'Looks good?'
|
78
|
+
json = build_nutella_json( name, version, type, description, repo )
|
79
|
+
puts JSON.pretty_generate json
|
80
|
+
print '(yes/no) '
|
81
|
+
|
82
|
+
# If user confirms, create template
|
83
|
+
confirm = $stdin.gets.chomp!
|
84
|
+
if confirm=='yes'
|
85
|
+
create_template_files json
|
86
|
+
else
|
87
|
+
console.warn 'Template creation aborted'
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
|
93
|
+
def create_template_files( json )
|
94
|
+
# First validate the JSON
|
95
|
+
unless validate_nutella_file_json json
|
96
|
+
console.error 'Something was wrong with your nutella.json file. Template creation aborted'
|
97
|
+
return
|
98
|
+
end
|
99
|
+
# Assemble destination directory
|
100
|
+
template_dir = File.join( Dir.pwd, json['name'] )
|
101
|
+
# Check that the directory doesn't exist already
|
102
|
+
if File.directory?(template_dir)
|
103
|
+
console.error("The directory #{template_dir} already exists! Can't create template #{json[:name]}")
|
104
|
+
return
|
105
|
+
end
|
106
|
+
# Create directory
|
107
|
+
Dir.mkdir template_dir
|
108
|
+
# Create nutella.json file
|
109
|
+
File.open("#{template_dir}/nutella.json", 'w') { |f| f.write(JSON.pretty_generate json) }
|
110
|
+
# Add bot/interface specific files
|
111
|
+
bot_specific_file = nil
|
112
|
+
bot_specific_file = File.join( Nutella.config['nutella_home'], 'data/startup' ) if json['type']=='bot'
|
113
|
+
bot_specific_file = File.join( Nutella.config['nutella_home'], 'data/index.html' ) if json['type']=='interface'
|
114
|
+
FileUtils.copy( bot_specific_file, template_dir )
|
115
|
+
console.success "Template #{json['name']} created successfully!"
|
116
|
+
end
|
117
|
+
|
118
|
+
|
119
|
+
def build_nutella_json( name, version, type, description, repo )
|
120
|
+
json = { 'name' => name, 'version' => version, 'type' => type}
|
121
|
+
unless description.empty?
|
122
|
+
json['description'] = description
|
123
|
+
end
|
124
|
+
unless repo.empty?
|
125
|
+
json['repo'] = repo
|
126
|
+
end
|
127
|
+
json
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
data/lib/core/run_command.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'core/command'
|
2
|
+
require 'slop'
|
2
3
|
|
3
4
|
module Nutella
|
4
5
|
# This class describes a run command which can be either start or stop.
|
@@ -9,22 +10,59 @@ module Nutella
|
|
9
10
|
console.error 'Running generic RunCommand!!! WAT?'
|
10
11
|
end
|
11
12
|
|
13
|
+
|
12
14
|
# Extracts run name and run_id
|
13
15
|
# @param [Array<String>] args command line arguments passed to the command
|
14
16
|
# @return [String, String ] the run name (cleaned of nils) and the run_id
|
15
17
|
def extract_names( args )
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
18
|
+
|
19
|
+
# Simple `nutella start/stop`
|
20
|
+
if args.nil? || args.empty?
|
21
|
+
run = nil
|
22
|
+
run_id = Nutella.runlist.extract_run_id( '' )
|
23
|
+
return run, run_id
|
24
|
+
end
|
25
|
+
|
26
|
+
# Check if the first argument is a parameter or a run name
|
27
|
+
if args[0].start_with? '-'
|
28
|
+
run = nil
|
29
|
+
run_id = Nutella.runlist.extract_run_id( '' )
|
30
|
+
else
|
31
|
+
# If it's a run name, store the run name and shift so we are left with only
|
32
|
+
# the parameters in args
|
33
|
+
run = args[0]
|
34
|
+
run_id = Nutella.runlist.extract_run_id( args[0] )
|
35
|
+
args.shift
|
36
|
+
end
|
37
|
+
|
20
38
|
return run, run_id
|
21
39
|
end
|
22
40
|
|
41
|
+
|
42
|
+
# Extracts the command line parameters
|
43
|
+
# @param [Array<String>] args command line arguments passed to the command
|
44
|
+
# @return [Hash] an hash containing the parameters
|
45
|
+
def extract_parameters( args )
|
46
|
+
opts = Slop::Options.new
|
47
|
+
opts.array '-wo', '--without', 'A list of actors NOT to start'
|
48
|
+
opts.array '-w', '--with', 'A list of actors that needs to be started'
|
49
|
+
parser = Slop::Parser.new(opts)
|
50
|
+
result = parser.parse(args)
|
51
|
+
result.to_hash
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
# Returns all the actors in a certain directory
|
56
|
+
def run_actors_list( actors_dir )
|
57
|
+
Dir.entries(actors_dir).select {|entry| File.directory?(File.join(actors_dir, entry)) && !(entry =='.' || entry == '..') }
|
58
|
+
end
|
59
|
+
|
60
|
+
|
23
61
|
# Executes a code block for each actor in a certain directory
|
24
62
|
# @param [String] actors_dir directory where we are iterating
|
25
63
|
# @yield [actor_dir] Gives the actor directory to the block
|
26
64
|
def for_each_actor_in_dir( actors_dir, &block )
|
27
|
-
|
65
|
+
run_actors_list(actors_dir).each do |actor_dir|
|
28
66
|
block.call actor_dir
|
29
67
|
end
|
30
68
|
end
|
@@ -37,6 +75,22 @@ module Nutella
|
|
37
75
|
console.success "Project #{Nutella.current_project.config['name']}, run #{run} #{action}!"
|
38
76
|
end
|
39
77
|
end
|
78
|
+
|
79
|
+
|
80
|
+
def prepare_bot( cur_prj_dir, script, message )
|
81
|
+
for_each_actor_in_dir cur_prj_dir do |bot|
|
82
|
+
# Skip bot if there is no script
|
83
|
+
next unless File.exist? "#{cur_prj_dir}/bots/#{bot}/#{script}"
|
84
|
+
# Output message
|
85
|
+
console.info "#{message} bot #{bot}."
|
86
|
+
# Execute 'script' script
|
87
|
+
cur_dir = Dir.pwd
|
88
|
+
Dir.chdir "#{cur_prj_dir}/bots/#{bot}"
|
89
|
+
system "./#{script}"
|
90
|
+
Dir.chdir cur_dir
|
91
|
+
end
|
92
|
+
true
|
93
|
+
end
|
40
94
|
|
41
95
|
|
42
96
|
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'core/command'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module Nutella
|
5
|
+
# This class describes a template command which can be either install or template
|
6
|
+
# It is mostly a commodity class for code reuse.
|
7
|
+
|
8
|
+
class TemplateCommand < Command
|
9
|
+
|
10
|
+
def run (args=nil)
|
11
|
+
console.error 'Running generic TemplateCommand!!! WAT?'
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
# Validates a template in a certain folder
|
16
|
+
# @param [String] dir the directory where the template is stored
|
17
|
+
def validate_template( dir )
|
18
|
+
# Parse and validate the template's nutella.json file
|
19
|
+
begin
|
20
|
+
template_nutella_file_json = JSON.parse(IO.read("#{dir}/nutella.json"))
|
21
|
+
rescue
|
22
|
+
return false
|
23
|
+
end
|
24
|
+
return false unless validate_nutella_file_json template_nutella_file_json
|
25
|
+
# If template is a bot, perform the appropriate checks
|
26
|
+
if template_nutella_file_json['type']=='bot'
|
27
|
+
# Is there a mandatory 'startup' script and is it executable
|
28
|
+
return false unless File.executable? "#{dir}/startup"
|
29
|
+
end
|
30
|
+
# If template is an interface, perform the appropriate checks
|
31
|
+
if template_nutella_file_json['type']=='interface'
|
32
|
+
# Is there the mandatory index.html file
|
33
|
+
return false unless File.exist? "#{dir}/index.html"
|
34
|
+
end
|
35
|
+
true
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
def validate_nutella_file_json( json )
|
40
|
+
!json['name'].nil? && !json['version'].nil? && !json['type'].nil? && (json['type']=='bot' || json['type']=='interface')
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
data/lib/core/tmux.rb
CHANGED
data/nutella_framework.gemspec
CHANGED
@@ -2,16 +2,16 @@
|
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
3
|
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
|
-
# stub: nutella_framework 0.
|
5
|
+
# stub: nutella_framework 0.3.0 ruby lib
|
6
6
|
|
7
7
|
Gem::Specification.new do |s|
|
8
8
|
s.name = "nutella_framework"
|
9
|
-
s.version = "0.
|
9
|
+
s.version = "0.3.0"
|
10
10
|
|
11
11
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
12
12
|
s.require_paths = ["lib"]
|
13
13
|
s.authors = ["Alessandro Gnoli"]
|
14
|
-
s.date = "2015-01-
|
14
|
+
s.date = "2015-01-29"
|
15
15
|
s.description = "Nutella is a framework to build and run \"Internet of Things\"-like learning applications"
|
16
16
|
s.email = "tebemis@gmail.com"
|
17
17
|
s.executables = ["nutella"]
|
@@ -33,6 +33,8 @@ Gem::Specification.new do |s|
|
|
33
33
|
"actors/main_interface/views/index.erb",
|
34
34
|
"actors/main_interface/views/not_found_404.erb",
|
35
35
|
"bin/nutella",
|
36
|
+
"data/index.html",
|
37
|
+
"data/startup",
|
36
38
|
"lib/cli/nutella_cli.rb",
|
37
39
|
"lib/config/config.rb",
|
38
40
|
"lib/config/current_project.rb",
|
@@ -41,14 +43,18 @@ Gem::Specification.new do |s|
|
|
41
43
|
"lib/core/command.rb",
|
42
44
|
"lib/core/commands/broker.rb",
|
43
45
|
"lib/core/commands/checkup.rb",
|
46
|
+
"lib/core/commands/compile.rb",
|
47
|
+
"lib/core/commands/dependencies.rb",
|
44
48
|
"lib/core/commands/help.rb",
|
45
49
|
"lib/core/commands/install.rb",
|
46
50
|
"lib/core/commands/new.rb",
|
47
51
|
"lib/core/commands/runs.rb",
|
48
52
|
"lib/core/commands/start.rb",
|
49
53
|
"lib/core/commands/stop.rb",
|
54
|
+
"lib/core/commands/template.rb",
|
50
55
|
"lib/core/nutella_core.rb",
|
51
56
|
"lib/core/run_command.rb",
|
57
|
+
"lib/core/template_command.rb",
|
52
58
|
"lib/core/tmux.rb",
|
53
59
|
"lib/logging/nutella_logger-remote.rb",
|
54
60
|
"lib/logging/nutella_logger.rb",
|
@@ -59,7 +65,8 @@ Gem::Specification.new do |s|
|
|
59
65
|
"test/config/test_project.rb",
|
60
66
|
"test/config/test_runlist.rb",
|
61
67
|
"test/helper.rb",
|
62
|
-
"test/logging/test_logging.rb"
|
68
|
+
"test/logging/test_logging.rb",
|
69
|
+
"test/test_run_command.rb"
|
63
70
|
]
|
64
71
|
s.homepage = "https://github.com/nutella-framework/nutella_framework"
|
65
72
|
s.licenses = ["MIT"]
|
@@ -76,6 +83,7 @@ Gem::Specification.new do |s|
|
|
76
83
|
s.add_runtime_dependency(%q<git>, [">= 1.2.8", "~> 1.2"])
|
77
84
|
s.add_runtime_dependency(%q<sinatra>, [">= 1.4.5", "~> 1.4.5"])
|
78
85
|
s.add_runtime_dependency(%q<nokogiri>, [">= 1.6.3", "~> 1.6.3"])
|
86
|
+
s.add_runtime_dependency(%q<slop>, [">= 4.0.0", "~> 4.0.0"])
|
79
87
|
s.add_development_dependency(%q<shoulda>, [">= 3", "~> 3"])
|
80
88
|
s.add_development_dependency(%q<yard>, [">= 0.8.7", "~> 0.8"])
|
81
89
|
s.add_development_dependency(%q<rdoc>, [">= 4.0", "~> 4.0"])
|
@@ -89,6 +97,7 @@ Gem::Specification.new do |s|
|
|
89
97
|
s.add_dependency(%q<git>, [">= 1.2.8", "~> 1.2"])
|
90
98
|
s.add_dependency(%q<sinatra>, [">= 1.4.5", "~> 1.4.5"])
|
91
99
|
s.add_dependency(%q<nokogiri>, [">= 1.6.3", "~> 1.6.3"])
|
100
|
+
s.add_dependency(%q<slop>, [">= 4.0.0", "~> 4.0.0"])
|
92
101
|
s.add_dependency(%q<shoulda>, [">= 3", "~> 3"])
|
93
102
|
s.add_dependency(%q<yard>, [">= 0.8.7", "~> 0.8"])
|
94
103
|
s.add_dependency(%q<rdoc>, [">= 4.0", "~> 4.0"])
|
@@ -103,6 +112,7 @@ Gem::Specification.new do |s|
|
|
103
112
|
s.add_dependency(%q<git>, [">= 1.2.8", "~> 1.2"])
|
104
113
|
s.add_dependency(%q<sinatra>, [">= 1.4.5", "~> 1.4.5"])
|
105
114
|
s.add_dependency(%q<nokogiri>, [">= 1.6.3", "~> 1.6.3"])
|
115
|
+
s.add_dependency(%q<slop>, [">= 4.0.0", "~> 4.0.0"])
|
106
116
|
s.add_dependency(%q<shoulda>, [">= 3", "~> 3"])
|
107
117
|
s.add_dependency(%q<yard>, [">= 0.8.7", "~> 0.8"])
|
108
118
|
s.add_dependency(%q<rdoc>, [">= 4.0", "~> 4.0"])
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
module Nutella
|
4
|
+
|
5
|
+
class TestRunCommand < MiniTest::Test
|
6
|
+
|
7
|
+
@@run_cmd = RunCommand.new
|
8
|
+
|
9
|
+
|
10
|
+
should 'parse app long argument' do
|
11
|
+
params = @@run_cmd.send( :extract_parameters, ['--with=bot1,bot2,bot3'] )
|
12
|
+
assert_equal ['bot1', 'bot2', 'bot3'], params[:with]
|
13
|
+
end
|
14
|
+
|
15
|
+
should 'parse without long argument' do
|
16
|
+
params = @@run_cmd.send( :extract_parameters, ['--without=botA,botB,botC'] )
|
17
|
+
assert_equal ['botA', 'botB', 'botC'], params[:without]
|
18
|
+
end
|
19
|
+
|
20
|
+
should 'parse with long argument' do
|
21
|
+
params = @@run_cmd.send( :extract_parameters, ['--with=botX,botY,botZ'] )
|
22
|
+
assert_equal ['botX', 'botY', 'botZ'], params[:with]
|
23
|
+
end
|
24
|
+
|
25
|
+
should 'parse one short argument' do
|
26
|
+
params = @@run_cmd.send( :extract_parameters, ['-w=bot1,bot2,bot3'] )
|
27
|
+
assert_equal ['bot1', 'bot2', 'bot3'], params[:with]
|
28
|
+
end
|
29
|
+
|
30
|
+
should 'parse two long arguments' do
|
31
|
+
params = @@run_cmd.send( :extract_parameters, ['--with=bot1,bot2,bot3', '--without=botA,botB,botC'] )
|
32
|
+
assert_equal ['bot1', 'bot2', 'bot3'], params[:with]
|
33
|
+
assert_equal ['botA', 'botB', 'botC'], params[:without]
|
34
|
+
end
|
35
|
+
|
36
|
+
should 'parse two short arguments' do
|
37
|
+
params = @@run_cmd.send( :extract_parameters, ['-wo=bot1,bot2,bot3', '-w=botA,botB,botC'] )
|
38
|
+
assert_equal ['bot1', 'bot2', 'bot3'], params[:without]
|
39
|
+
assert_equal ['botA', 'botB', 'botC'], params[:with]
|
40
|
+
end
|
41
|
+
|
42
|
+
should 'parse one short and one long argument' do
|
43
|
+
params = @@run_cmd.send( :extract_parameters, ['--with=bot1,bot2,bot3', '-wo=botA,botB,botC'] )
|
44
|
+
assert_equal ['bot1', 'bot2', 'bot3'], params[:with]
|
45
|
+
assert_equal ['botA', 'botB', 'botC'], params[:without]
|
46
|
+
end
|
47
|
+
|
48
|
+
should 'raise an exception when trying to parse params that do not exist' do
|
49
|
+
assert_raises (Slop::UnknownOption) { @@run_cmd.send( :extract_parameters, ['--wit=bot1,bot2,bot3', '-o=botA,botB,botC'] ) }
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nutella_framework
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alessandro Gnoli
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-01-
|
11
|
+
date: 2015-01-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ansi
|
@@ -130,6 +130,26 @@ dependencies:
|
|
130
130
|
- - "~>"
|
131
131
|
- !ruby/object:Gem::Version
|
132
132
|
version: 1.6.3
|
133
|
+
- !ruby/object:Gem::Dependency
|
134
|
+
name: slop
|
135
|
+
requirement: !ruby/object:Gem::Requirement
|
136
|
+
requirements:
|
137
|
+
- - ">="
|
138
|
+
- !ruby/object:Gem::Version
|
139
|
+
version: 4.0.0
|
140
|
+
- - "~>"
|
141
|
+
- !ruby/object:Gem::Version
|
142
|
+
version: 4.0.0
|
143
|
+
type: :runtime
|
144
|
+
prerelease: false
|
145
|
+
version_requirements: !ruby/object:Gem::Requirement
|
146
|
+
requirements:
|
147
|
+
- - ">="
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: 4.0.0
|
150
|
+
- - "~>"
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: 4.0.0
|
133
153
|
- !ruby/object:Gem::Dependency
|
134
154
|
name: shoulda
|
135
155
|
requirement: !ruby/object:Gem::Requirement
|
@@ -273,6 +293,8 @@ files:
|
|
273
293
|
- actors/main_interface/views/index.erb
|
274
294
|
- actors/main_interface/views/not_found_404.erb
|
275
295
|
- bin/nutella
|
296
|
+
- data/index.html
|
297
|
+
- data/startup
|
276
298
|
- lib/cli/nutella_cli.rb
|
277
299
|
- lib/config/config.rb
|
278
300
|
- lib/config/current_project.rb
|
@@ -281,14 +303,18 @@ files:
|
|
281
303
|
- lib/core/command.rb
|
282
304
|
- lib/core/commands/broker.rb
|
283
305
|
- lib/core/commands/checkup.rb
|
306
|
+
- lib/core/commands/compile.rb
|
307
|
+
- lib/core/commands/dependencies.rb
|
284
308
|
- lib/core/commands/help.rb
|
285
309
|
- lib/core/commands/install.rb
|
286
310
|
- lib/core/commands/new.rb
|
287
311
|
- lib/core/commands/runs.rb
|
288
312
|
- lib/core/commands/start.rb
|
289
313
|
- lib/core/commands/stop.rb
|
314
|
+
- lib/core/commands/template.rb
|
290
315
|
- lib/core/nutella_core.rb
|
291
316
|
- lib/core/run_command.rb
|
317
|
+
- lib/core/template_command.rb
|
292
318
|
- lib/core/tmux.rb
|
293
319
|
- lib/logging/nutella_logger-remote.rb
|
294
320
|
- lib/logging/nutella_logger.rb
|
@@ -300,6 +326,7 @@ files:
|
|
300
326
|
- test/config/test_runlist.rb
|
301
327
|
- test/helper.rb
|
302
328
|
- test/logging/test_logging.rb
|
329
|
+
- test/test_run_command.rb
|
303
330
|
homepage: https://github.com/nutella-framework/nutella_framework
|
304
331
|
licenses:
|
305
332
|
- MIT
|