fudge 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/bin/fudge +9 -0
- data/lib/fudge.rb +16 -0
- data/lib/fudge/build.rb +31 -0
- data/lib/fudge/cli.rb +29 -0
- data/lib/fudge/description.rb +53 -0
- data/lib/fudge/exceptions.rb +31 -0
- data/lib/fudge/helpers.rb +5 -0
- data/lib/fudge/helpers/bundle_aware.rb +14 -0
- data/lib/fudge/parser.rb +12 -0
- data/lib/fudge/railtie.rb +17 -0
- data/lib/fudge/runner.rb +25 -0
- data/lib/fudge/task_dsl.rb +41 -0
- data/lib/fudge/tasks.rb +31 -0
- data/lib/fudge/tasks/clean_bundler_env.rb +24 -0
- data/lib/fudge/tasks/composite_task.rb +24 -0
- data/lib/fudge/tasks/each_directory.rb +34 -0
- data/lib/fudge/tasks/in_directory.rb +24 -0
- data/lib/fudge/tasks/rake.rb +19 -0
- data/lib/fudge/tasks/rspec.rb +29 -0
- data/lib/fudge/tasks/shell.rb +76 -0
- data/lib/fudge/tasks/task.rb +18 -0
- data/lib/fudge/tasks/yard.rb +29 -0
- data/lib/fudge/version.rb +3 -0
- data/lib/tasks/fudge.rake +7 -0
- data/spec/lib/fudge/build_spec.rb +5 -0
- data/spec/lib/fudge/cli_spec.rb +76 -0
- data/spec/lib/fudge/description_spec.rb +241 -0
- data/spec/lib/fudge/exceptions_spec.rb +36 -0
- data/spec/lib/fudge/parser_spec.rb +23 -0
- data/spec/lib/fudge/runner_spec.rb +25 -0
- data/spec/lib/fudge/tasks/bundler_spec.rb +39 -0
- data/spec/lib/fudge/tasks/composite_task_spec.rb +60 -0
- data/spec/lib/fudge/tasks/each_directory_spec.rb +57 -0
- data/spec/lib/fudge/tasks/in_directory_spec.rb +39 -0
- data/spec/lib/fudge/tasks/rake_spec.rb +18 -0
- data/spec/lib/fudge/tasks/rspec_spec.rb +36 -0
- data/spec/lib/fudge/tasks/shell_spec.rb +38 -0
- data/spec/lib/fudge/tasks/task_spec.rb +14 -0
- data/spec/lib/fudge/tasks/yard_spec.rb +25 -0
- data/spec/lib/fudge/tasks_spec.rb +33 -0
- data/spec/spec_helper.rb +22 -0
- data/spec/support/dummy_task.rb +40 -0
- data/spec/support/matchers.rb +47 -0
- data/spec/support/tmpdir.rb +12 -0
- metadata +302 -0
@@ -0,0 +1,29 @@
|
|
1
|
+
module Fudge
|
2
|
+
module Tasks
|
3
|
+
class Rspec < Shell
|
4
|
+
include Helpers::BundleAware
|
5
|
+
|
6
|
+
attr_accessor :color, :coverage
|
7
|
+
|
8
|
+
def self.name
|
9
|
+
:rspec
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
def cmd(options={})
|
14
|
+
self.arguments = 'spec/' if arguments.blank?
|
15
|
+
bundle_cmd("rspec#{' --tty' unless color == false} #{arguments}", options)
|
16
|
+
end
|
17
|
+
|
18
|
+
def check_for
|
19
|
+
if coverage
|
20
|
+
[/(\d+\.\d+)%\) covered/, lambda { |matches| matches[1].to_f >= coverage ? true : 'Insufficient Coverage.' }]
|
21
|
+
else
|
22
|
+
super
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
register Rspec
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module Fudge
|
2
|
+
module Tasks
|
3
|
+
class Shell < Task
|
4
|
+
attr_accessor :arguments, :check_for
|
5
|
+
|
6
|
+
def self.name
|
7
|
+
:shell
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(*args)
|
11
|
+
self.arguments = super.join(' ')
|
12
|
+
end
|
13
|
+
|
14
|
+
def run(options={})
|
15
|
+
@output, success = run_command(cmd(options))
|
16
|
+
return false unless success
|
17
|
+
return check_for_output
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def run_command(cmd)
|
23
|
+
output = ''
|
24
|
+
IO.popen(cmd) do |f|
|
25
|
+
until f.eof?
|
26
|
+
bit = f.getc
|
27
|
+
output << bit
|
28
|
+
putc bit
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
[output, $?.success?]
|
33
|
+
end
|
34
|
+
|
35
|
+
def check_for_output
|
36
|
+
return true unless check_for # We're ok if no output defined
|
37
|
+
|
38
|
+
# Check if we have a callable to parse the regex matches
|
39
|
+
if check_for.is_a? Enumerable
|
40
|
+
regex, pass_if = check_for[0], check_for[1]
|
41
|
+
else
|
42
|
+
regex, pass_if = check_for, nil
|
43
|
+
end
|
44
|
+
|
45
|
+
# Do regex match and fail if no match
|
46
|
+
match = @output.match(regex)
|
47
|
+
unless match
|
48
|
+
puts "Output didn't match #{regex}."
|
49
|
+
return false
|
50
|
+
end
|
51
|
+
|
52
|
+
# If we've got a callable, call it to check on regex matches
|
53
|
+
if pass_if
|
54
|
+
passed = pass_if.call(match)
|
55
|
+
unless passed == true
|
56
|
+
if passed.is_a? String
|
57
|
+
puts passed
|
58
|
+
else
|
59
|
+
puts "Output matched #{check_for} but condition failed."
|
60
|
+
end
|
61
|
+
return false
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
true
|
66
|
+
end
|
67
|
+
|
68
|
+
# Defines the command to run
|
69
|
+
def cmd(options={})
|
70
|
+
arguments
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
register Shell
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Fudge
|
2
|
+
module Tasks
|
3
|
+
class Task
|
4
|
+
attr_reader :args
|
5
|
+
|
6
|
+
def initialize(*args)
|
7
|
+
@args = args.dup
|
8
|
+
options = args.extract_options!
|
9
|
+
|
10
|
+
options.each do |k,v|
|
11
|
+
send("#{k}=", v) if respond_to?("#{k}=")
|
12
|
+
end
|
13
|
+
|
14
|
+
args
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Fudge
|
2
|
+
module Tasks
|
3
|
+
class Yard < Shell
|
4
|
+
include Helpers::BundleAware
|
5
|
+
|
6
|
+
attr_accessor :coverage
|
7
|
+
|
8
|
+
def self.name
|
9
|
+
:yard
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def cmd(options={})
|
15
|
+
bundle_cmd("yard #{arguments}", options)
|
16
|
+
end
|
17
|
+
|
18
|
+
def check_for
|
19
|
+
if coverage
|
20
|
+
[/(\d+\.\d+)% documented/, lambda { |matches| matches[1].to_f >= coverage ? true : 'Insufficient Documentation.' }]
|
21
|
+
else
|
22
|
+
super
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
register Yard
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class AnotherDummyTask < DummyTask
|
4
|
+
def self.name
|
5
|
+
:another_dummy
|
6
|
+
end
|
7
|
+
|
8
|
+
def run(options={})
|
9
|
+
self.class.ran = true
|
10
|
+
end
|
11
|
+
end
|
12
|
+
Fudge::Tasks.register(AnotherDummyTask)
|
13
|
+
|
14
|
+
describe Fudge::Cli do
|
15
|
+
use_tmp_dir
|
16
|
+
|
17
|
+
after :each do
|
18
|
+
AnotherDummyTask.ran = false
|
19
|
+
end
|
20
|
+
|
21
|
+
describe :build do
|
22
|
+
before :each do
|
23
|
+
File.open('Fudgefile', 'w') do |f|
|
24
|
+
contents = <<-RUBY
|
25
|
+
build :default do
|
26
|
+
dummy
|
27
|
+
end
|
28
|
+
|
29
|
+
build :other do
|
30
|
+
another_dummy
|
31
|
+
end
|
32
|
+
RUBY
|
33
|
+
f.write(contents)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should run the default build" do
|
38
|
+
subject.build 'default'
|
39
|
+
DummyTask.ran.should be_true
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should accept a build name to run" do
|
43
|
+
subject.build 'other'
|
44
|
+
DummyTask.ran.should be_false
|
45
|
+
AnotherDummyTask.ran.should be_true
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe :init do
|
50
|
+
it "should create a new FudgeFile in the current directory" do
|
51
|
+
File.should_not be_exists('Fudgefile')
|
52
|
+
|
53
|
+
subject.init
|
54
|
+
|
55
|
+
File.should be_exists('Fudgefile')
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should contain a default build" do
|
59
|
+
subject.init
|
60
|
+
|
61
|
+
File.open('Fudgefile', 'r') do |f|
|
62
|
+
f.read.should include "build :default do\n task :rspec\nend"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should not modify the Fudgefile if one exists" do
|
67
|
+
File.open('Fudgefile', 'w') do |f|
|
68
|
+
f.write('foo')
|
69
|
+
end
|
70
|
+
|
71
|
+
subject.init
|
72
|
+
|
73
|
+
File.open('Fudgefile') { |f| f.read }.should == 'foo'
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,241 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Fudge::Description do
|
4
|
+
let(:build) { subject.builds.values.first }
|
5
|
+
let(:build_tasks) { build.tasks.map }
|
6
|
+
|
7
|
+
def make_build
|
8
|
+
subject.build :default do
|
9
|
+
subject.task :dummy
|
10
|
+
yield
|
11
|
+
end
|
12
|
+
build.callbacks = callbacks
|
13
|
+
end
|
14
|
+
|
15
|
+
let(:input) { '' }
|
16
|
+
let(:file) { StringIO.new(input).tap { |s| s.stub(:path).and_return('') } }
|
17
|
+
subject { described_class.new file }
|
18
|
+
|
19
|
+
describe :initialize do
|
20
|
+
let(:input) { 'build :foo do; end' }
|
21
|
+
|
22
|
+
it "should add the builds in the given string" do
|
23
|
+
subject.builds[:foo].should be_a Fudge::Build
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe :build do
|
28
|
+
it "should create a new build and add it to the builds array" do
|
29
|
+
subject.builds.should be_empty
|
30
|
+
|
31
|
+
subject.build :some_branch do
|
32
|
+
end
|
33
|
+
|
34
|
+
subject.builds.should have(1).item
|
35
|
+
subject.builds[:some_branch].should be_a Fudge::Build
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe :task do
|
40
|
+
it "should add a task to the current scope" do
|
41
|
+
subject.build :default do
|
42
|
+
subject.task :dummy
|
43
|
+
end
|
44
|
+
|
45
|
+
build_tasks.should have(1).item
|
46
|
+
build_tasks.first.should be_a DummyTask
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should pass arguments to the initializer" do
|
50
|
+
subject.build :default do
|
51
|
+
subject.task :dummy, :foo, :bar
|
52
|
+
end
|
53
|
+
|
54
|
+
build_tasks.first.args.should == [:foo, :bar]
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should forward missing methods to task" do
|
58
|
+
subject.build :default do
|
59
|
+
subject.dummy :foo, :bar
|
60
|
+
end
|
61
|
+
|
62
|
+
build_tasks.first.args.should == [:foo, :bar]
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should super method_missing if no task found" do
|
66
|
+
expect { subject.non_existeng_task :foo, :bar }.to raise_error(NoMethodError)
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should add tasks recursively to composite tasks" do
|
70
|
+
subject.build :default do
|
71
|
+
subject.dummy_composite do
|
72
|
+
subject.dummy
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
build_tasks.first.tasks.first.should be_a DummyTask
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe :task_group do
|
81
|
+
it "should add a task group and allow it to be used in a build" do
|
82
|
+
subject.task_group :group1 do
|
83
|
+
subject.task :dummy
|
84
|
+
end
|
85
|
+
|
86
|
+
subject.build :default do
|
87
|
+
subject.task_group :group1
|
88
|
+
end
|
89
|
+
|
90
|
+
subject.builds[:default].run
|
91
|
+
DummyTask.ran.should be_true
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should allow passing arguments to task groups" do
|
95
|
+
subject.task_group :special_group do |which|
|
96
|
+
subject.task which
|
97
|
+
end
|
98
|
+
|
99
|
+
subject.build :default do
|
100
|
+
subject.task_group :special_group, :dummy
|
101
|
+
end
|
102
|
+
|
103
|
+
subject.builds[:default].run
|
104
|
+
DummyTask.ran.should be_true
|
105
|
+
end
|
106
|
+
|
107
|
+
it "should raise an exception if task group not found" do
|
108
|
+
expect do
|
109
|
+
subject.build :default do
|
110
|
+
subject.task_group :unkown_group
|
111
|
+
end
|
112
|
+
end.to raise_error Fudge::Exceptions::TaskGroupNotFound
|
113
|
+
end
|
114
|
+
|
115
|
+
it "should allow the use of task groups through nested layers of composite tasks" do
|
116
|
+
subject.task_group :group1 do
|
117
|
+
subject.task :dummy
|
118
|
+
end
|
119
|
+
|
120
|
+
subject.build :default do
|
121
|
+
subject.task :dummy_composite do
|
122
|
+
subject.task_group :group1
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
subject.builds[:default].run
|
127
|
+
DummyTask.ran.should be_true
|
128
|
+
end
|
129
|
+
|
130
|
+
it "should allow the use of task groups through nested layers of composite nodes when options are given" do
|
131
|
+
subject.task_group :group1 do
|
132
|
+
subject.task :dummy
|
133
|
+
end
|
134
|
+
|
135
|
+
subject.build :default do
|
136
|
+
subject.task :dummy_composite, :hello, :foobar => true do
|
137
|
+
subject.task_group :group1
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
subject.builds[:default].run
|
142
|
+
|
143
|
+
# Check that the options are maintained through the call
|
144
|
+
build_tasks.first.args.should have(2).items
|
145
|
+
build_tasks.first.args[1][:foobar].should be_true
|
146
|
+
DummyTask.ran.should be_true
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
describe "Callback Hooks" do
|
151
|
+
before :each do
|
152
|
+
@ran = []
|
153
|
+
Fudge::Tasks::Shell.any_instance.stub(:run_command) do |cmd|
|
154
|
+
@ran << cmd
|
155
|
+
['', cmd != 'fail']
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
describe :on_success do
|
160
|
+
context "when callbacks is set to true" do
|
161
|
+
let(:callbacks) { true }
|
162
|
+
|
163
|
+
it "should add success hooks that run after the build is successful" do
|
164
|
+
make_build do
|
165
|
+
subject.on_success { subject.shell 'FOO' }
|
166
|
+
subject.on_success { subject.shell 'BAR' }
|
167
|
+
end
|
168
|
+
|
169
|
+
build.run.should be_true
|
170
|
+
@ran.should == ['FOO', 'BAR']
|
171
|
+
end
|
172
|
+
|
173
|
+
it "should fail the build and stop running callbacks if any of the success hooks fail" do
|
174
|
+
make_build do
|
175
|
+
subject.on_success { subject.shell 'fail'; subject.shell 'FOO' }
|
176
|
+
subject.on_success { subject.shell 'BAR' }
|
177
|
+
end
|
178
|
+
|
179
|
+
build.run.should be_false
|
180
|
+
@ran.should == ['fail']
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
context "when callbacks is set to false" do
|
185
|
+
let(:callbacks) { false }
|
186
|
+
|
187
|
+
it "should not run the callbacks if the build succeeds" do
|
188
|
+
make_build do
|
189
|
+
subject.on_success { subject.shell 'echo "WOOP"' }
|
190
|
+
end
|
191
|
+
|
192
|
+
build.run.should be_true
|
193
|
+
@ran.should == []
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
describe :on_failure do
|
199
|
+
before :each do
|
200
|
+
DummyTask.any_instance.stub(:run).and_return(false)
|
201
|
+
end
|
202
|
+
|
203
|
+
context "when callbacks is set to true" do
|
204
|
+
let(:callbacks) { true }
|
205
|
+
|
206
|
+
it "should add failure hooks that run after the build fails" do
|
207
|
+
make_build do
|
208
|
+
subject.on_failure { subject.shell 'WOOP' }
|
209
|
+
subject.on_failure { subject.shell 'BAR' }
|
210
|
+
end
|
211
|
+
|
212
|
+
build.run.should be_false
|
213
|
+
@ran.should == ['WOOP', 'BAR']
|
214
|
+
end
|
215
|
+
|
216
|
+
it "should fail the build and stop running callbacks if any of the failure hooks fail" do
|
217
|
+
make_build do
|
218
|
+
subject.on_failure { subject.shell 'fail'; subject.shell 'FOO' }
|
219
|
+
subject.on_failure { subject.shell 'BAR' }
|
220
|
+
end
|
221
|
+
|
222
|
+
build.run.should be_false
|
223
|
+
@ran.should == ['fail']
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
context "when callbacks is set to false" do
|
228
|
+
let(:callbacks) { false }
|
229
|
+
|
230
|
+
it "should not run the callbacks if the build fails" do
|
231
|
+
make_build do
|
232
|
+
subject.on_failure { subject.shell 'WOOP' }
|
233
|
+
end
|
234
|
+
|
235
|
+
build.run.should be_false
|
236
|
+
@ran.should == []
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|