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
@@ -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
|
data/install
ADDED
data/lib/scripted.rb
ADDED
@@ -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,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
|