socialcast-git-extensions 3.1.15 → 3.1.17

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b008a4a9b2357de82f9e62a7fa5ef31fd9e705b3
4
+ data.tar.gz: b30af5a791a45d3283cc79c54ff34ca2dc5fdd88
5
+ SHA512:
6
+ metadata.gz: 9eac64d626bf695194df42d14631f7d192d1b8524571916edecdb45b7a0c245097664e65d055c9f4b39779ec885731472b63b1479c181c57fb8af03777d9bf89
7
+ data.tar.gz: 80a3e05443dc8a8264754411d32b604b37338f0294674b4e69d3096313cd628cc36889c07bcecea5aa5106f1d9f32a1bf1f43236687fadfe30721b08df061c6f
data/README.md CHANGED
@@ -86,4 +86,5 @@ reset an aggregate branch (ex: prototype, staging) back to a known good state.
86
86
 
87
87
  ## Copyright
88
88
 
89
- Copyright (c) 2010 Socialcast, Inc. See LICENSE for details.
89
+ Copyright (c) 2014 Socialcast, Inc. See LICENSE for details.
90
+
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'socialcast-git-extensions', 'cli.rb')
4
+ Socialcast::Gitx::CLI.start (['backportpr'] + ARGV)
@@ -8,6 +8,7 @@ module Socialcast
8
8
  module Gitx
9
9
  DEFAULT_BASE_BRANCH = 'master'
10
10
  DEFAULT_STAGING_BRANCH = 'staging'
11
+ DEFAULT_LAST_KNOWN_GOOD_STAGING_BRANCH = 'last_known_good_staging'
11
12
  DEFAULT_PROTOTYPE_BRANCH = 'prototype'
12
13
 
13
14
  private
@@ -33,8 +33,6 @@ module Socialcast
33
33
  method_option :skip_additional_reviewers, :type => :string, :aliases => '-s', :desc => 'Skips adding additional reviewers'
34
34
  # @see http://developer.github.com/v3/pulls/
35
35
  def reviewrequest(*additional_reviewers)
36
- token = authorization_token
37
-
38
36
  update
39
37
 
40
38
  review_mention = if buddy = socialcast_review_buddy(current_user)
@@ -63,7 +61,7 @@ module Socialcast
63
61
  description = options[:description] || editor_input(PULL_REQUEST_DESCRIPTION)
64
62
  branch = current_branch
65
63
  repo = current_repo
66
- url = create_pull_request token, branch, repo, description, assignee
64
+ url = create_pull_request branch, repo, description, assignee
67
65
  say "Pull request created: #{url}"
68
66
 
69
67
  short_description = description.split("\n").first(5).join("\n")
@@ -73,9 +71,8 @@ module Socialcast
73
71
 
74
72
  desc "findpr", "Find pull requests including a given commit"
75
73
  def findpr(commit_hash)
76
- token = authorization_token
77
74
  repo = current_repo
78
- data = pull_requests_for_commit(token, repo, commit_hash)
75
+ data = pull_requests_for_commit(repo, commit_hash)
79
76
 
80
77
  if data['items']
81
78
  data['items'].each do |entry|
@@ -84,7 +81,38 @@ module Socialcast
84
81
  else
85
82
  say "No results found", :yellow
86
83
  end
84
+ end
85
+
86
+ desc "backportpr", "Backport a pull request"
87
+ def backportpr(pull_request_num, maintenance_branch)
88
+ original_base_branch = ENV['BASE_BRANCH']
89
+ ENV['BASE_BRANCH'] = maintenance_branch
90
+ repo = current_repo
91
+ assignee = github_track_reviewer('Backport')
92
+ socialcast_reviewer = socialcast_track_reviewer('Backport')
93
+
94
+ pull_request_data = github_api_request('GET', "repos/#{repo}/pulls/#{pull_request_num}")
95
+ commits_data = github_api_request('GET', pull_request_data['commits_url'])
87
96
 
97
+ non_merge_commits_data = commits_data.select { |commit_data| commit_data['parents'].length == 1 }
98
+ shas = non_merge_commits_data.map { |commit| commit['sha'] }
99
+
100
+ backport_branch = "backport_#{pull_request_num}_to_#{maintenance_branch}"
101
+ backport_to(backport_branch, shas)
102
+
103
+ maintenance_branch_url = "https://github.com/#{repo}/tree/#{maintenance_branch}"
104
+ description = "Backport ##{pull_request_num} to #{maintenance_branch_url}\n***\n#{pull_request_data['body']}"
105
+
106
+ pull_request_url = create_pull_request(backport_branch, repo, description, assignee)
107
+
108
+ review_message = ["#reviewrequest backport ##{pull_request_num} to #{maintenance_branch} #scgitx"]
109
+ if socialcast_reviewer
110
+ review_message << "/cc @#{socialcast_reviewer} for #backport track"
111
+ end
112
+ review_message << "/cc @SocialcastDevelopers"
113
+ post review_message.join("\n\n"), :url => pull_request_url, :message_type => 'review_request'
114
+ ensure
115
+ ENV['BASE_BRANCH'] = original_base_branch
88
116
  end
89
117
 
90
118
  # TODO: use --no-edit to skip merge messages
@@ -193,6 +221,7 @@ module Socialcast
193
221
  def release
194
222
  branch = current_branch
195
223
  assert_not_protected_branch!(branch, 'release')
224
+ assert_in_last_known_good_staging(branch)
196
225
 
197
226
  return unless yes?("Release #{branch} to production? (y/n)", :green)
198
227
 
@@ -9,6 +9,13 @@ module Socialcast
9
9
  raise "Cannot #{action} reserved branch" if reserved_branch?(branch)
10
10
  end
11
11
 
12
+ def assert_in_last_known_good_staging(branch)
13
+ branches_in_last_known_staging = branches(:remote => true, :merged => last_known_good_staging_branch)
14
+ unless branches_in_last_known_staging.include? branch
15
+ raise "Cannot release #{branch} unless it has already been promoted separately to #{staging_branch} and the build has passed."
16
+ end
17
+ end
18
+
12
19
  # lookup the current branch of the PWD
13
20
  def current_branch
14
21
  repo = Grit::Repo.new(Dir.pwd)
@@ -28,6 +35,27 @@ module Socialcast
28
35
  `git config -z --global --get github.user`.strip
29
36
  end
30
37
 
38
+ def backport_to(branch, shas)
39
+ run_cmd "git checkout #{base_branch}"
40
+ run_cmd "git checkout -b #{branch}"
41
+ begin
42
+ run_cmd "git cherry-pick #{shas.join(' ')}"
43
+ rescue
44
+ while true
45
+ proceed = $terminal.ask "Error during cherry-pick. You can proceed by resolving the conflicts and using 'git cherry-pick --continue' to finish the cherry-pick in another terminal. Would you like to proceed (y/n)?"
46
+ if proceed.to_s.downcase == 'n'
47
+ run_cmd "git cherry-pick --abort"
48
+ exit 1
49
+ elsif proceed.to_s.downcase == 'y'
50
+ break
51
+ else
52
+ say "Invalid response"
53
+ end
54
+ end
55
+ end
56
+ run_cmd "git push origin HEAD"
57
+ end
58
+
31
59
  # retrieve a list of branches
32
60
  def branches(options = {})
33
61
  branches = []
@@ -185,6 +213,10 @@ module Socialcast
185
213
  config['staging_branch'] || Socialcast::Gitx::DEFAULT_STAGING_BRANCH
186
214
  end
187
215
 
216
+ def last_known_good_staging_branch
217
+ config['last_known_good_staging_branch'] || Socialcast::Gitx::DEFAULT_LAST_KNOWN_GOOD_STAGING_BRANCH
218
+ end
219
+
188
220
  def prototype_branch
189
221
  config['prototype_branch'] || Socialcast::Gitx::DEFAULT_PROTOTYPE_BRANCH
190
222
  end
@@ -32,7 +32,7 @@ module Socialcast
32
32
 
33
33
  # returns the url of the created pull request
34
34
  # @see http://developer.github.com/v3/pulls/
35
- def create_pull_request(token, branch, repo, body, assignee)
35
+ def create_pull_request(branch, repo, body, assignee)
36
36
  payload = {:title => branch, :base => base_branch, :head => branch, :body => body}.to_json
37
37
  say "Creating pull request for "
38
38
  say "#{branch} ", :green
@@ -40,41 +40,26 @@ module Socialcast
40
40
  say "#{base_branch} ", :green
41
41
  say "in "
42
42
  say repo, :green
43
- response = RestClient::Request.new(:url => "https://api.github.com/repos/#{repo}/pulls", :method => "POST", :payload => payload, :headers => {:accept => :json, :content_type => :json, 'Authorization' => "token #{token}"}).execute
44
- data = JSON.parse response.body
45
-
46
- assign_pull_request(token, branch, assignee, data) if assignee ## Unfortunately this needs to be done in a seperate request.
43
+ data = github_api_request("POST", "repos/#{repo}/pulls", payload)
44
+ assign_pull_request(branch, assignee, data) if assignee ## Unfortunately this needs to be done in a seperate request.
47
45
 
48
46
  url = data['html_url']
49
47
  url
50
- rescue RestClient::Exception => e
51
- process_error e
52
- throw e
53
48
  end
54
49
 
55
50
  # find the PRs matching the given commit hash
56
51
  # https://developer.github.com/v3/search/#search-issues
57
- def pull_requests_for_commit(token, repo, commit_hash)
52
+ def pull_requests_for_commit(repo, commit_hash)
58
53
  query = "#{commit_hash}+type:pr+repo:#{repo}"
59
54
  say "Searching github pull requests for #{commit_hash}"
60
- response = RestClient::Request.new(:url => "https://api.github.com/search/issues?q=#{query}", :method => "GET", :headers => {:accept => :json, :content_type => :json, 'Authorization' => "token #{token}"}).execute
61
- JSON.parse response.body
62
- rescue RestClient::Exception => e
63
- process_error e
64
- throw e
55
+ github_api_request "GET", "search/issues?q=#{query}"
65
56
  end
66
57
 
67
- def assign_pull_request(token, branch, assignee, data)
58
+ def assign_pull_request(branch, assignee, data)
68
59
  issue_payload = { :title => branch, :assignee => assignee }.to_json
69
- RestClient::Request.new(:url => data['issue_url'], :method => "PATCH", :payload => issue_payload, :headers => {:accept => :json, :content_type => :json, 'Authorization' => "token #{token}"}).execute
70
- rescue RestClient::Exception => e
71
- data = JSON.parse e.http_body
72
- say "Failed to assign pull request: #{data['message']}", :red
73
- end
74
-
75
- def process_error(e)
76
- data = JSON.parse e.http_body
77
- say "Failed to create pull request: #{data['message']}", :red
60
+ github_api_request "PATCH", data['issue_url'], issue_payload
61
+ rescue => e
62
+ say "Failed to assign pull request: #{e.message}", :red
78
63
  end
79
64
 
80
65
  # @returns [String] socialcast username to assign the review to
@@ -94,6 +79,43 @@ module Socialcast
94
79
  end
95
80
  end
96
81
 
82
+ # @returns [String] github username responsible for the track
83
+ # @returns [nil] when user not found
84
+ def github_track_reviewer(track)
85
+ github_username_for_socialcast_username(socialcast_track_reviewer(track))
86
+ end
87
+
88
+ # @returns [String] Socialcast username responsible for the track
89
+ # @returns [nil] when user not found
90
+ def socialcast_track_reviewer(track)
91
+ specialty_reviewers.values.each do |reviewer_hash|
92
+ return reviewer_hash['socialcast_username'] if reviewer_hash['label'].to_s.downcase == track.downcase
93
+ end
94
+ nil
95
+ end
96
+
97
+ # @returns [String] github username corresponding to the Socialcast username
98
+ # @returns [nil] when user not found
99
+ def github_username_for_socialcast_username(socialcast_username)
100
+ return if socialcast_username.nil? || socialcast_username == ""
101
+
102
+ review_buddies.each_pair do |github_username, review_buddy_hash|
103
+ return github_username if review_buddy_hash['socialcast_username'] == socialcast_username
104
+ end
105
+ end
106
+
107
+ def github_api_request(method, path, payload = nil)
108
+ url = path.include?('http') ? path : "https://api.github.com/#{path}"
109
+ JSON.parse RestClient::Request.new(:url => url, :method => method, :payload => payload, :headers => { :accept => :json, :content_type => :json, 'Authorization' => "token #{authorization_token}", :user_agent => 'socialcast-git-extensions' }).execute
110
+ rescue RestClient::Exception => e
111
+ process_error e
112
+ throw e
113
+ end
114
+
115
+ def process_error(e)
116
+ data = JSON.parse e.http_body
117
+ say "GitHub request failed: #{data['message']}", :red
118
+ end
97
119
  end
98
120
  end
99
121
  end
@@ -1,5 +1,5 @@
1
1
  module Socialcast
2
2
  module Gitx
3
- VERSION = "3.1.15"
3
+ VERSION = "3.1.17"
4
4
  end
5
5
  end
@@ -14,15 +14,14 @@ Gem::Specification.new do |s|
14
14
 
15
15
  s.rubyforge_project = "socialcast-git-extensions"
16
16
 
17
- s.add_dependency(%q<json_pure>, [">= 0"])
18
- s.add_runtime_dependency(%q<grit>, [">= 0"])
19
- s.add_runtime_dependency(%q<socialcast>, [">= 1.3.0"])
20
- s.add_runtime_dependency(%q<rest-client>, [">= 1.4.0"])
21
- s.add_runtime_dependency(%q<thor>, [">= 0"])
22
- s.add_development_dependency(%q<rake>, ["0.9.2.2"])
23
- s.add_development_dependency "rspec", '>= 2.11.0'
24
- s.add_development_dependency "pry", '>= 0'
25
- s.add_development_dependency "webmock", '>= 0'
17
+ s.add_runtime_dependency 'grit', '~> 2.5.0'
18
+ s.add_runtime_dependency 'socialcast', '~> 1.3.0'
19
+ s.add_runtime_dependency 'rest-client', '~> 1.6.7'
20
+ s.add_runtime_dependency 'thor', '~> 0.19.1'
21
+ s.add_runtime_dependency 'rake', '~> 10.3.2'
22
+ s.add_development_dependency 'rspec', '~> 3.0.0'
23
+ s.add_development_dependency 'pry', '~> 0.9.12.6'
24
+ s.add_development_dependency 'webmock', '~> 1.18.0'
26
25
 
27
26
  s.files = `git ls-files`.split("\n")
28
27
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
data/spec/cli_spec.rb CHANGED
@@ -14,10 +14,10 @@ describe Socialcast::Gitx::CLI do
14
14
  end
15
15
 
16
16
  def stub_message(message_body, params = {})
17
- json_body = { :message => { :body => message_body }.merge!(params) }
17
+ json_body = { :message => params.merge!(:body => message_body) }
18
18
 
19
19
  stub_request(:post, "https://testuser:testpassword@testdomain/api/messages.json")
20
- .with(:body => json_body)
20
+ .with(:body => json_body.to_json)
21
21
  .to_return(:status => 200, :body => '', :headers => {})
22
22
  end
23
23
 
@@ -27,23 +27,23 @@ describe Socialcast::Gitx::CLI do
27
27
  end
28
28
 
29
29
  Socialcast::Gitx::CLI.stubbed_executed_commands = []
30
- Socialcast::Gitx::CLI.any_instance.stub(:current_branch).and_return('FOO')
31
- Socialcast::Gitx::CLI.any_instance.stub(:current_user).and_return('wireframe')
32
- Socialcast::CommandLine.stub(:credentials).and_return(:domain => 'testdomain', :user => 'testuser', :password => 'testpassword', :scgitx_token => 'faketoken')
30
+ allow_any_instance_of(Socialcast::Gitx::CLI).to receive(:current_branch).and_return('FOO')
31
+ allow_any_instance_of(Socialcast::Gitx::CLI).to receive(:current_user).and_return('wireframe')
32
+ allow(Socialcast::CommandLine).to receive(:credentials).and_return(:domain => 'testdomain', :user => 'testuser', :password => 'testpassword', :scgitx_token => 'faketoken')
33
33
  end
34
34
 
35
35
  describe '#update' do
36
36
  before do
37
- Socialcast::Gitx::CLI.any_instance.should_not_receive(:post)
37
+ expect_any_instance_of(Socialcast::Gitx::CLI).not_to receive(:post)
38
38
  Socialcast::Gitx::CLI.start ['update']
39
39
  end
40
40
  it 'should not post message to socialcast' do end # see expectations
41
41
  it 'should run expected commands' do
42
- Socialcast::Gitx::CLI.stubbed_executed_commands.should == [
42
+ expect(Socialcast::Gitx::CLI.stubbed_executed_commands).to eq([
43
43
  'git pull origin FOO',
44
44
  'git pull origin master',
45
45
  'git push origin HEAD'
46
- ]
46
+ ])
47
47
  end
48
48
  end
49
49
 
@@ -56,7 +56,7 @@ describe Socialcast::Gitx::CLI do
56
56
  end
57
57
  it 'should post message to socialcast' do end # see expectations
58
58
  it 'should default to prototype' do
59
- Socialcast::Gitx::CLI.stubbed_executed_commands.should == [
59
+ expect(Socialcast::Gitx::CLI.stubbed_executed_commands).to eq([
60
60
  "git pull origin FOO",
61
61
  "git pull origin master",
62
62
  "git push origin HEAD",
@@ -67,12 +67,12 @@ describe Socialcast::Gitx::CLI do
67
67
  "git push origin HEAD",
68
68
  "git checkout FOO",
69
69
  "git checkout FOO"
70
- ]
70
+ ])
71
71
  end
72
72
  end
73
73
  context 'when target branch is ommitted with custom prototype branch' do
74
74
  before do
75
- Socialcast::Gitx::CLI.any_instance.stub(:prototype_branch).and_return('special-prototype')
75
+ allow_any_instance_of(Socialcast::Gitx::CLI).to receive(:prototype_branch).and_return('special-prototype')
76
76
 
77
77
  stub_message "#worklog integrating FOO into special-prototype #scgitx"
78
78
 
@@ -80,7 +80,7 @@ describe Socialcast::Gitx::CLI do
80
80
  end
81
81
  it 'should post message to socialcast' do end # see expectations
82
82
  it 'should default to prototype' do
83
- Socialcast::Gitx::CLI.stubbed_executed_commands.should == [
83
+ expect(Socialcast::Gitx::CLI.stubbed_executed_commands).to eq([
84
84
  "git pull origin FOO",
85
85
  "git pull origin master",
86
86
  "git push origin HEAD",
@@ -91,7 +91,7 @@ describe Socialcast::Gitx::CLI do
91
91
  "git push origin HEAD",
92
92
  "git checkout FOO",
93
93
  "git checkout FOO"
94
- ]
94
+ ])
95
95
  end
96
96
  end
97
97
  context 'when target branch == prototype' do
@@ -102,7 +102,7 @@ describe Socialcast::Gitx::CLI do
102
102
  end
103
103
  it 'should post message to socialcast' do end # see expectations
104
104
  it 'should run expected commands' do
105
- Socialcast::Gitx::CLI.stubbed_executed_commands.should == [
105
+ expect(Socialcast::Gitx::CLI.stubbed_executed_commands).to eq([
106
106
  "git pull origin FOO",
107
107
  "git pull origin master",
108
108
  "git push origin HEAD",
@@ -113,7 +113,7 @@ describe Socialcast::Gitx::CLI do
113
113
  "git push origin HEAD",
114
114
  "git checkout FOO",
115
115
  "git checkout FOO"
116
- ]
116
+ ])
117
117
  end
118
118
  end
119
119
  context 'when target branch == staging' do
@@ -124,7 +124,7 @@ describe Socialcast::Gitx::CLI do
124
124
  end
125
125
  it 'should post message to socialcast' do end # see expectations
126
126
  it 'should also integrate into prototype and run expected commands' do
127
- Socialcast::Gitx::CLI.stubbed_executed_commands.should == [
127
+ expect(Socialcast::Gitx::CLI.stubbed_executed_commands).to eq([
128
128
  "git pull origin FOO",
129
129
  "git pull origin master",
130
130
  "git push origin HEAD",
@@ -141,39 +141,44 @@ describe Socialcast::Gitx::CLI do
141
141
  "git push origin HEAD",
142
142
  "git checkout staging",
143
143
  "git checkout FOO"
144
- ]
144
+ ])
145
145
  end
146
146
  end
147
147
  context 'when target branch != staging || prototype' do
148
148
  it 'should raise an error' do
149
- lambda {
149
+ expect {
150
150
  Socialcast::Gitx::CLI.start ['integrate', 'asdfasdfasdf']
151
- }.should raise_error(/Only aggregate branches are allowed for integration/)
151
+ }.to raise_error(/Only aggregate branches are allowed for integration/)
152
152
  end
153
153
  end
154
154
  end
155
155
 
156
156
  describe '#release' do
157
+ let(:branches_in_last_known_good_staging) { ['FOO'] }
158
+ before do
159
+ expect_any_instance_of(Socialcast::Gitx::CLI).to receive(:branches).with(:remote => true, :merged => 'last_known_good_staging').and_return(branches_in_last_known_good_staging)
160
+ end
161
+
157
162
  context 'when user rejects release' do
158
163
  before do
159
- Socialcast::Gitx::CLI.any_instance.should_receive(:yes?).and_return(false)
164
+ expect_any_instance_of(Socialcast::Gitx::CLI).to receive(:yes?).and_return(false)
160
165
  Socialcast::Gitx::CLI.start ['release']
161
166
  end
162
167
  it 'should run no commands' do
163
- Socialcast::Gitx::CLI.stubbed_executed_commands.should == []
168
+ expect(Socialcast::Gitx::CLI.stubbed_executed_commands).to eq([])
164
169
  end
165
170
  end
166
171
  context 'when user confirms release' do
167
172
  before do
168
173
  stub_message "#worklog releasing FOO to master #scgitx"
169
174
 
170
- Socialcast::Gitx::CLI.any_instance.should_receive(:yes?).and_return(true)
171
- Socialcast::Gitx::CLI.any_instance.should_receive(:cleanup)
175
+ expect_any_instance_of(Socialcast::Gitx::CLI).to receive(:yes?).and_return(true)
176
+ expect_any_instance_of(Socialcast::Gitx::CLI).to receive(:cleanup)
172
177
  Socialcast::Gitx::CLI.start ['release']
173
178
  end
174
179
  it 'should post message to socialcast' do end # see expectations
175
180
  it 'should run expected commands' do
176
- Socialcast::Gitx::CLI.stubbed_executed_commands.should == [
181
+ expect(Socialcast::Gitx::CLI.stubbed_executed_commands).to eq([
177
182
  "git pull origin FOO",
178
183
  "git pull origin master",
179
184
  "git push origin HEAD",
@@ -187,20 +192,31 @@ describe Socialcast::Gitx::CLI do
187
192
  "git pull . master",
188
193
  "git push origin HEAD",
189
194
  "git checkout master"
190
- ]
195
+ ])
196
+ end
197
+ end
198
+
199
+ context 'when the branch is not in last_known_good_staging' do
200
+ let(:branches_in_last_known_good_staging) { ['another-branch'] }
201
+ before do
202
+ expect_any_instance_of(Socialcast::Gitx::CLI).not_to receive(:yes?)
203
+ end
204
+ it 'prevents the release of the branch' do
205
+ expect { Socialcast::Gitx::CLI.start ['release'] }.to raise_error(RuntimeError, 'Cannot release FOO unless it has already been promoted separately to staging and the build has passed.')
191
206
  end
192
207
  end
193
208
 
194
209
  context 'with reserved_branches via config file' do
195
210
  before do
196
211
  stub_message "#worklog releasing FOO to master #scgitx"
197
- Socialcast::Gitx::CLI.any_instance.should_receive(:yes?).and_return(true)
198
- Socialcast::Gitx::CLI.any_instance.stub(:config).and_return( { 'reserved_branches' => ['dont-del-me','dont-del-me-2'] })
212
+ expect_any_instance_of(Socialcast::Gitx::CLI).to receive(:yes?).and_return(true)
213
+ allow_any_instance_of(Socialcast::Gitx::CLI).to receive(:config).and_return( { 'reserved_branches' => ['dont-del-me','dont-del-me-2'] })
214
+ expect_any_instance_of(Socialcast::Gitx::CLI).to receive(:cleanup)
199
215
  Socialcast::Gitx::CLI.start ['release']
200
216
  end
201
217
  it "treats the alternative base branch as reserved" do
202
- Socialcast::Gitx::CLI.new.send(:reserved_branches).should include 'dont-del-me'
203
- Socialcast::Gitx::CLI.new.send(:reserved_branches).should include 'dont-del-me-2'
218
+ expect(Socialcast::Gitx::CLI.new.send(:reserved_branches)).to include 'dont-del-me'
219
+ expect(Socialcast::Gitx::CLI.new.send(:reserved_branches)).to include 'dont-del-me-2'
204
220
  end
205
221
  end
206
222
 
@@ -208,17 +224,17 @@ describe Socialcast::Gitx::CLI do
208
224
  before do
209
225
  stub_message "#worklog releasing FOO to special-master #scgitx"
210
226
 
211
- Socialcast::Gitx::CLI.any_instance.should_receive(:yes?).and_return(true)
212
- Socialcast::Gitx::CLI.any_instance.stub(:config).and_return( { 'base_branch' => 'special-master' })
213
- Socialcast::Gitx::CLI.any_instance.should_receive(:cleanup)
227
+ expect_any_instance_of(Socialcast::Gitx::CLI).to receive(:yes?).and_return(true)
228
+ allow_any_instance_of(Socialcast::Gitx::CLI).to receive(:config).and_return( { 'base_branch' => 'special-master' })
229
+ expect_any_instance_of(Socialcast::Gitx::CLI).to receive(:cleanup)
214
230
  Socialcast::Gitx::CLI.start ['release']
215
231
  end
216
232
  it 'should post message to socialcast' do end # see expectations
217
233
  it "treats the alternative base branch as reserved" do
218
- Socialcast::Gitx::CLI.new.send(:reserved_branches).should include 'special-master'
234
+ expect(Socialcast::Gitx::CLI.new.send(:reserved_branches)).to include 'special-master'
219
235
  end
220
236
  it 'should run expected commands' do
221
- Socialcast::Gitx::CLI.stubbed_executed_commands.should == [
237
+ expect(Socialcast::Gitx::CLI.stubbed_executed_commands).to eq([
222
238
  "git pull origin FOO",
223
239
  "git pull origin special-master",
224
240
  "git push origin HEAD",
@@ -232,7 +248,7 @@ describe Socialcast::Gitx::CLI do
232
248
  "git pull . special-master",
233
249
  "git push origin HEAD",
234
250
  "git checkout special-master"
235
- ]
251
+ ])
236
252
  end
237
253
  end
238
254
 
@@ -240,9 +256,9 @@ describe Socialcast::Gitx::CLI do
240
256
  before do
241
257
  stub_message "#worklog releasing FOO to special-master #scgitx"
242
258
 
243
- Socialcast::Gitx::CLI.any_instance.should_receive(:yes?).and_return(true)
244
- Socialcast::Gitx::CLI.any_instance.stub(:config).and_return({})
245
- Socialcast::Gitx::CLI.any_instance.should_receive(:cleanup)
259
+ expect_any_instance_of(Socialcast::Gitx::CLI).to receive(:yes?).and_return(true)
260
+ allow_any_instance_of(Socialcast::Gitx::CLI).to receive(:config).and_return({})
261
+ expect_any_instance_of(Socialcast::Gitx::CLI).to receive(:cleanup)
246
262
  ENV['BASE_BRANCH'] = 'special-master'
247
263
  Socialcast::Gitx::CLI.start ['release']
248
264
  end
@@ -250,11 +266,11 @@ describe Socialcast::Gitx::CLI do
250
266
  ENV.delete('BASE_BRANCH')
251
267
  end
252
268
  it "treats the alternative base branch as reserved" do
253
- Socialcast::Gitx::CLI.new.send(:reserved_branches).should include 'special-master'
269
+ expect(Socialcast::Gitx::CLI.new.send(:reserved_branches)).to include 'special-master'
254
270
  end
255
271
  it 'should post message to socialcast' do end # see expectations
256
272
  it 'should run expected commands' do
257
- Socialcast::Gitx::CLI.stubbed_executed_commands.should == [
273
+ expect(Socialcast::Gitx::CLI.stubbed_executed_commands).to eq([
258
274
  "git pull origin FOO",
259
275
  "git pull origin special-master",
260
276
  "git push origin HEAD",
@@ -268,7 +284,7 @@ describe Socialcast::Gitx::CLI do
268
284
  "git pull . special-master",
269
285
  "git push origin HEAD",
270
286
  "git checkout special-master"
271
- ]
287
+ ])
272
288
  end
273
289
  end
274
290
 
@@ -276,9 +292,9 @@ describe Socialcast::Gitx::CLI do
276
292
  before do
277
293
  stub_message "#worklog releasing FOO to special-master #scgitx"
278
294
 
279
- Socialcast::Gitx::CLI.any_instance.should_receive(:yes?).and_return(true)
280
- Socialcast::Gitx::CLI.any_instance.stub(:config).and_return({ 'base_branch' => 'extra-special-master' })
281
- Socialcast::Gitx::CLI.any_instance.should_receive(:cleanup)
295
+ expect_any_instance_of(Socialcast::Gitx::CLI).to receive(:yes?).and_return(true)
296
+ allow_any_instance_of(Socialcast::Gitx::CLI).to receive(:config).and_return({ 'base_branch' => 'extra-special-master' })
297
+ expect_any_instance_of(Socialcast::Gitx::CLI).to receive(:cleanup)
282
298
  ENV['BASE_BRANCH'] = 'special-master'
283
299
  Socialcast::Gitx::CLI.start ['release']
284
300
  end
@@ -286,12 +302,12 @@ describe Socialcast::Gitx::CLI do
286
302
  ENV.delete('BASE_BRANCH')
287
303
  end
288
304
  it "treats the alternative base branch as reserved" do
289
- Socialcast::Gitx::CLI.new.send(:reserved_branches).should include 'special-master'
290
- Socialcast::Gitx::CLI.new.send(:reserved_branches).should include 'extra-special-master'
305
+ expect(Socialcast::Gitx::CLI.new.send(:reserved_branches)).to include 'special-master'
306
+ expect(Socialcast::Gitx::CLI.new.send(:reserved_branches)).to include 'extra-special-master'
291
307
  end
292
308
  it 'should post message to socialcast' do end # see expectations
293
309
  it 'should run expected commands' do
294
- Socialcast::Gitx::CLI.stubbed_executed_commands.should == [
310
+ expect(Socialcast::Gitx::CLI.stubbed_executed_commands).to eq([
295
311
  "git pull origin FOO",
296
312
  "git pull origin special-master",
297
313
  "git push origin HEAD",
@@ -305,7 +321,7 @@ describe Socialcast::Gitx::CLI do
305
321
  "git pull . special-master",
306
322
  "git push origin HEAD",
307
323
  "git checkout special-master"
308
- ]
324
+ ])
309
325
  end
310
326
  end
311
327
  end
@@ -315,13 +331,13 @@ describe Socialcast::Gitx::CLI do
315
331
  before do
316
332
  prototype_branches = %w( dev-foo dev-bar )
317
333
  master_branches = %w( dev-foo )
318
- Socialcast::Gitx::CLI.any_instance.should_receive(:branches).and_return(prototype_branches, master_branches, prototype_branches, master_branches)
334
+ expect_any_instance_of(Socialcast::Gitx::CLI).to receive(:branches).and_return(prototype_branches, master_branches, prototype_branches, master_branches)
319
335
  stub_message "#worklog resetting prototype branch to last_known_good_master #scgitx\n/cc @SocialcastDevelopers\n\nthe following branches were affected:\n* dev-bar"
320
336
  Socialcast::Gitx::CLI.start ['nuke', 'prototype', '--destination', 'master']
321
337
  end
322
338
  it 'should publish message into socialcast' do end # see expectations
323
339
  it 'should run expected commands' do
324
- Socialcast::Gitx::CLI.stubbed_executed_commands.should == [
340
+ expect(Socialcast::Gitx::CLI.stubbed_executed_commands).to eq([
325
341
  "git checkout master",
326
342
  "git branch -D last_known_good_master",
327
343
  "git fetch origin",
@@ -342,7 +358,7 @@ describe Socialcast::Gitx::CLI do
342
358
  "git push origin last_known_good_prototype",
343
359
  "git branch --set-upstream last_known_good_prototype origin/last_known_good_prototype",
344
360
  "git checkout master"
345
- ]
361
+ ])
346
362
  end
347
363
  end
348
364
  context 'when target branch == staging and --destination == last_known_good_staging' do
@@ -352,7 +368,7 @@ describe Socialcast::Gitx::CLI do
352
368
  Socialcast::Gitx::CLI.start ['nuke', 'staging', '--destination', 'last_known_good_staging']
353
369
  end
354
370
  it 'should run expected commands' do
355
- Socialcast::Gitx::CLI.stubbed_executed_commands.should == [
371
+ expect(Socialcast::Gitx::CLI.stubbed_executed_commands).to eq([
356
372
  "git checkout master",
357
373
  "git branch -D last_known_good_staging",
358
374
  "git fetch origin",
@@ -363,18 +379,18 @@ describe Socialcast::Gitx::CLI do
363
379
  "git push origin staging",
364
380
  "git branch --set-upstream staging origin/staging",
365
381
  "git checkout master"
366
- ]
382
+ ])
367
383
  end
368
384
  end
369
385
  context 'when target branch == prototype and destination prompt == nil' do
370
386
  before do
371
387
  stub_message "#worklog resetting prototype branch to last_known_good_prototype #scgitx\n/cc @SocialcastDevelopers"
372
388
 
373
- Socialcast::Gitx::CLI.any_instance.should_receive(:ask).and_return('')
389
+ expect_any_instance_of(Socialcast::Gitx::CLI).to receive(:ask).and_return('')
374
390
  Socialcast::Gitx::CLI.start ['nuke', 'prototype']
375
391
  end
376
392
  it 'defaults to last_known_good_prototype and should run expected commands' do
377
- Socialcast::Gitx::CLI.stubbed_executed_commands.should == [
393
+ expect(Socialcast::Gitx::CLI.stubbed_executed_commands).to eq([
378
394
  "git checkout master",
379
395
  "git branch -D last_known_good_prototype",
380
396
  "git fetch origin",
@@ -385,18 +401,18 @@ describe Socialcast::Gitx::CLI do
385
401
  "git push origin prototype",
386
402
  "git branch --set-upstream prototype origin/prototype",
387
403
  "git checkout master"
388
- ]
404
+ ])
389
405
  end
390
406
  end
391
407
  context 'when target branch == prototype and destination prompt = master' do
392
408
  before do
393
409
  stub_message "#worklog resetting prototype branch to last_known_good_master #scgitx\n/cc @SocialcastDevelopers"
394
410
 
395
- Socialcast::Gitx::CLI.any_instance.should_receive(:ask).and_return('master')
411
+ expect_any_instance_of(Socialcast::Gitx::CLI).to receive(:ask).and_return('master')
396
412
  Socialcast::Gitx::CLI.start ['nuke', 'prototype']
397
413
  end
398
414
  it 'should run expected commands' do
399
- Socialcast::Gitx::CLI.stubbed_executed_commands.should == [
415
+ expect(Socialcast::Gitx::CLI.stubbed_executed_commands).to eq([
400
416
  "git checkout master",
401
417
  "git branch -D last_known_good_master",
402
418
  "git fetch origin",
@@ -417,19 +433,415 @@ describe Socialcast::Gitx::CLI do
417
433
  "git push origin last_known_good_prototype",
418
434
  "git branch --set-upstream last_known_good_prototype origin/last_known_good_prototype",
419
435
  "git checkout master"
420
- ]
436
+ ])
421
437
  end
422
438
  end
423
439
  context 'when target branch != staging || prototype' do
424
440
  it 'should raise error' do
425
- lambda {
426
- Socialcast::Gitx::CLI.any_instance.should_receive(:ask).and_return('master')
441
+ expect {
442
+ expect_any_instance_of(Socialcast::Gitx::CLI).to receive(:ask).and_return('master')
427
443
  Socialcast::Gitx::CLI.start ['nuke', 'asdfasdf']
428
- }.should raise_error(/Only aggregate branches are allowed to be reset/)
444
+ }.to raise_error(/Only aggregate branches are allowed to be reset/)
429
445
  end
430
446
  end
431
447
  end
432
448
 
449
+ describe '#backportpr' do
450
+ before do
451
+ # https://developer.github.com/v3/search/#search-issues
452
+ pr_response = {
453
+ "url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/pulls/59",
454
+ "id" => 13712197,
455
+ "html_url" => "https://github.com/socialcast/socialcast-git-extensions/pull/59",
456
+ "diff_url" => "https://github.com/socialcast/socialcast-git-extensions/pull/59.diff",
457
+ "patch_url" => "https://github.com/socialcast/socialcast-git-extensions/pull/59.patch",
458
+ "issue_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/issues/59",
459
+ "number" => 59,
460
+ "state" => "closed",
461
+ "title" => "additional-notifications",
462
+ "user" => {
463
+ "login" => "MikeSilvis",
464
+ "id" => 152323,
465
+ "avatar_url" => "https://avatars.githubusercontent.com/u/152323?",
466
+ "gravatar_id" => "1bb5f2e12dcbfb8c103689f4ae94f431",
467
+ "url" => "https://api.github.com/users/MikeSilvis",
468
+ "html_url" => "https://github.com/MikeSilvis",
469
+ "followers_url" => "https://api.github.com/users/MikeSilvis/followers",
470
+ "following_url" => "https://api.github.com/users/MikeSilvis/following{/other_user}",
471
+ "gists_url" => "https://api.github.com/users/MikeSilvis/gists{/gist_id}",
472
+ "starred_url" => "https://api.github.com/users/MikeSilvis/starred{/owner}{/repo}",
473
+ "subscriptions_url" => "https://api.github.com/users/MikeSilvis/subscriptions",
474
+ "organizations_url" => "https://api.github.com/users/MikeSilvis/orgs",
475
+ "repos_url" => "https://api.github.com/users/MikeSilvis/repos",
476
+ "events_url" => "https://api.github.com/users/MikeSilvis/events{/privacy}",
477
+ "received_events_url" => "https://api.github.com/users/MikeSilvis/received_events",
478
+ "type" => "User",
479
+ "site_admin" => false
480
+ },
481
+ "body" => "simply testing this out",
482
+ "created_at" => "2014-03-18T22:39:37Z",
483
+ "updated_at" => "2014-03-18T22:40:18Z",
484
+ "closed_at" => "2014-03-18T22:39:46Z",
485
+ "merged_at" => nil,
486
+ "merge_commit_sha" => "f73009f4eb245c84da90e8abf9be846c58bc1e3b",
487
+ "assignee" => nil,
488
+ "milestone" => nil,
489
+ "commits_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/pulls/59/commits",
490
+ "review_comments_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/pulls/59/comments",
491
+ "review_comment_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/pulls/comments/{number}",
492
+ "comments_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/issues/59/comments",
493
+ "statuses_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/statuses/5e30d5af3f4d1bb3a34cc97568299be028b65f6f",
494
+ "head" => {
495
+ "label" => "socialcast:additional-notifications",
496
+ "ref" => "additional-notifications",
497
+ "sha" => "5e30d5af3f4d1bb3a34cc97568299be028b65f6f",
498
+ "user" => {
499
+ "login" => "socialcast",
500
+ "id" => 57931,
501
+ "avatar_url" => "https://avatars.githubusercontent.com/u/57931?",
502
+ "gravatar_id" => "489ec347da22410e9770ea022e6e2038",
503
+ "url" => "https://api.github.com/users/socialcast",
504
+ "html_url" => "https://github.com/socialcast",
505
+ "followers_url" => "https://api.github.com/users/socialcast/followers",
506
+ "following_url" => "https://api.github.com/users/socialcast/following{/other_user}",
507
+ "gists_url" => "https://api.github.com/users/socialcast/gists{/gist_id}",
508
+ "starred_url" => "https://api.github.com/users/socialcast/starred{/owner}{/repo}",
509
+ "subscriptions_url" => "https://api.github.com/users/socialcast/subscriptions",
510
+ "organizations_url" => "https://api.github.com/users/socialcast/orgs",
511
+ "repos_url" => "https://api.github.com/users/socialcast/repos",
512
+ "events_url" => "https://api.github.com/users/socialcast/events{/privacy}",
513
+ "received_events_url" => "https://api.github.com/users/socialcast/received_events",
514
+ "type" => "Organization",
515
+ "site_admin" => false
516
+ },
517
+ "repo" => {
518
+ "id" => 1000634,
519
+ "name" => "socialcast-git-extensions",
520
+ "full_name" => "socialcast/socialcast-git-extensions",
521
+ "owner" => {
522
+ "login" => "socialcast",
523
+ "id" => 57931,
524
+ "avatar_url" => "https://avatars.githubusercontent.com/u/57931?",
525
+ "gravatar_id" => "489ec347da22410e9770ea022e6e2038",
526
+ "url" => "https://api.github.com/users/socialcast",
527
+ "html_url" => "https://github.com/socialcast",
528
+ "followers_url" => "https://api.github.com/users/socialcast/followers",
529
+ "following_url" => "https://api.github.com/users/socialcast/following{/other_user}",
530
+ "gists_url" => "https://api.github.com/users/socialcast/gists{/gist_id}",
531
+ "starred_url" => "https://api.github.com/users/socialcast/starred{/owner}{/repo}",
532
+ "subscriptions_url" => "https://api.github.com/users/socialcast/subscriptions",
533
+ "organizations_url" => "https://api.github.com/users/socialcast/orgs",
534
+ "repos_url" => "https://api.github.com/users/socialcast/repos",
535
+ "events_url" => "https://api.github.com/users/socialcast/events{/privacy}",
536
+ "received_events_url" => "https://api.github.com/users/socialcast/received_events",
537
+ "type" => "Organization",
538
+ "site_admin" => false
539
+ },
540
+ "private" => false,
541
+ "html_url" => "https://github.com/socialcast/socialcast-git-extensions",
542
+ "description" => "",
543
+ "fork" => false,
544
+ "url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions",
545
+ "forks_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/forks",
546
+ "keys_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/keys{/key_id}",
547
+ "collaborators_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/collaborators{/collaborator}",
548
+ "teams_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/teams",
549
+ "hooks_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/hooks",
550
+ "issue_events_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/issues/events{/number}",
551
+ "events_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/events",
552
+ "assignees_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/assignees{/user}",
553
+ "branches_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/branches{/branch}",
554
+ "tags_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/tags",
555
+ "blobs_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/git/blobs{/sha}",
556
+ "git_tags_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/git/tags{/sha}",
557
+ "git_refs_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/git/refs{/sha}",
558
+ "trees_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/git/trees{/sha}",
559
+ "statuses_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/statuses/{sha}",
560
+ "languages_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/languages",
561
+ "stargazers_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/stargazers",
562
+ "contributors_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/contributors",
563
+ "subscribers_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/subscribers",
564
+ "subscription_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/subscription",
565
+ "commits_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/commits{/sha}",
566
+ "git_commits_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/git/commits{/sha}",
567
+ "comments_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/comments{/number}",
568
+ "issue_comment_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/issues/comments/{number}",
569
+ "contents_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/contents/{+path}",
570
+ "compare_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/compare/{base}...{head}",
571
+ "merges_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/merges",
572
+ "archive_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/{archive_format}{/ref}",
573
+ "downloads_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/downloads",
574
+ "issues_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/issues{/number}",
575
+ "pulls_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/pulls{/number}",
576
+ "milestones_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/milestones{/number}",
577
+ "notifications_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/notifications{?since,all,participating}",
578
+ "labels_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/labels{/name}",
579
+ "releases_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/releases{/id}",
580
+ "created_at" => "2010-10-18T21:23:25Z",
581
+ "updated_at" => "2014-05-12T20:03:30Z",
582
+ "pushed_at" => "2014-05-12T20:03:31Z",
583
+ "git_url" => "git://github.com/socialcast/socialcast-git-extensions.git",
584
+ "ssh_url" => "git@github.com:socialcast/socialcast-git-extensions.git",
585
+ "clone_url" => "https://github.com/socialcast/socialcast-git-extensions.git",
586
+ "svn_url" => "https://github.com/socialcast/socialcast-git-extensions",
587
+ "homepage" => "",
588
+ "size" => 1719,
589
+ "stargazers_count" => 3,
590
+ "watchers_count" => 3,
591
+ "language" => "Ruby",
592
+ "has_issues" => true,
593
+ "has_downloads" => true,
594
+ "has_wiki" => true,
595
+ "forks_count" => 6,
596
+ "mirror_url" => nil,
597
+ "open_issues_count" => 13,
598
+ "forks" => 6,
599
+ "open_issues" => 13,
600
+ "watchers" => 3,
601
+ "default_branch" => "master"
602
+ }
603
+ },
604
+ "base" => {
605
+ "label" => "socialcast:master",
606
+ "ref" => "master",
607
+ "sha" => "1baae2de301c43d44297647f3f9c1e06697748ad",
608
+ "user" => {
609
+ "login" => "socialcast",
610
+ "id" => 57931,
611
+ "avatar_url" => "https://avatars.githubusercontent.com/u/57931?",
612
+ "gravatar_id" => "489ec347da22410e9770ea022e6e2038",
613
+ "url" => "https://api.github.com/users/socialcast",
614
+ "html_url" => "https://github.com/socialcast",
615
+ "followers_url" => "https://api.github.com/users/socialcast/followers",
616
+ "following_url" => "https://api.github.com/users/socialcast/following{/other_user}",
617
+ "gists_url" => "https://api.github.com/users/socialcast/gists{/gist_id}",
618
+ "starred_url" => "https://api.github.com/users/socialcast/starred{/owner}{/repo}",
619
+ "subscriptions_url" => "https://api.github.com/users/socialcast/subscriptions",
620
+ "organizations_url" => "https://api.github.com/users/socialcast/orgs",
621
+ "repos_url" => "https://api.github.com/users/socialcast/repos",
622
+ "events_url" => "https://api.github.com/users/socialcast/events{/privacy}",
623
+ "received_events_url" => "https://api.github.com/users/socialcast/received_events",
624
+ "type" => "Organization",
625
+ "site_admin" => false
626
+ },
627
+ "repo" => {
628
+ "id" => 1000634,
629
+ "name" => "socialcast-git-extensions",
630
+ "full_name" => "socialcast/socialcast-git-extensions",
631
+ "owner" => {
632
+ "login" => "socialcast",
633
+ "id" => 57931,
634
+ "avatar_url" => "https://avatars.githubusercontent.com/u/57931?",
635
+ "gravatar_id" => "489ec347da22410e9770ea022e6e2038",
636
+ "url" => "https://api.github.com/users/socialcast",
637
+ "html_url" => "https://github.com/socialcast",
638
+ "followers_url" => "https://api.github.com/users/socialcast/followers",
639
+ "following_url" => "https://api.github.com/users/socialcast/following{/other_user}",
640
+ "gists_url" => "https://api.github.com/users/socialcast/gists{/gist_id}",
641
+ "starred_url" => "https://api.github.com/users/socialcast/starred{/owner}{/repo}",
642
+ "subscriptions_url" => "https://api.github.com/users/socialcast/subscriptions",
643
+ "organizations_url" => "https://api.github.com/users/socialcast/orgs",
644
+ "repos_url" => "https://api.github.com/users/socialcast/repos",
645
+ "events_url" => "https://api.github.com/users/socialcast/events{/privacy}",
646
+ "received_events_url" => "https://api.github.com/users/socialcast/received_events",
647
+ "type" => "Organization",
648
+ "site_admin" => false
649
+ },
650
+ "private" => false,
651
+ "html_url" => "https://github.com/socialcast/socialcast-git-extensions",
652
+ "description" => "",
653
+ "fork" => false,
654
+ "url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions",
655
+ "forks_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/forks",
656
+ "keys_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/keys{/key_id}",
657
+ "collaborators_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/collaborators{/collaborator}",
658
+ "teams_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/teams",
659
+ "hooks_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/hooks",
660
+ "issue_events_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/issues/events{/number}",
661
+ "events_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/events",
662
+ "assignees_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/assignees{/user}",
663
+ "branches_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/branches{/branch}",
664
+ "tags_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/tags",
665
+ "blobs_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/git/blobs{/sha}",
666
+ "git_tags_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/git/tags{/sha}",
667
+ "git_refs_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/git/refs{/sha}",
668
+ "trees_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/git/trees{/sha}",
669
+ "statuses_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/statuses/{sha}",
670
+ "languages_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/languages",
671
+ "stargazers_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/stargazers",
672
+ "contributors_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/contributors",
673
+ "subscribers_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/subscribers",
674
+ "subscription_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/subscription",
675
+ "commits_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/commits{/sha}",
676
+ "git_commits_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/git/commits{/sha}",
677
+ "comments_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/comments{/number}",
678
+ "issue_comment_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/issues/comments/{number}",
679
+ "contents_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/contents/{+path}",
680
+ "compare_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/compare/{base}...{head}",
681
+ "merges_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/merges",
682
+ "archive_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/{archive_format}{/ref}",
683
+ "downloads_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/downloads",
684
+ "issues_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/issues{/number}",
685
+ "pulls_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/pulls{/number}",
686
+ "milestones_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/milestones{/number}",
687
+ "notifications_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/notifications{?since,all,participating}",
688
+ "labels_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/labels{/name}",
689
+ "releases_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/releases{/id}",
690
+ "created_at" => "2010-10-18T21:23:25Z",
691
+ "updated_at" => "2014-05-12T20:03:30Z",
692
+ "pushed_at" => "2014-05-12T20:03:31Z",
693
+ "git_url" => "git://github.com/socialcast/socialcast-git-extensions.git",
694
+ "ssh_url" => "git@github.com:socialcast/socialcast-git-extensions.git",
695
+ "clone_url" => "https://github.com/socialcast/socialcast-git-extensions.git",
696
+ "svn_url" => "https://github.com/socialcast/socialcast-git-extensions",
697
+ "homepage" => "",
698
+ "size" => 1719,
699
+ "stargazers_count" => 3,
700
+ "watchers_count" => 3,
701
+ "language" => "Ruby",
702
+ "has_issues" => true,
703
+ "has_downloads" => true,
704
+ "has_wiki" => true,
705
+ "forks_count" => 6,
706
+ "mirror_url" => nil,
707
+ "open_issues_count" => 13,
708
+ "forks" => 6,
709
+ "open_issues" => 13,
710
+ "watchers" => 3,
711
+ "default_branch" => "master"
712
+ }
713
+ },
714
+ "_links" => {
715
+ "self" => {
716
+ "href" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/pulls/59"
717
+ },
718
+ "html" => {
719
+ "href" => "https://github.com/socialcast/socialcast-git-extensions/pull/59"
720
+ },
721
+ "issue" => {
722
+ "href" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/issues/59"
723
+ },
724
+ "comments" => {
725
+ "href" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/issues/59/comments"
726
+ },
727
+ "review_comments" => {
728
+ "href" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/pulls/59/comments"
729
+ },
730
+ "review_comment" => {
731
+ "href" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/pulls/comments/{number}"
732
+ },
733
+ "commits" => {
734
+ "href" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/pulls/59/commits"
735
+ },
736
+ "statuses" => {
737
+ "href" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/statuses/5e30d5af3f4d1bb3a34cc97568299be028b65f6f"
738
+ }
739
+ },
740
+ "merged" => false,
741
+ "mergeable" => true,
742
+ "mergeable_state" => "unstable",
743
+ "merged_by" => nil,
744
+ "comments" => 0,
745
+ "review_comments" => 0,
746
+ "commits" => 1,
747
+ "additions" => 14,
748
+ "deletions" => 2,
749
+ "changed_files" => 2
750
+ }
751
+
752
+ commits_response = [
753
+ {
754
+ "sha" => "5e30d5af3f4d1bb3a34cc97568299be028b65f6f",
755
+ "commit" => {
756
+ "author" => {
757
+ "name" => "Mike Silvis",
758
+ "email" => "mikesilvis@gmail.com",
759
+ "date" => "2014-03-18T22:39:12Z"
760
+ },
761
+ "committer" => {
762
+ "name" => "Mike Silvis",
763
+ "email" => "mikesilvis@gmail.com",
764
+ "date" => "2014-03-18T22:39:12Z"
765
+ },
766
+ "message" => "adding the ability to specify additional reviewers",
767
+ "tree" => {
768
+ "sha" => "dcf05deb22223997a5184cd3a1866249f3e73e3b",
769
+ "url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/git/trees/dcf05deb22223997a5184cd3a1866249f3e73e3b"
770
+ },
771
+ "url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/git/commits/5e30d5af3f4d1bb3a34cc97568299be028b65f6f",
772
+ "comment_count" => 0
773
+ },
774
+ "url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/commits/5e30d5af3f4d1bb3a34cc97568299be028b65f6f",
775
+ "html_url" => "https://github.com/socialcast/socialcast-git-extensions/commit/5e30d5af3f4d1bb3a34cc97568299be028b65f6f",
776
+ "comments_url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/commits/5e30d5af3f4d1bb3a34cc97568299be028b65f6f/comments",
777
+ "author" => {
778
+ "login" => "MikeSilvis",
779
+ "id" => 152323,
780
+ "avatar_url" => "https://avatars.githubusercontent.com/u/152323?",
781
+ "gravatar_id" => "1bb5f2e12dcbfb8c103689f4ae94f431",
782
+ "url" => "https://api.github.com/users/MikeSilvis",
783
+ "html_url" => "https://github.com/MikeSilvis",
784
+ "followers_url" => "https://api.github.com/users/MikeSilvis/followers",
785
+ "following_url" => "https://api.github.com/users/MikeSilvis/following{/other_user}",
786
+ "gists_url" => "https://api.github.com/users/MikeSilvis/gists{/gist_id}",
787
+ "starred_url" => "https://api.github.com/users/MikeSilvis/starred{/owner}{/repo}",
788
+ "subscriptions_url" => "https://api.github.com/users/MikeSilvis/subscriptions",
789
+ "organizations_url" => "https://api.github.com/users/MikeSilvis/orgs",
790
+ "repos_url" => "https://api.github.com/users/MikeSilvis/repos",
791
+ "events_url" => "https://api.github.com/users/MikeSilvis/events{/privacy}",
792
+ "received_events_url" => "https://api.github.com/users/MikeSilvis/received_events",
793
+ "type" => "User",
794
+ "site_admin" => false
795
+ },
796
+ "committer" => {
797
+ "login" => "MikeSilvis",
798
+ "id" => 152323,
799
+ "avatar_url" => "https://avatars.githubusercontent.com/u/152323?",
800
+ "gravatar_id" => "1bb5f2e12dcbfb8c103689f4ae94f431",
801
+ "url" => "https://api.github.com/users/MikeSilvis",
802
+ "html_url" => "https://github.com/MikeSilvis",
803
+ "followers_url" => "https://api.github.com/users/MikeSilvis/followers",
804
+ "following_url" => "https://api.github.com/users/MikeSilvis/following{/other_user}",
805
+ "gists_url" => "https://api.github.com/users/MikeSilvis/gists{/gist_id}",
806
+ "starred_url" => "https://api.github.com/users/MikeSilvis/starred{/owner}{/repo}",
807
+ "subscriptions_url" => "https://api.github.com/users/MikeSilvis/subscriptions",
808
+ "organizations_url" => "https://api.github.com/users/MikeSilvis/orgs",
809
+ "repos_url" => "https://api.github.com/users/MikeSilvis/repos",
810
+ "events_url" => "https://api.github.com/users/MikeSilvis/events{/privacy}",
811
+ "received_events_url" => "https://api.github.com/users/MikeSilvis/received_events",
812
+ "type" => "User",
813
+ "site_admin" => false
814
+ },
815
+ "parents" => [
816
+ {
817
+ "sha" => "1baae2de301c43d44297647f3f9c1e06697748ad",
818
+ "url" => "https://api.github.com/repos/socialcast/socialcast-git-extensions/commits/1baae2de301c43d44297647f3f9c1e06697748ad",
819
+ "html_url" => "https://github.com/socialcast/socialcast-git-extensions/commit/1baae2de301c43d44297647f3f9c1e06697748ad"
820
+ }
821
+ ]
822
+ }
823
+ ]
824
+
825
+ stub_request(:get, "https://api.github.com/repos/socialcast/socialcast-git-extensions/pulls/59").
826
+ with(:headers => { 'Accept' => 'application/json', 'Accept-Encoding' => 'gzip, deflate', 'Authorization' => /token\s\w+/, 'Content-Type' => 'application/json', 'User-Agent' => 'socialcast-git-extensions' }).
827
+ to_return(:status => 200, :body => pr_response.to_json, :headers => {})
828
+ stub_request(:get, "https://api.github.com/repos/socialcast/socialcast-git-extensions/pulls/59/commits").
829
+ with(:headers => { 'Accept' => 'application/json', 'Accept-Encoding' => 'gzip, deflate', 'Authorization' => /token\s\w+/, 'Content-Type' => 'application/json', 'User-Agent' => 'socialcast-git-extensions' }).
830
+ to_return(:status => 200, :body => commits_response.to_json, :headers => {})
831
+ stub_request(:post, "https://api.github.com/repos/socialcast/socialcast-git-extensions/pulls").
832
+ with(:body => "{\"title\":\"backport_59_to_v1.x\",\"base\":\"v1.x\",\"head\":\"backport_59_to_v1.x\",\"body\":\"Backport #59 to https://github.com/socialcast/socialcast-git-extensions/tree/v1.x\\n***\\nsimply testing this out\"}",
833
+ :headers => { 'Accept' => 'application/json', 'Accept-Encoding' => 'gzip, deflate', 'Authorization' => /token\s\w+/, 'Content-Type' => 'application/json', 'User-Agent'=>'socialcast-git-extensions' }).
834
+ to_return(:status => 200, :body => '{"html_url": "https://github.com/socialcast/socialcast-git-extensions/pulls/60"}', :headers => { 'Content-Type' => 'application/json' })
835
+
836
+ stub_message "#reviewrequest backport #59 to v1.x #scgitx\n\n/cc @SocialcastDevelopers", :url => 'https://github.com/socialcast/socialcast-git-extensions/pulls/60', :message_type => 'review_request'
837
+
838
+ expect_any_instance_of(Socialcast::Gitx::CLI).to receive(:backportpr).and_call_original
839
+ expect_any_instance_of(Socialcast::Gitx::CLI).to receive(:post).with("#reviewrequest backport #59 to v1.x #scgitx\n\n/cc @SocialcastDevelopers", { :url => "https://github.com/socialcast/socialcast-git-extensions/pulls/60", :message_type => "review_request" })
840
+ Socialcast::Gitx::CLI.start ['backportpr', '59', 'v1.x']
841
+ end
842
+ it 'creates a branch based on v1.x and cherry-picks in PR 59' do end
843
+ end
844
+
433
845
  describe '#findpr' do
434
846
  before do
435
847
  # https://developer.github.com/v3/search/#search-issues
@@ -485,42 +897,38 @@ describe Socialcast::Gitx::CLI do
485
897
  }
486
898
 
487
899
  stub_request(:get, "https://api.github.com/search/issues?q=abc123%20type:pr%20repo:socialcast/socialcast-git-extensions").
488
- with(:headers => {'Accept'=>'application/json', 'Accept-Encoding'=>'gzip, deflate', 'Authorization'=>/token\s\w+/, 'Content-Type'=>'application/json', 'User-Agent'=>'Ruby'}).
900
+ with(:headers => { 'Accept' => 'application/json', 'Accept-Encoding' => 'gzip, deflate', 'Authorization' => /token\s\w+/, 'Content-Type' => 'application/json', 'User-Agent' => 'socialcast-git-extensions'}).
489
901
  to_return(:status => 200, :body => stub_response.to_json, :headers => {})
490
- Socialcast::Gitx::CLI.any_instance.should_receive(:findpr).and_call_original
491
- Socialcast::Gitx::CLI.any_instance.stub(:say).with do |message|
492
- @said_text = @said_text.to_s + message
493
- end
902
+ expect_any_instance_of(Socialcast::Gitx::CLI).to receive(:findpr).and_call_original
903
+ allow_any_instance_of(Socialcast::Gitx::CLI).to receive(:say).with("Searching github pull requests for abc123")
904
+ allow_any_instance_of(Socialcast::Gitx::CLI).to receive(:say).with("\nhttps://github.com/batterseapower/pinyin-toolkit/issues/132\n\tLine Number Indexes Beyond 20 Not Displayed\n\tNick3C 2009-07-12T20:10:41Z")
494
905
  Socialcast::Gitx::CLI.start ['findpr', 'abc123']
495
906
  end
496
- it 'fetches the data from github and prints it out' do
497
- @said_text.should include "https://github.com/batterseapower/pinyin-toolkit/issues/132"
498
- @said_text.should include "Nick3C"
499
- @said_text.should include "Line Number Indexes Beyond 20 Not Displayed"
500
- end
907
+ it 'fetches the data from github and prints it out' do end
501
908
  end
502
909
 
503
910
  describe '#reviewrequest' do
504
911
  context 'when there are no review_buddies specified' do
505
912
  before do
506
- Socialcast::Gitx::CLI.any_instance.stub(:config_file).and_return(Pathname(''))
913
+ allow_any_instance_of(Socialcast::Gitx::CLI).to receive(:config_file).and_return(Pathname(''))
507
914
  end
508
- context 'when description != null' do
915
+ context 'when description != nil' do
509
916
  before do
510
917
  stub_request(:post, "https://api.github.com/repos/socialcast/socialcast-git-extensions/pulls").
511
918
  to_return(:status => 200, :body => %q({"html_url": "http://github.com/repo/project/pulls/1"}), :headers => {})
512
919
 
513
- stub_message "#reviewrequest for FOO #scgitx\n\n/cc @SocialcastDevelopers\n\ntesting\n\n", :url => 'http://github.com/repo/project/pulls/1', :message_type => 'review_request'
920
+ stub_message "#reviewrequest for FOO #scgitx\n\n/cc @SocialcastDevelopers\n\ntesting\n\n1 file changed", :url => 'http://github.com/repo/project/pulls/1', :message_type => 'review_request'
921
+ allow_any_instance_of(Socialcast::Gitx::CLI).to receive(:changelog_summary).and_return('1 file changed')
514
922
  Socialcast::Gitx::CLI.start ['reviewrequest', '--description', 'testing', '-s']
515
923
  end
516
924
  it 'should create github pull request' do end # see expectations
517
925
  it 'should post socialcast message' do end # see expectations
518
926
  it 'should run expected commands' do
519
- Socialcast::Gitx::CLI.stubbed_executed_commands.should == [
927
+ expect(Socialcast::Gitx::CLI.stubbed_executed_commands).to eq([
520
928
  "git pull origin FOO",
521
929
  "git pull origin master",
522
930
  "git push origin HEAD"
523
- ]
931
+ ])
524
932
  end
525
933
  end
526
934
  end
@@ -532,8 +940,9 @@ describe Socialcast::Gitx::CLI do
532
940
  stub_request(:patch, "http://github.com/repos/repo/project/issues/1").to_return(:status => 200)
533
941
  end
534
942
  context 'and additional reviewers are specified' do
535
- let(:message_body) { "#reviewrequest for FOO #scgitx\n\n/cc @SocialcastDevelopers\n\n\nAssigned additionally to @JohnSmith for API review\n\ntesting\n\n" }
943
+ let(:message_body) { "#reviewrequest for FOO #scgitx\n\n/cc @SocialcastDevelopers\n\n\nAssigned additionally to @JohnSmith for API review\n\ntesting\n\n1 file changed" }
536
944
  before do
945
+ allow_any_instance_of(Socialcast::Gitx::CLI).to receive(:changelog_summary).and_return('1 file changed')
537
946
  # The Review Buddy should be @mentioned in the message
538
947
  stub_message message_body, :url => 'http://github.com/repo/project/pulls/1', :message_type => 'review_request'
539
948
  Socialcast::Gitx::CLI.start ['reviewrequest', '--description', 'testing', '-a', 'a']
@@ -541,16 +950,17 @@ describe Socialcast::Gitx::CLI do
541
950
  it 'should create github pull request' do end # see expectations
542
951
  it 'should post socialcast message' do end # see expectations
543
952
  it 'should run expected commands' do
544
- Socialcast::Gitx::CLI.stubbed_executed_commands.should == [
953
+ expect(Socialcast::Gitx::CLI.stubbed_executed_commands).to eq([
545
954
  "git pull origin FOO",
546
955
  "git pull origin master",
547
956
  "git push origin HEAD"
548
- ]
957
+ ])
549
958
  end
550
959
  end
551
960
  context 'and additional reviewers are not specified' do
552
- let(:message_body) { "#reviewrequest for FOO #scgitx\n\n/cc @SocialcastDevelopers\n\ntesting\n\n" }
961
+ let(:message_body) { "#reviewrequest for FOO #scgitx\n\n/cc @SocialcastDevelopers\n\ntesting\n\n1 file changed" }
553
962
  before do
963
+ allow_any_instance_of(Socialcast::Gitx::CLI).to receive(:changelog_summary).and_return('1 file changed')
554
964
  # The Review Buddy should be @mentioned in the message
555
965
  stub_message message_body, :url => 'http://github.com/repo/project/pulls/1', :message_type => 'review_request'
556
966
  Socialcast::Gitx::CLI.start ['reviewrequest', '--description', 'testing', '-s']
@@ -566,7 +976,7 @@ describe Socialcast::Gitx::CLI do
566
976
  Socialcast::Gitx::CLI.start ['promote']
567
977
  end
568
978
  it 'should integrate into staging' do
569
- Socialcast::Gitx::CLI.stubbed_executed_commands.should == [
979
+ expect(Socialcast::Gitx::CLI.stubbed_executed_commands).to eq([
570
980
  "git pull origin FOO",
571
981
  "git pull origin master",
572
982
  "git push origin HEAD",
@@ -583,24 +993,24 @@ describe Socialcast::Gitx::CLI do
583
993
  "git push origin HEAD",
584
994
  "git checkout staging",
585
995
  "git checkout FOO"
586
- ]
996
+ ])
587
997
  end
588
998
  end
589
999
 
590
1000
  describe '#cleanup' do
591
1001
  before do
592
- Socialcast::Gitx::CLI.any_instance.should_receive(:branches).with(:merged => true, :remote => true).and_return(['master', 'foobar', 'last_known_good_master'])
593
- Socialcast::Gitx::CLI.any_instance.should_receive(:branches).with(:merged => true).and_return(['staging', 'bazquux', 'last_known_good_prototype'])
1002
+ expect_any_instance_of(Socialcast::Gitx::CLI).to receive(:branches).with(:merged => true, :remote => true).and_return(['master', 'foobar', 'last_known_good_master'])
1003
+ expect_any_instance_of(Socialcast::Gitx::CLI).to receive(:branches).with(:merged => true).and_return(['staging', 'bazquux', 'last_known_good_prototype'])
594
1004
  Socialcast::Gitx::CLI.start ['cleanup']
595
1005
  end
596
1006
  it 'should only cleanup non-reserved branches' do
597
- Socialcast::Gitx::CLI.stubbed_executed_commands.should == [
1007
+ expect(Socialcast::Gitx::CLI.stubbed_executed_commands).to eq([
598
1008
  "git checkout master",
599
1009
  "git pull",
600
1010
  "git remote prune origin",
601
1011
  "git push origin --delete foobar",
602
1012
  "git branch -d bazquux"
603
- ]
1013
+ ])
604
1014
  end
605
1015
  end
606
1016
  end