scripted 0.0.1
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/.gitignore +19 -0
- data/.rspec +3 -0
- data/.travis.yml +8 -0
- data/Gemfile +6 -0
- data/MIT-LICENSE +22 -0
- data/README.md +423 -0
- data/Rakefile +39 -0
- data/bin/scripted +67 -0
- data/cucumber.yml +3 -0
- data/examples/important.rb +31 -0
- data/examples/parallel.rb +30 -0
- data/examples/pride.rb +37 -0
- data/examples/websockets.png +0 -0
- data/examples/websockets.rb +37 -0
- data/examples/websockets/public/ansiparse.js +156 -0
- data/examples/websockets/server.rb +32 -0
- data/examples/websockets/server.ru +10 -0
- data/examples/websockets/views/_client.handlebars +47 -0
- data/examples/websockets/views/app.coffee +210 -0
- data/examples/websockets/views/index.erb +1 -0
- data/examples/websockets/views/layout.erb +14 -0
- data/examples/websockets/views/style.sass +61 -0
- data/features/controlling_exit_status.feature +124 -0
- data/features/formatters.feature +142 -0
- data/features/rake_integration.feature +86 -0
- data/features/running_commands_in_parallel.feature +27 -0
- data/features/running_from_command_line.feature +56 -0
- data/features/running_from_ruby.feature +38 -0
- data/features/running_groups.feature +39 -0
- data/features/specifying_which_commands_to_run.feature +122 -0
- data/features/steps/scripted_steps.rb +25 -0
- data/features/support/aruba.rb +5 -0
- data/features/support/env.rb +2 -0
- data/install +5 -0
- data/lib/scripted.rb +28 -0
- data/lib/scripted/command.rb +82 -0
- data/lib/scripted/commands/rake.rb +25 -0
- data/lib/scripted/commands/ruby.rb +22 -0
- data/lib/scripted/commands/shell.rb +28 -0
- data/lib/scripted/configuration.rb +103 -0
- data/lib/scripted/error.rb +13 -0
- data/lib/scripted/formatters/announcer.rb +39 -0
- data/lib/scripted/formatters/blank.rb +97 -0
- data/lib/scripted/formatters/default.rb +62 -0
- data/lib/scripted/formatters/human_status.rb +38 -0
- data/lib/scripted/formatters/stats.rb +38 -0
- data/lib/scripted/formatters/table.rb +99 -0
- data/lib/scripted/formatters/websocket.rb +137 -0
- data/lib/scripted/group.rb +49 -0
- data/lib/scripted/output/command_logger.rb +42 -0
- data/lib/scripted/output/logger.rb +139 -0
- data/lib/scripted/rake_task.rb +24 -0
- data/lib/scripted/runner.rb +19 -0
- data/lib/scripted/running/execute.rb +16 -0
- data/lib/scripted/running/run_command.rb +101 -0
- data/lib/scripted/running/run_commands.rb +98 -0
- data/lib/scripted/running/select_commands.rb +22 -0
- data/lib/scripted/version.rb +3 -0
- data/scripted.gemspec +35 -0
- data/scripted.rb +16 -0
- data/spec/scripted/command_spec.rb +72 -0
- data/spec/scripted/commands/ruby_spec.rb +10 -0
- data/spec/scripted/commands/shell_spec.rb +18 -0
- data/spec/scripted/configuration_spec.rb +50 -0
- data/spec/scripted/formatters/websocket_spec.rb +14 -0
- data/spec/scripted/group_spec.rb +49 -0
- data/spec/scripted/running/run_command_spec.rb +157 -0
- data/spec/scripted/running/run_commands_spec.rb +150 -0
- data/spec/scripted/running/select_commands_spec.rb +28 -0
- data/spec/spec_helper.rb +15 -0
- data/spec/support/expect_to_receive.rb +17 -0
- data/spec/support/io_capture.rb +50 -0
- metadata +340 -0
data/Rakefile
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
require "bundler/gem_tasks"
|
3
|
+
|
4
|
+
Bundler.setup
|
5
|
+
|
6
|
+
require 'rspec/core/rake_task'
|
7
|
+
RSpec::Core::RakeTask.new(:spec)
|
8
|
+
|
9
|
+
require 'cucumber/rake/task'
|
10
|
+
Cucumber::Rake::Task.new(:cucumber)
|
11
|
+
|
12
|
+
require 'scripted/rake_task'
|
13
|
+
Scripted::RakeTask.new(:scripted)
|
14
|
+
|
15
|
+
task :default => [:scripted]
|
16
|
+
|
17
|
+
namespace :examples do
|
18
|
+
|
19
|
+
desc "Runs the websockets example (really cool)"
|
20
|
+
Scripted::RakeTask.new(:websockets) do
|
21
|
+
config_file "examples/websockets.rb"
|
22
|
+
end
|
23
|
+
|
24
|
+
desc "Runs the parallel example"
|
25
|
+
Scripted::RakeTask.new(:parallel) do
|
26
|
+
config_file "examples/parallel.rb"
|
27
|
+
end
|
28
|
+
|
29
|
+
desc "Runs the important example"
|
30
|
+
Scripted::RakeTask.new(:important) do
|
31
|
+
config_file "examples/important.rb"
|
32
|
+
end
|
33
|
+
|
34
|
+
desc "Runs the pride example"
|
35
|
+
Scripted::RakeTask.new(:pride) do
|
36
|
+
config_file "examples/pride.rb"
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
data/bin/scripted
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'scripted'
|
4
|
+
require 'optparse'
|
5
|
+
require 'set'
|
6
|
+
|
7
|
+
config = Scripted.configure
|
8
|
+
groups = Set.new
|
9
|
+
|
10
|
+
parser = OptionParser.new do |opts|
|
11
|
+
|
12
|
+
opts.on_tail("-h", "--help", "Shows the available options") do
|
13
|
+
puts opts
|
14
|
+
exit
|
15
|
+
end
|
16
|
+
|
17
|
+
opts.on_tail("-v", "--version", "Shows the version") do
|
18
|
+
puts Scripted::VERSION
|
19
|
+
exit
|
20
|
+
end
|
21
|
+
|
22
|
+
opts.on("-r", "--require a,b", Array, "Which files should be loaded (defaults to `scripted.rb`)") do |file_names|
|
23
|
+
config.config_file(*file_names)
|
24
|
+
end
|
25
|
+
|
26
|
+
opts.on("-g", "--group a,b", Array, "Which groups to run (defaults to `:default`)") do |group_names|
|
27
|
+
groups += group_names
|
28
|
+
end
|
29
|
+
|
30
|
+
opts.on("-f", "--format name", "Specify a formatter (defaults to `default`)") do |formatter|
|
31
|
+
config.formatter formatter
|
32
|
+
end
|
33
|
+
|
34
|
+
opts.on("-o", "--out output", "Specify the output of the previously specified formatter") do |out|
|
35
|
+
config.out out
|
36
|
+
end
|
37
|
+
|
38
|
+
opts.on("-c", "--[no-]color", "Use color in formatters? (defaults to yes)") do |color|
|
39
|
+
config.color = color
|
40
|
+
end
|
41
|
+
|
42
|
+
opts.on("-I PATH", "Add a directory to your load path") do |load_path|
|
43
|
+
$LOAD_PATH.unshift(load_path)
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
begin
|
49
|
+
parser.parse!
|
50
|
+
config.with_default_config_file!
|
51
|
+
config.load_files
|
52
|
+
rescue OptionParser::InvalidOption => error
|
53
|
+
puts error
|
54
|
+
puts ""
|
55
|
+
puts parser
|
56
|
+
exit 1
|
57
|
+
rescue Scripted::ConfigurationError => error
|
58
|
+
puts "#{error} at #{error.backtrace.first}"
|
59
|
+
exit 1
|
60
|
+
end
|
61
|
+
|
62
|
+
begin
|
63
|
+
Scripted.run config, *groups
|
64
|
+
rescue Scripted::RunningFailed => error
|
65
|
+
puts error
|
66
|
+
exit 1
|
67
|
+
end
|
data/cucumber.yml
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
formatter :table
|
2
|
+
formatter :announcer
|
3
|
+
formatter :default
|
4
|
+
|
5
|
+
run "an unimportant failing command" do
|
6
|
+
`false`
|
7
|
+
unimportant!
|
8
|
+
end
|
9
|
+
|
10
|
+
run "a normal command" do
|
11
|
+
`echo You should see this output`
|
12
|
+
end
|
13
|
+
|
14
|
+
run "an important failing command" do
|
15
|
+
`false`
|
16
|
+
important!
|
17
|
+
end
|
18
|
+
|
19
|
+
run "another normal command" do
|
20
|
+
`echo You should never see this`
|
21
|
+
end
|
22
|
+
|
23
|
+
run "a forced command" do
|
24
|
+
`true`
|
25
|
+
forced!
|
26
|
+
end
|
27
|
+
|
28
|
+
run "this runs because it failed" do
|
29
|
+
`true`
|
30
|
+
only_when_failed!
|
31
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
# This example shows of parallel commands.
|
4
|
+
#
|
5
|
+
# The output should look something like:
|
6
|
+
#
|
7
|
+
# ┌─────────┬─────────┬─────────┐
|
8
|
+
# │ Command │ Runtime │ Status │
|
9
|
+
# ├─────────┼─────────┼─────────┤
|
10
|
+
# │ sleep 1 │ 1.061s │ success │
|
11
|
+
# │ sleep 1 │ 1.062s │ success │
|
12
|
+
# │ sleep 1 │ 1.064s │ success │
|
13
|
+
# │ sleep 1 │ 1.066s │ success │
|
14
|
+
# │ sleep 1 │ 1.064s │ success │
|
15
|
+
# └─────────┴─────────┴─────────┘
|
16
|
+
# Total runtime: 2.307s
|
17
|
+
#
|
18
|
+
# To run: rake examples:parallel
|
19
|
+
|
20
|
+
formatter :table
|
21
|
+
|
22
|
+
parallel do
|
23
|
+
run "sleep 1"
|
24
|
+
run "sleep 1"
|
25
|
+
end
|
26
|
+
parallel do
|
27
|
+
run "sleep 1"
|
28
|
+
run "sleep 1"
|
29
|
+
run "sleep 1"
|
30
|
+
end
|
data/examples/pride.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# Taken from Minitest's Pride formatter
|
2
|
+
# Meant as example of custom formatters
|
3
|
+
|
4
|
+
require "scripted/formatters/blank"
|
5
|
+
|
6
|
+
class Pride < Scripted::Formatters::Blank
|
7
|
+
|
8
|
+
PI_3 = Math::PI / 3
|
9
|
+
|
10
|
+
def initialize(*)
|
11
|
+
super
|
12
|
+
@index = 0
|
13
|
+
@colors = (0...(6 * 7)).map { |n|
|
14
|
+
n *= 1.0 / 6
|
15
|
+
r = (3 * Math.sin(n ) + 3).to_i
|
16
|
+
g = (3 * Math.sin(n + 2 * PI_3) + 3).to_i
|
17
|
+
b = (3 * Math.sin(n + 4 * PI_3) + 3).to_i
|
18
|
+
36 * r + 6 * g + b + 16
|
19
|
+
}
|
20
|
+
@size = @colors.size
|
21
|
+
end
|
22
|
+
|
23
|
+
def each_char(char, command)
|
24
|
+
print pride(char)
|
25
|
+
end
|
26
|
+
|
27
|
+
def pride(string)
|
28
|
+
color = @colors[@index % @size]
|
29
|
+
@index += 1
|
30
|
+
"\e[38;5;#{color}m#{string}\e[0m"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
formatter Pride
|
35
|
+
|
36
|
+
run "rspec --no-color"
|
37
|
+
run "cucumber -p no-color"
|
Binary file
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# This is an example of using the websockets formatter.
|
2
|
+
#
|
3
|
+
# The web application is made with Ember.js. Also, even though some commands
|
4
|
+
# might run in parallel, their output will appear properly in the web view.
|
5
|
+
#
|
6
|
+
# To run this: rake examples:websockets
|
7
|
+
|
8
|
+
dir = File.expand_path("../websockets", __FILE__)
|
9
|
+
|
10
|
+
formatter :default
|
11
|
+
formatter :table
|
12
|
+
|
13
|
+
run "start server" do
|
14
|
+
`bundle exec thin -e production -R #{File.join(dir, "server.ru")} -p 9292 -d start`
|
15
|
+
end
|
16
|
+
|
17
|
+
# give the server some time to start
|
18
|
+
run "sleep 1"
|
19
|
+
|
20
|
+
# opens the web page with the ember.js app
|
21
|
+
run "open client" do
|
22
|
+
`bundle exec launchy http://localhost:9292/`
|
23
|
+
end
|
24
|
+
|
25
|
+
# how unbelievable meta! :)
|
26
|
+
run "scripted with websocket formatter" do
|
27
|
+
`bundle exec scripted -f websocket -o http://localhost:9292/faye`
|
28
|
+
end
|
29
|
+
|
30
|
+
# keep the connection open for just a bit longer
|
31
|
+
run "sleep 1"
|
32
|
+
|
33
|
+
# forcefully shut down, because websocket connections will cause a timeout anyway
|
34
|
+
run "shutdown server" do
|
35
|
+
`bundle exec thin -f stop`
|
36
|
+
forced!
|
37
|
+
end
|
@@ -0,0 +1,156 @@
|
|
1
|
+
// "Borrowed" from TravisCI. Love you guys!
|
2
|
+
ansiparse = function (str) {
|
3
|
+
//
|
4
|
+
// I'm terrible at writing parsers.
|
5
|
+
//
|
6
|
+
var matchingControl = null,
|
7
|
+
matchingData = null,
|
8
|
+
matchingText = '',
|
9
|
+
ansiState = [],
|
10
|
+
result = [],
|
11
|
+
state = {};
|
12
|
+
|
13
|
+
//
|
14
|
+
// General workflow for this thing is:
|
15
|
+
// \033\[33mText
|
16
|
+
// | | |
|
17
|
+
// | | matchingText
|
18
|
+
// | matchingData
|
19
|
+
// matchingControl
|
20
|
+
//
|
21
|
+
// In further steps we hope it's all going to be fine. It usually is.
|
22
|
+
//
|
23
|
+
|
24
|
+
for (var i = 0; i < str.length; i++) {
|
25
|
+
if (matchingControl != null) {
|
26
|
+
if (matchingControl == '\033' && str[i] == '\[') {
|
27
|
+
//
|
28
|
+
// We've matched full control code. Lets start matching formating data.
|
29
|
+
//
|
30
|
+
|
31
|
+
//
|
32
|
+
// "emit" matched text with correct state
|
33
|
+
//
|
34
|
+
if (matchingText) {
|
35
|
+
state.text = matchingText;
|
36
|
+
result.push(state);
|
37
|
+
state = {};
|
38
|
+
matchingText = "";
|
39
|
+
}
|
40
|
+
|
41
|
+
matchingControl = null;
|
42
|
+
matchingData = '';
|
43
|
+
}
|
44
|
+
else {
|
45
|
+
//
|
46
|
+
// We failed to match anything - most likely a bad control code. We
|
47
|
+
// go back to matching regular strings.
|
48
|
+
//
|
49
|
+
matchingText += matchingControl + str[i];
|
50
|
+
matchingControl = null;
|
51
|
+
}
|
52
|
+
continue;
|
53
|
+
}
|
54
|
+
else if (matchingData != null) {
|
55
|
+
if (str[i] == ';') {
|
56
|
+
//
|
57
|
+
// `;` separates many formatting codes, for example: `\033[33;43m`
|
58
|
+
// means that both `33` and `43` should be applied.
|
59
|
+
//
|
60
|
+
// TODO: this can be simplified by modifying state here.
|
61
|
+
//
|
62
|
+
ansiState.push(matchingData);
|
63
|
+
matchingData = '';
|
64
|
+
}
|
65
|
+
else if (str[i] == 'm') {
|
66
|
+
//
|
67
|
+
// `m` finished whole formatting code. We can proceed to matching
|
68
|
+
// formatted text.
|
69
|
+
//
|
70
|
+
ansiState.push(matchingData);
|
71
|
+
matchingData = null;
|
72
|
+
matchingText = '';
|
73
|
+
|
74
|
+
//
|
75
|
+
// Convert matched formatting data into user-friendly state object.
|
76
|
+
//
|
77
|
+
// TODO: DRY.
|
78
|
+
//
|
79
|
+
ansiState.forEach(function (ansiCode) {
|
80
|
+
if (ansiparse.foregroundColors[ansiCode]) {
|
81
|
+
state.foreground = ansiparse.foregroundColors[ansiCode];
|
82
|
+
}
|
83
|
+
else if (ansiparse.backgroundColors[ansiCode]) {
|
84
|
+
state.background = ansiparse.backgroundColors[ansiCode];
|
85
|
+
}
|
86
|
+
else if (ansiCode == 39) {
|
87
|
+
delete state.foreground;
|
88
|
+
}
|
89
|
+
else if (ansiCode == 49) {
|
90
|
+
delete state.background;
|
91
|
+
}
|
92
|
+
else if (ansiparse.styles[ansiCode]) {
|
93
|
+
state[ansiparse.styles[ansiCode]] = true;
|
94
|
+
}
|
95
|
+
else if (ansiCode == 22) {
|
96
|
+
state.bold = false;
|
97
|
+
}
|
98
|
+
else if (ansiCode == 23) {
|
99
|
+
state.italic = false;
|
100
|
+
}
|
101
|
+
else if (ansiCode == 24) {
|
102
|
+
state.underline = false;
|
103
|
+
}
|
104
|
+
});
|
105
|
+
ansiState = [];
|
106
|
+
}
|
107
|
+
else {
|
108
|
+
matchingData += str[i];
|
109
|
+
}
|
110
|
+
continue;
|
111
|
+
}
|
112
|
+
|
113
|
+
if (str[i] == '\033') {
|
114
|
+
matchingControl = str[i];
|
115
|
+
|
116
|
+
}
|
117
|
+
else {
|
118
|
+
matchingText += str[i];
|
119
|
+
}
|
120
|
+
}
|
121
|
+
|
122
|
+
if (matchingText) {
|
123
|
+
state.text = matchingText + (matchingControl ? matchingControl : '');
|
124
|
+
result.push(state);
|
125
|
+
}
|
126
|
+
return result;
|
127
|
+
}
|
128
|
+
|
129
|
+
ansiparse.foregroundColors = {
|
130
|
+
'30': 'black',
|
131
|
+
'31': 'red',
|
132
|
+
'32': 'green',
|
133
|
+
'33': 'yellow',
|
134
|
+
'34': 'blue',
|
135
|
+
'35': 'magenta',
|
136
|
+
'36': 'cyan',
|
137
|
+
'37': 'white',
|
138
|
+
'90': 'grey'
|
139
|
+
};
|
140
|
+
|
141
|
+
ansiparse.backgroundColors = {
|
142
|
+
'40': 'black',
|
143
|
+
'41': 'red',
|
144
|
+
'42': 'green',
|
145
|
+
'43': 'yellow',
|
146
|
+
'44': 'blue',
|
147
|
+
'45': 'magenta',
|
148
|
+
'46': 'cyan',
|
149
|
+
'47': 'white'
|
150
|
+
};
|
151
|
+
|
152
|
+
ansiparse.styles = {
|
153
|
+
'1': 'bold',
|
154
|
+
'3': 'italic',
|
155
|
+
'4': 'underline'
|
156
|
+
};
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'sinatra'
|
2
|
+
require 'coffee-script'
|
3
|
+
require 'sass'
|
4
|
+
|
5
|
+
Tilt.register Tilt::ERBTemplate, 'handlebars'
|
6
|
+
|
7
|
+
helpers do
|
8
|
+
def handlebars(name)
|
9
|
+
partial = render :handlebars, :"_#{name}"
|
10
|
+
"<script type='text/x-handlebars'>#{partial}</script>"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
error do
|
15
|
+
<<-HTML
|
16
|
+
<h1>HALP!</h1>
|
17
|
+
<p>The shit hit the fan in such a way that I don't know what to do.</p>
|
18
|
+
<p>The error message: #{request.env['sinatra.error'].message}</p>
|
19
|
+
HTML
|
20
|
+
end
|
21
|
+
|
22
|
+
get "/" do
|
23
|
+
erb :index
|
24
|
+
end
|
25
|
+
|
26
|
+
get '/app.js' do
|
27
|
+
coffee :app
|
28
|
+
end
|
29
|
+
|
30
|
+
get '/style.css' do
|
31
|
+
sass :style
|
32
|
+
end
|