Class: Socialcast::Gitx::CLI

Inherits:
Thor
  • Object
show all
Includes:
Socialcast::Gitx, Git, Github
Defined in:
lib/socialcast-git-extensions/cli.rb

Constant Summary collapse

PULL_REQUEST_DESCRIPTION =
"\n\n" + <<-EOS.dedent
  # Use GitHub flavored Markdown http://github.github.com/github-flavored-markdown/
  # Links to screencasts or screenshots with a desciption of what this is showcasing. For architectual changes please include diagrams that will make it easier for the reviewer to understand the change. Format is ![title](url).
  # Link to ticket describing feature/bug (plantain, JIRA, bugzilla). Format is [title](url).
  # Brief description of the change, and how it accomplishes the task they set out to do.
EOS

Constants included from Socialcast::Gitx

DEFAULT_BASE_BRANCH, DEFAULT_LAST_KNOWN_GOOD_STAGING_BRANCH, DEFAULT_PROTOTYPE_BRANCH, DEFAULT_STAGING_BRANCH, VERSION

Instance Method Summary collapse

Constructor Details

#initialize(*args) ⇒ CLI

Returns a new instance of CLI.



25
26
27
28
29
# File 'lib/socialcast-git-extensions/cli.rb', line 25

def initialize(*args)
  super(*args)
  RestClient.proxy = ENV['HTTPS_PROXY'] if ENV.has_key?('HTTPS_PROXY')
  RestClient.log = Logger.new(STDOUT) if options[:trace]
end

Instance Method Details

#assignpr(*additional_reviewers) ⇒ Object



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/socialcast-git-extensions/cli.rb', line 53

def assignpr(*additional_reviewers)
  update unless @skip_update

  primary_mention = if buddy = socialcast_review_buddy(current_user)
    "assigned to @#{buddy}"
  end

  secondary_mention = nil
  if !specialty_reviewers.empty? && !options.key?('skip_additional_reviewers')
    additional_reviewers = options[:additional_reviewers] || additional_reviewers

    if additional_reviewers.empty?
      prompt_text = "#{specialty_reviewers.map { |_,v| v['command'] }.join(", ")} or (or hit enter to continue): "
      additional_reviewers = $terminal.ask("Notify additional people? #{prompt_text} ")
    end

    additional_reviewers = additional_reviewers.is_a?(String) ? additional_reviewers.split(" ") : additional_reviewers

    (specialty_reviewers.keys & additional_reviewers).each do |command|
      reviewer = specialty_reviewers[command]
      secondary_mention ||= ''
      secondary_mention += "\nAssigned additionally to @#{reviewer['socialcast_username']} for #{reviewer['label']} review"
    end
  end

  branch = current_branch
  repo = current_repo
  current_pr = current_pr_for_branch(repo, branch)
  issue_url = current_pr['issue_url']
  url = current_pr['html_url']

  assignee = github_review_buddy(current_user)
  assign_pull_request(assignee, issue_url) if assignee

  if use_pr_comments?
    issue_message = ['#reviewrequest', primary_mention, secondary_mention, "\n/cc @#{developer_group} #scgitx"].compact.join(' ')
    comment_on_issue(issue_url, issue_message)
  else
    review_message = ["#reviewrequest for #{branch} in #{current_repo}", "PR #{url} #{primary_mention}", '', current_pr['body'], '', secondary_mention, "/cc @#{developer_group} #scgitx", '', changelog_summary(branch)].compact.join("\n").gsub(/\n{2,}/, "\n\n")
    post review_message, :message_type => 'review_request'
  end
end

#backportpr(pull_request_num, maintenance_branch) ⇒ Object



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/socialcast-git-extensions/cli.rb', line 123

def backportpr(pull_request_num, maintenance_branch)
  original_base_branch = ENV['BASE_BRANCH']
  ENV['BASE_BRANCH'] = maintenance_branch
  repo = current_repo
  assignee = github_track_reviewer('Backport')
  socialcast_reviewer = socialcast_track_reviewer('Backport')

  pull_request_data = github_api_request('GET', "repos/#{repo}/pulls/#{pull_request_num}")

  commits_to_cherry_pick = []
  commit_count = pull_request_data['commits']
  commits_checked_count = 0
  page = 1

  while commits_checked_count < commit_count
    commits_data = github_api_request('GET', pull_request_data['commits_url'] + "?page=#{page}")
    commits_checked_count += commits_data.size
    raise 'Received empty commits data response! Could not pull all commits from PR' if commits_data.size == 0

    non_merge_commits_data = commits_data.select { |commit_data| commit_data['parents'].length == 1 }
    commits_to_cherry_pick += non_merge_commits_data.map { |commit| commit['sha'] }
    page += 1
  end

  backport_branch = "backport_#{pull_request_num}_to_#{maintenance_branch}"
  backport_to(backport_branch, commits_to_cherry_pick)

  maintenance_branch_url = "https://github.com/#{repo}/tree/#{maintenance_branch}"
  description = "Backport ##{pull_request_num} to #{maintenance_branch_url}\n***\n#{pull_request_data['body']}"

  pr_hash = create_pull_request(backport_branch, repo, description)
  assign_pull_request(assignee, pr_hash['issue_url']) if assignee

  reviewer_mention = "@#{socialcast_reviewer}" if socialcast_reviewer
  if use_pr_comments?
    issue_message = ['#reviewrequest backport', reviewer_mention, "/cc @#{developer_group} #scgitx"].compact.join(' ')
    comment_on_issue(pr_hash['issue_url'], issue_message)
  else
    review_message = ["#reviewrequest backport ##{pull_request_num} to #{maintenance_branch} in #{current_repo} #scgitx"]
    if socialcast_reviewer
      review_message << "/cc #{reviewer_mention} for #backport track"
    end
    review_message << "/cc @#{developer_group}"
    post review_message.join("\n\n"), :url => pr_hash['html_url'], :message_type => 'review_request'
  end
ensure
  ENV['BASE_BRANCH'] = original_base_branch
end

#branchdiff(branch = nil, other_branch = 'master') ⇒ Object



303
304
305
306
307
308
309
310
311
312
# File 'lib/socialcast-git-extensions/cli.rb', line 303

def branchdiff(branch = nil, other_branch = 'master')
  branch ||= ask "What remote branch would you like to compare against '#{other_branch}' (ex: staging)?"
  run_cmd "git fetch origin"
  results = branch_difference(branch, other_branch)
  if results.any?
    say "\nBranches in origin/#{branch} and not in origin/#{other_branch}:\n\n#{results.join("\n")}\n\n"
  else
    say "\nNo branches found in origin/#{branch} that are not also in origin/#{other_branch}\n\n"
  end
end

#cleanupObject



189
190
191
192
193
194
195
196
197
198
199
200
201
202
# File 'lib/socialcast-git-extensions/cli.rb', line 189

def cleanup
  run_cmd "git checkout #{base_branch}"
  run_cmd "git pull"
  run_cmd 'git remote prune origin'

  say "Deleting branches that have been merged into "
  say base_branch, :green
  branches(:merged => true, :remote => true).each do |branch|
    run_cmd "git push origin --delete #{branch}" unless reserved_branch?(branch)
  end
  branches(:merged => true).each do |branch|
    run_cmd "git branch -d #{branch}" unless reserved_branch?(branch)
  end
end

#createprObject



34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/socialcast-git-extensions/cli.rb', line 34

def createpr
  update unless @skip_update
  description = options[:description] || editor_input(PULL_REQUEST_DESCRIPTION)

  if use_pr_comments?
    creator_socialcast_username = Socialcast::CommandLine::Authenticate.current_user['username'] rescue nil
    description = "#{description}\n\nCreated by @#{creator_socialcast_username}" if creator_socialcast_username
  end

  branch = current_branch
  repo = current_repo
  url = create_pull_request(branch, repo, description)['html_url']
  say "Pull request created: #{url}"
end

#findpr(commit_hash) ⇒ Object



109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/socialcast-git-extensions/cli.rb', line 109

def findpr(commit_hash)
  repo = current_repo
  data = pull_requests_for_commit(repo, commit_hash)

  if data['items']
    data['items'].each do |entry|
      say "\n" << [entry['html_url'], entry['title'], "#{entry['user'] && entry['user']['login']} #{entry['created_at']}"].join("\n\t")
    end
  else
    say "No results found", :yellow
  end
end

#integrate(target_branch = prototype_branch) ⇒ Object



240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
# File 'lib/socialcast-git-extensions/cli.rb', line 240

def integrate(target_branch = prototype_branch)
  branch = current_branch

  update
  integrate_branch(branch, target_branch)
  integrate_branch(target_branch, prototype_branch) if target_branch == staging_branch
  run_cmd "git checkout #{branch}"

  current_pr = begin
    current_pr_for_branch(current_repo, current_branch)
  rescue => e
    say e.message.to_s
    nil
  end

  say("WARNING: Unable to find current pull request.  Use `git createpr` to create one.", :red) unless current_pr

  if use_pr_comments? && current_pr
    issue_message = "Integrated into #{target_branch}"
    comment_on_issue(current_pr['issue_url'], issue_message) unless options[:quiet]
  else
    message = <<-EOS.strip_heredoc
      #worklog integrating #{branch} into #{target_branch} in #{current_repo} #scgitx
      /cc @#{developer_group}
    EOS

    post message.strip
  end
end

#nuke(bad_branch) ⇒ Object



277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
# File 'lib/socialcast-git-extensions/cli.rb', line 277

def nuke(bad_branch)
  default_good_branch = "last_known_good_#{bad_branch}"
  good_branch = options[:destination] || ask("What branch do you want to reset #{bad_branch} to? (default: #{default_good_branch})")
  good_branch = default_good_branch if good_branch.length == 0
  good_branch = "last_known_good_#{good_branch}" unless good_branch.starts_with?('last_known_good_')

  removed_branches = nuke_branch(bad_branch, good_branch)
  nuke_branch("last_known_good_#{bad_branch}", good_branch)

  message = <<-EOS.strip_heredoc
    #worklog resetting #{bad_branch} branch to #{good_branch} in #{current_repo} #scgitx
    /cc @#{developer_group}
  EOS

  if removed_branches.any?
    message += <<-EOS.strip_heredoc

      The following branches were affected:
    EOS
    message += removed_branches.map{ |b| ['*', b].join(' ') }.join("\n")
  end

  post message.strip
end

#promoteObject



271
272
273
# File 'lib/socialcast-git-extensions/cli.rb', line 271

def promote
  integrate staging_branch
end

#releaseObject



315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
# File 'lib/socialcast-git-extensions/cli.rb', line 315

def release
  branch = current_branch
  assert_not_protected_branch!(branch, 'release')

  if enforce_staging_before_release?
    assert_in_last_known_good_staging(branch)
  end

  return unless yes?("Release #{branch} to production? (y/n)", :green)

  update
  run_cmd "git checkout #{base_branch}"
  run_cmd "git pull origin #{base_branch}"
  run_cmd "git pull . #{branch}"
  run_cmd "git push origin HEAD"
  integrate_branch(base_branch, staging_branch)
  integrate_branch(base_branch, prototype_branch)
  cleanup

  unless use_pr_comments?
    message = <<-EOS.strip_heredoc
      #worklog releasing #{branch} to #{base_branch} in #{current_repo} #scgitx
      /cc @#{developer_group}
    EOS

    post message.strip
  end
end

#reviewrequest(*additional_reviewers) ⇒ Object



101
102
103
104
105
106
# File 'lib/socialcast-git-extensions/cli.rb', line 101

def reviewrequest(*additional_reviewers)
  update
  @skip_update = true
  createpr
  assignpr(*additional_reviewers)
end

#shareObject



235
236
237
# File 'lib/socialcast-git-extensions/cli.rb', line 235

def share
  share_branch current_branch
end

#start(branch_name = nil) ⇒ Object



210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
# File 'lib/socialcast-git-extensions/cli.rb', line 210

def start(branch_name = nil)
  unless branch_name
    example_branch = %w{ cpr-3922-api-fix-invalid-auth red-212-desktop-cleanup-avatar-markup red-3212-share-form-add-edit-link }.sample
    repo = Rugged::Repository.new(Dir.pwd)
    remote_branches = repo.remotes.collect {|b| b.name.split('/').last }
    ## Explicitly use Highline.ask
    branch_name = $terminal.ask("What would you like to name your branch? (ex: #{example_branch})") do |q|
      q.validate = lambda { |branch| branch =~ /^[A-Za-z0-9\-_]+$/ && !remote_branches.include?(branch) }
      q.responses[:not_valid] = "This branch name is either already taken, or is not a valid branch name"
    end
  end

  run_cmd "git checkout #{base_branch}"
  run_cmd 'git pull'
  run_cmd "git checkout -b #{branch_name}"

  message = <<-EOS.strip_heredoc
    #worklog starting work on #{branch_name} in #{current_repo} #scgitx
    /cc @#{developer_group}
  EOS

  post message.strip
end

#trackObject



205
206
207
# File 'lib/socialcast-git-extensions/cli.rb', line 205

def track
  track_branch current_branch
end

#updateObject



175
176
177
178
179
180
181
182
183
184
185
186
# File 'lib/socialcast-git-extensions/cli.rb', line 175

def update
  branch = current_branch

  say 'updating '
  say "#{branch} ", :green
  say "to have most recent changes from "
  say base_branch, :green

  run_cmd "git pull origin #{branch}" rescue nil
  run_cmd "git pull origin #{base_branch}"
  run_cmd 'git push origin HEAD'
end