daq_flow 1.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +11 -0
- data/.ruby-version +1 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +62 -0
- data/LICENSE.txt +22 -0
- data/README.md +165 -0
- data/Rakefile +10 -0
- data/bin/daq_flow +23 -0
- data/flash_flow.gemspec +28 -0
- data/flash_flow.yml.erb.example +42 -0
- data/lib/flash_flow.rb +7 -0
- data/lib/flash_flow/branch_merger.rb +55 -0
- data/lib/flash_flow/cmd_runner.rb +54 -0
- data/lib/flash_flow/config.rb +84 -0
- data/lib/flash_flow/data.rb +6 -0
- data/lib/flash_flow/data/base.rb +89 -0
- data/lib/flash_flow/data/bitbucket.rb +152 -0
- data/lib/flash_flow/data/branch.rb +124 -0
- data/lib/flash_flow/data/collection.rb +211 -0
- data/lib/flash_flow/data/github.rb +140 -0
- data/lib/flash_flow/data/store.rb +44 -0
- data/lib/flash_flow/git.rb +267 -0
- data/lib/flash_flow/install.rb +19 -0
- data/lib/flash_flow/lock.rb +23 -0
- data/lib/flash_flow/merge.rb +6 -0
- data/lib/flash_flow/merge/acceptance.rb +154 -0
- data/lib/flash_flow/merge/base.rb +116 -0
- data/lib/flash_flow/merge_order.rb +27 -0
- data/lib/flash_flow/notifier.rb +23 -0
- data/lib/flash_flow/options.rb +34 -0
- data/lib/flash_flow/resolve.rb +143 -0
- data/lib/flash_flow/shadow_repo.rb +44 -0
- data/lib/flash_flow/time_helper.rb +32 -0
- data/lib/flash_flow/version.rb +4 -0
- data/log/.keep +0 -0
- data/test/lib/data/test_base.rb +10 -0
- data/test/lib/data/test_branch.rb +206 -0
- data/test/lib/data/test_collection.rb +308 -0
- data/test/lib/data/test_store.rb +70 -0
- data/test/lib/lock/test_github.rb +74 -0
- data/test/lib/merge/test_acceptance.rb +230 -0
- data/test/lib/test_branch_merger.rb +78 -0
- data/test/lib/test_config.rb +63 -0
- data/test/lib/test_git.rb +73 -0
- data/test/lib/test_merge_order.rb +71 -0
- data/test/lib/test_notifier.rb +33 -0
- data/test/lib/test_resolve.rb +69 -0
- data/test/minitest_helper.rb +41 -0
- data/update_gem.sh +5 -0
- metadata +192 -0
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'minitest_helper'
|
2
|
+
require 'flash_flow/data/store'
|
3
|
+
|
4
|
+
module FlashFlow
|
5
|
+
module Data
|
6
|
+
class TestStore < Minitest::Test
|
7
|
+
def setup
|
8
|
+
@mock_git = MockGit.new
|
9
|
+
@collection = Collection.new
|
10
|
+
@branch = Branch.new('some_branch')
|
11
|
+
@storage = Store.new('/dev/null', @mock_git)
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_get
|
15
|
+
@mock_git.stub(:read_file_from_merge_branch, JSON.pretty_generate(old_branches)) do
|
16
|
+
assert_equal(@storage.get, old_branches)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_write
|
21
|
+
str = StringIO.new
|
22
|
+
@storage.write(old_branches, str)
|
23
|
+
|
24
|
+
assert_equal(str.string.strip, JSON.pretty_generate(sorted_branches).strip)
|
25
|
+
end
|
26
|
+
|
27
|
+
private # Helpers
|
28
|
+
|
29
|
+
def old_branches
|
30
|
+
@old_branches ||= {
|
31
|
+
'version' => '1.0.0',
|
32
|
+
'branches' => {
|
33
|
+
'some_old_branch' => {'ref' => 'some_old_branch', 'created_at' => (Time.now - 3600).to_s, 'stories' => ['111']},
|
34
|
+
'some_branch' => {'ref' => 'some_branch', 'status' => 'success', 'created_at' => (Time.now - 1800).to_s, 'stories' => ['222']}
|
35
|
+
}
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
def sorted_branches
|
40
|
+
@sorted_branches ||= {
|
41
|
+
'branches' => {
|
42
|
+
'some_branch' => {
|
43
|
+
'created_at' => (Time.now - 1800).to_s,
|
44
|
+
'ref' => 'some_branch',
|
45
|
+
'status' => 'success',
|
46
|
+
'stories' => ['222']
|
47
|
+
},
|
48
|
+
'some_old_branch' => {
|
49
|
+
'created_at' => (Time.now - 3600).to_s,
|
50
|
+
'ref' => 'some_old_branch',
|
51
|
+
'stories' => ['111']},
|
52
|
+
},
|
53
|
+
'version' => '1.0.0',
|
54
|
+
}
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class MockGit
|
59
|
+
def read_file_from_merge_branch; end
|
60
|
+
def add_and_commit(_,_,_=nil); end
|
61
|
+
|
62
|
+
def in_dir
|
63
|
+
yield
|
64
|
+
end
|
65
|
+
def in_temp_merge_branch
|
66
|
+
yield
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'minitest_helper'
|
2
|
+
require 'minitest/stub_any_instance'
|
3
|
+
|
4
|
+
module FlashFlow
|
5
|
+
module Lock
|
6
|
+
class TestGithub < Minitest::Test
|
7
|
+
class SomeFakeError < RuntimeError;
|
8
|
+
end
|
9
|
+
|
10
|
+
def setup
|
11
|
+
@lock = Lock::Github.new(params)
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_raises_without_required_params
|
15
|
+
config = params.select { |k, _| k != 'token' }
|
16
|
+
assert_raises(Lock::Error) { Lock::Github.new(config) }
|
17
|
+
|
18
|
+
config = params.select { |k, _| k != 'repo' }
|
19
|
+
assert_raises(Lock::Error) { Lock::Github.new(config) }
|
20
|
+
|
21
|
+
config = params.select { |k, _| k != 'issue_id' }
|
22
|
+
assert_raises(Lock::Error) { Lock::Github.new(config) }
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_error_message_when_issue_locked
|
26
|
+
@lock.stub(:actor, 'actor') do
|
27
|
+
@lock.stub(:issue_locked?, true) do
|
28
|
+
@lock.stub(:get_lock_labels, {name: @lock.send(:locked_label)}) do
|
29
|
+
assert_raises(FlashFlow::Lock::Error) do
|
30
|
+
@lock.with_lock
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_with_lock_calls_the_block
|
38
|
+
my_mock = Minitest::Mock.new.expect(:block_call, true).expect(:unlock_issue, true)
|
39
|
+
|
40
|
+
@lock.stub(:issue_locked?, false) do
|
41
|
+
@lock.stub(:lock_issue, nil) do
|
42
|
+
@lock.stub(:unlock_issue, -> { my_mock.unlock_issue }) do
|
43
|
+
@lock.with_lock { my_mock.block_call }
|
44
|
+
my_mock.verify
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_with_unlock_issue_no_matter_what
|
51
|
+
my_mock = Minitest::Mock.new
|
52
|
+
.expect(:some_method, true)
|
53
|
+
.expect(:actor, 'actor')
|
54
|
+
|
55
|
+
@lock.stub(:issue_locked?, false) do
|
56
|
+
@lock.stub(:lock_issue, nil) do
|
57
|
+
@lock.stub(:unlock_issue, -> { my_mock.some_method }) do
|
58
|
+
assert_raises(SomeFakeError) do
|
59
|
+
@lock.with_lock { raise SomeFakeError }
|
60
|
+
my_mock.verify
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def params
|
70
|
+
{'token' => '1234567890', 'repo' => 'f/f', 'issue_id' => '123'}
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,230 @@
|
|
1
|
+
require 'minitest_helper'
|
2
|
+
require 'minitest/stub_any_instance'
|
3
|
+
|
4
|
+
module FlashFlow
|
5
|
+
module Merge
|
6
|
+
class TestAcceptance < Minitest::Test
|
7
|
+
|
8
|
+
def setup
|
9
|
+
reset_config!
|
10
|
+
config!(git:
|
11
|
+
{
|
12
|
+
'merge_branch' => 'test_acceptance',
|
13
|
+
'remote' => 'test_remote',
|
14
|
+
'master_branch' => 'test_master',
|
15
|
+
'use_rerere' => true
|
16
|
+
},
|
17
|
+
branches: {}
|
18
|
+
)
|
19
|
+
|
20
|
+
@branch = Data::Branch.from_hash({'ref' => 'pushing_branch', 'status' => 'fail', 'stories' => []})
|
21
|
+
@deploy = Acceptance.new
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_version_is_nil
|
25
|
+
with_versions('1.1.1', nil) do
|
26
|
+
assert_nil(@deploy.check_version)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_check_version_greater
|
31
|
+
with_versions('2.0.0', '1.1.1') do
|
32
|
+
assert_nil(@deploy.check_version)
|
33
|
+
end
|
34
|
+
|
35
|
+
with_versions('1.2.0', '1.1.1') do
|
36
|
+
assert_nil(@deploy.check_version)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_check_version_less_raises
|
41
|
+
with_versions('1.1.1', '2.1.0') do
|
42
|
+
assert_raises(RuntimeError) { @deploy.check_version }
|
43
|
+
end
|
44
|
+
|
45
|
+
with_versions('1.2.0', '2.1.0') do
|
46
|
+
assert_raises(RuntimeError) { @deploy.check_version }
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_git_version_is_nil
|
51
|
+
with_git_versions(nil, '2.0.0') do
|
52
|
+
assert_nil(@deploy.check_git_version)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_check_git_version_greater
|
57
|
+
with_git_versions('3.0.0', '2.0.0') do
|
58
|
+
assert_nil(@deploy.check_git_version)
|
59
|
+
end
|
60
|
+
|
61
|
+
with_git_versions('2.15.1', '2.0.0') do
|
62
|
+
assert_nil(@deploy.check_git_version)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_check_git_version_less_raises
|
67
|
+
with_git_versions('2.0.0', '2.15.1') do
|
68
|
+
assert_output(/Warning/) { @deploy.check_git_version }
|
69
|
+
end
|
70
|
+
|
71
|
+
with_git_versions('1.7.8', '2.15.1') do
|
72
|
+
assert_output(/Warning/) { @deploy.check_git_version }
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_print_errors_with_no_errors
|
77
|
+
data.expect(:failures, [])
|
78
|
+
assert_equal(@deploy.format_errors, 'Success!')
|
79
|
+
end
|
80
|
+
|
81
|
+
def test_print_errors_when_current_branch_cant_merge
|
82
|
+
data.expect(:failures, [@branch])
|
83
|
+
@branch.fail!('some_random_sha')
|
84
|
+
|
85
|
+
current_branch_error = "ERROR: Your branch did not merge to test_acceptance. Run 'flash_flow --resolve', fix the merge conflict(s) and then re-run this script\n"
|
86
|
+
|
87
|
+
@deploy.instance_variable_get('@local_git'.to_sym).stub(:working_branch, 'pushing_branch') do
|
88
|
+
assert_equal(current_branch_error, @deploy.format_errors)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def test_print_errors_when_another_branch_cant_merge
|
93
|
+
data.expect(:failures, [@branch])
|
94
|
+
|
95
|
+
other_branch_error = "WARNING: Unable to merge branch test_remote/pushing_branch to test_acceptance due to conflicts."
|
96
|
+
|
97
|
+
assert_equal(@deploy.format_errors, other_branch_error)
|
98
|
+
end
|
99
|
+
|
100
|
+
def test_check_out_to_working_branch
|
101
|
+
@deploy.stub(:check_repo, true) do
|
102
|
+
@deploy.stub(:check_version, true) do
|
103
|
+
Lock::Base.stub_any_instance(:with_lock, -> { raise Lock::Error }) do
|
104
|
+
assert_output(/Failure!/) { @deploy.run }
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def test_deleted_branch
|
111
|
+
data.expect(:mark_deleted, true, [@branch])
|
112
|
+
|
113
|
+
notifier.expect(:deleted_branch, true, [@branch])
|
114
|
+
|
115
|
+
merger.expect(:result, :deleted)
|
116
|
+
|
117
|
+
@deploy.process_result(@branch, merger)
|
118
|
+
|
119
|
+
notifier.verify
|
120
|
+
data.verify
|
121
|
+
merger.verify
|
122
|
+
end
|
123
|
+
|
124
|
+
def test_merge_conflict
|
125
|
+
data.expect(:mark_failure, true, [@branch, nil])
|
126
|
+
|
127
|
+
notifier.expect(:merge_conflict, true, [@branch])
|
128
|
+
|
129
|
+
merger.expect(:result, :conflict)
|
130
|
+
|
131
|
+
@deploy.process_result(@branch, merger)
|
132
|
+
|
133
|
+
notifier.verify
|
134
|
+
data.verify
|
135
|
+
merger.verify
|
136
|
+
end
|
137
|
+
|
138
|
+
def test_successful_merge
|
139
|
+
data.expect(:mark_success, true, [@branch])
|
140
|
+
data.expect(:set_resolutions, true, [@branch, {'filename' => ["resolution_sha"]}])
|
141
|
+
|
142
|
+
merger.
|
143
|
+
expect(:result, :success).
|
144
|
+
expect(:sha, 'sha').
|
145
|
+
expect(:resolutions, {'filename' => ["resolution_sha"]})
|
146
|
+
|
147
|
+
@deploy.process_result(@branch, merger)
|
148
|
+
|
149
|
+
data.verify
|
150
|
+
merger.verify
|
151
|
+
assert_equal(@branch.sha, 'sha')
|
152
|
+
end
|
153
|
+
|
154
|
+
def test_ignore_pushing_master_or_acceptance
|
155
|
+
['test_master', 'test_acceptance'].each do |branch|
|
156
|
+
@deploy.instance_variable_get('@local_git'.to_sym).stub(:working_branch, branch) do
|
157
|
+
refute(@deploy.open_pull_request)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def test_commit_message_ordering
|
163
|
+
data
|
164
|
+
.expect(:successes, ['data_successes'])
|
165
|
+
.expect(:successes, sample_branches)
|
166
|
+
.expect(:failures, [])
|
167
|
+
.expect(:removals, [])
|
168
|
+
|
169
|
+
expected_message = sample_branches.sort_by(&:merge_order).map(&:ref).join("\n")
|
170
|
+
assert_output(/#{expected_message}/) { print @deploy.commit_message }
|
171
|
+
data.verify
|
172
|
+
end
|
173
|
+
|
174
|
+
private
|
175
|
+
|
176
|
+
def with_versions(current, written)
|
177
|
+
original_version = FlashFlow::VERSION
|
178
|
+
FlashFlow.send(:remove_const, :VERSION)
|
179
|
+
FlashFlow.const_set(:VERSION, current)
|
180
|
+
data.expect(:version, written)
|
181
|
+
yield
|
182
|
+
data.verify
|
183
|
+
FlashFlow.send(:remove_const, :VERSION)
|
184
|
+
FlashFlow.const_set(:VERSION, original_version)
|
185
|
+
end
|
186
|
+
|
187
|
+
def with_git_versions(running, expected)
|
188
|
+
original_version = FlashFlow::GIT_VERSION
|
189
|
+
FlashFlow.send(:remove_const, :GIT_VERSION)
|
190
|
+
FlashFlow.const_set(:GIT_VERSION, expected)
|
191
|
+
local_git.expect(:version, running)
|
192
|
+
yield
|
193
|
+
local_git.verify
|
194
|
+
FlashFlow.send(:remove_const, :GIT_VERSION)
|
195
|
+
FlashFlow.const_set(:GIT_VERSION, original_version)
|
196
|
+
end
|
197
|
+
|
198
|
+
def merger
|
199
|
+
@merger ||= Minitest::Mock.new
|
200
|
+
end
|
201
|
+
|
202
|
+
def notifier
|
203
|
+
return @notifier if @notifier
|
204
|
+
|
205
|
+
@notifier = Minitest::Mock.new
|
206
|
+
@deploy.instance_variable_set('@notifier'.to_sym, @notifier)
|
207
|
+
end
|
208
|
+
|
209
|
+
def data
|
210
|
+
return @data if @data
|
211
|
+
|
212
|
+
@data = Minitest::Mock.new
|
213
|
+
@deploy.instance_variable_set('@data'.to_sym, @data)
|
214
|
+
end
|
215
|
+
|
216
|
+
def local_git
|
217
|
+
return @local_git if @local_git
|
218
|
+
|
219
|
+
@local_git = Minitest::Mock.new
|
220
|
+
@deploy.instance_variable_set('@local_git'.to_sym, @local_git)
|
221
|
+
end
|
222
|
+
|
223
|
+
def sample_branches
|
224
|
+
@sample_branches ||= [Data::Branch.from_hash({'ref' => 'branch3', 'merge_order' => 2}),
|
225
|
+
Data::Branch.from_hash({'ref' => 'branch1', 'merge_order' => 3}),
|
226
|
+
Data::Branch.from_hash({'ref' => 'branch2', 'merge_order' => 1})]
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'minitest_helper'
|
2
|
+
require 'minitest/stub_any_instance'
|
3
|
+
|
4
|
+
module FlashFlow
|
5
|
+
class TestBranchMerger < Minitest::Test
|
6
|
+
|
7
|
+
def setup
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_deleted_branch
|
11
|
+
merger.stub(:sha, nil) do
|
12
|
+
merger.do_merge(true)
|
13
|
+
assert_equal(merger.result, :deleted)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_successful_merge
|
18
|
+
git.expect(:last_success?, true)
|
19
|
+
|
20
|
+
merger.stub(:sha, 'some_sha') do
|
21
|
+
assert_equal(merger.do_merge(false), :success)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_successful_rerere
|
26
|
+
git.expect(:last_success?, false)
|
27
|
+
.expect(:rerere_resolve!, true)
|
28
|
+
|
29
|
+
merger.stub(:sha, 'some_sha') do
|
30
|
+
assert_equal(merger.do_merge(false), :success)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_rerere_forget
|
35
|
+
git.expect(:last_success?, false)
|
36
|
+
.expect(:run, true, [ 'rerere forget' ])
|
37
|
+
.expect(:run, true, [ 'reset --hard HEAD' ])
|
38
|
+
.expect(:run, true, [ 'rev-parse HEAD' ])
|
39
|
+
.expect(:last_stdout, 'conflict sha', )
|
40
|
+
|
41
|
+
merger.stub(:sha, 'some_sha') do
|
42
|
+
assert_equal(merger.do_merge(true), :conflict)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_failed_rerere
|
47
|
+
git.expect(:last_success?, false)
|
48
|
+
.expect(:rerere_resolve!, false)
|
49
|
+
.expect(:run, true, [ 'reset --hard HEAD' ])
|
50
|
+
.expect(:run, true, [ 'rev-parse HEAD' ])
|
51
|
+
.expect(:last_stdout, 'conflict sha', )
|
52
|
+
|
53
|
+
merger.stub(:sha, 'some_sha') do
|
54
|
+
assert_equal(merger.do_merge(false), :conflict)
|
55
|
+
assert_equal(merger.conflict_sha, 'conflict sha')
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def merger
|
62
|
+
@merger ||= BranchMerger.new(git, branch)
|
63
|
+
end
|
64
|
+
|
65
|
+
def branch
|
66
|
+
@branch ||= Data::Branch.from_hash({'ref' => 'pushing_branch', 'status' => 'fail', 'stories' => []})
|
67
|
+
end
|
68
|
+
|
69
|
+
def git
|
70
|
+
return @git if @git
|
71
|
+
|
72
|
+
@git = Minitest::Mock.new
|
73
|
+
@git.expect(:remote, 'origin', [])
|
74
|
+
@git.expect(:run, true, ["merge --no-ff origin/#{branch.ref}"])
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|