scripted 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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,25 @@
1
+ Then /^it should pass$/ do
2
+ assert_success true
3
+ end
4
+
5
+ Then /^it should fail$/ do
6
+ assert_success false
7
+ end
8
+
9
+ Given /^the configuration:$/ do |string|
10
+ write_file "scripted.rb", string
11
+ end
12
+
13
+ Then /^it should have taken about (\d+) seconds$/ do |seconds|
14
+ last_line = all_output.split("\n").reject { |line| line =~ /^\s*$/ }.last.strip
15
+ total = /Total runtime: (\d+\.\d+)s/.match(last_line)
16
+ expect(total[1].to_f).to be_within(0.5).of(seconds.to_f)
17
+ end
18
+
19
+ When /^I run scripted$/ do
20
+ run_simple "scripted", false
21
+ end
22
+
23
+ Then /^the file "(.*?)" should contain:$/ do |file, partial_content|
24
+ check_file_content(file, partial_content, true)
25
+ end
@@ -0,0 +1,5 @@
1
+ require 'aruba/cucumber'
2
+
3
+ Before do
4
+ @aruba_timeout_seconds = 10
5
+ end
@@ -0,0 +1,2 @@
1
+ require 'bundler/setup'
2
+ Bundler.setup
data/install ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env bash
2
+ set -e
3
+ gem which bundler >/dev/null 2>&1 || gem install bundler
4
+ bundle update
5
+ bundle exec scripted
@@ -0,0 +1,28 @@
1
+ Thread.abort_on_exception = true
2
+
3
+ require "scripted/error"
4
+ require "scripted/version" unless defined?(Scripted::VERSION)
5
+ require "scripted/configuration"
6
+ require "scripted/runner"
7
+
8
+ module Scripted
9
+
10
+ # Runs scripted.
11
+ #
12
+ # You can pass a configuration with group names,
13
+ # or you can pass a block, which will be the ad-hoc configuration.
14
+ def self.run(configuration = nil, *group_names, &block)
15
+ raise NotConfigured if configuration.nil? && block.nil?
16
+ config = configuration || configure(&block)
17
+ Runner.start!(config, *group_names)
18
+ end
19
+
20
+ # Configure scripted, returning a configuration which can be run later.
21
+ # Optionally pass a block to configure in place.
22
+ def self.configure(&block)
23
+ config = Configuration.new
24
+ config.evaluate(&block) if block_given?
25
+ config
26
+ end
27
+
28
+ end
@@ -0,0 +1,82 @@
1
+ require "scripted/commands/shell"
2
+ require "scripted/commands/rake"
3
+ require "scripted/commands/ruby"
4
+
5
+ module Scripted
6
+ class Command
7
+
8
+ attr_reader :name
9
+
10
+ def initialize(name, options = {}, &block)
11
+ @name = name
12
+ @options = options
13
+ define(&block) if block
14
+ end
15
+
16
+ def define(&block)
17
+ instance_eval &block
18
+ end
19
+
20
+ def executable
21
+ @command || Commands::Shell.new(@name)
22
+ end
23
+
24
+ def execute!(logger)
25
+ executable.execute!(logger)
26
+ end
27
+
28
+ def sh(command)
29
+ @command = Commands::Shell.new(command)
30
+ end
31
+ alias_method :`, :sh
32
+
33
+ def rake(command)
34
+ @command = Commands::Rake.new(command)
35
+ end
36
+
37
+ def ruby(&code)
38
+ @command = Commands::Ruby.new(code)
39
+ end
40
+
41
+ def important!
42
+ @important = true
43
+ end
44
+
45
+ def important?
46
+ !!@important
47
+ end
48
+
49
+ def unimportant?
50
+ !!@unimportant
51
+ end
52
+
53
+ def unimportant!
54
+ @unimportant = true
55
+ end
56
+
57
+ def forced!
58
+ @forced = true
59
+ end
60
+
61
+ def forced?
62
+ !!@forced
63
+ end
64
+
65
+ def parallel_id
66
+ @options[:parallel_id]
67
+ end
68
+
69
+ def run_in_parallel_with?(other)
70
+ other && parallel_id == other.parallel_id
71
+ end
72
+
73
+ def only_when_failed?
74
+ !!@only_when_failed
75
+ end
76
+
77
+ def only_when_failed!
78
+ @only_when_failed = true
79
+ end
80
+
81
+ end
82
+ end
@@ -0,0 +1,25 @@
1
+ require 'scripted/commands/shell'
2
+ require 'scripted/commands/ruby'
3
+
4
+ module Scripted
5
+ module Commands
6
+ class Rake
7
+
8
+ attr_reader :command
9
+
10
+ def initialize(command)
11
+ @command = command
12
+ end
13
+
14
+ def execute!(log = STDOUT)
15
+ task = Rake::Task[command]
16
+ rescue NameError
17
+ # not running from within Rake, so falling back shelling out
18
+ Shell.new("rake #{command} --trace").execute!(log)
19
+ else
20
+ Ruby.new(lambda { task.invoke }).execute!(log)
21
+ end
22
+
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,22 @@
1
+ module Scripted
2
+ module Commands
3
+ class Ruby
4
+
5
+ def initialize(code)
6
+ @code = code
7
+ end
8
+
9
+ def execute!(log = STDOUT)
10
+ old_stdout = $stdout
11
+ old_stderr = $stderr
12
+ $stdout = log
13
+ $stderr = log
14
+ @code.call
15
+ ensure
16
+ $stdout = old_stdout
17
+ $stderr = old_stderr
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,28 @@
1
+ require 'childprocess'
2
+
3
+ module Scripted
4
+ module Commands
5
+ class Shell
6
+
7
+ attr_reader :command
8
+
9
+ def initialize(command)
10
+ @command = command
11
+ end
12
+
13
+ def execute!(log = STDOUT)
14
+ process = ChildProcess.build(command)
15
+
16
+ process.io.stdout = log
17
+ process.io.stderr = log
18
+ process.start
19
+
20
+ process.wait
21
+ if process.exit_code != 0
22
+ fail CommandFailed, "`#{command}` failed with exit status #{process.exit_code}"
23
+ end
24
+ end
25
+
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,103 @@
1
+ require "scripted/error"
2
+ require "scripted/group"
3
+
4
+ module Scripted
5
+
6
+ class ConfigurationError < Error; end
7
+ class NoFormatterForOutput < ConfigurationError; end
8
+ class ConfigFileNotFound < ConfigurationError; end
9
+ class ConfigurationSyntaxError < ConfigurationError; end
10
+
11
+ class Configuration
12
+
13
+ attr_accessor :color
14
+
15
+ # Defines a group
16
+ def group(name, &block)
17
+ groups[name].define(&block)
18
+ end
19
+
20
+ # Defines a command in the `:default` group.
21
+ def run(name, &block)
22
+ groups[:default].define { run(name, &block) }
23
+ end
24
+ alias :` :run
25
+
26
+ # Makes a parallel block inside the `:default` group.
27
+ def parallel(&block)
28
+ groups[:default].define { parallel(&block) }
29
+ end
30
+
31
+ def formatter(name, options = {})
32
+ formatters << options.merge(:name => name)
33
+ end
34
+
35
+ def color?
36
+ color.nil? ? true : color
37
+ end
38
+
39
+ def out(out)
40
+ if formatters.any?
41
+ formatters.last[:out] = out
42
+ else
43
+ fail NoFormatterForOutput, "You must first specify a formatter to use this option"
44
+ end
45
+ end
46
+
47
+ def formatters
48
+ @formatters ||= []
49
+ end
50
+
51
+ def config_file(*file_names)
52
+ file_names.each do |file_name|
53
+ config_files << file_name
54
+ end
55
+ end
56
+
57
+ def config_files
58
+ @config_files ||= []
59
+ end
60
+
61
+ def absolute_config_files
62
+ config_files.map { |file_name| File.expand_path(file_name) }.uniq
63
+ end
64
+
65
+ def groups
66
+ @groups ||= Hash.new { |hash, key| hash[key] = Group.new(key) }
67
+ end
68
+
69
+ def with_default_config_file!
70
+ config_file "scripted.rb" if config_files.empty? && File.exist?("scripted.rb")
71
+ end
72
+
73
+ def load_files
74
+ absolute_config_files.each do |file_name|
75
+ load_file(file_name)
76
+ end
77
+ if groups.empty?
78
+ raise_config_file_error("scripted.rb")
79
+ end
80
+ end
81
+
82
+ def load_file(file_name)
83
+ source = File.open(file_name, 'r:utf-8').read
84
+ evaluate source, file_name, 1
85
+ rescue Errno::ENOENT => error
86
+ raise_config_file_error(file_name)
87
+ end
88
+
89
+ def evaluate(*args, &block)
90
+ instance_eval(*args, &block)
91
+ rescue Exception => error
92
+ my_error = ConfigurationSyntaxError.new("#{error.message} (#{error.class})")
93
+ my_error.set_backtrace error.backtrace
94
+ raise my_error
95
+ end
96
+
97
+ def raise_config_file_error(file_name)
98
+ raise ConfigFileNotFound, "No such file -- #{File.expand_path(file_name)}\nEither create a file called 'scripted.rb', or specify another file to load"
99
+ end
100
+
101
+ end
102
+
103
+ end
@@ -0,0 +1,13 @@
1
+ module Scripted
2
+ class Error < RuntimeError
3
+ end
4
+
5
+ class RunningFailed < Error
6
+ end
7
+
8
+ class CommandFailed < Error
9
+ end
10
+
11
+ class NotConfigured < Error
12
+ end
13
+ end
@@ -0,0 +1,39 @@
1
+ # encoding: utf-8
2
+ require "scripted/formatters/blank"
3
+ require 'thread'
4
+
5
+ module Scripted
6
+ module Formatters
7
+ class Announcer < Blank
8
+
9
+ def initialize(*)
10
+ super
11
+ @semaphore = Mutex.new
12
+ end
13
+
14
+ def start(commands, runner)
15
+ @length = [ 50, commands.max_by { |command| command.name.size }.name.size + 4 ].max
16
+ end
17
+
18
+ # wrapped in a synchronize block to prevent asynchronous output clogging the output
19
+ def execute(command)
20
+ @semaphore.synchronize do
21
+ puts ""
22
+ puts cyan("┌" + force_encoding("─" * (@length - 2)) + "┐")
23
+ puts "#{cyan("│")} #{command.name.center(@length - 4)} #{cyan("│")}"
24
+ puts cyan("└" + force_encoding("─" * (@length - 2)) + "┘")
25
+ puts ""
26
+ end
27
+ end
28
+
29
+ def force_encoding(text)
30
+ if text.respond_to?(:force_encoding)
31
+ text.force_encoding('utf-8')
32
+ else # ruby 1.8
33
+ text
34
+ end
35
+ end
36
+
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,97 @@
1
+ module Scripted
2
+ module Formatters
3
+ class Blank
4
+
5
+ attr_reader :raw_out, :configuration
6
+
7
+ # Called whn Scripted starts.
8
+ # If you override this, don't forget to call `super`.
9
+ def initialize(out, configuration)
10
+ @raw_out = out || $stderr
11
+ @configuration = configuration
12
+ end
13
+
14
+ # Called when the commands to be run have been selected
15
+ # No command has yet been run
16
+ def start(commands, runner)
17
+ end
18
+
19
+ # Called after all commands have been run.
20
+ def stop(commands, runner)
21
+ end
22
+
23
+ # Called whenever a command has failed.
24
+ def exception(command, exception)
25
+ end
26
+
27
+ # Called whenever a command has been run.
28
+ def done(command)
29
+ end
30
+
31
+ # Called when an important command has failed.
32
+ def halted(command)
33
+ end
34
+
35
+ # Called just before a command starts running
36
+ def execute(command)
37
+ end
38
+
39
+ # Called just before scripted exits. Useful for closing streams and
40
+ # closing connections.
41
+ def close
42
+ end
43
+
44
+ # Called for each character that went to stdout or stderr.
45
+ # Doesn't include output of other formatters.
46
+ def each_char(output, command)
47
+ end
48
+
49
+ private
50
+
51
+ def red(text); color(31, text); end
52
+ def green(text); color(32, text); end
53
+ def yellow(text); color(33, text); end
54
+ def blue(text); color(34, text); end
55
+ def magenta(text); color(35, text); end
56
+ def cyan(text); color(36, text); end
57
+ def white(text); color(37, text); end
58
+
59
+ def color(code, text)
60
+ if color_enabled?
61
+ "\e[#{code}m#{text}\e[0m"
62
+ else
63
+ text
64
+ end
65
+ end
66
+
67
+ def out
68
+ @out ||= build_out
69
+ end
70
+
71
+ def build_out
72
+ if raw_out.is_a?(String)
73
+ File.open(raw_out, 'w:utf-8')
74
+ else
75
+ raw_out
76
+ end
77
+ end
78
+
79
+ def file?
80
+ out.is_a?(File)
81
+ end
82
+
83
+ def puts(*args)
84
+ out.puts(*args)
85
+ end
86
+
87
+ def print(*args)
88
+ out.print(*args)
89
+ end
90
+
91
+ def color_enabled?
92
+ configuration.color? && !out.is_a?(File)
93
+ end
94
+
95
+ end
96
+ end
97
+ end