nutella_framework 0.2.1 → 0.3.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.
- 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
|