flash_flow 1.2.1 → 1.2.2.a

Sign up to get free protection for your applications and to get access to all the features.
@@ -21,6 +21,7 @@ module FlashFlow
21
21
  opts.on('-c', '--config-file FILE_PATH', 'The path to your config file. Defaults to config/flash_flow.yml.erb') { |v| options[:config_file] = v }
22
22
  opts.on('--resolve', 'Launch a bash shell to save your conflict resolutions') { |v| options[:resolve] = true }
23
23
  opts.on('--resolve-manual', 'Print instructions to use git to resolve conflicts') { |v| options[:resolve_manual] = true }
24
+ opts.on('--merge-status', 'Print instructions to use git to resolve conflicts') { |v| options[:merge_status] = true }
24
25
 
25
26
  opts.on_tail("-h", "--help", "Show this message") do
26
27
  puts opts
@@ -12,7 +12,7 @@ module FlashFlow
12
12
  @logger = opts[:logger]
13
13
  @branch_info_file = branch_info_file
14
14
  @cmd_runner = CmdRunner.new(logger: @logger)
15
- @git = Git.new(git_config, @logger)
15
+ @git = ShadowGit.new(git_config, @logger)
16
16
  end
17
17
 
18
18
  def manual_instructions
@@ -23,24 +23,22 @@ module FlashFlow
23
23
  def start
24
24
  check_for_conflict
25
25
 
26
- in_shadow_repo do
27
- in_working_branch do
28
- merge_conflicted
26
+ in_working_branch do
27
+ merge_conflicted
29
28
 
30
- if unresolved_conflicts.empty?
31
- puts "You have already resolved all conflicts."
32
- else
33
- launch_bash
29
+ if unresolved_conflicts.empty?
30
+ puts "You have already resolved all conflicts."
31
+ else
32
+ launch_bash
34
33
 
35
- rerere
34
+ rerere
36
35
 
37
- unless unresolved_conflicts.empty?
38
- puts "There are still unresolved conflicts in these files:\n#{unresolved_conflicts.join("\n")}\n\n"
39
- end
36
+ unless unresolved_conflicts.empty?
37
+ puts "There are still unresolved conflicts in these files:\n#{unresolved_conflicts.join("\n")}\n\n"
40
38
  end
41
-
42
- git_reset
43
39
  end
40
+
41
+ git_reset
44
42
  end
45
43
  end
46
44
 
@@ -102,42 +100,27 @@ Run the following commands to fix the merge conflict and then re-run flash_flow:
102
100
  private
103
101
 
104
102
  def data
105
- return @data if @data
106
-
107
- in_shadow_repo do
108
- @data = Data::Base.new({}, @branch_info_file, @git, logger: @logger)
109
- end
110
-
111
- @data
112
-
103
+ @data ||= Data::Base.new({}, @branch_info_file, @git, logger: @logger)
113
104
  end
114
105
 
115
106
  def branch
116
107
  @branch ||= data.saved_branches.detect { |branch| branch.ref == working_branch }
117
108
  end
118
109
 
119
- def shadow_repo
120
- @shadow_repo ||= ShadowRepo.new(@git, logger: @logger)
121
- end
122
-
123
- def in_shadow_repo
124
- shadow_repo.in_dir do
125
- yield
126
- end
127
- end
128
-
129
110
  def working_branch
130
111
  @git.working_branch
131
112
  end
132
113
 
133
114
  def in_working_branch
134
- @git.in_branch(working_branch) do
135
- yield
115
+ @git.in_dir do
116
+ @git.in_branch(working_branch) do
117
+ yield
118
+ end
136
119
  end
137
120
  end
138
121
 
139
122
  def flash_flow_directory
140
- shadow_repo.flash_flow_dir
123
+ @git.flash_flow_dir
141
124
  end
142
125
 
143
126
  def init_file_contents
@@ -3,24 +3,17 @@ require 'logger'
3
3
  require 'flash_flow/git'
4
4
 
5
5
  module FlashFlow
6
- class ShadowRepo
6
+ class ShadowGit < Git
7
7
 
8
+ def initialize(config, logger=nil)
9
+ super
8
10
 
9
- def initialize(git, opts={})
10
- @git = git
11
- @cmd_runner = CmdRunner.new(logger: opts[:logger])
12
- end
13
-
14
- def in_dir(opts={})
15
- opts = { reset: true, go_back: true }.merge(opts)
16
11
  create_shadow_repo
12
+ @cmd_runner.dir = flash_flow_dir
17
13
 
18
- Dir.chdir(flash_flow_dir) do
19
- @git.fetch(@git.merge_remote)
20
- @git.run("reset --hard HEAD") if opts[:reset]
21
-
22
- yield
23
- end
14
+ run("clean -x -f")
15
+ fetch(merge_remote)
16
+ run("reset --hard HEAD")
24
17
  end
25
18
 
26
19
  def create_shadow_repo
@@ -1,3 +1,3 @@
1
1
  module FlashFlow
2
- VERSION = "1.2.1"
2
+ VERSION = "1.2.2.a"
3
3
  end
@@ -214,6 +214,56 @@ module FlashFlow
214
214
  @fake_branches.verify
215
215
  end
216
216
 
217
+ def test_can_ship_returns_true
218
+ collection = Collection.new({})
219
+ assert(collection.can_ship?(@branch))
220
+ end
221
+
222
+ def test_can_ship_calls_branches_class
223
+ @fake_branches.expect(:can_ship?, true, [@branch])
224
+ @collection.can_ship?(@branch)
225
+ @fake_branches.verify
226
+ end
227
+
228
+ def test_branch_link_returns_nil
229
+ collection = Collection.new({})
230
+ assert_nil(collection.branch_link(@branch))
231
+ end
232
+
233
+ def test_branch_link_calls_branches_class
234
+ @fake_branches.expect(:branch_link, 'http://link_to_branch.com', [@branch])
235
+ assert_equal('http://link_to_branch.com', @collection.branch_link(@branch))
236
+ @fake_branches.verify
237
+ end
238
+
239
+ def test_current_branches
240
+ branch1 = Branch.new('111', '111', '111')
241
+ branch2 = Branch.new('222', '222', '222')
242
+ branch3 = Branch.new('333', '333', '333')
243
+ branch2.current_record = true
244
+ @collection.mark_success(branch1)
245
+ @collection.mark_success(branch2)
246
+ @collection.mark_success(branch3)
247
+
248
+ assert_equal(@collection.current_branches, [branch2])
249
+ end
250
+
251
+ def test_mark_all_as_current
252
+ branch1 = Branch.new('111', '111', '111')
253
+ branch2 = Branch.new('222', '222', '222')
254
+ branch3 = Branch.new('333', '333', '333')
255
+ branch2.current_record = true
256
+ @collection.mark_success(branch1)
257
+ @collection.mark_success(branch2)
258
+ @collection.mark_success(branch3)
259
+
260
+ assert_equal(@collection.current_branches, [branch2])
261
+
262
+ @collection.mark_all_as_current
263
+
264
+ assert_equal(@collection.current_branches, [branch1, branch2, branch3])
265
+ end
266
+
217
267
  def test_failures
218
268
  branch1 = Branch.new('111', '111', '111')
219
269
  branch2 = Branch.new('222', '222', '222')
@@ -63,6 +63,9 @@ module FlashFlow
63
63
  def read_file_from_merge_branch; end
64
64
  def add_and_commit(_,_,_=nil); end
65
65
 
66
+ def in_dir
67
+ yield
68
+ end
66
69
  def in_temp_merge_branch
67
70
  yield
68
71
  end
@@ -172,6 +172,73 @@ module FlashFlow
172
172
  fake_file.verify
173
173
  end
174
174
 
175
+ def test_story_deployable
176
+ story_mock = MiniTest::Mock.new
177
+ .expect(:id, '111')
178
+ .expect(:current_state, 'accepted')
179
+
180
+ stub_tracker_gem(@project_mock) do
181
+ mock_find(story_mock)
182
+ assert(Pivotal.new(sample_branches, mock_git).story_deployable?('111'))
183
+ end
184
+ end
185
+
186
+ def test_story_deployable_false
187
+ story_mock = MiniTest::Mock.new
188
+ .expect(:id, '111')
189
+ .expect(:current_state, 'delivered')
190
+
191
+ stub_tracker_gem(@project_mock) do
192
+ mock_find(story_mock)
193
+ refute(Pivotal.new(sample_branches, mock_git).story_deployable?('111'))
194
+ end
195
+ end
196
+
197
+ def test_story_link
198
+ story_mock = MiniTest::Mock.new
199
+ .expect(:id, '111')
200
+ .expect(:url, 'http://some_url')
201
+
202
+ stub_tracker_gem(@project_mock) do
203
+ mock_find(story_mock)
204
+ assert_equal('http://some_url', Pivotal.new(sample_branches, mock_git).story_link('111'))
205
+ end
206
+ end
207
+
208
+ def test_story_title
209
+ story_mock = MiniTest::Mock.new
210
+ .expect(:id, '111')
211
+ .expect(:name, 'Some Title')
212
+
213
+ stub_tracker_gem(@project_mock) do
214
+ mock_find(story_mock)
215
+ assert_equal('Some Title', Pivotal.new(sample_branches, mock_git).story_title('111'))
216
+ end
217
+ end
218
+
219
+ def test_release_keys
220
+ story_mock = MiniTest::Mock.new
221
+ .expect(:id, '111')
222
+ .expect(:labels, 'Release-1, Not-A-Release-2, release-3')
223
+ .expect(:labels, 'Release-1, Not-A-Release-2, release-3')
224
+
225
+ stub_tracker_gem(@project_mock) do
226
+ mock_find(story_mock)
227
+ assert_equal(['Release-1', 'release-3'], Pivotal.new(sample_branches, mock_git, 'release_label_prefix' => 'release').release_keys('111'))
228
+ end
229
+ end
230
+
231
+ def test_stories_for_release
232
+ story_mock = MiniTest::Mock.new
233
+ .expect(:id, '111')
234
+
235
+ stub_tracker_gem(@project_mock) do
236
+ mock_all([story_mock], label: 'release')
237
+ assert_equal(['111'], Pivotal.new(sample_branches, mock_git).stories_for_release('release'))
238
+ end
239
+
240
+ end
241
+
175
242
  private
176
243
 
177
244
  def stub_tracker_gem(project)
@@ -208,6 +275,10 @@ module FlashFlow
208
275
  @project_mock.expect(:stories, @stories.expect(:find, story, [story_id]))
209
276
  end
210
277
 
278
+ def mock_all(stories, opts={})
279
+ @project_mock.expect(:stories, @stories.expect(:all, stories, [opts]))
280
+ end
281
+
211
282
  def sample_branches
212
283
  @sample_branches ||= [Data::Branch.from_hash({'ref' => 'branch1', 'remote' => 'origin', 'sha' => 'sha1', 'status' => 'success', 'created_at' => (Time.now - 3600), 'stories' => ['111']}),
213
284
  Data::Branch.from_hash({'ref' => 'branch2', 'remote' => 'origin', 'sha' => 'sha2', 'status' => 'success', 'created_at' => (Time.now - 1800), 'stories' => ['222']}),
@@ -0,0 +1,74 @@
1
+ require 'minitest_helper'
2
+ require 'flash_flow/merge_master'
3
+
4
+ module FlashFlow
5
+ module MergeMaster
6
+ class TestReleaseGraph < Minitest::Test
7
+
8
+ class FakeIssueTracker
9
+ STORY_ID1 = '111'
10
+ STORY_ID2 = '222'
11
+ STORY_ID3 = '333'
12
+ STORY_ID4 = '444'
13
+ STORY_ID5 = '555'
14
+ STORY_ID6 = '666'
15
+
16
+ def release_keys(story_id)
17
+ {
18
+ STORY_ID1 => ['release1', 'release2'],
19
+ STORY_ID2 => [],
20
+ STORY_ID3 => ['release2'],
21
+ STORY_ID4 => ['release3', 'release4'],
22
+ STORY_ID5 => [],
23
+ STORY_ID6 => ['release3'],
24
+ }[story_id]
25
+ end
26
+
27
+ def stories_for_release(release_keys)
28
+ hash = Hash.new([])
29
+ hash['release1'] = [STORY_ID1]
30
+ hash['release2'] = [STORY_ID1, STORY_ID3]
31
+
32
+ hash[release_keys]
33
+ end
34
+
35
+ end
36
+
37
+ BRANCH1 = Data::Branch.from_hash('ref' => 'branch1', 'stories' => ['111', '222'])
38
+ BRANCH2 = Data::Branch.from_hash('ref' => 'branch2', 'stories' => ['333'])
39
+ BRANCH3 = Data::Branch.from_hash('ref' => 'branch3', 'stories' => ['444', '555', '666'])
40
+
41
+ ################
42
+ ## Begin actual tests
43
+
44
+ def setup
45
+ @graph = ReleaseGraph.build([BRANCH1, BRANCH2, BRANCH3], FakeIssueTracker.new)
46
+ end
47
+
48
+ def test_connected_branches
49
+ assert_equal(@graph.connected_branches(BRANCH1.ref), [BRANCH1, BRANCH2])
50
+ assert_equal(@graph.connected_branches(BRANCH2.ref), [BRANCH1, BRANCH2])
51
+ assert_equal(@graph.connected_branches(BRANCH3.ref), [BRANCH3])
52
+ end
53
+
54
+ def test_connected_stories
55
+ assert_equal(@graph.connected_stories(BRANCH1.ref), ['111', '222', '333'])
56
+ assert_equal(@graph.connected_stories(BRANCH2.ref), ['111', '222', '333'])
57
+ assert_equal(@graph.connected_stories(BRANCH3.ref), ['444', '555', '666'])
58
+ end
59
+
60
+ def test_connected_releases
61
+ assert_equal(@graph.connected_releases(BRANCH1.ref), ['release1', 'release2'])
62
+ assert_equal(@graph.connected_releases(BRANCH2.ref), ['release1', 'release2'])
63
+ assert_equal(@graph.connected_releases(BRANCH3.ref), ['release3', 'release4'])
64
+ end
65
+
66
+ def test_no_branches
67
+ g = ReleaseGraph.build([], FakeIssueTracker.new)
68
+ assert(g.connected_branches(BRANCH1.ref).empty?)
69
+ assert(g.connected_stories(BRANCH1.ref).empty?)
70
+ assert(g.connected_releases(BRANCH1.ref).empty?)
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,119 @@
1
+ require 'minitest_helper'
2
+ require 'flash_flow/merge_master'
3
+
4
+ module FlashFlow
5
+ module MergeMaster
6
+ class TestStatus < Minitest::Test
7
+
8
+ class TestableMergeMaster < Status
9
+ def initialize;
10
+ end
11
+
12
+ attr_accessor :issue_tracker, :collection
13
+
14
+ end
15
+
16
+ class FakeIssueTracker
17
+ STORY_ID1 = '111'
18
+ STORY_ID2 = '222'
19
+ STORY_ID3 = '333'
20
+ STORY_ID4 = '444'
21
+ STORY_ID5 = '555'
22
+ STORY_ID6 = '666'
23
+
24
+ def story_deployable?(story_id)
25
+ {
26
+ STORY_ID1 => true,
27
+ STORY_ID2 => true,
28
+ STORY_ID3 => true,
29
+ STORY_ID4 => true,
30
+ STORY_ID5 => false,
31
+ STORY_ID6 => true,
32
+ }[story_id]
33
+ end
34
+
35
+ def release_keys(story_id)
36
+ {
37
+ STORY_ID1 => ['release1', 'release2'],
38
+ STORY_ID2 => [],
39
+ STORY_ID3 => ['release2'],
40
+ STORY_ID4 => ['release3', 'release4'],
41
+ STORY_ID5 => [],
42
+ STORY_ID6 => ['release4'],
43
+ }[story_id]
44
+ end
45
+
46
+ def stories_for_release(release_keys)
47
+ hash = Hash.new([])
48
+ hash['release1'] = [STORY_ID1]
49
+ hash['release2'] = [STORY_ID1, STORY_ID3]
50
+
51
+ hash[release_keys]
52
+ end
53
+
54
+ def story_link(story_id)
55
+ ; "link: #{story_id}";
56
+ end
57
+
58
+ def story_title(story_id)
59
+ ; "title: #{story_id}";
60
+ end
61
+ end
62
+
63
+ class FakeCollection
64
+ BRANCH1 = Data::Branch.from_hash('ref' => 'branch1', 'stories' => ['111', '222'])
65
+ BRANCH2 = Data::Branch.from_hash('ref' => 'branch2', 'stories' => ['333'])
66
+ BRANCH3 = Data::Branch.from_hash('ref' => 'branch3', 'stories' => ['444', '555', '666'])
67
+
68
+ def branch_link(branch)
69
+ return "link-#{branch.ref}"
70
+ end
71
+
72
+ def can_ship?(branch)
73
+ {
74
+ BRANCH1 => true,
75
+ BRANCH2 => true,
76
+ BRANCH3 => false,
77
+ }[branch]
78
+ end
79
+
80
+ def current_branches
81
+ [BRANCH1, BRANCH2, BRANCH3]
82
+ end
83
+ end
84
+
85
+ ################
86
+ ## Begin actual tests
87
+
88
+ def setup
89
+ @merge_master = TestableMergeMaster.new
90
+ @merge_master.issue_tracker = FakeIssueTracker.new
91
+ @merge_master.collection = FakeCollection.new
92
+ end
93
+
94
+ def test_shippable_branch
95
+ branches = @merge_master.branches
96
+ branch1 = branches[FakeCollection::BRANCH1]
97
+ branch2 = branches[FakeCollection::BRANCH2]
98
+
99
+ assert(branch1[:stories].all? { |s| @merge_master.stories[s][:can_ship?] })
100
+ assert_equal(branch1[:stories], branch2[:stories])
101
+ assert(branch1[:shippable?])
102
+ end
103
+
104
+ def test_not_shippable_branch
105
+ branches = @merge_master.branches
106
+ branch3 = branches[FakeCollection::BRANCH3]
107
+
108
+ assert_equal(branch3[:stories].map { |s| @merge_master.stories[s][:can_ship?] }, [true, false, true])
109
+ refute(branch3[:shippable?])
110
+ end
111
+
112
+ private
113
+
114
+ def find_branch(status_list, branch)
115
+ status_list[branch]
116
+ end
117
+ end
118
+ end
119
+ end