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,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