wip-runner 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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,9 @@
1
+ require 'wip/runner/shell/runner'
2
+ require 'wip/runner/shell/task'
3
+ require 'wip/runner/shell/handlers'
4
+
5
+ module WIP
6
+ module Runner
7
+ module Shell ; end
8
+ end
9
+ end
@@ -0,0 +1,5 @@
1
+ module WIP
2
+ module Runner
3
+ VERSION = "0.1.0"
4
+ end
5
+ 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