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.
@@ -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