flash_flow 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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