flash_flow 1.0.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.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.ruby-version +1 -0
  4. data/Gemfile +4 -0
  5. data/Gemfile.lock +81 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +152 -0
  8. data/Rakefile +10 -0
  9. data/bin/flash_flow +23 -0
  10. data/flash_flow.gemspec +28 -0
  11. data/flash_flow.yml.erb.example +83 -0
  12. data/lib/flash_flow.rb +7 -0
  13. data/lib/flash_flow/branch_merger.rb +52 -0
  14. data/lib/flash_flow/cmd_runner.rb +37 -0
  15. data/lib/flash_flow/config.rb +71 -0
  16. data/lib/flash_flow/data.rb +6 -0
  17. data/lib/flash_flow/data/base.rb +58 -0
  18. data/lib/flash_flow/data/branch.rb +131 -0
  19. data/lib/flash_flow/data/collection.rb +181 -0
  20. data/lib/flash_flow/data/github.rb +129 -0
  21. data/lib/flash_flow/data/store.rb +33 -0
  22. data/lib/flash_flow/deploy.rb +184 -0
  23. data/lib/flash_flow/git.rb +248 -0
  24. data/lib/flash_flow/install.rb +19 -0
  25. data/lib/flash_flow/issue_tracker.rb +52 -0
  26. data/lib/flash_flow/issue_tracker/pivotal.rb +160 -0
  27. data/lib/flash_flow/lock.rb +25 -0
  28. data/lib/flash_flow/lock/github.rb +91 -0
  29. data/lib/flash_flow/notifier.rb +24 -0
  30. data/lib/flash_flow/notifier/hipchat.rb +36 -0
  31. data/lib/flash_flow/options.rb +36 -0
  32. data/lib/flash_flow/time_helper.rb +11 -0
  33. data/lib/flash_flow/version.rb +3 -0
  34. data/test/lib/data/test_base.rb +10 -0
  35. data/test/lib/data/test_branch.rb +203 -0
  36. data/test/lib/data/test_collection.rb +238 -0
  37. data/test/lib/data/test_github.rb +23 -0
  38. data/test/lib/data/test_store.rb +53 -0
  39. data/test/lib/issue_tracker/test_pivotal.rb +221 -0
  40. data/test/lib/lock/test_github.rb +70 -0
  41. data/test/lib/test_branch_merger.rb +76 -0
  42. data/test/lib/test_config.rb +84 -0
  43. data/test/lib/test_deploy.rb +175 -0
  44. data/test/lib/test_git.rb +73 -0
  45. data/test/lib/test_issue_tracker.rb +43 -0
  46. data/test/lib/test_notifier.rb +33 -0
  47. data/test/minitest_helper.rb +38 -0
  48. metadata +217 -0
@@ -0,0 +1,11 @@
1
+ module FlashFlow
2
+ module TimeHelper
3
+ def with_time_zone(tz_name)
4
+ prev_tz = ENV['TZ']
5
+ ENV['TZ'] = tz_name
6
+ yield
7
+ ensure
8
+ ENV['TZ'] = prev_tz
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ module FlashFlow
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,10 @@
1
+ require 'minitest_helper'
2
+ require 'flash_flow/data/store'
3
+
4
+ module FlashFlow
5
+ module Data
6
+ class TestBase < Minitest::Test
7
+ # Embarrassingly lazy. Write some tests.
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,203 @@
1
+ require 'minitest_helper'
2
+ require 'flash_flow/data/store'
3
+
4
+ module FlashFlow
5
+ module Data
6
+ class TestBranch < Minitest::Test
7
+
8
+ def test_merge_returns_self_if_other_is_nil
9
+ branch = Branch.from_hash(branch_hash)
10
+ assert_equal(branch.merge(nil), branch)
11
+ end
12
+
13
+ def test_merge_keeps_the_oldest_created_at
14
+ new_branch = Branch.from_hash(branch_hash)
15
+ old_branch = Branch.from_hash(branch_hash)
16
+ old_branch.created_at -= 1000
17
+
18
+ assert_equal(new_branch.merge(old_branch).created_at, old_branch.created_at)
19
+ assert_equal(old_branch.merge(new_branch).created_at, old_branch.created_at)
20
+ end
21
+
22
+ def test_merge_handles_nil_stories
23
+ new_branch = Branch.from_hash(branch_hash)
24
+ old_branch = Branch.from_hash(branch_hash)
25
+
26
+ new_branch.stories = nil
27
+ old_branch.stories = ['456', '789']
28
+
29
+ assert_equal(old_branch.merge(new_branch).stories, ['456', '789'])
30
+ assert_equal(new_branch.merge(old_branch).stories, ['456', '789'])
31
+ end
32
+
33
+ def test_merge_unions_the_stories
34
+ new_branch = Branch.from_hash(branch_hash)
35
+ old_branch = Branch.from_hash(branch_hash)
36
+
37
+ new_branch.stories = ['123', '456']
38
+ old_branch.stories = ['456', '789']
39
+
40
+ assert_equal(new_branch.merge(old_branch).stories, ['123', '456', '789'])
41
+ end
42
+
43
+ def test_merge_uses_other_status
44
+ new_branch = Branch.from_hash(branch_hash)
45
+ old_branch = Branch.from_hash(branch_hash)
46
+
47
+ old_branch.success!
48
+ new_branch.fail!
49
+ old_branch.merge(new_branch)
50
+
51
+ assert(old_branch.fail?)
52
+ assert(new_branch.fail?)
53
+ end
54
+
55
+ def test_merge_sets_updated_at
56
+ new_branch = Branch.from_hash(branch_hash)
57
+ old_branch = Branch.from_hash(branch_hash)
58
+
59
+ original_updated_at = new_branch.updated_at
60
+
61
+ assert(old_branch.merge(new_branch).updated_at > original_updated_at)
62
+ end
63
+
64
+ def test_merge_sets_created_at_if_not_set
65
+ new_branch = Branch.from_hash(branch_hash)
66
+ old_branch = Branch.from_hash(branch_hash)
67
+
68
+ new_branch.created_at = old_branch.created_at = nil
69
+
70
+ assert_in_delta(old_branch.merge(new_branch).created_at.to_i, Time.now.to_i, 100)
71
+ end
72
+
73
+ def test_from_hash
74
+ branch = Branch.from_hash(branch_hash)
75
+ assert_equal(branch.ref, branch_hash['ref'])
76
+ assert_equal(branch.remote_url, branch_hash['remote_url'])
77
+ assert_equal(branch.remote, branch_hash['remote'])
78
+ assert_equal(branch.status, branch_hash['status'])
79
+ assert_equal(branch.stories, branch_hash['stories'])
80
+ assert_equal(branch.metadata, branch_hash['metadata'])
81
+ end
82
+
83
+ def test_from_hash_with_time_objects
84
+ branch_hash['updated_at'] = Time.now - 200
85
+ branch_hash['created_at'] = Time.now - 200
86
+ branch = Branch.from_hash(branch_hash)
87
+ assert_equal(branch.updated_at, branch_hash['updated_at'])
88
+ assert_equal(branch.created_at, branch_hash['created_at'])
89
+ end
90
+
91
+ def test_from_hash_with_nil_times
92
+ time = Time.parse('2015-05-22 09:47:07 -0700')
93
+ branch_hash['updated_at'] = branch_hash['created_at'] = nil
94
+ Time.stub(:now, time) do
95
+ branch = Branch.from_hash(branch_hash)
96
+
97
+ assert_equal(branch.updated_at, time)
98
+ assert_equal(branch.created_at, time)
99
+ end
100
+ end
101
+
102
+ def test_from_hash_with_string_times
103
+ time = Time.parse('2015-05-22 09:47:07 -0700')
104
+ branch_hash['updated_at'] = '2015-05-22 09:47:07 -0700'
105
+ branch_hash['created_at'] = '2015-05-22 09:47:07 -0700'
106
+ branch = Branch.from_hash(branch_hash)
107
+ assert_equal(branch.updated_at, time)
108
+ assert_equal(branch.created_at, time)
109
+ end
110
+
111
+ def test_double_equals
112
+ branch1 = Branch.from_hash(branch_hash)
113
+ branch2 = Branch.from_hash(branch_hash)
114
+ assert(branch1 == branch2)
115
+
116
+ branch1.remote_url = 'different_url'
117
+ refute(branch1 == branch2)
118
+
119
+ branch1.remote_url = branch2.remote_url
120
+ branch1.remote = 'different remote'
121
+ refute(branch1 == branch2)
122
+
123
+ branch1.remote = branch2.remote
124
+ branch1.ref = 'different ref'
125
+ refute(branch1 == branch2)
126
+ end
127
+
128
+ def test_to_hash
129
+ branch1 = Branch.from_hash(branch_hash)
130
+ assert_equal(branch1.to_hash, branch_hash)
131
+ end
132
+
133
+ def test_success
134
+ branch = Branch.new(1,2,3)
135
+
136
+ branch.success!
137
+ assert(branch.success?)
138
+
139
+ branch.fail!
140
+ refute(branch.success?)
141
+ end
142
+
143
+ def test_fail
144
+ branch = Branch.new(1,2,3)
145
+
146
+ branch.fail!
147
+ assert(branch.fail?)
148
+
149
+ branch.success!
150
+ refute(branch.fail?)
151
+ end
152
+
153
+ def test_removed
154
+ branch = Branch.new(1,2,3)
155
+
156
+ branch.removed!
157
+ assert(branch.removed?)
158
+
159
+ branch.success!
160
+ refute(branch.removed?)
161
+ end
162
+
163
+ def test_deleted
164
+ branch = Branch.new(1,2,3)
165
+
166
+ branch.deleted!
167
+ assert(branch.deleted?)
168
+
169
+ branch.success!
170
+ refute(branch.deleted?)
171
+ end
172
+
173
+ def test_unknown
174
+ branch = Branch.new(1,2,3)
175
+
176
+ branch.unknown!
177
+ assert(branch.unknown?)
178
+
179
+ branch.success!
180
+ refute(branch.unknown?)
181
+ end
182
+
183
+ private
184
+
185
+ def branch_hash
186
+ @branch_hash ||= {
187
+ 'ref' => 'branch 1',
188
+ 'remote_url' => 'the_origin_url',
189
+ 'remote' => 'origin',
190
+ 'sha' => 'random_sha',
191
+ 'status' => 'success',
192
+ 'resolutions' => {},
193
+ 'stories' => ['123'],
194
+ 'metadata' => {
195
+ 'some' => 'data'
196
+ },
197
+ 'updated_at' => Time.now - 1000,
198
+ 'created_at' => Time.now - 1000,
199
+ }
200
+ end
201
+ end
202
+ end
203
+ end
@@ -0,0 +1,238 @@
1
+ require 'minitest_helper'
2
+
3
+ module FlashFlow
4
+ module Data
5
+ class TestCollection < Minitest::Test
6
+
7
+ def setup_fake_branches
8
+ Object.send(:remove_const, :FakeBranches) if Object.const_defined?(:FakeBranches)
9
+
10
+ fake_branches_class = Class.new do
11
+ @branches = Minitest::Mock.new
12
+ def self.new(opts=nil); @branches; end
13
+ def self.branches; @branches; end
14
+ end
15
+ Object.const_set('FakeBranches', fake_branches_class)
16
+ end
17
+
18
+ def setup
19
+ setup_fake_branches
20
+ @fake_branches = FakeBranches.branches
21
+ @branch = Branch.new('origin', 'the_origin_url', 'some_branch')
22
+ @collection = Collection.new({ 'origin' => 'the_origin_url' }, { 'class' => { 'name' => 'FakeBranches' }})
23
+ end
24
+
25
+ def test_from_hash_set_branches
26
+ hash = { 'some_url/some_branch' => Branch.new('origin', 'the_origin_url', 'some_branch') }
27
+ assert_equal(Collection.from_hash({}, hash).branches, hash)
28
+ end
29
+
30
+ def test_from_hash_set_remotes
31
+ remotes = { 'some_remote' => 'some_remote_url' }
32
+ assert_equal(Collection.from_hash(remotes, {}).remotes, remotes)
33
+ end
34
+
35
+ def test_fetch_calls_collection_class
36
+ @fake_branches.expect(:fetch, [], [])
37
+ @collection.fetch
38
+
39
+ @fake_branches.verify
40
+ end
41
+
42
+ def test_fetch_returns_nil_if_no_collection_class
43
+ assert_nil(@collection.fetch)
44
+ end
45
+
46
+ def test_fetch_maps_collection_class_to_branches
47
+ branch = Data::Branch.new('origin', 'the_origin_url', 'some_branch')
48
+ @fake_branches.expect(:fetch, [Branch.from_hash({'remote' => branch.remote, 'remote_url' => branch.remote_url, 'ref' => branch.ref })], [])
49
+ @collection.fetch
50
+
51
+ assert_equal(@collection.branches.values, [branch])
52
+ @fake_branches.verify
53
+ end
54
+
55
+ def test_fetch_finds_the_remote
56
+ branch = Data::Branch.new('origin', 'the_origin_url', 'some_branch')
57
+ @fake_branches.expect(:fetch, [Branch.from_hash({'remote_url' => branch.remote_url, 'ref' => branch.ref })], [])
58
+ @collection.fetch
59
+
60
+ assert_equal(@collection.branches.values, [branch])
61
+ @fake_branches.verify
62
+ end
63
+
64
+ def test_reverse_merge_when_old_is_empty
65
+ @collection.mark_success(@branch)
66
+
67
+ merged = @collection.reverse_merge(Collection.from_hash({}, {}))
68
+ assert_equal(['the_origin_url/some_branch'], merged.to_h.keys)
69
+ assert(merged.get('the_origin_url', 'some_branch').success?)
70
+ end
71
+
72
+ def test_reverse_merge_old_marks_old_branches
73
+ @collection.mark_success(@branch)
74
+
75
+ merged = @collection.reverse_merge(Collection.from_hash({}, old_branches))
76
+ assert(merged.get('the_origin_url', 'some_old_branch').unknown?)
77
+ end
78
+
79
+ def test_reverse_merge_old_adds_new_stories
80
+ @collection.mark_success(@branch)
81
+ @collection.add_story('origin', 'some_branch', '456')
82
+ merged = @collection.reverse_merge(Collection.from_hash({}, old_branches))
83
+
84
+ assert_equal(['222', '456'], merged.get('the_origin_url', 'some_branch').stories)
85
+ end
86
+
87
+ def test_reverse_merge_old_uses_old_created_at
88
+ @collection.add_to_merge('origin', 'some_old_branch')
89
+ @collection.add_to_merge('origin', 'some_new_branch')
90
+ old_branch_collection = Collection.from_hash({}, old_branches)
91
+ merged = @collection.reverse_merge(old_branch_collection)
92
+
93
+ assert_equal(old_branch_collection.get('the_origin_url', 'some_branch').created_at, merged.get('the_origin_url', 'some_branch').created_at)
94
+ # Assert the new branch is created_at within the last minute
95
+ assert(merged.get('the_origin_url', 'some_new_branch').created_at > (Time.now - 60))
96
+ end
97
+
98
+ def test_reverse_merge_old_uses_new_status
99
+ @collection.mark_failure(old_branches['the_origin_url/some_branch'])
100
+ merged = @collection.reverse_merge(Collection.from_hash({}, old_branches))
101
+
102
+ assert(merged.get('the_origin_url', 'some_branch').fail?)
103
+ end
104
+
105
+ def test_fetch_returns_a_collection_instance
106
+ FakeBranches.branches.expect(:fetch, [])
107
+ collection = Collection.fetch({ 'origin' => 'the_origin_url' }, { 'class' => { 'name' => 'FakeBranches' }})
108
+ assert(collection.is_a?(Collection))
109
+ end
110
+
111
+ def test_add_to_merge_new_branch
112
+ @collection.add_to_merge('origin', 'some_branch')
113
+ assert_equal(@collection.get('the_origin_url', 'some_branch').ref, 'some_branch')
114
+ assert_equal(@collection.get('the_origin_url', 'some_branch').remote, 'origin')
115
+ assert_equal(@collection.get('the_origin_url', 'some_branch').remote_url, 'the_origin_url')
116
+ end
117
+
118
+ def test_add_to_merge_existing_branch
119
+ @collection.mark_failure(@branch)
120
+ @collection.add_to_merge(@branch.remote, @branch.ref)
121
+
122
+ assert_equal(@collection.get(@branch.remote_url, @branch.ref), @branch)
123
+ end
124
+
125
+ def test_add_to_merge_calls_branches_class
126
+ @fake_branches.expect(:add_to_merge, true, [@branch])
127
+ @collection.add_to_merge(@branch.remote, @branch.ref)
128
+
129
+ @fake_branches.verify
130
+ end
131
+
132
+ def test_remove_from_merge_new_branch
133
+ @collection.remove_from_merge(@branch.remote, @branch.ref)
134
+ assert(@collection.get(@branch.remote_url, @branch.ref).removed?)
135
+ end
136
+
137
+ def test_remove_from_merge_existing_branch
138
+ @collection.mark_success(@branch)
139
+ assert(@collection.get(@branch.remote_url, @branch.ref).success?)
140
+ @collection.remove_from_merge(@branch.remote, @branch.ref)
141
+ assert(@collection.get(@branch.remote_url, @branch.ref).removed?)
142
+ end
143
+
144
+ def test_remove_from_merge_calls_branches_class
145
+ @fake_branches.expect(:remove_from_merge, true, [@branch])
146
+ @collection.remove_from_merge(@branch.remote, @branch.ref)
147
+ @fake_branches.verify
148
+ end
149
+
150
+ def test_mark_success_new_branch
151
+ @collection.mark_success(@branch)
152
+ assert(@collection.get(@branch.remote_url, @branch.ref).success?)
153
+ end
154
+
155
+ def test_mark_success_existing_branch
156
+ branch = @collection.add_to_merge(@branch.remote, @branch.ref)
157
+ @collection.mark_failure(branch)
158
+ @collection.mark_success(branch)
159
+ assert(@collection.get(@branch.remote_url, @branch.ref).success?)
160
+ end
161
+
162
+ def test_mark_success_calls_branches_class
163
+ @fake_branches.expect(:mark_success, true, [@branch])
164
+ @collection.mark_success(@branch)
165
+ @fake_branches.verify
166
+ end
167
+
168
+ def test_mark_failure_existing_branch
169
+ branch = @collection.add_to_merge(@branch.remote, @branch.ref)
170
+ @collection.mark_success(branch)
171
+ @collection.mark_failure(branch)
172
+ assert(@collection.get(@branch.remote_url, @branch.ref).fail?)
173
+ end
174
+
175
+ def test_mark_failure_new_branch
176
+ @collection.mark_failure(@branch)
177
+ assert(@branch.fail?)
178
+ end
179
+
180
+ def test_mark_failure_calls_branches_class
181
+ @fake_branches.expect(:mark_failure, true, [@branch])
182
+ @collection.mark_failure(@branch)
183
+ @fake_branches.verify
184
+ end
185
+
186
+ def test_mark_deleted_new_branch
187
+ @collection.mark_deleted(@branch)
188
+ assert(@collection.get(@branch.remote_url, @branch.ref).deleted?)
189
+ end
190
+
191
+ def test_mark_deleted_existing_branch
192
+ branch = @collection.add_to_merge(@branch.remote, @branch.ref)
193
+ @collection.mark_failure(branch)
194
+ @collection.mark_deleted(branch)
195
+ assert(@collection.get(@branch.remote_url, @branch.ref).deleted?)
196
+ end
197
+
198
+ def test_mark_deleted_calls_branches_class
199
+ @fake_branches.expect(:mark_deleted, true, [@branch])
200
+ @collection.mark_deleted(@branch)
201
+ @fake_branches.verify
202
+ end
203
+
204
+ def test_add_story
205
+ @collection.add_to_merge('origin', 'some_branch')
206
+ @collection.add_story('origin', 'some_branch', '999')
207
+ assert_equal(@collection.get('the_origin_url', 'some_branch').stories, ['999'])
208
+ end
209
+
210
+ def test_add_story_calls_branches_class
211
+ @fake_branches.expect(:add_story, true, [@branch, '999'])
212
+ @collection.add_to_merge('origin', 'some_branch')
213
+ @collection.add_story('origin', 'some_branch', '999')
214
+ @fake_branches.verify
215
+ end
216
+
217
+ def test_failures
218
+ branch1 = Branch.new('111', '111', '111')
219
+ branch2 = Branch.new('222', '222', '222')
220
+ branch3 = Branch.new('333', '333', '333')
221
+ @collection.mark_failure(branch1)
222
+ @collection.mark_success(branch2)
223
+ @collection.mark_failure(branch3)
224
+
225
+ assert_equal(@collection.failures.values, [branch1, branch3])
226
+ end
227
+
228
+ private
229
+
230
+ def old_branches
231
+ @old_branches ||= {
232
+ 'the_origin_url/some_old_branch' => Branch.from_hash({'ref' => 'some_old_branch', 'remote_url' => 'the_origin_url', 'remote' => 'origin', 'created_at' => (Time.now - 3600), 'stories' => ['111']}),
233
+ 'the_origin_url/some_branch' => Branch.from_hash({'ref' => 'some_branch', 'remote_url' => 'the_origin_url', 'remote' => 'origin', 'status' => 'success', 'created_at' => (Time.now - 1800), 'stories' => ['222']})
234
+ }
235
+ end
236
+ end
237
+ end
238
+ end