flash_flow 1.2.1 → 1.2.2.a

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