flash_flow 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a836475713f58f0a511b59b28b404af5d43b20bd
4
- data.tar.gz: f301d93bfcab8720f1a9f76b6e60d69fb8da0530
3
+ metadata.gz: 8006bd8e5430d1b82498e75a5ee3d0990804d19f
4
+ data.tar.gz: 168531a6539335bf8a5b5bc8bf00e3097ca1c134
5
5
  SHA512:
6
- metadata.gz: 57776f50518921b2f3c16407b11bb54cc294d141d11e1936509ecd72d1ee0343ca2931e319561395c68c3eaa644393bd29611d52dd87e7eb3d787f3b3533af04
7
- data.tar.gz: e60cd7cb7dcd124dfdb62ee1900f5fae8785e04d02c4179f91f814971ef7e5be32200b6dee1ab88ef539415f3f760027a4e426cd68f3637227e9a9951bfbca0f
6
+ metadata.gz: c3349383a3fe10ef4924c2c99273922408572b18ec2169b853a630f19649d43ef0ecdfd5ac3150901c7c241c1b923f180d1775aef61d6bd3336da1a29620a93c
7
+ data.tar.gz: 8630a2e23c234677f2379d90376bac227099f0c9cddde3c72a4f4f50f3c74ab33275a2496c6d1d2844d399f14a090100ab003075a0f24f5948446604b5e4aee5
data/bin/flash_flow CHANGED
@@ -17,6 +17,10 @@ case
17
17
  FlashFlow::IssueTracker::Base.new(FlashFlow::Config.configuration.issue_tracker).stories_delivered
18
18
  when options[:release_notes]
19
19
  FlashFlow::IssueTracker::Base.new(FlashFlow::Config.configuration.issue_tracker).release_notes(options[:release_notes])
20
+ when options[:resolve]
21
+ FlashFlow::Resolve.new(Config.configuration.git, Config.configuration.branch_info_file, logger: Config.configuration.logger).start
22
+ when options[:resolve_manual]
23
+ FlashFlow::Resolve.new(Config.configuration.git, Config.configuration.branch_info_file, logger: Config.configuration.logger).manual_instructions
20
24
  else
21
25
  FlashFlow::Deploy.new(options).run
22
26
  FlashFlow::IssueTracker::Base.new(FlashFlow::Config.configuration.issue_tracker).stories_pushed
data/lib/flash_flow.rb CHANGED
@@ -5,3 +5,4 @@ require 'flash_flow/install'
5
5
  require 'flash_flow/config'
6
6
  require 'flash_flow/deploy'
7
7
  require 'flash_flow/issue_tracker'
8
+ require 'flash_flow/resolve'
@@ -5,6 +5,7 @@ require 'flash_flow/data'
5
5
  require 'flash_flow/lock'
6
6
  require 'flash_flow/notifier'
7
7
  require 'flash_flow/branch_merger'
8
+ require 'flash_flow/shadow_repo'
8
9
 
9
10
  module FlashFlow
10
11
  class Deploy
@@ -33,25 +34,28 @@ module FlashFlow
33
34
  puts "Building #{@git.merge_branch}... Log can be found in #{FlashFlow::Config.configuration.log_file}"
34
35
  logger.info "\n\n### Beginning #{@git.merge_branch} merge ###\n\n"
35
36
 
36
- fetch(@git.merge_remote)
37
- @git.in_original_merge_branch do
38
- @git.initialize_rerere
39
- end
40
37
 
41
38
  begin
42
- @lock.with_lock do
43
- open_pull_request
44
-
45
- @git.reset_temp_merge_branch
46
- @git.in_temp_merge_branch do
47
- merge_branches
48
- commit_branch_info
49
- commit_rerere
39
+ open_pull_request
40
+
41
+ in_shadow_repo do
42
+ @lock.with_lock do
43
+ fetch(@git.merge_remote)
44
+ @git.in_original_merge_branch do
45
+ @git.initialize_rerere
46
+ end
47
+
48
+ @git.reset_temp_merge_branch
49
+ @git.in_temp_merge_branch do
50
+ merge_branches
51
+ commit_branch_info
52
+ commit_rerere
53
+ end
54
+
55
+ @git.copy_temp_to_merge_branch
56
+ @git.delete_temp_merge_branch
57
+ @git.push_merge_branch
50
58
  end
51
-
52
- @git.copy_temp_to_merge_branch
53
- @git.delete_temp_merge_branch
54
- @git.push_merge_branch
55
59
  end
56
60
 
57
61
  print_errors
@@ -157,7 +161,7 @@ module FlashFlow
157
161
  branch_not_merged = nil
158
162
  @data.failures.each do |full_ref, failure|
159
163
  if failure.ref == @git.working_branch
160
- branch_not_merged = "\nERROR: Your branch did not merge to #{@git.merge_branch}. Run the following commands to fix the merge conflict and then re-run this script:\n\n git checkout #{failure.metadata['conflict_sha']}\n git merge #{@git.working_branch}\n # Resolve the conflicts\n git add <conflicted files>\n git commit --no-edit"
164
+ branch_not_merged = "ERROR: Your branch did not merge to #{@git.merge_branch}. Run 'flash_flow --resolve', fix the merge conflict(s) and then re-run this script\n"
161
165
  else
162
166
  errors << "WARNING: Unable to merge branch #{failure.remote}/#{failure.ref} to #{@git.merge_branch} due to conflicts."
163
167
  end
@@ -173,6 +177,12 @@ module FlashFlow
173
177
 
174
178
  private
175
179
 
180
+ def in_shadow_repo
181
+ ShadowRepo.new(@git, logger: logger).in_dir do
182
+ yield
183
+ end
184
+ end
185
+
176
186
  def fetch(remote)
177
187
  @fetched_remotes ||= {}
178
188
  unless @fetched_remotes[remote]
@@ -99,22 +99,25 @@ module FlashFlow
99
99
  def rerere_resolve!
100
100
  return false unless use_rerere
101
101
 
102
- merging_files = staged_and_working_dir_files.select { |s| UNMERGED_STATUSES.include?(s[0..1]) }.map { |s| s[3..-1] }
102
+ if unresolved_conflicts.empty?
103
+ merging_files = staged_and_working_dir_files.select { |s| UNMERGED_STATUSES.include?(s[0..1]) }.map { |s| s[3..-1] }
104
+ conflicts = conflicted_files
103
105
 
104
- conflicts = merging_files.map do |file|
105
- File.open(file) { |f| f.grep(/>>>>/) }
106
- end
107
-
108
- if conflicts.all? { |c| c.empty? }
109
106
  run("add #{merging_files.join(" ")}")
110
107
  run('commit --no-edit')
111
108
 
112
- resolutions(merging_files)
109
+ resolutions(conflicts)
113
110
  else
114
111
  false
115
112
  end
116
113
  end
117
114
 
115
+ def unresolved_conflicts
116
+ conflicted_files.map do |file|
117
+ File.open(file) { |f| f.grep(/>>>>/) }.empty? ? nil : file
118
+ end.compact
119
+ end
120
+
118
121
  def resolutions(files)
119
122
  {}.tap do |hash|
120
123
  files.map do |file|
@@ -165,6 +168,11 @@ module FlashFlow
165
168
  last_stdout.split("\n").reject { |line| line[0..1] == '??' }
166
169
  end
167
170
 
171
+ def conflicted_files
172
+ run("diff --name-only --diff-filter=U")
173
+ last_stdout.split("\n")
174
+ end
175
+
168
176
  def current_branch
169
177
  run("rev-parse --abbrev-ref HEAD")
170
178
  last_stdout.strip
@@ -214,6 +222,17 @@ module FlashFlow
214
222
  in_branch(merge_branch, &block)
215
223
  end
216
224
 
225
+ def in_branch(branch)
226
+ begin
227
+ starting_branch = current_branch
228
+ run("checkout #{branch}")
229
+
230
+ yield
231
+ ensure
232
+ run("checkout #{starting_branch}")
233
+ end
234
+ end
235
+
217
236
  private
218
237
 
219
238
  def squash_commits
@@ -234,15 +253,5 @@ module FlashFlow
234
253
  "flash_flow/#{merge_branch}"
235
254
  end
236
255
 
237
- def in_branch(branch)
238
- begin
239
- starting_branch = current_branch
240
- run("checkout #{branch}")
241
-
242
- yield
243
- ensure
244
- run("checkout #{starting_branch}")
245
- end
246
- end
247
256
  end
248
257
  end
@@ -18,6 +18,8 @@ module FlashFlow
18
18
  opts.on('--stories id1,id2', 'comma-delimited list of story ids for this branch') { |v| options[:stories] = v.split(',') }
19
19
  opts.on('-f', '--force-push', 'Force push your branch') { |v| options[:force] = v }
20
20
  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 }
21
+ opts.on('--resolve', 'Launch a bash shell to save your conflict resolutions') { |v| options[:resolve] = true }
22
+ opts.on('--resolve-manual', 'Print instructions to use git to resolve conflicts') { |v| options[:resolve_manual] = true }
21
23
 
22
24
  opts.on_tail("-h", "--help", "Show this message") do
23
25
  puts opts
@@ -0,0 +1,158 @@
1
+ require 'logger'
2
+
3
+ require 'flash_flow/git'
4
+ require 'flash_flow/data'
5
+
6
+ module FlashFlow
7
+ class Resolve
8
+
9
+ class NothingToResolve < StandardError; end
10
+
11
+ def initialize(git_config, branch_info_file, opts={})
12
+ @logger = opts[:logger]
13
+ @branch_info_file = branch_info_file
14
+ @cmd_runner = CmdRunner.new(logger: @logger)
15
+ @git = Git.new(git_config, @logger)
16
+ end
17
+
18
+ def manual_instructions
19
+ branch = check_for_conflict
20
+ puts manual_not_merged_instructions(branch)
21
+ end
22
+
23
+ def start
24
+ check_for_conflict
25
+
26
+ in_shadow_repo do
27
+ in_working_branch do
28
+ merge_conflicted
29
+
30
+ if unresolved_conflicts.empty?
31
+ puts "You have already resolved all conflicts."
32
+ else
33
+ launch_bash
34
+
35
+ rerere
36
+
37
+ unless unresolved_conflicts.empty?
38
+ puts "There are still unresolved conflicts in these files:\n#{unresolved_conflicts.join("\n")}\n\n"
39
+ end
40
+ end
41
+
42
+ git_reset
43
+ end
44
+ end
45
+ end
46
+
47
+ def unresolved_conflicts
48
+ @git.unresolved_conflicts
49
+ end
50
+
51
+ def merge_conflicted
52
+ @git.run("checkout #{branch.metadata['conflict_sha']}")
53
+ @git.run("merge origin/#{working_branch}")
54
+ end
55
+
56
+ def git_reset
57
+ @git.run("reset --hard HEAD")
58
+ end
59
+
60
+ def rerere
61
+ @git.run("rerere")
62
+ end
63
+
64
+ def bash_message
65
+ puts "\nPlease fix the following conflicts and then 'exit':\n#{unresolved_conflicts.join("\n")}\n\n"
66
+ end
67
+
68
+ def launch_bash
69
+ with_init_file do |file|
70
+ system("bash --init-file #{file} -i")
71
+ end
72
+ end
73
+
74
+ def with_init_file
75
+ filename = '.flash_flow_init'
76
+ File.open(filename, 'w') do |f|
77
+ f.puts(init_file_contents)
78
+ end
79
+
80
+ yield filename
81
+
82
+ File.delete(filename)
83
+ end
84
+
85
+ def manual_not_merged_instructions(branch)
86
+ <<-EOS
87
+
88
+ Run the following commands to fix the merge conflict and then re-run flash_flow:
89
+ pushd #{flash_flow_directory}
90
+ git checkout #{branch.metadata['conflict_sha']}
91
+ git merge #{working_branch}
92
+ # Resolve the conflicts
93
+ git add <conflicted files>
94
+ git commit --no-edit
95
+ popd
96
+
97
+ EOS
98
+ end
99
+
100
+ private
101
+
102
+ def data
103
+ return @data if @data
104
+
105
+ in_shadow_repo do
106
+ @data = Data::Base.new({}, @branch_info_file, @git, logger: logger)
107
+ end
108
+
109
+ @data
110
+
111
+ end
112
+
113
+ def branch
114
+ @branch ||= data.saved_branches.detect { |branch| branch.ref == working_branch }
115
+ end
116
+
117
+ def shadow_repo
118
+ @shadow_repo ||= ShadowRepo.new(@git, logger: @logger)
119
+ end
120
+
121
+ def in_shadow_repo
122
+ shadow_repo.in_dir do
123
+ yield
124
+ end
125
+ end
126
+
127
+ def working_branch
128
+ @git.working_branch
129
+ end
130
+
131
+ def in_working_branch
132
+ @git.in_branch(working_branch) do
133
+ yield
134
+ end
135
+ end
136
+
137
+ def flash_flow_directory
138
+ shadow_repo.flash_flow_dir
139
+ end
140
+
141
+ def init_file_contents
142
+ <<-EOS
143
+ # Commented this one out because it was causing lots of spurious "saving session..." type messages
144
+ # [[ -s /etc/profile ]] && source /etc/profile
145
+ [[ -s ~/.bash_profile ]] && source ~/.bash_profile
146
+ [[ -s ~/.bash_login ]] && source ~/.bash_login
147
+ [[ -s ~/.profile ]] && source ~/.profile
148
+ [[ -s ~/.bashrc ]] && source ~/.bashrc
149
+
150
+ PS1='flash_flow resolve: (type "exit" after your conflicts are resolved)$ '
151
+ EOS
152
+ end
153
+
154
+ def check_for_conflict
155
+ raise NothingToResolve.new("The current branch (#{working_branch}) does not appear to be in conflict.") unless branch.metadata['conflict_sha']
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,45 @@
1
+ require 'logger'
2
+
3
+ require 'flash_flow/git'
4
+
5
+ module FlashFlow
6
+ class ShadowRepo
7
+
8
+
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
+ create_shadow_repo
17
+
18
+ Dir.chdir(flash_flow_dir) do
19
+ @git.run("reset --hard HEAD") if opts[:reset]
20
+
21
+ yield
22
+ end
23
+ end
24
+
25
+ def create_shadow_repo
26
+ unless Dir.exists?(flash_flow_dir)
27
+ @cmd_runner.run("mkdir -p #{flash_flow_dir}")
28
+ @cmd_runner.run("cp -R #{current_dir} #{flash_flow_base_dir}")
29
+ end
30
+ end
31
+
32
+ def flash_flow_base_dir
33
+ @flash_flow_base_dir ||= current_dir + "/../.flash_flow"
34
+ end
35
+
36
+ def current_dir
37
+ @current_dir ||= Dir.getwd
38
+ end
39
+
40
+ def flash_flow_dir
41
+ @flash_flow_dir ||= flash_flow_base_dir + "/#{File.basename(current_dir)}"
42
+ end
43
+ end
44
+ end
45
+
@@ -1,3 +1,3 @@
1
1
  module FlashFlow
2
- VERSION = "1.0.0"
2
+ VERSION = "1.1.0"
3
3
  end
@@ -18,17 +18,6 @@ module FlashFlow
18
18
  @deploy = Deploy.new
19
19
  end
20
20
 
21
- def with_versions(current, written)
22
- original_version = FlashFlow::VERSION
23
- FlashFlow.send(:remove_const, :VERSION)
24
- FlashFlow.const_set(:VERSION, current)
25
- data.expect(:version, written)
26
- yield
27
- data.verify
28
- FlashFlow.send(:remove_const, :VERSION)
29
- FlashFlow.const_set(:VERSION, original_version)
30
- end
31
-
32
21
  def test_version_is_nil
33
22
  with_versions('1.1.1', nil) do
34
23
  assert_nil(@deploy.check_version)
@@ -68,7 +57,7 @@ module FlashFlow
68
57
  data.expect(:failures, {'origin/pushing_branch' => @branch})
69
58
  @branch.fail!('some_random_sha')
70
59
 
71
- current_branch_error = "\nERROR: Your branch did not merge to test_acceptance. Run the following commands to fix the merge conflict and then re-run this script:\n\n git checkout some_random_sha\n git merge pushing_branch\n # Resolve the conflicts\n git add <conflicted files>\n git commit --no-edit"
60
+ current_branch_error = "ERROR: Your branch did not merge to test_acceptance. Run 'flash_flow --resolve', fix the merge conflict(s) and then re-run this script\n"
72
61
 
73
62
  @deploy.instance_variable_get('@git'.to_sym).stub(:working_branch, 'pushing_branch') do
74
63
  assert_equal(current_branch_error, @deploy.format_errors)
@@ -84,9 +73,11 @@ module FlashFlow
84
73
  end
85
74
 
86
75
  def test_check_out_to_working_branch
87
- @deploy.stub(:check_repo, true) do
88
- Lock::Base.stub_any_instance(:with_lock, -> { raise Lock::Error }) do
89
- assert_output(/Failure!/) { @deploy.run }
76
+ @deploy.stub(:in_shadow_repo, true) do
77
+ @deploy.stub(:check_repo, true) do
78
+ Lock::Base.stub_any_instance(:with_lock, -> { raise Lock::Error }) do
79
+ assert_output(/Failure!/) { @deploy.run }
80
+ end
90
81
  end
91
82
  end
92
83
  end
@@ -153,6 +144,17 @@ module FlashFlow
153
144
 
154
145
  private
155
146
 
147
+ def with_versions(current, written)
148
+ original_version = FlashFlow::VERSION
149
+ FlashFlow.send(:remove_const, :VERSION)
150
+ FlashFlow.const_set(:VERSION, current)
151
+ data.expect(:version, written)
152
+ yield
153
+ data.verify
154
+ FlashFlow.send(:remove_const, :VERSION)
155
+ FlashFlow.const_set(:VERSION, original_version)
156
+ end
157
+
156
158
  def merger
157
159
  @merger ||= Minitest::Mock.new
158
160
  end
@@ -0,0 +1,100 @@
1
+ require 'minitest_helper'
2
+
3
+ module FlashFlow
4
+ class TestResolve< Minitest::Test
5
+
6
+ class ResolveTester < Resolve
7
+ def in_shadow_repo
8
+ yield
9
+ end
10
+
11
+ def in_working_branch
12
+ yield
13
+ end
14
+
15
+ def working_branch
16
+ 'working_branch'
17
+ end
18
+
19
+ def merge_conflicted
20
+ true
21
+ end
22
+
23
+ def launch_bash
24
+ puts 'launch_bash'
25
+ end
26
+
27
+ def rerere
28
+ puts 'rerere'
29
+ end
30
+
31
+ def git_reset
32
+ puts 'git_reset'
33
+ end
34
+
35
+ def branch
36
+ Data::Branch.from_hash({ 'metadata' => { 'conflict_sha' => '123' }})
37
+ end
38
+ end
39
+
40
+ def setup
41
+ @resolve_tester = ResolveTester.new({ 'merge_branch' => 'test_acceptance',
42
+ 'merge_remote' => 'test_remote',
43
+ 'master_branch' => 'test_master',
44
+ 'remotes' => ['fake_origin'],
45
+ 'use_rerere' => true
46
+ }, 'some_file')
47
+
48
+ @resolve = Resolve.new({ 'merge_branch' => 'test_acceptance',
49
+ 'merge_remote' => 'test_remote',
50
+ 'master_branch' => 'test_master',
51
+ 'remotes' => ['fake_origin'],
52
+ 'use_rerere' => true
53
+ }, 'some_file')
54
+ end
55
+
56
+ def test_no_conflict_sha
57
+ @resolve_tester.stub(:branch, Data::Branch.from_hash({ 'metadata' => { }})) do
58
+ assert_raises(Resolve::NothingToResolve) { @resolve_tester.start }
59
+ end
60
+ end
61
+
62
+ def test_conflicts_already_resolved
63
+ @resolve_tester.stub(:unresolved_conflicts, []) do
64
+ assert_output(/You have already resolved all conflicts/) { @resolve_tester.start }
65
+ end
66
+ end
67
+
68
+ def test_user_did_not_resolve_conflicts
69
+ @resolve_tester.stub(:unresolved_conflicts, ['conflict']) do
70
+ assert_output(/launch_bash.*rerere.*There are still.*git_reset/m) { @resolve_tester.start }
71
+ end
72
+ end
73
+
74
+
75
+
76
+ # def start
77
+ # if unresolved_conflicts.empty?
78
+ # puts "You have already resolved all conflicts."
79
+ # else
80
+ # launch_bash
81
+ # @git.run("rerere")
82
+ #
83
+ # unless unresolved_conflicts.empty?
84
+ # puts "There are still unresolved conflicts in these files:\n#{unresolved_conflicts.join("\n")}\n\n"
85
+ # end
86
+ # end
87
+ #
88
+ # @git.run("reset --hard HEAD")
89
+ # end
90
+ # end
91
+ # end
92
+
93
+
94
+ private
95
+
96
+ def shadow_repo
97
+ Minitest::Mock.new.expect(:in_dir, true)
98
+ end
99
+ end
100
+ end
@@ -17,6 +17,7 @@ class Minitest::Test
17
17
  def last_command; ''; end
18
18
  end
19
19
 
20
+ FlashFlow.send(:remove_const, :CmdRunner) if FlashFlow.const_defined?(:CmdRunner)
20
21
  FlashFlow::CmdRunner = TestCmdRunner
21
22
 
22
23
  def reset_config!
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: flash_flow
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Flashfunders
@@ -160,6 +160,8 @@ files:
160
160
  - lib/flash_flow/notifier.rb
161
161
  - lib/flash_flow/notifier/hipchat.rb
162
162
  - lib/flash_flow/options.rb
163
+ - lib/flash_flow/resolve.rb
164
+ - lib/flash_flow/shadow_repo.rb
163
165
  - lib/flash_flow/time_helper.rb
164
166
  - lib/flash_flow/version.rb
165
167
  - test/lib/data/test_base.rb
@@ -175,6 +177,7 @@ files:
175
177
  - test/lib/test_git.rb
176
178
  - test/lib/test_issue_tracker.rb
177
179
  - test/lib/test_notifier.rb
180
+ - test/lib/test_resolve.rb
178
181
  - test/minitest_helper.rb
179
182
  homepage: ''
180
183
  licenses:
@@ -214,4 +217,5 @@ test_files:
214
217
  - test/lib/test_git.rb
215
218
  - test/lib/test_issue_tracker.rb
216
219
  - test/lib/test_notifier.rb
220
+ - test/lib/test_resolve.rb
217
221
  - test/minitest_helper.rb