flash_flow 1.2.1 → 1.2.2.a

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: 49d73fb6eb9880932364ebc295d6ad33ca786fa1
4
- data.tar.gz: 4c573c7241c9df3e771f09bb04a7eeab3294f65d
3
+ metadata.gz: 3aab35b80fc7adc67ef12b0833d6434f49c6ec52
4
+ data.tar.gz: 4c400dc91f4caf0c10c2f15adbec98d00861fa6a
5
5
  SHA512:
6
- metadata.gz: 508ada836ac3ca4a1eda158034af4190dd841e448d251b6a75ed15fbc005f1cf849254e55c1d11bbf473378722830afac5725c41be982d1d33d4468310e6fb1b
7
- data.tar.gz: df18ba6a3399e38fda31dd14d9a1f0a0f401a8f21eaa989d9acca7558cb043c6036303e80e7d423889819f73f03a2cd14c0355dea839268090393dc095e835bd
6
+ metadata.gz: 324c1d564582f90976a185bc6fc5491b6b4f7003694609c17b73cb42ebb5fc9533d78e4c04618b2bbebccc1e540c3ba04cfb4f3776b385f2c754abdb24b12d8e
7
+ data.tar.gz: 4d96b4211dea4aed6d7b6c7bb89a0b2c39bd61f0e33cfdbd701e60b30930f58231782e53f80fcf1728220c3c93e48938765098960a1b899467c63c41f1cfbff1
data/Gemfile.lock CHANGED
@@ -1,10 +1,11 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- flash_flow (1.2.1)
4
+ flash_flow (1.2.2.a)
5
5
  hipchat (~> 1.5)
6
6
  octokit (~> 4.1)
7
7
  pivotal-tracker (~> 0.5)
8
+ ruby-graphviz (> 0)
8
9
 
9
10
  GEM
10
11
  remote: https://rubygems.org/
@@ -57,6 +58,7 @@ GEM
57
58
  http-cookie (>= 1.0.2, < 2.0)
58
59
  mime-types (>= 1.16, < 3.0)
59
60
  netrc (~> 0.7)
61
+ ruby-graphviz (1.2.2)
60
62
  safe_yaml (1.0.4)
61
63
  sawyer (0.6.0)
62
64
  addressable (~> 2.3.5)
data/bin/flash_flow CHANGED
@@ -24,6 +24,8 @@ case
24
24
  FlashFlow::Resolve.new(FlashFlow::Config.configuration.git, FlashFlow::Config.configuration.branch_info_file, logger: FlashFlow::Config.configuration.logger).start
25
25
  when options[:resolve_manual]
26
26
  FlashFlow::Resolve.new(FlashFlow::Config.configuration.git, FlashFlow::Config.configuration.branch_info_file, logger: FlashFlow::Config.configuration.logger).manual_instructions
27
+ when options[:merge_status]
28
+ FlashFlow::MergeMaster::Status.new(FlashFlow::Config.configuration.issue_tracker, FlashFlow::Config.configuration.branches, FlashFlow::Config.configuration.branch_info_file, FlashFlow::Config.configuration.git, logger: FlashFlow::Config.configuration.logger).status
27
29
  else
28
30
  FlashFlow::Deploy.new(options).run
29
31
  FlashFlow::IssueTracker::Base.new(FlashFlow::Config.configuration.issue_tracker).stories_pushed
data/flash_flow.gemspec CHANGED
@@ -20,6 +20,7 @@ Gem::Specification.new do |spec|
20
20
  spec.add_dependency 'octokit', "~> 4.1"
21
21
  spec.add_dependency 'hipchat', "~> 1.5"
22
22
  spec.add_dependency 'pivotal-tracker', "~> 0.5"
23
+ spec.add_dependency 'ruby-graphviz', "> 0"
23
24
 
24
25
  spec.add_development_dependency "bundler", "~> 1.6"
25
26
  spec.add_development_dependency "rake", "> 0"
data/lib/flash_flow.rb CHANGED
@@ -6,3 +6,4 @@ require 'flash_flow/config'
6
6
  require 'flash_flow/deploy'
7
7
  require 'flash_flow/issue_tracker'
8
8
  require 'flash_flow/resolve'
9
+ require 'flash_flow/merge_master'
@@ -3,7 +3,11 @@ require 'open3'
3
3
 
4
4
  module FlashFlow
5
5
  class CmdRunner
6
- attr_reader :dry_run, :dir, :last_command, :last_stderr, :last_stdout
6
+ LOG_NONE = :log_none
7
+ LOG_CMD = :log_cmd
8
+
9
+ attr_reader :dry_run, :last_command, :last_stderr, :last_stdout
10
+ attr_accessor :dir
7
11
 
8
12
  def initialize(opts={})
9
13
  @dir = opts[:dir] || '.'
@@ -11,7 +15,7 @@ module FlashFlow
11
15
  @logger = opts[:logger] || Logger.new('/dev/null')
12
16
  end
13
17
 
14
- def run(cmd)
18
+ def run(cmd, opts={})
15
19
  @last_command = cmd
16
20
  if dry_run
17
21
  puts "#{dir}$ #{cmd}"
@@ -24,14 +28,26 @@ module FlashFlow
24
28
  @success = wait_thr.value.success?
25
29
  end
26
30
  end
27
- @logger.debug("#{dir}$ #{cmd}")
28
- last_stdout.split("\n").each { |line| @logger.debug(line) }
29
- last_stderr.split("\n").each { |line| @logger.debug(line) }
31
+ log(cmd, opts[:log])
30
32
  end
31
33
  end
32
34
 
33
35
  def last_success?
34
36
  @success
35
37
  end
38
+
39
+ private
40
+
41
+ def log(cmd, log_what)
42
+ if log_what == LOG_NONE
43
+ # Do nothing
44
+ else
45
+ @logger.debug("#{dir}$ #{cmd}")
46
+ unless log_what == LOG_CMD
47
+ last_stdout.split("\n").each { |line| @logger.debug(line) }
48
+ last_stderr.split("\n").each { |line| @logger.debug(line) }
49
+ end
50
+ end
51
+ end
36
52
  end
37
53
  end
@@ -9,8 +9,8 @@ module FlashFlow
9
9
  class Base
10
10
  extend Forwardable
11
11
 
12
- def_delegators :@collection, :add_story, :mergeable, :mark_deleted, :mark_success,
13
- :mark_failure, :remove_from_merge, :add_to_merge, :failures, :set_resolutions
12
+ def_delegators :@collection, :add_story, :mergeable, :mark_deleted, :mark_success, :mark_failure,
13
+ :remove_from_merge, :add_to_merge, :failures, :set_resolutions, :to_a, :can_ship?, :branch_link
14
14
 
15
15
  def initialize(branch_config, filename, git, opts={})
16
16
  @git = git
@@ -19,8 +19,10 @@ module FlashFlow
19
19
  end
20
20
 
21
21
  def initialize_collection(branch_config, remotes)
22
- Collection.fetch(remotes, branch_config) ||
22
+ collection = Collection.fetch(remotes, branch_config) ||
23
23
  Collection.from_hash(remotes, backwards_compatible_store['branches'])
24
+ collection.mark_all_as_current
25
+ collection
24
26
  end
25
27
 
26
28
  def version
@@ -44,9 +46,7 @@ module FlashFlow
44
46
 
45
47
  def backwards_compatible_store
46
48
  @backwards_compatible_store ||= begin
47
- hash = in_shadow_repo do
48
- @store.get
49
- end
49
+ hash = @store.get
50
50
 
51
51
  hash.has_key?('branches') ? hash : { 'branches' => hash }
52
52
  end
@@ -55,14 +55,6 @@ module FlashFlow
55
55
  def saved_branches
56
56
  Collection.from_hash(@git.remotes, backwards_compatible_store['branches']).to_a
57
57
  end
58
-
59
- private
60
-
61
- def in_shadow_repo
62
- ShadowRepo.new(@git).in_dir do
63
- yield
64
- end
65
- end
66
58
  end
67
59
  end
68
60
  end
@@ -4,7 +4,8 @@ module FlashFlow
4
4
  module Data
5
5
 
6
6
  class Branch
7
- attr_accessor :remote, :remote_url, :ref, :sha, :status, :resolutions, :stories, :conflict_sha, :metadata, :updated_at, :created_at
7
+ attr_accessor :remote, :remote_url, :ref, :sha, :status, :resolutions, :stories, :conflict_sha, :metadata,
8
+ :current_record, :updated_at, :created_at
8
9
 
9
10
  def initialize(_remote, _remote_url, _ref)
10
11
  @remote = _remote
@@ -24,9 +24,10 @@ module FlashFlow
24
24
  collection
25
25
  end
26
26
 
27
- def self.from_hash(remotes, hash)
27
+ def self.from_hash(remotes, hash, collection_instance=nil)
28
28
  collection = new(remotes)
29
29
  collection.branches = branches_from_hash(hash.dup)
30
+ collection.instance_variable_set(:@collection_instance, collection_instance)
30
31
  collection
31
32
  end
32
33
 
@@ -73,7 +74,7 @@ module FlashFlow
73
74
  end
74
75
  end
75
76
 
76
- self.class.from_hash(remotes, merged_branches)
77
+ self.class.from_hash(remotes, merged_branches, @collection_instance)
77
78
  end
78
79
 
79
80
  def to_a
@@ -84,8 +85,12 @@ module FlashFlow
84
85
  to_a.each
85
86
  end
86
87
 
88
+ def current_branches
89
+ to_a.select { |branch| branch.current_record }
90
+ end
91
+
87
92
  def mergeable
88
- to_a.select { |branch| branch.success? || branch.fail? || branch.unknown? }
93
+ current_branches.select { |branch| (branch.success? || branch.fail? || branch.unknown?) }
89
94
  end
90
95
 
91
96
  def failures
@@ -100,6 +105,12 @@ module FlashFlow
100
105
  end
101
106
  end
102
107
 
108
+ def mark_all_as_current
109
+ @branches.each do |_, branch|
110
+ branch.current_record = true
111
+ end
112
+ end
113
+
103
114
  def add_to_merge(remote, ref)
104
115
  branch = record(remote, nil, ref)
105
116
  @collection_instance.add_to_merge(branch) if @collection_instance.respond_to?(:add_to_merge)
@@ -143,6 +154,14 @@ module FlashFlow
143
154
  branch
144
155
  end
145
156
 
157
+ def can_ship?(branch)
158
+ @collection_instance.respond_to?(:can_ship?) ? @collection_instance.can_ship?(branch) : true
159
+ end
160
+
161
+ def branch_link(branch)
162
+ @collection_instance.branch_link(branch) if @collection_instance.respond_to?(:branch_link)
163
+ end
164
+
146
165
  def set_resolutions(branch, resolutions)
147
166
  update_or_add(branch)
148
167
  branch.set_resolutions(resolutions)
@@ -13,6 +13,7 @@ module FlashFlow
13
13
  @master_branch = config['master_branch'] || master
14
14
  @unmergeable_label = config['unmergeable_label'] || 'unmergeable'
15
15
  @do_not_merge_label = config['do_not_merge_label'] || 'do not merge'
16
+ @code_reviewed_label = config['code_reviewed_label'] || 'code reviewed'
16
17
  end
17
18
 
18
19
  def initialize_connection!(token)
@@ -61,6 +62,14 @@ module FlashFlow
61
62
  add_label(branch.metadata['pr_number'], @unmergeable_label)
62
63
  end
63
64
 
65
+ def can_ship?(branch)
66
+ has_label?(branch.metadata['pr_number'], @code_reviewed_label)
67
+ end
68
+
69
+ def branch_link(branch)
70
+ branch.metadata['pr_url']
71
+ end
72
+
64
73
  private
65
74
 
66
75
  def status_from_labels(pull_request)
@@ -105,19 +114,20 @@ module FlashFlow
105
114
  end
106
115
 
107
116
  def has_label?(pull_request_number, label_name)
108
- labels(pull_request_number).detect { |label| label.name == label_name }
117
+ !!labels(pull_request_number).detect { |label| label == label_name }
109
118
  end
110
119
 
111
120
  def labels(pull_request_number)
112
121
  @labels ||= {}
113
- @labels[pull_request_number] ||= octokit.labels_for_issue(repo, pull_request_number)
122
+ @labels[pull_request_number] ||= octokit.labels_for_issue(repo, pull_request_number).map(&:name)
114
123
  end
115
124
 
116
125
  def metadata(pr)
117
126
  {
118
127
  'pr_number' => pr.number,
119
- 'user_url' => pr.user.html_url,
120
- 'repo_url' => pr.head.repo.html_url
128
+ 'pr_url' => pr.html_url,
129
+ 'user_url' => pr.user.html_url,
130
+ 'repo_url' => pr.head.repo.html_url
121
131
  }
122
132
  end
123
133
 
@@ -20,12 +20,14 @@ module FlashFlow
20
20
  end
21
21
 
22
22
  def write(branches, file=nil)
23
- @git.in_temp_merge_branch do
23
+ @git.in_dir do
24
24
  file ||= File.open(@filename, 'w')
25
25
  file.puts JSON.pretty_generate(sort_branches(branches))
26
26
  file.close
27
+ end
27
28
 
28
- @git.add_and_commit(@filename, 'Branch Info', add: { force: true })
29
+ @git.in_temp_merge_branch do
30
+ @git.add_and_commit(@filename, 'Branch Info', add: {force: true})
29
31
  end
30
32
  end
31
33
 
@@ -18,7 +18,8 @@ module FlashFlow
18
18
  @rerere_forget = opts[:rerere_forget]
19
19
  @stories = [opts[:stories]].flatten.compact
20
20
 
21
- @git = Git.new(Config.configuration.git, logger)
21
+ @local_git = Git.new(Config.configuration.git, logger)
22
+ @git = ShadowGit.new(Config.configuration.git, logger)
22
23
  @lock = Lock::Base.new(Config.configuration.lock)
23
24
  @notifier = Notifier::Base.new(Config.configuration.notifier)
24
25
  @data = Data::Base.new(Config.configuration.branches, Config.configuration.branch_info_file, @git, logger: logger)
@@ -31,45 +32,42 @@ module FlashFlow
31
32
  def run
32
33
  check_version
33
34
  check_repo
34
- puts "Building #{@git.merge_branch}... Log can be found in #{FlashFlow::Config.configuration.log_file}"
35
- logger.info "\n\n### Beginning #{@git.merge_branch} merge ###\n\n"
36
-
35
+ puts "Building #{@local_git.merge_branch}... Log can be found in #{FlashFlow::Config.configuration.log_file}"
36
+ logger.info "\n\n### Beginning #{@local_git.merge_branch} merge ###\n\n"
37
37
 
38
38
  begin
39
39
  open_pull_request
40
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
41
+ @lock.with_lock do
42
+ @git.fetch(@git.merge_remote)
43
+ @git.in_original_merge_branch do
44
+ @git.initialize_rerere
45
+ end
46
+
47
+ @git.reset_temp_merge_branch
48
+ @git.in_temp_merge_branch do
49
+ merge_branches
50
+ commit_branch_info
51
+ commit_rerere
58
52
  end
53
+
54
+ @git.copy_temp_to_merge_branch
55
+ @git.delete_temp_merge_branch
56
+ @git.push_merge_branch
59
57
  end
60
58
 
61
59
  print_errors
62
- logger.info "### Finished #{@git.merge_branch} merge ###"
60
+ logger.info "### Finished #{@local_git.merge_branch} merge ###"
63
61
  rescue Lock::Error, OutOfSyncWithRemote => e
64
62
  puts 'Failure!'
65
63
  puts e.message
66
64
  ensure
67
- @git.run("checkout #{@git.working_branch}")
65
+ @local_git.run("checkout #{@local_git.working_branch}")
68
66
  end
69
67
  end
70
68
 
71
69
  def check_repo
72
- if @git.staged_and_working_dir_files.any?
70
+ if @local_git.staged_and_working_dir_files.any?
73
71
  raise RuntimeError.new('You have changes in your working directory. Please stash and try again')
74
72
  end
75
73
  end
@@ -112,7 +110,7 @@ module FlashFlow
112
110
  raise RuntimeError.new("No remote found for #{branch.remote_url}. Please run 'git remote add *your_remote_name* #{branch.remote_url}' and try again.")
113
111
  end
114
112
 
115
- fetch(branch.remote)
113
+ @git.fetch(branch.remote)
116
114
  git_merge(branch, branch.ref == @git.working_branch)
117
115
  end
118
116
  end
@@ -142,17 +140,17 @@ module FlashFlow
142
140
  end
143
141
 
144
142
  def open_pull_request
145
- return false if [@git.master_branch, @git.merge_branch].include?(@git.working_branch)
143
+ return false if [@local_git.master_branch, @local_git.merge_branch].include?(@local_git.working_branch)
146
144
 
147
145
  # TODO - This should use the actual remote for the branch we're on
148
- @git.push(@git.working_branch, force: @force)
149
- raise OutOfSyncWithRemote.new("Your branch is out of sync with the remote. If you want to force push, run 'flash_flow -f'") unless @git.last_success?
146
+ @local_git.push(@local_git.working_branch, force: @force)
147
+ 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?
150
148
 
151
149
  # TODO - This should use the actual remote for the branch we're on
152
150
  if @do_not_merge
153
- @data.remove_from_merge(@git.merge_remote, @git.working_branch)
151
+ @data.remove_from_merge(@local_git.merge_remote, @local_git.working_branch)
154
152
  else
155
- @data.add_to_merge(@git.merge_remote, @git.working_branch)
153
+ @data.add_to_merge(@local_git.merge_remote, @local_git.working_branch)
156
154
  end
157
155
  end
158
156
 
@@ -164,10 +162,10 @@ module FlashFlow
164
162
  errors = []
165
163
  branch_not_merged = nil
166
164
  @data.failures.each do |full_ref, failure|
167
- if failure.ref == @git.working_branch
168
- 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"
165
+ if failure.ref == @local_git.working_branch
166
+ 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"
169
167
  else
170
- errors << "WARNING: Unable to merge branch #{failure.remote}/#{failure.ref} to #{@git.merge_branch} due to conflicts."
168
+ errors << "WARNING: Unable to merge branch #{failure.remote}/#{failure.ref} to #{@local_git.merge_branch} due to conflicts."
171
169
  end
172
170
  end
173
171
  errors << branch_not_merged if branch_not_merged
@@ -179,20 +177,5 @@ module FlashFlow
179
177
  end
180
178
  end
181
179
 
182
- private
183
-
184
- def in_shadow_repo
185
- ShadowRepo.new(@git, logger: logger).in_dir do
186
- yield
187
- end
188
- end
189
-
190
- def fetch(remote)
191
- @fetched_remotes ||= {}
192
- unless @fetched_remotes[remote]
193
- @git.fetch(remote)
194
- @fetched_remotes[remote] = true
195
- end
196
- end
197
180
  end
198
181
  end
@@ -1,4 +1,5 @@
1
1
  require 'flash_flow/cmd_runner'
2
+ require 'shellwords'
2
3
 
3
4
  module FlashFlow
4
5
  class Git
@@ -22,6 +23,12 @@ module FlashFlow
22
23
  @working_branch = current_branch
23
24
  end
24
25
 
26
+ def in_dir
27
+ Dir.chdir(@cmd_runner.dir) do
28
+ yield
29
+ end
30
+ end
31
+
25
32
  def last_stdout
26
33
  @cmd_runner.last_stdout
27
34
  end
@@ -34,8 +41,8 @@ module FlashFlow
34
41
  @cmd_runner.last_success?
35
42
  end
36
43
 
37
- def run(cmd)
38
- @cmd_runner.run("git #{cmd}")
44
+ def run(cmd, opts={})
45
+ @cmd_runner.run("git #{cmd}", opts)
39
46
  end
40
47
 
41
48
  def add_and_commit(files, message, opts={})
@@ -53,7 +60,11 @@ module FlashFlow
53
60
  end
54
61
 
55
62
  def fetch(remote)
56
- run("fetch #{remote}")
63
+ @fetched_remotes ||= {}
64
+ unless @fetched_remotes[remote]
65
+ run("fetch #{remote}")
66
+ @fetched_remotes[remote] = true
67
+ end
57
68
  end
58
69
 
59
70
  def master_branch_contains?(ref)
@@ -73,7 +84,7 @@ module FlashFlow
73
84
  end
74
85
 
75
86
  def read_file_from_merge_branch(filename)
76
- run("show #{merge_remote}/#{merge_branch}:#{filename}")
87
+ run("show #{merge_remote}/#{merge_branch}:#{filename}", log: CmdRunner::LOG_CMD)
77
88
  last_stdout
78
89
  end
79
90
 
@@ -113,9 +124,11 @@ module FlashFlow
113
124
  end
114
125
 
115
126
  def unresolved_conflicts
116
- conflicted_files.map do |file|
117
- File.open(file) { |f| f.grep(/>>>>/) }.empty? ? nil : file
118
- end.compact
127
+ in_dir do
128
+ conflicted_files.map do |file|
129
+ File.open(file) { |f| f.grep(/>>>>/) }.empty? ? nil : file
130
+ end.compact
131
+ end
119
132
  end
120
133
 
121
134
  def resolutions(files)
@@ -128,10 +141,10 @@ module FlashFlow
128
141
 
129
142
  # git rerere doesn't give you a deterministic way to determine which resolution was used
130
143
  def resolution_candidates(file)
131
- @cmd_runner.run("diff -q --from-file #{file} .git/rr-cache/*/postimage")
144
+ @cmd_runner.run("diff -q --from-file #{file} .git/rr-cache/*/postimage", log: CmdRunner::LOG_CMD)
132
145
  different_files = split_diff_lines(@cmd_runner.last_stdout)
133
146
 
134
- @cmd_runner.run('ls -la .git/rr-cache/*/postimage')
147
+ @cmd_runner.run('ls -la .git/rr-cache/*/postimage', log: CmdRunner::LOG_CMD)
135
148
  all_files = split_diff_lines(@cmd_runner.last_stdout)
136
149
 
137
150
  all_files - different_files
@@ -237,14 +250,15 @@ module FlashFlow
237
250
 
238
251
  def squash_commits
239
252
  # There are three commits created by flash flow that we don't need in the message
240
- run("log #{merge_remote}/#{merge_branch}..#{merge_branch}~3")
253
+ run("log #{merge_remote}/#{merge_branch}..#{merge_branch}~3", log: CmdRunner::LOG_CMD)
241
254
  log = last_stdout
242
255
 
243
256
  # Get all the files that differ between existing acceptance and new acceptance
244
257
  run("diff --name-only #{merge_remote}/#{merge_branch} #{merge_branch}")
245
258
  files = last_stdout.split("\n")
246
259
  run("reset #{merge_remote}/#{merge_branch}")
247
- run("add #{files.map { |f| "'#{f}'" }.join(" ")}")
260
+
261
+ run("add -f #{files.map { |f| "\"#{Shellwords.escape(f)}\"" }.join(" ")}")
248
262
 
249
263
  run("commit -m '#{commit_message(log)}'")
250
264
  end