flash_flow 1.3.2.1 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,8 +1,8 @@
1
1
  require 'minitest_helper'
2
- require 'flash_flow/merge_master'
2
+ require 'flash_flow/merge'
3
3
 
4
4
  module FlashFlow
5
- module MergeMaster
5
+ module Merge
6
6
  class TestReleaseGraph < Minitest::Test
7
7
 
8
8
  class FakeIssueTracker
@@ -1,11 +1,11 @@
1
1
  require 'minitest_helper'
2
- require 'flash_flow/merge_master'
2
+ require 'flash_flow/merge'
3
3
 
4
4
  module FlashFlow
5
- module MergeMaster
5
+ module Merge
6
6
  class TestStatus < Minitest::Test
7
7
 
8
- class TestableMergeMaster < Status
8
+ class TestableMerge < Status
9
9
  def initialize;
10
10
  end
11
11
 
@@ -100,7 +100,7 @@ module FlashFlow
100
100
  ## Begin actual tests
101
101
 
102
102
  def setup
103
- @merge_master = TestableMergeMaster.new
103
+ @merge_master = TestableMerge.new
104
104
  @merge_master.issue_tracker = FakeIssueTracker.new
105
105
  @merge_master.collection = FakeCollection.new
106
106
  end
@@ -0,0 +1,92 @@
1
+ require 'minitest_helper'
2
+ require 'flash_flow/release/percy_client'
3
+
4
+ module FlashFlow
5
+ module Release
6
+ class TestPercyClient < Minitest::Test
7
+
8
+ def setup
9
+ @percy_client = Release::PercyClient.new({'token' => ''})
10
+ end
11
+
12
+ def test_find_latest_by_sha
13
+ @percy_client.stub(:get_builds, sample_response) do
14
+ results = @percy_client.send(:find_latest_by_sha, 'aaaaa')
15
+ assert_equal(results[:url], 'https://percy.io/repo/builds/2222')
16
+ assert_equal(results[:approved], true)
17
+ end
18
+ end
19
+
20
+ def test_find_commit_by_sha
21
+ commit = @percy_client.send(:find_commit_by_sha, sample_response, 'bbbbb')
22
+ assert_equal(commit['id'], '8888')
23
+ end
24
+
25
+ def test_find_build_by_commit_id
26
+ commit = @percy_client.send(:find_commit_by_sha, sample_response, 'aaaaa')
27
+ build = @percy_client.send(:find_build_by_commit_id, sample_response, commit['id'])
28
+
29
+ assert_equal(build['web-url'], 'https://percy.io/repo/builds/2222')
30
+ assert(!build['approved-at'].nil?)
31
+ end
32
+
33
+ private
34
+
35
+ def sample_response
36
+ JSON.parse(
37
+ '{"data": [
38
+ {
39
+ "type": "builds",
40
+ "attributes": {
41
+ "web-url": "https://percy.io/repo/builds/1111",
42
+ "approved-at": null,
43
+ "created-at": "2016-08-01T00:00:00.000Z"
44
+ },
45
+ "relationships": {
46
+ "commit": {
47
+ "data": {
48
+ "type": "commits",
49
+ "id": "9999"
50
+ }
51
+ }
52
+ }
53
+ },
54
+ {
55
+ "type": "builds",
56
+ "attributes": {
57
+ "web-url": "https://percy.io/repo/builds/2222",
58
+ "approved-at": "2016-08-01T22:41:58.000Z",
59
+ "created-at": "2016-08-01T11:11:11.111Z"
60
+ },
61
+ "relationships": {
62
+ "commit": {
63
+ "data": {
64
+ "type": "commits",
65
+ "id": "9999"
66
+ }
67
+ }
68
+ }
69
+ }
70
+ ],
71
+ "included": [
72
+ {
73
+ "id": "9999",
74
+ "type": "commits",
75
+ "attributes": {
76
+ "sha": "aaaaa"
77
+ }
78
+ },
79
+ {
80
+ "id": "8888",
81
+ "type": "commits",
82
+ "attributes": {
83
+ "sha": "bbbbb"
84
+ }
85
+ }
86
+ ]
87
+ }')
88
+ end
89
+
90
+ end
91
+ end
92
+ end
@@ -46,9 +46,7 @@ module FlashFlow
46
46
  private
47
47
 
48
48
  def mock_current_sha(branch, sha)
49
- @git.expect(:run, sha, ["rev-parse #{branch.remote}/#{branch.ref}"])
50
- .expect(:last_success?, true)
51
- .expect(:last_stdout, sha)
49
+ @git.expect(:get_sha, sha, ["#{branch.remote}/#{branch.ref}"])
52
50
  end
53
51
 
54
52
  def mock_working_branch(branch)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: flash_flow
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.2.1
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Flashfunders
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-07-28 00:00:00.000000000 Z
11
+ date: 2016-08-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: octokit
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - ">"
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: percy-client
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: bundler
71
85
  requirement: !ruby/object:Gem::Requirement
@@ -165,21 +179,25 @@ files:
165
179
  - lib/flash_flow/data/collection.rb
166
180
  - lib/flash_flow/data/github.rb
167
181
  - lib/flash_flow/data/store.rb
168
- - lib/flash_flow/deploy.rb
169
182
  - lib/flash_flow/git.rb
170
183
  - lib/flash_flow/install.rb
171
184
  - lib/flash_flow/issue_tracker.rb
172
185
  - lib/flash_flow/issue_tracker/pivotal.rb
173
186
  - lib/flash_flow/lock.rb
174
187
  - lib/flash_flow/lock/github.rb
175
- - lib/flash_flow/merge_master.rb
176
- - lib/flash_flow/merge_master/merge_status.html.erb
177
- - lib/flash_flow/merge_master/release_graph.rb
178
- - lib/flash_flow/merge_master/status.rb
188
+ - lib/flash_flow/merge.rb
189
+ - lib/flash_flow/merge/acceptance.rb
190
+ - lib/flash_flow/merge/base.rb
191
+ - lib/flash_flow/merge/master.rb
192
+ - lib/flash_flow/merge/merge_status.html.erb
193
+ - lib/flash_flow/merge/release_graph.rb
194
+ - lib/flash_flow/merge/status.rb
179
195
  - lib/flash_flow/merge_order.rb
180
196
  - lib/flash_flow/notifier.rb
181
197
  - lib/flash_flow/notifier/hipchat.rb
182
198
  - lib/flash_flow/options.rb
199
+ - lib/flash_flow/release.rb
200
+ - lib/flash_flow/release/percy_client.rb
183
201
  - lib/flash_flow/resolve.rb
184
202
  - lib/flash_flow/shadow_repo.rb
185
203
  - lib/flash_flow/time_helper.rb
@@ -192,11 +210,12 @@ files:
192
210
  - test/lib/data/test_store.rb
193
211
  - test/lib/issue_tracker/test_pivotal.rb
194
212
  - test/lib/lock/test_github.rb
195
- - test/lib/merge_master/test_release_graph.rb
196
- - test/lib/merge_master/test_status.rb
213
+ - test/lib/merge/test_acceptance.rb
214
+ - test/lib/merge/test_release_graph.rb
215
+ - test/lib/merge/test_status.rb
216
+ - test/lib/release/test_percy_client.rb
197
217
  - test/lib/test_branch_merger.rb
198
218
  - test/lib/test_config.rb
199
- - test/lib/test_deploy.rb
200
219
  - test/lib/test_git.rb
201
220
  - test/lib/test_issue_tracker.rb
202
221
  - test/lib/test_merge_order.rb
@@ -236,11 +255,12 @@ test_files:
236
255
  - test/lib/data/test_store.rb
237
256
  - test/lib/issue_tracker/test_pivotal.rb
238
257
  - test/lib/lock/test_github.rb
239
- - test/lib/merge_master/test_release_graph.rb
240
- - test/lib/merge_master/test_status.rb
258
+ - test/lib/merge/test_acceptance.rb
259
+ - test/lib/merge/test_release_graph.rb
260
+ - test/lib/merge/test_status.rb
261
+ - test/lib/release/test_percy_client.rb
241
262
  - test/lib/test_branch_merger.rb
242
263
  - test/lib/test_config.rb
243
- - test/lib/test_deploy.rb
244
264
  - test/lib/test_git.rb
245
265
  - test/lib/test_issue_tracker.rb
246
266
  - test/lib/test_merge_order.rb
@@ -1,295 +0,0 @@
1
- require 'logger'
2
-
3
- require 'flash_flow/git'
4
- require 'flash_flow/data'
5
- require 'flash_flow/lock'
6
- require 'flash_flow/notifier'
7
- require 'flash_flow/branch_merger'
8
- require 'flash_flow/merge_order'
9
- require 'flash_flow/shadow_repo'
10
-
11
- module FlashFlow
12
- class Deploy
13
-
14
- class GitPushFailure < RuntimeError ; end
15
- class OutOfSyncWithRemote < RuntimeError ; end
16
- class UnmergeableBranch < RuntimeError ; end
17
-
18
- def initialize(opts={})
19
- @do_not_merge = opts[:do_not_merge]
20
- @force = opts[:force]
21
- @rerere_forget = opts[:rerere_forget]
22
- @stories = [opts[:stories]].flatten.compact
23
-
24
- @local_git = Git.new(Config.configuration.git, logger)
25
- @git = ShadowGit.new(Config.configuration.git, logger)
26
- @lock = Lock::Base.new(Config.configuration.lock)
27
- @notifier = Notifier::Base.new(Config.configuration.notifier)
28
- @data = Data::Base.new(Config.configuration.branches, Config.configuration.branch_info_file, @git, logger: logger)
29
-
30
- @release_branches = parse_branches(opts[:release_branches])
31
- end
32
-
33
- def logger
34
- @logger ||= FlashFlow::Config.configuration.logger
35
- end
36
-
37
- def parse_branches(user_branches)
38
- branch_list = user_branches == ['ready'] ? shippable_branch_names : [user_branches].flatten.compact
39
-
40
- branch_list.map { |b| Data::Branch.new('origin', @git.remotes_hash['origin'], b) }
41
- end
42
-
43
- def run_release
44
- check_version
45
- check_branches
46
- puts "Merging these branches into #{@git.release_branch}:\n #{@release_branches.map(&:ref).join("\n ")}"
47
- logger.info "\n\n### Beginning #{@local_git.merge_branch} merge ###\n\n"
48
-
49
- begin
50
- mergers, errors = [], []
51
-
52
- @lock.with_lock do
53
- @git.fetch(@git.merge_remote)
54
- @git.in_original_merge_branch do
55
- @git.initialize_rerere
56
- end
57
-
58
- @git.reset_temp_merge_branch
59
- @git.in_temp_merge_branch do
60
- merge_branches(@release_branches) do |branch, merger|
61
- mergers << [branch, merger]
62
- end
63
- end
64
-
65
- errors = mergers.select { |m| m.last.result != :success }
66
-
67
- if errors.empty?
68
- @git.copy_temp_to_branch(@git.release_branch)
69
- @git.delete_temp_merge_branch
70
- unless @git.push(@git.release_branch, false)
71
- raise GitPushFailure.new("Unable to push to #{@git.release_branch}. See log for details.")
72
- end
73
- end
74
- end
75
-
76
- if errors.empty?
77
- puts 'Success!'
78
- else
79
- raise UnmergeableBranch.new("The following branches didn't merge successfully:\n #{errors.map {|e| e.first.ref }.join("\n ")}")
80
- end
81
-
82
- logger.info "### Finished #{@git.release_branch} merge ###"
83
- rescue Lock::Error, OutOfSyncWithRemote, UnmergeableBranch, GitPushFailure => e
84
- puts 'Failure!'
85
- puts e.message
86
- ensure
87
- @local_git.run("checkout #{@local_git.working_branch}")
88
- end
89
- end
90
-
91
- def run
92
- check_version
93
- check_repo
94
- puts "Building #{@local_git.merge_branch}... Log can be found in #{FlashFlow::Config.configuration.log_file}"
95
- logger.info "\n\n### Beginning #{@local_git.merge_branch} merge ###\n\n"
96
-
97
- begin
98
- open_pull_request
99
-
100
- @lock.with_lock do
101
- @git.fetch(@git.merge_remote)
102
- @git.in_original_merge_branch do
103
- @git.initialize_rerere
104
- end
105
-
106
- @git.reset_temp_merge_branch
107
- @git.in_temp_merge_branch do
108
- merge_branches(@data.merged_branches.mergeable) do |branch, merger|
109
- process_result(branch, merger)
110
- end
111
- commit_branch_info
112
- commit_rerere
113
- end
114
-
115
- @git.copy_temp_to_branch(@git.merge_branch, commit_message)
116
- @git.delete_temp_merge_branch
117
- @git.push(@git.merge_branch, true)
118
- end
119
-
120
- print_errors
121
- logger.info "### Finished #{@local_git.merge_branch} merge ###"
122
- rescue Lock::Error, OutOfSyncWithRemote => e
123
- puts 'Failure!'
124
- puts e.message
125
- ensure
126
- @local_git.run("checkout #{@local_git.working_branch}")
127
- end
128
- end
129
-
130
- def check_branches
131
- requested_not_ready_branches = (@release_branches.map(&:ref) - shippable_branch_names)
132
- raise RuntimeError.new("The following branches are not ready to ship:\n#{requested_not_ready_branches.join("\n")}") unless requested_not_ready_branches.empty?
133
- end
134
-
135
- def shippable_branch_names
136
- @shippable_branch_names ||= begin
137
- status = MergeMaster::Status.new(Config.configuration.issue_tracker, Config.configuration.branches, Config.configuration.branch_info_file, Config.configuration.git, logger: logger)
138
-
139
- all_branches = status.branches
140
- all_branches.values.select { |b| b[:shippable?] }.map { |b| b[:name] }
141
- end
142
- end
143
-
144
- def check_repo
145
- if @local_git.staged_and_working_dir_files.any?
146
- raise RuntimeError.new('You have changes in your working directory. Please stash and try again')
147
- end
148
- end
149
-
150
- def check_version
151
- data_version = @data.version
152
- return if data_version.nil?
153
-
154
- written_version = data_version.split(".").map(&:to_i)
155
- running_version = FlashFlow::VERSION.split(".").map(&:to_i)
156
-
157
- unless written_version[0] < running_version[0] ||
158
- (written_version[0] == running_version[0] && written_version[1] <= running_version[1]) # Ignore the point release number
159
- raise RuntimeError.new("Your version of flash flow (#{FlashFlow::VERSION}) is behind the version that was last used (#{data_version}) by a member of your team. Please upgrade to at least #{written_version[0]}.#{written_version[1]}.0 and try again.")
160
- end
161
- end
162
-
163
- def commit_branch_info
164
- @stories.each do |story_id|
165
- @data.add_story(@git.merge_remote, @git.working_branch, story_id)
166
- end
167
- @data.save!
168
- end
169
-
170
- def commit_rerere
171
- current_branches = @data.merged_branches.to_a.select { |branch| !@git.master_branch_contains?(branch.sha) && (Time.now - branch.updated_at < two_weeks) }
172
- current_rereres = current_branches.map { |branch| branch.resolutions.to_h.values }.flatten
173
-
174
- @git.commit_rerere(current_rereres)
175
- end
176
-
177
- def two_weeks
178
- 60 * 60 * 24 * 14
179
- end
180
-
181
- def merge_branches(branches)
182
- ordered_branches = MergeOrder.new(@git, branches).get_order
183
- ordered_branches.each_with_index do |branch, index|
184
- branch.merge_order = index + 1
185
-
186
- remote = @git.fetch_remote_for_url(branch.remote_url)
187
- if remote.nil?
188
- raise RuntimeError.new("No remote found for #{branch.remote_url}. Please run 'git remote add *your_remote_name* #{branch.remote_url}' and try again.")
189
- end
190
-
191
- @git.fetch(branch.remote)
192
- merger = git_merge(branch)
193
-
194
- yield(branch, merger)
195
- end
196
- end
197
-
198
- def git_merge(branch)
199
- merger = BranchMerger.new(@git, branch)
200
- forget_rerere = is_working_branch(branch) && @rerere_forget
201
-
202
- merger.do_merge(forget_rerere)
203
-
204
- merger
205
- end
206
-
207
- def process_result(branch, merger)
208
- case merger.result
209
- when :deleted
210
- @data.mark_deleted(branch)
211
- @notifier.deleted_branch(branch) unless is_working_branch(branch)
212
-
213
- when :success
214
- branch.sha = merger.sha
215
- @data.mark_success(branch)
216
- @data.set_resolutions(branch, merger.resolutions)
217
-
218
- when :conflict
219
- if is_working_branch(branch)
220
- @data.mark_failure(branch, merger.conflict_sha)
221
- else
222
- @data.mark_failure(branch, nil)
223
- @notifier.merge_conflict(branch)
224
- end
225
- end
226
- end
227
-
228
- def is_working_branch(branch)
229
- branch.ref == @git.working_branch
230
- end
231
-
232
- def open_pull_request
233
- return false if [@local_git.master_branch, @local_git.merge_branch].include?(@local_git.working_branch)
234
-
235
- # TODO - This should use the actual remote for the branch we're on
236
- @local_git.push(@local_git.working_branch, @force)
237
- raise OutOfSyncWithRemote.new("Your branch is out of sync with the remote. If you want to force push, run 'flash_flow -f'") unless @local_git.last_success?
238
-
239
- # TODO - This should use the actual remote for the branch we're on
240
- if @do_not_merge
241
- @data.remove_from_merge(@local_git.merge_remote, @local_git.working_branch)
242
- else
243
- @data.add_to_merge(@local_git.merge_remote, @local_git.working_branch)
244
- end
245
- end
246
-
247
- def print_errors
248
- puts format_errors
249
- end
250
-
251
- def format_errors
252
- errors = []
253
- branch_not_merged = nil
254
- @data.failures.each do |branch|
255
- if branch.ref == @local_git.working_branch
256
- branch_not_merged = "ERROR: Your branch did not merge to #{@local_git.merge_branch}. Run 'flash_flow --resolve', fix the merge conflict(s) and then re-run this script\n"
257
- else
258
- errors << "WARNING: Unable to merge branch #{branch.remote}/#{branch.ref} to #{@local_git.merge_branch} due to conflicts."
259
- end
260
- end
261
- errors << branch_not_merged if branch_not_merged
262
-
263
- if errors.empty?
264
- "Success!"
265
- else
266
- errors.join("\n")
267
- end
268
- end
269
-
270
- def release_commit_message
271
- message =<<-EOS
272
- Flash Flow merged these branches:
273
- #{@release_branches.map(&:ref).join("\n")}
274
- EOS
275
- message.gsub(/'/, '')
276
- end
277
-
278
- def commit_message
279
- message =<<-EOS
280
- Flash Flow run from branch: #{@local_git.working_branch}
281
-
282
- Merged branches:
283
- #{@data.successes.empty? ? 'None' : @data.successes.sort_by(&:merge_order).map(&:ref).join("\n")}
284
-
285
- Failed branches:
286
- #{@data.failures.empty? ? 'None' : @data.failures.map(&:ref).join("\n")}
287
-
288
- Removed branches:
289
- #{@data.removals.empty? ? 'None' : @data.removals.map(&:ref).join("\n")}
290
- EOS
291
- message.gsub(/'/, '')
292
- end
293
-
294
- end
295
- end