flash_flow 1.0.0 → 1.1.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.
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