wip-runner 0.1.0
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.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.travis.yml +4 -0
- data/CODE_OF_CONDUCT.md +13 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +45 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/exe/wip-runner +4 -0
- data/lib/wip/runner/cli/help.rb +27 -0
- data/lib/wip/runner/cli/version.rb +11 -0
- data/lib/wip/runner/cli.rb +117 -0
- data/lib/wip/runner/command.rb +103 -0
- data/lib/wip/runner/commands.rb +47 -0
- data/lib/wip/runner/errors.rb +24 -0
- data/lib/wip/runner/options.rb +7 -0
- data/lib/wip/runner/parser.rb +93 -0
- data/lib/wip/runner/shell/handlers/base.rb +46 -0
- data/lib/wip/runner/shell/handlers/script.rb +63 -0
- data/lib/wip/runner/shell/handlers/system.rb +22 -0
- data/lib/wip/runner/shell/handlers.rb +17 -0
- data/lib/wip/runner/shell/runner.rb +147 -0
- data/lib/wip/runner/shell/task.rb +50 -0
- data/lib/wip/runner/shell.rb +9 -0
- data/lib/wip/runner/version.rb +5 -0
- data/lib/wip/runner/workflow/builder/component.rb +43 -0
- data/lib/wip/runner/workflow/builder/step.rb +15 -0
- data/lib/wip/runner/workflow/builder/task.rb +20 -0
- data/lib/wip/runner/workflow/builder/workflow.rb +50 -0
- data/lib/wip/runner/workflow/builder.rb +18 -0
- data/lib/wip/runner/workflow/runner.rb +518 -0
- data/lib/wip/runner/workflow.rb +49 -0
- data/lib/wip/runner.rb +17 -0
- data/wip-runner.gemspec +37 -0
- metadata +139 -0
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'expect'
|
2
|
+
require 'open3'
|
3
|
+
|
4
|
+
module WIP
|
5
|
+
module Runner
|
6
|
+
module Shell
|
7
|
+
module Handlers
|
8
|
+
class Script < Base
|
9
|
+
attr_reader :prompts
|
10
|
+
|
11
|
+
def initialize(*)
|
12
|
+
@prompts = []
|
13
|
+
super
|
14
|
+
end
|
15
|
+
|
16
|
+
def description
|
17
|
+
@description ||= "```\n#{@content}\n```"
|
18
|
+
# @description ||= @content
|
19
|
+
end
|
20
|
+
|
21
|
+
def execute(io, env, &block)
|
22
|
+
prompts.empty? ? simplex!(io, env, &block) : complex!(io, env, &block)
|
23
|
+
end
|
24
|
+
|
25
|
+
def prompt(term, options = {})
|
26
|
+
@prompts << [term, options] # Prompt.new(...)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def simplex!(io, env, &block)
|
32
|
+
Open3.popen2e(env, executable) do |stdin, stdoe, thread|
|
33
|
+
while line = stdoe.gets
|
34
|
+
block.call(line)
|
35
|
+
end
|
36
|
+
|
37
|
+
thread.value
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def complex!(io, env, &block)
|
42
|
+
Open3.popen2e(env, executable) do |stdin, stdoe, thread|
|
43
|
+
prompts.each do |term, options|
|
44
|
+
stdoe.expect(term) do |result|
|
45
|
+
stdin.puts io.ask(term) do |q|
|
46
|
+
options.each { |k, v| q.send(:"#{k}=", v) }
|
47
|
+
end
|
48
|
+
stdoe.gets
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
while line = stdoe.gets
|
53
|
+
block.call(line)
|
54
|
+
end
|
55
|
+
|
56
|
+
thread.value
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module WIP
|
2
|
+
module Runner
|
3
|
+
module Shell
|
4
|
+
module Handlers
|
5
|
+
class System < Base
|
6
|
+
def description
|
7
|
+
# @description ||= "```\n#{@content}\n```"
|
8
|
+
@description ||= @content
|
9
|
+
end
|
10
|
+
|
11
|
+
def execute(io, env, &block)
|
12
|
+
IO.popen(env, executable, 'r') do |pipe|
|
13
|
+
pipe.each(&block)
|
14
|
+
end
|
15
|
+
|
16
|
+
$?
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'wip/runner/shell/handlers/base'
|
2
|
+
require 'wip/runner/shell/handlers/script'
|
3
|
+
require 'wip/runner/shell/handlers/system'
|
4
|
+
|
5
|
+
module WIP
|
6
|
+
module Runner
|
7
|
+
module Shell
|
8
|
+
module Handlers
|
9
|
+
class << self
|
10
|
+
def locate(key)
|
11
|
+
const_get(key.to_s.capitalize)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,147 @@
|
|
1
|
+
require 'open3'
|
2
|
+
|
3
|
+
module WIP
|
4
|
+
module Runner
|
5
|
+
module Shell
|
6
|
+
class Runner
|
7
|
+
attr_reader :arguments, :options
|
8
|
+
|
9
|
+
def initialize(io, tasks, env = {})
|
10
|
+
@io = io
|
11
|
+
@tasks = tasks
|
12
|
+
@env = env
|
13
|
+
@io.indent_size = 2
|
14
|
+
end
|
15
|
+
|
16
|
+
def run(arguments, options)
|
17
|
+
@arguments = arguments
|
18
|
+
@options = options
|
19
|
+
|
20
|
+
@io.indent do
|
21
|
+
@tasks.each do |task|
|
22
|
+
evaluate(task)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def evaluate(task)
|
30
|
+
task.build(arguments, options)
|
31
|
+
prefix = options.preview ? :preview : :execute
|
32
|
+
|
33
|
+
section('Config') do
|
34
|
+
# @io.newline
|
35
|
+
# @io.say '```'
|
36
|
+
task.configs.each do |term, options|
|
37
|
+
send(:"#{prefix}_config", term, options)
|
38
|
+
end
|
39
|
+
# @io.say '```'
|
40
|
+
end unless task.configs.empty?
|
41
|
+
|
42
|
+
task.shells.each do |shell|
|
43
|
+
section("Shell #{shell.type.downcase}") do
|
44
|
+
@io.newline
|
45
|
+
send(:"#{prefix}_shell", shell)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# p task.children
|
50
|
+
task.children.each do |child|
|
51
|
+
section('Task') do
|
52
|
+
evaluate(child)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# ---
|
58
|
+
|
59
|
+
# class Execute
|
60
|
+
# end
|
61
|
+
#
|
62
|
+
# class Preview
|
63
|
+
# end
|
64
|
+
|
65
|
+
def execute_config(term, options = {})
|
66
|
+
query = options[:required] ? "#{term} (*)" : term
|
67
|
+
answer = @io.ask("- #{query}: ") do |q|
|
68
|
+
q.default = (options[:default] || ENV[term]) unless options[:password]
|
69
|
+
q.echo = false if options[:password]
|
70
|
+
q.validate = /^.+$/ if options[:required]
|
71
|
+
end
|
72
|
+
@env[term] = answer unless answer.empty?
|
73
|
+
@env[term] ||= ENV[term]
|
74
|
+
end
|
75
|
+
|
76
|
+
def execute_shell(shell)
|
77
|
+
preview_shell(shell)
|
78
|
+
|
79
|
+
if approved?
|
80
|
+
@io.newline
|
81
|
+
result = shell.execute(@io, @env) do |line|
|
82
|
+
# @io.say "> `#{line.rstrip}`<br>"
|
83
|
+
@io.say "> #{line.rstrip}"
|
84
|
+
# puts "#{@io.indentation}> `#{line.rstrip}` "
|
85
|
+
# @io.instance_variable_get(:@output).puts "> `#{line.rstrip}` "
|
86
|
+
|
87
|
+
# @io.send((action || :say), line)
|
88
|
+
end
|
89
|
+
@io.newline
|
90
|
+
|
91
|
+
# TODO: raise instead of exit.
|
92
|
+
exit 1 unless result.success?
|
93
|
+
else
|
94
|
+
@io.newline
|
95
|
+
@io.say '> (skipped)'
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def preview_config(term, options = {})
|
100
|
+
message = options[:required] ? "#{term} (*)" : term
|
101
|
+
@io.say "- #{message}"
|
102
|
+
end
|
103
|
+
|
104
|
+
def preview_shell(shell)
|
105
|
+
@io.say shell.description
|
106
|
+
end
|
107
|
+
|
108
|
+
# ---
|
109
|
+
|
110
|
+
def section(heading, &block)
|
111
|
+
@io.say "- [ ] #{heading}..."
|
112
|
+
@io.indent(&block)
|
113
|
+
end
|
114
|
+
|
115
|
+
# $ wip-runner x --format=markdown
|
116
|
+
# $ wip-runner x --log=out.md # log format determined by extension
|
117
|
+
#
|
118
|
+
# @formatter = Formatter::Markdown.new(@io)
|
119
|
+
#
|
120
|
+
# def item(text)
|
121
|
+
# formatter.item(text)
|
122
|
+
# end
|
123
|
+
#
|
124
|
+
# class Formatter::Markdown
|
125
|
+
# def item(text, check = true)
|
126
|
+
# @io.say check ? "- [ ] #{text}" : "- #{text}"
|
127
|
+
# end
|
128
|
+
# end
|
129
|
+
#
|
130
|
+
# class Formatter::Plain
|
131
|
+
# class Formatter::Color
|
132
|
+
# class Formatter::HTML
|
133
|
+
# class Formatter::JSON
|
134
|
+
|
135
|
+
# ---
|
136
|
+
|
137
|
+
def approved?
|
138
|
+
return true unless @options.stepwise
|
139
|
+
|
140
|
+
# case choice
|
141
|
+
# when 'yes'
|
142
|
+
# end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module WIP
|
2
|
+
module Runner
|
3
|
+
module Shell
|
4
|
+
class Task
|
5
|
+
attr_reader :configs, :shells, :children
|
6
|
+
|
7
|
+
def initialize(command, &block)
|
8
|
+
@command = command
|
9
|
+
@configs = []
|
10
|
+
@shells = []
|
11
|
+
@children = []
|
12
|
+
@block = block
|
13
|
+
end
|
14
|
+
|
15
|
+
def build(arguments, options)
|
16
|
+
self.instance_exec(arguments, options, &@block) ; self
|
17
|
+
end
|
18
|
+
|
19
|
+
def config(term, options = {})
|
20
|
+
@configs << [term.to_s, options] # Config.new(...)
|
21
|
+
end
|
22
|
+
|
23
|
+
def shell(handler, content, &block)
|
24
|
+
shells << Handlers.locate(handler).new(content, &block)
|
25
|
+
end
|
26
|
+
|
27
|
+
def task(&block)
|
28
|
+
children << Task.new(@command, &block)
|
29
|
+
end
|
30
|
+
|
31
|
+
protected
|
32
|
+
|
33
|
+
def method_missing(method_name, *args, &block)
|
34
|
+
if @command.respond_to?(method_name)
|
35
|
+
@command.send(method_name, *args, &block)
|
36
|
+
else
|
37
|
+
# super
|
38
|
+
@command.instance_eval {
|
39
|
+
method_missing(method_name, *args, &block)
|
40
|
+
}
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def respond_to_missing?(method_name, include_private = false)
|
45
|
+
@command.respond_to?(method_name) || super
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module WIP
|
2
|
+
module Runner
|
3
|
+
module Workflow
|
4
|
+
class Builder::Component
|
5
|
+
def prologue(value = nil)
|
6
|
+
@prologue = clean(value) unless value.nil?
|
7
|
+
@prologue
|
8
|
+
end
|
9
|
+
|
10
|
+
def shell(*args)
|
11
|
+
unless args.empty?
|
12
|
+
shells << [args[0], clean(args[1])]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def shells
|
17
|
+
@shells ||= []
|
18
|
+
end
|
19
|
+
|
20
|
+
def method_missing(method_name, *args, &block)
|
21
|
+
if @command.respond_to?(method_name)
|
22
|
+
@command.send(method_name, *args, &block)
|
23
|
+
else
|
24
|
+
super
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def respond_to_missing?(method_name, include_private = false)
|
29
|
+
@command.respond_to?(method_name) || super
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def clean(string)
|
35
|
+
return if string.nil?
|
36
|
+
|
37
|
+
indent = (string.scan(/^[ \t]*(?=\S)/).min || '').size
|
38
|
+
string.gsub(/^[ \t]{#{indent}}/, '').strip
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module WIP
|
2
|
+
module Runner
|
3
|
+
module Workflow
|
4
|
+
class Builder::Step < Builder::Component
|
5
|
+
attr_reader :heading
|
6
|
+
|
7
|
+
def initialize(command, heading, &block)
|
8
|
+
@command = command
|
9
|
+
@heading = heading
|
10
|
+
instance_exec(&block) if block_given?
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module WIP
|
2
|
+
module Runner
|
3
|
+
module Workflow
|
4
|
+
class Builder::Task < Builder::Component
|
5
|
+
attr_reader :heading, :steps
|
6
|
+
|
7
|
+
def initialize(command, heading, &block)
|
8
|
+
@command = command
|
9
|
+
@heading = heading
|
10
|
+
@steps = []
|
11
|
+
instance_exec(&block) if block_given?
|
12
|
+
end
|
13
|
+
|
14
|
+
def step(name, &block)
|
15
|
+
steps << Builder::Step.new(@command, name, &block)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module WIP
|
2
|
+
module Runner
|
3
|
+
module Workflow
|
4
|
+
class Builder::Workflow < Builder::Component
|
5
|
+
def initialize(command)
|
6
|
+
@command = command
|
7
|
+
@configs = []
|
8
|
+
@guards = []
|
9
|
+
@tasks = []
|
10
|
+
end
|
11
|
+
|
12
|
+
# ---
|
13
|
+
|
14
|
+
def config(key, options = {})
|
15
|
+
@configs << [key.to_s, options]
|
16
|
+
end
|
17
|
+
|
18
|
+
def guard(description, check, expectation = nil)
|
19
|
+
@guards << [description, check, expectation]
|
20
|
+
end
|
21
|
+
|
22
|
+
def task(heading, &block)
|
23
|
+
@tasks << Builder::Task.new(@command, heading, &block)
|
24
|
+
end
|
25
|
+
|
26
|
+
# ---
|
27
|
+
|
28
|
+
def heading
|
29
|
+
[@command.class.name.split('::').last, 'Workflow'].join(' ')
|
30
|
+
end
|
31
|
+
|
32
|
+
def overview
|
33
|
+
clean(@command.class.overview)
|
34
|
+
end
|
35
|
+
|
36
|
+
def configs
|
37
|
+
@configs
|
38
|
+
end
|
39
|
+
|
40
|
+
def guards
|
41
|
+
@guards
|
42
|
+
end
|
43
|
+
|
44
|
+
def tasks
|
45
|
+
@tasks
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module WIP
|
2
|
+
module Runner
|
3
|
+
module Workflow
|
4
|
+
class Builder
|
5
|
+
def initialize(command, &block)
|
6
|
+
@command = command
|
7
|
+
@block = block
|
8
|
+
end
|
9
|
+
|
10
|
+
def build(arguments, options)
|
11
|
+
workflow = Workflow.new(@command)
|
12
|
+
workflow.instance_exec(arguments, options, &@block)
|
13
|
+
workflow
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|