flash_flow 1.3.2.1 → 1.4.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.
@@ -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