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.
Files changed (73) hide show
  1. data/.gitignore +19 -0
  2. data/.rspec +3 -0
  3. data/.travis.yml +8 -0
  4. data/Gemfile +6 -0
  5. data/MIT-LICENSE +22 -0
  6. data/README.md +423 -0
  7. data/Rakefile +39 -0
  8. data/bin/scripted +67 -0
  9. data/cucumber.yml +3 -0
  10. data/examples/important.rb +31 -0
  11. data/examples/parallel.rb +30 -0
  12. data/examples/pride.rb +37 -0
  13. data/examples/websockets.png +0 -0
  14. data/examples/websockets.rb +37 -0
  15. data/examples/websockets/public/ansiparse.js +156 -0
  16. data/examples/websockets/server.rb +32 -0
  17. data/examples/websockets/server.ru +10 -0
  18. data/examples/websockets/views/_client.handlebars +47 -0
  19. data/examples/websockets/views/app.coffee +210 -0
  20. data/examples/websockets/views/index.erb +1 -0
  21. data/examples/websockets/views/layout.erb +14 -0
  22. data/examples/websockets/views/style.sass +61 -0
  23. data/features/controlling_exit_status.feature +124 -0
  24. data/features/formatters.feature +142 -0
  25. data/features/rake_integration.feature +86 -0
  26. data/features/running_commands_in_parallel.feature +27 -0
  27. data/features/running_from_command_line.feature +56 -0
  28. data/features/running_from_ruby.feature +38 -0
  29. data/features/running_groups.feature +39 -0
  30. data/features/specifying_which_commands_to_run.feature +122 -0
  31. data/features/steps/scripted_steps.rb +25 -0
  32. data/features/support/aruba.rb +5 -0
  33. data/features/support/env.rb +2 -0
  34. data/install +5 -0
  35. data/lib/scripted.rb +28 -0
  36. data/lib/scripted/command.rb +82 -0
  37. data/lib/scripted/commands/rake.rb +25 -0
  38. data/lib/scripted/commands/ruby.rb +22 -0
  39. data/lib/scripted/commands/shell.rb +28 -0
  40. data/lib/scripted/configuration.rb +103 -0
  41. data/lib/scripted/error.rb +13 -0
  42. data/lib/scripted/formatters/announcer.rb +39 -0
  43. data/lib/scripted/formatters/blank.rb +97 -0
  44. data/lib/scripted/formatters/default.rb +62 -0
  45. data/lib/scripted/formatters/human_status.rb +38 -0
  46. data/lib/scripted/formatters/stats.rb +38 -0
  47. data/lib/scripted/formatters/table.rb +99 -0
  48. data/lib/scripted/formatters/websocket.rb +137 -0
  49. data/lib/scripted/group.rb +49 -0
  50. data/lib/scripted/output/command_logger.rb +42 -0
  51. data/lib/scripted/output/logger.rb +139 -0
  52. data/lib/scripted/rake_task.rb +24 -0
  53. data/lib/scripted/runner.rb +19 -0
  54. data/lib/scripted/running/execute.rb +16 -0
  55. data/lib/scripted/running/run_command.rb +101 -0
  56. data/lib/scripted/running/run_commands.rb +98 -0
  57. data/lib/scripted/running/select_commands.rb +22 -0
  58. data/lib/scripted/version.rb +3 -0
  59. data/scripted.gemspec +35 -0
  60. data/scripted.rb +16 -0
  61. data/spec/scripted/command_spec.rb +72 -0
  62. data/spec/scripted/commands/ruby_spec.rb +10 -0
  63. data/spec/scripted/commands/shell_spec.rb +18 -0
  64. data/spec/scripted/configuration_spec.rb +50 -0
  65. data/spec/scripted/formatters/websocket_spec.rb +14 -0
  66. data/spec/scripted/group_spec.rb +49 -0
  67. data/spec/scripted/running/run_command_spec.rb +157 -0
  68. data/spec/scripted/running/run_commands_spec.rb +150 -0
  69. data/spec/scripted/running/select_commands_spec.rb +28 -0
  70. data/spec/spec_helper.rb +15 -0
  71. data/spec/support/expect_to_receive.rb +17 -0
  72. data/spec/support/io_capture.rb +50 -0
  73. metadata +340 -0
@@ -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
@@ -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
@@ -0,0 +1,3 @@
1
+ default: --format Fivemat --strict --tags ~@wip --color
2
+ wip: --format pretty --wip --tags @wip --color
3
+ no-color: --format Fivemat --strict --tags ~@wip
@@ -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
@@ -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
@@ -0,0 +1,10 @@
1
+ require 'bundler/setup'
2
+ Bundler.setup
3
+
4
+ require File.expand_path("../server.rb", __FILE__)
5
+
6
+ require 'faye'
7
+ Faye::WebSocket.load_adapter 'thin'
8
+ use Faye::RackAdapter, :mount => '/faye'
9
+
10
+ run Sinatra::Application