github-pivotal-flow 0.0.8 → 0.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b01fca13c1d638c7aaaa3ad82ca7015446622e46
4
- data.tar.gz: 314ca596836512bbef1f685e41f995f2afdce0b6
3
+ metadata.gz: 423519e9574e2048faaaec36c2583c3ec79273eb
4
+ data.tar.gz: 03fb90bd37c4fb777432e1e9c6c9732ac90b25cf
5
5
  SHA512:
6
- metadata.gz: 4a077289ddeb860882b9dea4779ac2be616783fabef1a1f6589c4c418c19200fced196d8495cdb3ca26848767b9ba8aab5f19ae557bcdfa112d567852d2e6def
7
- data.tar.gz: e37b3831fcb8b0f6af1310fe80b4f86d3ecf9cca6aeb5a3a25e386e8cc7b63a65a7a6160ad6485d9be468ceba851fc96d79480189855edb0c95ae5811e864778
6
+ metadata.gz: 2aa8e365a3df85d33431476ca8c8b93bf022edce02ec2f2655f9e32e547469fc6ac36aa9978928b257c09f925505c7d0ce69f77cc24f033b28f0237f9826fa44
7
+ data.tar.gz: e6a57b30856b8cfffad4b65b6f34e51109a6240b666e1c4cd1026b2275e2e7b0f40a0dfc3f7102432ece7a5ca4354b86f6fc0c1ae3a6a905b1b9e75dded30bf3
@@ -20,6 +20,22 @@ require 'pivotal-tracker'
20
20
 
21
21
  require File.join('core_ext', 'object', 'blank')
22
22
 
23
+ module GithubPivotalFlow
24
+ KEY_USER_NAME = 'user.name'.freeze
25
+ KEY_API_TOKEN = 'pivotal.api-token'.freeze
26
+ KEY_PROJECT_ID = 'pivotal.project-id'.freeze
27
+ KEY_STORY_ID = 'pivotal-story-id'.freeze
28
+ KEY_FEATURE_PREFIX = 'gitflow.prefix.feature'.freeze
29
+ KEY_HOTFIX_PREFIX = 'gitflow.prefix.hotfix'.freeze
30
+ KEY_RELEASE_PREFIX = 'gitflow.prefix.release'.freeze
31
+ KEY_DEVELOPMENT_BRANCH = 'gitflow.branch.develop'.freeze
32
+ KEY_MASTER_BRANCH = 'gitflow.branch.master'.freeze
33
+ KEY_GITHUB_USERNAME = 'github.username'.freeze
34
+ KEY_GITHUB_API_TOKEN = 'github.api-token'.freeze
35
+ end
36
+
37
+ require File.join('github_pivotal_flow', 'version')
38
+
23
39
  require File.join('github_pivotal_flow', 'shell')
24
40
  require File.join('github_pivotal_flow', 'git')
25
41
  require File.join('github_pivotal_flow', 'project')
@@ -15,21 +15,10 @@ module GithubPivotalFlow
15
15
  @options = {}
16
16
  args = parse_argv(*args)
17
17
  @options[:args] = args
18
-
19
- @repository_root = Git.repository_root
20
18
  @configuration = Configuration.new(@options)
21
-
22
- PivotalTracker::Client.token = @configuration.api_token
23
- PivotalTracker::Client.use_ssl = true
24
-
25
- @project = PivotalTracker::Project.find @configuration.project_id
26
-
27
- # Make sure that all the git flow config options are set up
28
- @configuration.development_branch
29
- @configuration.master_branch
30
- @configuration.feature_prefix
31
- @configuration.hotfix_prefix
32
- @configuration.release_prefix
19
+ # Validate the configuration to make sure everything is set up correctly
20
+ @configuration.validate
21
+ @project = @configuration.project
33
22
  end
34
23
 
35
24
  # The main entry point to the command's execution
@@ -9,6 +9,31 @@ module GithubPivotalFlow
9
9
  @github_password_cache = {}
10
10
  end
11
11
 
12
+ def validate
13
+ repository_root
14
+ user_name
15
+ ensure_github_api_token
16
+ ensure_pivotal_api_token
17
+ ensure_gitflow_config
18
+ project.config = self
19
+ return true
20
+ end
21
+
22
+ def repository_root
23
+ @repository_root ||= Git.repository_root
24
+ end
25
+
26
+ def user_name
27
+ user_name = Git.get_config(KEY_USER_NAME, :inherited).strip
28
+ if user_name.blank?
29
+ user_name = ask('Github user name (Should be the same as in your Pivotal profile): ').strip
30
+ Git.set_config(KEY_USER_NAME, user_name, :local) unless user_name.blank?
31
+ puts
32
+ end
33
+
34
+ user_name
35
+ end
36
+
12
37
  # Returns the user's Pivotal Tracker API token. If this token has not been
13
38
  # configured, prompts the user for the value. The value is checked for in
14
39
  # the _inherited_ Git configuration, but is stored in the _global_ Git
@@ -18,15 +43,33 @@ module GithubPivotalFlow
18
43
  def api_token
19
44
  api_token = @options[:api_token] || Git.get_config(KEY_API_TOKEN, :inherited)
20
45
 
21
- if api_token.empty?
46
+ if api_token.blank?
22
47
  api_token = ask('Pivotal API Token (found at https://www.pivotaltracker.com/profile): ').strip
23
- Git.set_config KEY_API_TOKEN, api_token, :global
48
+ Git.set_config(KEY_API_TOKEN, api_token, :local) unless api_token.blank?
24
49
  puts
25
50
  end
26
51
 
27
52
  api_token
28
53
  end
29
54
 
55
+ def ensure_pivotal_api_token
56
+ PivotalTracker::Client.use_ssl = true
57
+ while (PivotalTracker::Client.token = self.api_token).blank? || PivotalTracker::Project.all.empty?
58
+ puts "No projects found."
59
+ clear_pivotal_api_token!
60
+ end
61
+ return true
62
+ rescue RestClient::Unauthorized => e
63
+ puts "Invalid Pivotal token"
64
+ clear_pivotal_api_token!
65
+ retry
66
+ end
67
+
68
+ def clear_pivotal_api_token!
69
+ PivotalTracker::Client.token = nil
70
+ Git.delete_config(KEY_API_TOKEN, :local)
71
+ end
72
+
30
73
  # Returns the Pivotal Tracker project id for this repository. If this id
31
74
  # has not been configuration, prompts the user for the value. The value is
32
75
  # checked for in the _inherited_ Git configuration, but is stored in the
@@ -45,18 +88,22 @@ module GithubPivotalFlow
45
88
  end
46
89
  end
47
90
 
48
- Git.set_config KEY_PROJECT_ID, project_id, :local
91
+ Git.set_config(KEY_PROJECT_ID, project_id, :local)
49
92
  puts
50
93
  end
51
94
 
52
95
  project_id
53
96
  end
54
97
 
55
- # Returns the story associated with the current development branch
98
+ def project
99
+ @project ||= Project.new(config: self)
100
+ end
101
+
102
+ # Returns the story associated with the branch
56
103
  #
57
- # @param [PivotalTracker::Project] project the project the story belongs to
58
- # @return [PivotalTracker::Story] the story associated with the current development branch
59
- def story(project)
104
+ # @return [Story] the story associated with the current development branch
105
+ def story
106
+ return @story if @story
60
107
  story_id = Git.get_config(KEY_STORY_ID, :branch)
61
108
  if story_id.blank? && (matchdata = /^[a-z0-9_\-]+\/(\d+)(-[a-z0-9_\-]+)?$/i.match(Git.current_branch))
62
109
  story_id = matchdata[1]
@@ -67,7 +114,7 @@ module GithubPivotalFlow
67
114
  Git.set_config(KEY_STORY_ID, story_id, :branch) unless story_id.blank?
68
115
  end
69
116
  return nil if story_id.blank?
70
- Story.new(project.stories.find(story_id.to_i), :branch_name => Git.current_branch)
117
+ return (@story = Story.new(project, project.stories.find(story_id.to_i), branch_name: Git.current_branch))
71
118
  end
72
119
 
73
120
  # Stores the story associated with the current development branch
@@ -86,7 +133,6 @@ module GithubPivotalFlow
86
133
  feature_prefix = 'feature/' if feature_prefix.blank?
87
134
  feature_prefix = "#{feature_prefix}/" unless feature_prefix[-1,1] == '/'
88
135
  Git.set_config KEY_FEATURE_PREFIX, feature_prefix, :local
89
- puts
90
136
  end
91
137
 
92
138
  feature_prefix
@@ -100,7 +146,6 @@ module GithubPivotalFlow
100
146
  hotfix_prefix = 'hotfix/' if hotfix_prefix.blank?
101
147
  hotfix_prefix = "#{hotfix_prefix}/" unless hotfix_prefix[-1,1] == '/'
102
148
  Git.set_config KEY_HOTFIX_PREFIX, hotfix_prefix, :local
103
- puts
104
149
  end
105
150
 
106
151
  hotfix_prefix
@@ -113,8 +158,7 @@ module GithubPivotalFlow
113
158
  release_prefix = ask('Please enter your git-flow release branch prefix: [release/]').strip
114
159
  release_prefix = 'release' if release_prefix.blank?
115
160
  release_prefix = "#{release_prefix}/" unless release_prefix[-1,1] == '/'
116
- Git.set_config KEY_RELEASE_PREFIX, release_prefix, :local
117
- puts
161
+ Git.set_config(KEY_RELEASE_PREFIX, release_prefix, :local)
118
162
  end
119
163
 
120
164
  release_prefix
@@ -125,9 +169,8 @@ module GithubPivotalFlow
125
169
 
126
170
  if development_branch.empty?
127
171
  development_branch = ask('Please enter your git-flow development branch name: [development]').strip
128
- development_branch = 'development' if development_branch.nil? || development_branch.empty?
172
+ development_branch = 'development' if development_branch.blank?
129
173
  Git.set_config KEY_DEVELOPMENT_BRANCH, development_branch, :local
130
- puts
131
174
  end
132
175
  Git.ensure_branch_exists(development_branch)
133
176
 
@@ -139,20 +182,27 @@ module GithubPivotalFlow
139
182
 
140
183
  if master_branch.blank?
141
184
  master_branch = ask('Please enter your git-flow production branch name: [master]').strip
142
- master_branch = 'master' if master_branch.nil? || master_branch.empty?
185
+ master_branch = 'master' if master_branch.blank?
143
186
  Git.set_config KEY_MASTER_BRANCH, master_branch, :local
144
- puts
145
187
  end
146
188
  Git.ensure_branch_exists(master_branch)
147
189
 
148
190
  master_branch
149
191
  end
150
192
 
151
- def github_project
152
- @github_project ||= Project.from_url(Git.get_config("remote.#{Git.get_remote}.url"))
193
+ def ensure_gitflow_config
194
+ development_branch && master_branch && feature_prefix && hotfix_prefix && release_prefix
195
+ end
196
+
197
+ def github_client
198
+ @ghclient ||= GitHubAPI.new(self, :app_url => 'http://github.com/roomorama/github-pivotal-flow')
153
199
  end
154
200
 
155
- def github_username(host)
201
+ def github_host
202
+ project.host
203
+ end
204
+
205
+ def github_username(host = github_host)
156
206
  return ENV['GITHUB_USER'] unless ENV['GITHUB_USER'].to_s.blank?
157
207
  github_username = Git.get_config KEY_GITHUB_USERNAME, :inherited
158
208
  if github_username.blank?
@@ -162,27 +212,54 @@ module GithubPivotalFlow
162
212
  github_username
163
213
  end
164
214
 
165
- def github_username=(github_username)
166
- Git.set_config KEY_GITHUB_USERNAME, github_username, :local unless github_username.blank?
215
+ def github_username=(username)
216
+ Git.set_config KEY_GITHUB_USERNAME, username, :local unless username.blank?
167
217
  end
168
218
 
169
- def github_password(host, user)
170
- return ENV['GITHUB_PASSWORD'] unless ENV['GITHUB_PASSWORD'].to_s.empty?
171
- @github_password_cache["#{user}"] ||= ask_password(user)
219
+ def github_password(host = github_host, user = nil)
220
+ return ENV['GITHUB_PASSWORD'] unless ENV['GITHUB_PASSWORD'].to_s.blank?
221
+ user ||= github_username(host)
222
+ @github_password_cache["#{user}"] ||= ask_github_password(user)
172
223
  end
173
224
 
174
- def github_api_token(host, user)
225
+ def clear_github_auth_data!
226
+ @github_password_cache = {}
227
+ Git.delete_config(KEY_GITHUB_USERNAME, :local)
228
+ end
229
+
230
+ def ensure_github_api_token
231
+ begin
232
+ repo_found = github_client.repo_exists?(project)
233
+ end while github_api_token.blank?
234
+ raise("Could not find github project") unless repo_found
235
+ rescue Net::HTTPServerException => e
236
+ case e.response.code
237
+ when '401'
238
+ say "Invalid username/password combination. Please try again:"
239
+ else
240
+ say "Unknown error (#{e.response.code}). Please try again: "
241
+ end
242
+ clear_github_auth_data!
243
+ retry
244
+ end
245
+
246
+ def github_api_token(host = nil, user = nil)
247
+ host ||= github_host
248
+ user ||= github_username
175
249
  github_token = Git.get_config KEY_GITHUB_API_TOKEN, :inherited
176
250
  if github_token.blank?
177
- github_token = yield
178
- Git.set_config KEY_GITHUB_API_TOKEN, github_token, :local
251
+ if block_given?
252
+ github_token = yield
253
+ end
254
+ Git.set_config(KEY_GITHUB_API_TOKEN, github_token, :global) unless github_token.blank?
179
255
  end
180
256
  github_token
181
257
  end
182
258
 
183
259
  # special prompt that has hidden input
184
- def ask_password(user)
185
- print "Github password for #{user} (never stored): "
260
+ def ask_github_password(username = nil)
261
+ username ||= github_username
262
+ print "Github password for #{username} (never stored): "
186
263
  if $stdin.tty?
187
264
  password = askpass
188
265
  puts ''
@@ -247,18 +324,5 @@ module GithubPivotalFlow
247
324
  URI.parse proxy
248
325
  end
249
326
  end
250
-
251
- private
252
-
253
- KEY_API_TOKEN = 'pivotal.api-token'.freeze
254
- KEY_PROJECT_ID = 'pivotal.project-id'.freeze
255
- KEY_STORY_ID = 'pivotal-story-id'.freeze
256
- KEY_FEATURE_PREFIX = 'gitflow.prefix.feature'.freeze
257
- KEY_HOTFIX_PREFIX = 'gitflow.prefix.hotfix'.freeze
258
- KEY_RELEASE_PREFIX = 'gitflow.prefix.release'.freeze
259
- KEY_DEVELOPMENT_BRANCH = 'gitflow.branch.develop'.freeze
260
- KEY_MASTER_BRANCH = 'gitflow.branch.master'.freeze
261
- KEY_GITHUB_USERNAME = 'github.username'.freeze
262
- KEY_GITHUB_API_TOKEN = 'github.api-token'.freeze
263
327
  end
264
328
  end
@@ -4,7 +4,7 @@ module GithubPivotalFlow
4
4
 
5
5
  # Finishes a Pivotal Tracker story
6
6
  def run!
7
- story = @configuration.story(@project)
7
+ story = @configuration.story
8
8
  story.can_merge?
9
9
  commit_message = options[:commit_message]
10
10
  if story.release?
@@ -45,6 +45,7 @@ module GithubPivotalFlow
45
45
  def self.merge(branch_name, options = {})
46
46
  command = "git merge --quiet"
47
47
  command << " --no-ff" if options[:no_ff]
48
+ command << " --ff" if options[:ff] && !options[:no_ff]
48
49
  command << " -m \"#{options[:commit_message]}\"" unless options[:commit_message].blank?
49
50
  exec "#{command} #{branch_name}"
50
51
  puts 'OK'
@@ -118,6 +119,19 @@ module GithubPivotalFlow
118
119
  else
119
120
  raise "Unable to set Git configuration for scope '#{scope}'"
120
121
  end
122
+ return value
123
+ end
124
+
125
+ def self.delete_config(key, scope = :local)
126
+ if :branch == scope
127
+ exec "git config --local --unset branch.#{self.current_branch}.#{key}"
128
+ elsif :global == scope
129
+ exec "git config --global --unset #{key}"
130
+ elsif :local == scope
131
+ exec "git config --local --unset #{key}"
132
+ else
133
+ raise "Unable to delete Git configuration for scope '#{scope}'"
134
+ end
121
135
  end
122
136
 
123
137
  def self.repository_root
@@ -149,6 +163,14 @@ module GithubPivotalFlow
149
163
  end
150
164
  end
151
165
 
166
+ def self.clean_working_tree?
167
+ exec("git diff --no-ext-diff --ignore-submodules --quiet --exit-code", false)
168
+ fail("fatal: Working tree contains unstaged changes. Aborting.") if $?.exitstatus != 0
169
+ exec("git diff-index --cached --quiet --ignore-submodules HEAD --", false)
170
+ fail("fatal: Index contains uncommited changes. Aborting.") if $?.exitstatus != 0
171
+ return true
172
+ end
173
+
152
174
  private
153
175
  def self.escape_commit_message(message)
154
176
  message.gsub('"', '\"').sub(/!\z/, '! ')
@@ -34,6 +34,15 @@ module GithubPivotalFlow
34
34
  end
35
35
  end
36
36
 
37
+ def repo_info project
38
+ get "https://%s/repos/%s/%s" %
39
+ [api_host(project.host), project.owner, project.name]
40
+ end
41
+
42
+ def repo_exists? project
43
+ repo_info(project).success?
44
+ end
45
+
37
46
  # Returns parsed data from the new pull request.
38
47
  def create_pullrequest options
39
48
  project = options.fetch(:project)
@@ -124,7 +133,7 @@ module GithubPivotalFlow
124
133
  create_connection host_url
125
134
  end
126
135
 
127
- req['User-Agent'] = "Hub 1.11.1"
136
+ req['User-Agent'] = "Github-pivotal-flow #{GithubPivotalFlow::VERSION}"
128
137
  apply_authentication(req, url)
129
138
  yield req if block_given?
130
139
  finalize_request(req, url)
@@ -204,7 +213,6 @@ module GithubPivotalFlow
204
213
 
205
214
  def obtain_oauth_token host, user, two_factor_code = nil
206
215
  auth_url = URI.parse("https://%s@%s/authorizations" % [CGI.escape(user), host])
207
-
208
216
  # dummy request to trigger a 2FA SMS since a HTTP GET won't do it
209
217
  post(auth_url) if !two_factor_code
210
218
 
@@ -1,21 +1,29 @@
1
1
  module GithubPivotalFlow
2
- class Project < Struct.new(:owner, :name, :host)
3
- def self.from_url(url)
2
+ class Project
3
+ attr_accessor :owner, :name, :host, :config
4
+
5
+ def self.find(id)
6
+ id = id.to_i if id.is_a?(String)
7
+ return PivotalTracker::Project.find(id)
8
+ end
9
+
10
+ def initialize(args = {})
11
+ args.each do |k,v|
12
+ instance_variable_set("@#{k}", v) unless v.nil?
13
+ end
14
+ url = Git.get_config("remote.#{Git.get_remote}.url")
4
15
  if (matchdata = /^git@([a-z0-9\._-]+):([a-z0-9_-]+)\/([a-z0-9_-]+)(\.git)?$/.match(url.strip))
5
- host = matchdata[1]
6
- owner = matchdata[2]
7
- name = matchdata[3]
16
+ self.host ||= matchdata[1]
17
+ self.owner ||= matchdata[2]
18
+ self.name ||= matchdata[3]
8
19
  else
9
20
  url = URI(url) if !url.is_a?(URI)
10
- _, owner, name = url.path.split('/', 4)
11
- host = url.host
21
+ path_components = url.path.split('/', 4)
22
+ self.owner ||= path_components[1]
23
+ self.name ||= path_components[2]
24
+ self.host ||= url.host
12
25
  end
13
- self.new(owner, name.sub(/\.git$/, ''), host)
14
- end
15
-
16
- def initialize(*args)
17
- super
18
- self.name = self.name.tr(' ', '-')
26
+ self.name = self.name.tr(' ', '-').sub(/\.git$/, '')
19
27
  self.host ||= 'github.com'
20
28
  self.host = host.sub(/^ssh\./i, '') if 'ssh.github.com' == host.downcase
21
29
  end
@@ -27,5 +35,13 @@ module GithubPivotalFlow
27
35
  def ==(other)
28
36
  name_with_owner == other.name_with_owner
29
37
  end
38
+
39
+ def pivotal_project
40
+ @pivotal_project ||= self.class.find(self.config.project_id)
41
+ end
42
+
43
+ def method_missing(m, *args, &block)
44
+ return pivotal_project.send(m, *args, &block)
45
+ end
30
46
  end
31
47
  end
@@ -11,8 +11,10 @@ module GithubPivotalFlow
11
11
  @configuration.story = story # Tag the branch with story attributes
12
12
  Git.add_hook 'prepare-commit-msg', File.join(File.dirname(__FILE__), 'prepare-commit-msg.sh')
13
13
  unless story.release?
14
- @ghclient = GitHubAPI.new(@configuration, :app_url => 'http://github.com/roomorama/github-pivotal-flow')
15
- create_pull_request_for_story!(story)
14
+ print "Creating pull-request on Github... "
15
+ pull_request_params = story.params_for_pull_request.merge(project: @configuration.project)
16
+ @configuration.github_client.create_pullrequest(pull_request_params)
17
+ puts 'OK'
16
18
  end
17
19
  story.mark_started!
18
20
  return 0
@@ -20,12 +22,6 @@ module GithubPivotalFlow
20
22
 
21
23
  private
22
24
 
23
- def create_pull_request_for_story!(story)
24
- print "Creating pull-request on Github... "
25
- @ghclient.create_pullrequest({:project => @configuration.github_project}.merge(story.params_for_pull_request))
26
- puts 'OK'
27
- end
28
-
29
25
  def parse_argv(*args)
30
26
  OptionParser.new do |opts|
31
27
  opts.banner = "Usage: git start <feature|chore|bug|story_id>"
@@ -1,7 +1,7 @@
1
1
  # Utilities for dealing with +PivotalTracker::Story+s
2
2
  module GithubPivotalFlow
3
3
  class Story
4
- attr_accessor :story, :branch_name, :root_branch_name
4
+ attr_accessor :pivotal_story, :project, :branch_name, :root_branch_name
5
5
 
6
6
  # Print a human readable version of a story. This pretty prints the title,
7
7
  # description, and notes for the story.
@@ -28,7 +28,7 @@ module GithubPivotalFlow
28
28
 
29
29
  # Selects a Pivotal Tracker story by doing the following steps:
30
30
  #
31
- # @param [PivotalTracker::Project] project the project to select stories from
31
+ # @param [Project] project the project to select stories from
32
32
  # @param [String, nil] filter a filter for selecting the story to start. This
33
33
  # filter can be either:
34
34
  # * a story id: selects the story represented by the id
@@ -42,20 +42,22 @@ module GithubPivotalFlow
42
42
  else
43
43
  story = find_story project, filter, limit
44
44
  end
45
- self.new(story)
45
+ self.new(project, story)
46
46
  end
47
47
 
48
- # @param [PivotalTracker::Story] story the story to wrap
49
- def initialize(story, options = {})
50
- raise "Invalid PivotalTracker::Story" if story.nil?
51
- @story = story
48
+ # @param [Project] project the Project for this repo
49
+ # @param [PivotalTracker::Story] pivotal_story the Pivotal tracker story to wrap
50
+ def initialize(project, pivotal_story, options = {})
51
+ raise "Invalid PivotalTracker::Story" if pivotal_story.nil?
52
+ @project = project
53
+ @pivotal_story = pivotal_story
52
54
  @branch_name = options.delete(:branch_name)
53
55
  @branch_suffix = @branch_name.split('-').last if @branch_name
54
56
  @branch_suffix ||= nil
55
57
  end
56
58
 
57
59
  def release?
58
- story.story_type == 'release'
60
+ story_type == 'release'
59
61
  end
60
62
 
61
63
  def unestimated?
@@ -63,22 +65,22 @@ module GithubPivotalFlow
63
65
  end
64
66
 
65
67
  def request_estimation!
66
- self.story.update(
67
- :estimate => ask('Story is not yet estimated. Please estimate difficulty: ')
68
+ self.update(
69
+ estimate: ask('Story is not yet estimated. Please estimate difficulty: ')
68
70
  )
69
71
  end
70
72
 
71
73
  def mark_started!
72
74
  print 'Starting story on Pivotal Tracker... '
73
- self.story.update(
74
- :current_state => 'started',
75
- :owned_by => Git.get_config('user.name', :inherited)
75
+ self.update(
76
+ current_state: 'started',
77
+ owned_by: Git.get_config(KEY_USER_NAME, :inherited).strip
76
78
  )
77
79
  puts 'OK'
78
80
  end
79
81
 
80
82
  def create_branch!(commit_message = nil, options = {})
81
- commit_message ||= "Starting [#{story.story_type} ##{story.id}]: #{story.name}"
83
+ commit_message ||= "Starting [#{story_type} ##{id}]: #{name}"
82
84
  commit_message << " [ci skip]" unless options[:run_ci]
83
85
  print "Creating branch for story with branch name #{branch_name} from #{root_branch_name}... "
84
86
  Git.checkout(root_branch_name)
@@ -94,32 +96,44 @@ module GithubPivotalFlow
94
96
 
95
97
  def merge_to_root!(commit_message = nil, options = {})
96
98
  commit_message ||= "Merge #{branch_name} to #{root_branch_name}"
97
- commit_message << "\n\n[#{options[:no_complete] ? '' : 'Completes '}##{story.id}] "
99
+ commit_message << "\n\n[#{options[:no_complete] ? '' : 'Completes '}##{id}] "
98
100
  print "Merging #{branch_name} to #{root_branch_name}... "
99
101
  Git.checkout(root_branch_name)
100
102
  Git.pull_remote(root_branch_name)
101
- Git.merge(branch_name, commit_message: commit_message, no_ff: true)
102
- self.delete_branch!
103
+ if trivial_merge?
104
+ Git.merge(branch_name, commit_message: commit_message, ff: true)
105
+ else
106
+ Git.merge(branch_name, commit_message: commit_message, no_ff: true)
107
+ end
103
108
  Git.push(root_branch_name)
109
+ self.delete_branch!
104
110
  self.cleanup!
105
111
  end
106
112
 
107
113
  def merge_release!(commit_message = nil, options = {})
108
- commit_message ||= "Release #{story.name}"
109
- commit_message << "\n\n[#{options[:no_complete] ? '' : 'Completes '}##{story.id}] "
114
+ commit_message ||= "Release #{name}"
115
+ commit_message << "\n\n[#{options[:no_complete] ? '' : 'Completes '}##{id}] "
110
116
  print "Merging #{branch_name} to #{master_branch_name}... "
111
117
  Git.checkout(master_branch_name)
112
118
  Git.pull_remote(master_branch_name)
113
- Git.merge(master_branch_name, commit_message: commit_message, no_ff: true)
114
- Git.tag(story.name)
115
- print "Merging #{branch_name} to #{root_branch_name}... "
116
- Git checkout(root_branch_name)
117
- Git.pull_remote(root_branch_name)
118
- Git.merge(branch_name, commit_message: commit_message, no_ff: true)
119
+ if trivial_merge?(master_branch_name)
120
+ Git.merge(branch_name, commit_message: commit_message, ff: true)
121
+ else
122
+ Git.merge(branch_name, commit_message: commit_message, no_ff: true)
123
+ end
124
+ Git.tag(name)
125
+ print "Merging #{branch_name} to #{development_branch_name}... "
126
+ Git checkout(development_branch_name)
127
+ Git.pull_remote(development_branch_name)
128
+ if trivial_merge?(development_branch_name)
129
+ Git.merge(branch_name, commit_message: commit_message, ff: true)
130
+ else
131
+ Git.merge(branch_name, commit_message: commit_message, no_ff: true)
132
+ end
119
133
  Git.checkout(master_branch_name)
120
- self.delete_branch!
121
- Git.push(master_branch_name, root_branch_name)
134
+ Git.push(master_branch_name, development_branch_name)
122
135
  Git.push_tags
136
+ self.delete_branch!
123
137
  self.cleanup!
124
138
  end
125
139
 
@@ -138,17 +152,17 @@ module GithubPivotalFlow
138
152
  #end
139
153
 
140
154
  def branch_name
141
- @branch_name ||= branch_name_from(branch_prefix, story.id, branch_suffix)
155
+ @branch_name ||= branch_name_from(branch_prefix, id, branch_suffix)
142
156
  end
143
157
 
144
158
  def branch_suffix
145
- @branch_suffix ||= ask("Enter branch name (#{branch_name_from(branch_prefix, story.id, "<branch-name>")}): ")
159
+ @branch_suffix ||= ask("Enter branch name (#{branch_name_from(branch_prefix, id, "<branch-name>")}): ")
146
160
  end
147
161
 
148
162
  def branch_name_from(branch_prefix, story_id, branch_name)
149
163
  if story_type == 'release'
150
164
  # For release branches the format is release/5.0
151
- "#{Git.get_config('gitflow.prefix.release', :inherited)}#{branch_name}"
165
+ "#{Git.get_config(KEY_RELEASE_PREFIX, :inherited)}#{branch_name}"
152
166
  else
153
167
  n = "#{branch_prefix}#{story_id}"
154
168
  n << "-#{branch_name}" unless branch_name.blank?
@@ -168,42 +182,43 @@ module GithubPivotalFlow
168
182
  end
169
183
 
170
184
  def master_branch_name
171
- Git.get_config('gitflow.branch.master', :inherited)
185
+ Git.get_config(KEY_MASTER_BRANCH, :inherited)
172
186
  end
173
187
 
174
188
  def development_branch_name
175
- Git.get_config('gitflow.branch.develop', :inherited)
189
+ Git.get_config(KEY_DEVELOPMENT_BRANCH, :inherited)
176
190
  end
177
191
 
178
192
  def labels
179
- return [] if story.labels.blank?
180
- story.labels.split(',').collect(&:strip)
193
+ return [] if pivotal_story.labels.blank?
194
+ pivotal_story.labels.split(',').collect(&:strip)
181
195
  end
182
196
 
183
197
  def params_for_pull_request
184
198
  {
185
- :base => root_branch_name,
186
- :head => branch_name,
187
- :title => name,
188
- :body => description,
199
+ base: root_branch_name,
200
+ head: branch_name,
201
+ title: name,
202
+ body: description,
189
203
  }
190
204
  end
191
205
 
192
206
  def method_missing(m, *args, &block)
193
- return @story.send(m, *args, &block)
207
+ return @pivotal_story.send(m, *args, &block)
194
208
  end
195
209
 
196
210
  def can_merge?
197
- print "Checking for trivial merge from #{branch_name} to #{root_branch_name}... "
198
- Git.pull_remote(root_branch_name)
199
- root_tip = Shell.exec "git rev-parse #{root_branch_name}"
200
- common_ancestor = Shell.exec "git merge-base #{root_branch_name} #{branch_name}"
211
+ Git.clean_working_tree?
212
+ end
201
213
 
214
+ def trivial_merge?(to_branch = nil)
215
+ to_branch ||= root_branch_name
216
+ root_tip = Shell.exec "git rev-parse #{to_branch}"
217
+ common_ancestor = Shell.exec "git merge-base #{to_branch} #{branch_name}"
202
218
  if root_tip != common_ancestor
203
- abort 'FAIL'
219
+ return false
204
220
  end
205
-
206
- puts 'OK'
221
+ return true
207
222
  end
208
223
 
209
224
  private
@@ -260,16 +275,18 @@ module GithubPivotalFlow
260
275
  end
261
276
 
262
277
  def branch_prefix
263
- case self.story_type
278
+ case story_type
264
279
  when 'feature'
265
- Git.get_config('gitflow.prefix.feature', :inherited)
280
+ prefix = Git.get_config(KEY_FEATURE_PREFIX, :inherited)
266
281
  when 'bug'
267
- self.labels.include?('hotfix') ? Git.get_config('gitflow.prefix.hotfix', :inherited) : Git.get_config('gitflow.prefix.feature', :inherited)
282
+ prefix = labels.include?('hotfix') ? Git.get_config(KEY_HOTFIX_PREFIX, :inherited) : Git.get_config(KEY_FEATURE_PREFIX, :inherited)
268
283
  when 'release'
269
- Git.get_config('gitflow.prefix.release', :inherited)
284
+ prefix = Git.get_config(KEY_RELEASE_PREFIX, :inherited)
270
285
  else
271
- 'misc/'
286
+ prefix = 'misc/'
272
287
  end
288
+ prefix = "#{prefix.strip}/" unless prefix.strip[-1,1] == '/'
289
+ return prefix.strip
273
290
  end
274
291
  end
275
292
  end
@@ -0,0 +1,3 @@
1
+ module GithubPivotalFlow
2
+ VERSION = '0.0.9'
3
+ end
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ module GithubPivotalFlow
4
+ describe Command do
5
+ before do
6
+ $stdout = StringIO.new
7
+ $stderr = StringIO.new
8
+ @configuration = double('configuration')
9
+ @project = double('project')
10
+ allow(Configuration).to receive(:new).and_return(@configuration)
11
+ allow(PivotalTracker::Project).to receive(:find).and_return(@project)
12
+ @configuration.stub(
13
+ validate: true,
14
+ project_id: 123456,
15
+ project: @project,
16
+ )
17
+ end
18
+
19
+ describe '#initialize' do
20
+ it 'validates the configuration when started' do
21
+ expect(@configuration).to receive(:validate).once
22
+ @start = Command.new
23
+ end
24
+
25
+ it 'finds the project corresponding to the current repo' do
26
+ expect(@configuration).to receive(:project).once.and_return(@project)
27
+ @start = Command.new
28
+ end
29
+ end
30
+ end
31
+ end
@@ -7,105 +7,116 @@ module GithubPivotalFlow
7
7
  $stdout = StringIO.new
8
8
  $stderr = StringIO.new
9
9
  @configuration = Configuration.new
10
+ @project = double('project')
10
11
  end
11
12
 
12
- it 'does not prompt the user for the API token if it is already configured' do
13
- Git.should_receive(:get_config).with('pivotal.api-token', :inherited).and_return('test_api_token')
13
+ describe '#validate' do
14
14
 
15
- api_token = @configuration.api_token
16
-
17
- expect(api_token).to eq('test_api_token')
18
15
  end
19
16
 
20
- it 'prompts the user for the API token if it is not configured' do
21
- Git.should_receive(:get_config).with('pivotal.api-token', :inherited).and_return('')
22
- @configuration.should_receive(:ask).and_return('test_api_token')
23
- Git.should_receive(:set_config).with('pivotal.api-token', 'test_api_token', :global)
24
- api_token = @configuration.api_token
25
- expect(api_token).to eq('test_api_token')
26
- end
17
+ describe '#api_token' do
18
+ it 'does not prompt the user for the API token if it is already configured' do
19
+ Git.should_receive(:get_config).with('pivotal.api-token', :inherited).and_return('test_api_token')
27
20
 
28
- it 'does not prompt the user for the project id if it is already configured' do
29
- Git.should_receive(:get_config).with('pivotal.project-id', :inherited).and_return('test_project_id')
30
- project_id = @configuration.project_id
31
- expect(project_id).to eq('test_project_id')
21
+ api_token = @configuration.api_token
22
+
23
+ expect(api_token).to eq('test_api_token')
24
+ end
25
+
26
+ it 'prompts the user for the API token if it is not configured and stores it in the local git config' do
27
+ Git.should_receive(:get_config).with('pivotal.api-token', :inherited).and_return('')
28
+ @configuration.should_receive(:ask).and_return('test_api_token')
29
+ Git.should_receive(:set_config).with('pivotal.api-token', 'test_api_token', :local)
30
+ api_token = @configuration.api_token
31
+ expect(api_token).to eq('test_api_token')
32
+ end
32
33
  end
33
34
 
34
- it 'prompts the user for the API token if it is not configured' do
35
- Git.should_receive(:get_config).with('pivotal.project-id', :inherited).and_return('')
36
- menu = double('menu')
37
- menu.should_receive(:prompt=)
38
- PivotalTracker::Project.should_receive(:all).and_return([
39
- PivotalTracker::Project.new(:id => 'id-2', :name => 'name-2'),
40
- PivotalTracker::Project.new(:id => 'id-1', :name => 'name-1')])
41
- menu.should_receive(:choice).with('name-1')
42
- menu.should_receive(:choice).with('name-2')
43
- @configuration.should_receive(:choose) { |&arg| arg.call menu }.and_return('test_project_id')
44
- Git.should_receive(:set_config).with('pivotal.project-id', 'test_project_id', :local)
45
-
46
- project_id = @configuration.project_id
47
-
48
- expect(project_id).to eq('test_project_id')
35
+ describe '#project_id' do
36
+ it 'does not prompt the user for the project id if it is already configured' do
37
+ Git.should_receive(:get_config).with('pivotal.project-id', :inherited).and_return('test_project_id')
38
+ project_id = @configuration.project_id
39
+ expect(project_id).to eq('test_project_id')
40
+ end
41
+
42
+ it 'prompts the user for the project id if it is not configured' do
43
+ Git.should_receive(:get_config).with('pivotal.project-id', :inherited).and_return('')
44
+ menu = double('menu')
45
+ menu.should_receive(:prompt=)
46
+ PivotalTracker::Project.should_receive(:all).and_return([
47
+ PivotalTracker::Project.new(:id => 'id-2', :name => 'name-2'),
48
+ PivotalTracker::Project.new(:id => 'id-1', :name => 'name-1')])
49
+ menu.should_receive(:choice).with('name-1')
50
+ menu.should_receive(:choice).with('name-2')
51
+ @configuration.should_receive(:choose) { |&arg| arg.call menu }.and_return('test_project_id')
52
+ Git.should_receive(:set_config).with('pivotal.project-id', 'test_project_id', :local)
53
+
54
+ project_id = @configuration.project_id
55
+
56
+ expect(project_id).to eq('test_project_id')
57
+ end
49
58
  end
50
59
 
51
- it 'persists the story when requested' do
52
- Git.should_receive(:set_config).with('pivotal-story-id', 12345678, :branch)
60
+ describe '#story=' do
61
+ it 'persists the story when requested' do
62
+ expect(Git).to receive(:set_config).with('pivotal-story-id', 12345678, :branch)
53
63
 
54
- @configuration.story = Story.new(PivotalTracker::Story.new(:id => 12345678))
64
+ @configuration.story = Story.new(@project, PivotalTracker::Story.new(:id => 12345678))
65
+ end
55
66
  end
56
67
 
57
- describe 'story' do
68
+ describe '#story' do
69
+ let(:project) { double('project') }
70
+ let(:stories) { double('stories') }
71
+ let(:pivotal_story) { double('pivotal_story') }
72
+
73
+ before do
74
+ allow(@configuration).to receive(:project).and_return(project)
75
+ end
76
+
58
77
  it 'fetches the story based on the story id stored inside the git config' do
59
- project = double('project')
60
- stories = double('stories')
61
- story = double('story')
62
- Git.should_receive(:get_config).with('pivotal-story-id', :branch).and_return('12345678')
63
- project.should_receive(:stories).and_return(stories)
64
- stories.should_receive(:find).with(12345678).and_return(story)
78
+ expect(Git).to receive(:get_config).with('pivotal-story-id', :branch).and_return('12345678')
79
+ expect(project).to receive(:stories).and_return(stories)
80
+ expect(stories).to receive(:find).with(12345678).and_return(pivotal_story)
65
81
 
66
- result = @configuration.story project
82
+ result = @configuration.story
83
+ expect(result).to be_a(Story)
67
84
 
68
- expect(result.story).to eq(story)
85
+ expect(result.pivotal_story).to eq(pivotal_story)
69
86
  end
70
87
 
71
88
  it 'uses the branch name to deduce the story id if no git config is found' do
72
- project = double('project')
73
- stories = double('stories')
74
- story = double('story')
75
89
  Git.stub(:current_branch).and_return('feature/12345678-sample_feature')
76
- Git.should_receive(:get_config).with('pivotal-story-id', :branch).and_return(' ')
77
- project.should_receive(:stories).and_return(stories)
78
- stories.should_receive(:find).with(12345678).and_return(story)
90
+ expect(Git).to receive(:get_config).with('pivotal-story-id', :branch).and_return(' ')
91
+ expect(project).to receive(:stories).and_return(stories)
92
+ expect(stories).to receive(:find).with(12345678).and_return(pivotal_story)
79
93
 
80
- result = @configuration.story project
94
+ result = @configuration.story
81
95
 
82
- expect(result.story).to eq(story)
96
+ expect(result.pivotal_story).to eq(pivotal_story)
83
97
  end
84
98
 
85
99
  it 'prompts for the story id if the branch name does not match the known format' do
86
- project = double('project')
87
- stories = double('stories')
88
- story = double('story')
89
100
  Git.stub(:current_branch).and_return('unknownformat')
90
- Git.should_receive(:get_config).with('pivotal-story-id', :branch).and_return(' ')
91
- @configuration.should_receive(:ask).and_return('12345678')
92
- Git.should_receive(:set_config).with('pivotal-story-id', '12345678', :branch)
93
- project.should_receive(:stories).and_return(stories)
94
- stories.should_receive(:find).with(12345678).and_return(story)
101
+ expect(Git).to receive(:get_config).with('pivotal-story-id', :branch).and_return(' ')
102
+ expect(@configuration).to receive(:ask).and_return('12345678')
103
+ expect(Git).to receive(:set_config).with('pivotal-story-id', '12345678', :branch)
104
+ expect(project).to receive(:stories).and_return(stories)
105
+ expect(stories).to receive(:find).with(12345678).and_return(pivotal_story)
95
106
 
96
- result = @configuration.story project
107
+ result = @configuration.story
97
108
 
98
- expect(result.story).to eq(story)
109
+ expect(result.pivotal_story).to eq(pivotal_story)
99
110
  end
100
111
  end
101
112
 
102
- describe 'github_project' do
113
+ describe '#project' do
103
114
  it 'supports working with git urls from the configuration' do
104
115
  Git.should_receive(:get_remote).and_return('origin')
105
116
  Git.should_receive(:get_config).with('remote.origin.url').and_return('git@github.com:roomorama/github-pivotal-flow.git')
106
- github_project = @configuration.github_project
107
- expect(github_project.owner).to eq('roomorama')
108
- expect(github_project.name).to eq('github-pivotal-flow')
117
+ project = @configuration.project
118
+ expect(project.owner).to eq('roomorama')
119
+ expect(project.name).to eq('github-pivotal-flow')
109
120
  end
110
121
  end
111
122
  end
@@ -18,11 +18,15 @@ module GithubPivotalFlow
18
18
  release_prefix: 'release/',
19
19
  api_token: 'token',
20
20
  project_id: '123',
21
- story: @story)
22
- allow(Git).to receive(:repository_root)
21
+ project: @project,
22
+ github_client: @ghclient,
23
+ story: @story,
24
+ validate: true,
25
+ )
23
26
  allow(Configuration).to receive(:new).and_return(@configuration)
24
- allow(PivotalTracker::Project).to receive(:find).and_return(@project)
27
+ allow(Project).to receive(:find).and_return(@project)
25
28
  @finish = Finish.new
29
+ expect(@configuration).to receive(:story).and_return(@story)
26
30
  end
27
31
 
28
32
  it 'merges the branch back to its root by default' do
@@ -112,7 +112,7 @@ module GithubPivotalFlow
112
112
  expect(File).to receive(:exist?).with(hook).and_return(true)
113
113
 
114
114
  Git.add_hook 'prepare-commit-msg', __FILE__
115
- expect(File).to receive(:exist?).and_call_original
115
+ allow(File).to receive(:exist?).and_call_original
116
116
  expect(File.exist?(hook)).to be_false
117
117
  end
118
118
  end
@@ -5,9 +5,11 @@ module GithubPivotalFlow
5
5
  before do
6
6
  $stdout = StringIO.new
7
7
  $stderr = StringIO.new
8
-
9
- @project = double('project')
10
8
  @story = double('story')
9
+ @project = double('project')
10
+ @project.stub(
11
+ stories: [@story]
12
+ )
11
13
  @ghclient = double('ghclient')
12
14
  @ghproject = double('ghproject')
13
15
  @configuration = double('configuration')
@@ -19,16 +21,17 @@ module GithubPivotalFlow
19
21
  release_prefix: 'release/',
20
22
  api_token: 'token',
21
23
  project_id: '123',
22
- github_project: @ghproject,
23
- story: @story)
24
- allow(Git).to receive(:repository_root)
25
- allow(GitHubAPI).to receive(:new).and_return(@ghclient)
24
+ project: @project,
25
+ github_client: @ghclient,
26
+ story: @story,
27
+ validate: true,
28
+ )
26
29
  allow(Configuration).to receive(:new).and_return(@configuration)
27
30
  allow(PivotalTracker::Project).to receive(:find).and_return(@project)
28
31
  @start = Start.new()
29
32
  end
30
33
 
31
- it 'should run' do
34
+ it 'should runs correctly' do
32
35
  @start.options[:args] = 'test_filter'
33
36
  @story.stub(:unestimated? => false, :release? => false, params_for_pull_request: {})
34
37
 
@@ -10,7 +10,10 @@ module GithubPivotalFlow
10
10
  @project = double('project')
11
11
  @stories = double('stories')
12
12
  @story = double('story')
13
+ @pivotal_story = double('pivotal_story')
13
14
  @menu = double('menu')
15
+ allow(@project).to receive(:stories).and_return(@stories)
16
+ allow(@story).to receive(:pivotal_story).and_return(@pivotal_story)
14
17
  end
15
18
 
16
19
  describe '.pretty_print' do
@@ -59,32 +62,28 @@ module GithubPivotalFlow
59
62
 
60
63
  describe '.select_story' do
61
64
  it 'selects a story directly if the filter is a number' do
62
- @project.should_receive(:stories).and_return(@stories)
63
- @stories.should_receive(:find).with(12345678).and_return(@story)
64
-
65
+ expect(@stories).to receive(:find).with(12345678).and_return(@pivotal_story)
65
66
  story = Story.select_story @project, '12345678'
66
67
 
67
68
  expect(story).to be_a(Story)
68
- expect(story.story).to be(@story)
69
+ expect(story.pivotal_story).to eq(@pivotal_story)
69
70
  end
70
71
 
71
72
  it 'selects a story if the result of the query is a single story' do
72
- @project.should_receive(:stories).and_return(@stories)
73
- @stories.should_receive(:all).with(
73
+ expect(@stories).to receive(:all).with(
74
74
  :current_state => %w(rejected unstarted unscheduled),
75
75
  :limit => 1,
76
76
  :story_type => 'release'
77
- ).and_return([@story])
77
+ ).and_return([@pivotal_story])
78
78
 
79
79
  story = Story.select_story @project, 'release', 1
80
80
 
81
81
  expect(story).to be_a(Story)
82
- expect(story.story).to be(@story)
82
+ expect(story.pivotal_story).to eq(@pivotal_story)
83
83
  end
84
84
 
85
85
  it 'prompts the user for a story if the result of the query is more than a single story' do
86
- @project.should_receive(:stories).and_return(@stories)
87
- @stories.should_receive(:all).with(
86
+ expect(@stories).to receive(:all).with(
88
87
  :current_state => %w(rejected unstarted unscheduled),
89
88
  :limit => 5,
90
89
  :story_type => 'feature'
@@ -92,66 +91,77 @@ module GithubPivotalFlow
92
91
  PivotalTracker::Story.new(:name => 'name-1'),
93
92
  PivotalTracker::Story.new(:name => 'name-2')
94
93
  ])
95
- @menu.should_receive(:prompt=)
96
- @menu.should_receive(:choice).with('name-1')
97
- @menu.should_receive(:choice).with('name-2')
98
- Story.should_receive(:choose) { |&arg| arg.call @menu }.and_return(@story)
94
+ expect(@menu).to receive(:prompt=)
95
+ expect(@menu).to receive(:choice).with('name-1')
96
+ expect(@menu).to receive(:choice).with('name-2')
97
+ expect(Story).to receive(:choose) { |&arg| arg.call @menu }.and_return(@pivotal_story)
99
98
 
100
99
  story = Story.select_story @project, 'feature'
101
100
 
102
101
  expect(story).to be_a(Story)
103
- expect(story.story).to be(@story)
102
+ expect(story.pivotal_story).to eq(@pivotal_story)
104
103
  end
105
104
 
106
105
  it 'prompts the user with the story type if no filter is specified' do
107
- @project.should_receive(:stories).and_return(@stories)
108
- @stories.should_receive(:all).with(
106
+ expect(@stories).to receive(:all).with(
109
107
  :current_state => %w(rejected unstarted unscheduled),
110
108
  :limit => 5
111
109
  ).and_return([
112
110
  PivotalTracker::Story.new(:story_type => 'chore', :name => 'name-1'),
113
111
  PivotalTracker::Story.new(:story_type => 'bug', :name => 'name-2')
114
112
  ])
115
- @menu.should_receive(:prompt=)
116
- @menu.should_receive(:choice).with('CHORE name-1')
117
- @menu.should_receive(:choice).with('BUG name-2')
118
- Story.should_receive(:choose) { |&arg| arg.call @menu }.and_return(@story)
113
+ expect(@menu).to receive(:prompt=)
114
+ expect(@menu).to receive(:choice).with('CHORE name-1')
115
+ expect(@menu).to receive(:choice).with('BUG name-2')
116
+ expect(Story).to receive(:choose) { |&arg| arg.call @menu }.and_return(@pivotal_story)
119
117
 
120
118
  story = Story.select_story @project
121
119
 
122
120
  expect(story).to be_a(Story)
123
- expect(story.story).to be(@story)
121
+ expect(story.pivotal_story).to eq(@pivotal_story)
124
122
  end
125
123
  end
126
124
 
127
125
  describe '#create_branch!' do
128
126
  before do
129
- @story.stub(story_type: 'feature', id: '123456', name: 'test', description: 'description')
130
- @pstory = GithubPivotalFlow::Story.new(@story)
131
- allow(@pstory).to receive(:ask).and_return('test')
127
+ Git.stub(
128
+ checkout: nil,
129
+ pull_remote: nil,
130
+ create_branch: nil,
131
+ set_config: nil,
132
+ get_config: nil,
133
+ push: nil,
134
+ commit: nil,
135
+ get_remote: 'origin',
136
+ )
137
+ @pivotal_story.stub(
138
+ story_type: 'feature',
139
+ id: '123456',
140
+ name: 'test',
141
+ description: 'description')
142
+ @story = GithubPivotalFlow::Story.new(@project, @pivotal_story)
143
+ allow(@story).to receive(:ask).and_return('test')
132
144
  end
133
145
 
134
146
  it 'prompts the user for a branch extension name' do
135
- Git.stub(checkout: nil, pull_remote: nil, create_branch: nil, set_config: nil, push: nil, commit: nil)
136
- expect(@pstory).to receive(:ask).with("Enter branch name (123456-<branch-name>): ").and_return('super-branch')
147
+ allow(@story).to receive(:branch_prefix).and_return('feature/')
148
+ expect(@story).to receive(:ask).with("Enter branch name (feature/123456-<branch-name>): ").and_return('super-branch')
137
149
 
138
- @pstory.create_branch!('Message')
150
+ @story.create_branch!('Message')
139
151
  end
140
152
 
141
153
  it 'includes a tag to skip the ci build for the initial blank commit' do
142
154
  @story.stub(branch_name: 'feature/123456-my_branch')
143
- Git.stub(checkout: nil, pull_remote: nil, create_branch: nil, set_config: nil, push: nil)
144
- Git.should_receive(:commit).with(hash_including(commit_message: 'Message [ci skip]')).and_return(true)
155
+ expect(Git).to receive(:commit).with(hash_including(commit_message: 'Message [ci skip]')).and_return(true)
145
156
 
146
- @pstory.create_branch!('Message')
157
+ @story.create_branch!('Message')
147
158
  end
148
159
 
149
160
  it 'pushes the local branch and sets the upstream using the -u flag' do
150
161
  @story.stub(branch_name: 'feature/123456-my_branch')
151
- Git.stub(checkout: nil, pull_remote: nil, create_branch: nil, set_config: nil, commit: nil)
152
- Git.should_receive(:push).with(instance_of(String), hash_including(set_upstream: true))
162
+ expect(Git).to receive(:push).with(instance_of(String), hash_including(set_upstream: true))
153
163
 
154
- @pstory.create_branch!('Message')
164
+ @story.create_branch!('Message')
155
165
  end
156
166
  end
157
167
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: github-pivotal-flow
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.8
4
+ version: 0.0.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Donald Piret
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-01-22 00:00:00.000000000 Z
11
+ date: 2014-01-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: highline
@@ -108,20 +108,6 @@ dependencies:
108
108
  - - ~>
109
109
  - !ruby/object:Gem::Version
110
110
  version: '2.14'
111
- - !ruby/object:Gem::Dependency
112
- name: debugger
113
- requirement: !ruby/object:Gem::Requirement
114
- requirements:
115
- - - ~>
116
- - !ruby/object:Gem::Version
117
- version: '1.6'
118
- type: :development
119
- prerelease: false
120
- version_requirements: !ruby/object:Gem::Requirement
121
- requirements:
122
- - - ~>
123
- - !ruby/object:Gem::Version
124
- version: '1.6'
125
111
  - !ruby/object:Gem::Dependency
126
112
  name: simplecov
127
113
  requirement: !ruby/object:Gem::Requirement
@@ -161,20 +147,22 @@ extra_rdoc_files: []
161
147
  files:
162
148
  - LICENSE
163
149
  - README.md
150
+ - bin/git-finish
151
+ - bin/git-start
164
152
  - lib/core_ext/object/blank.rb
153
+ - lib/github_pivotal_flow.rb
165
154
  - lib/github_pivotal_flow/command.rb
166
155
  - lib/github_pivotal_flow/configuration.rb
167
156
  - lib/github_pivotal_flow/finish.rb
168
157
  - lib/github_pivotal_flow/git.rb
169
158
  - lib/github_pivotal_flow/github_api.rb
159
+ - lib/github_pivotal_flow/prepare-commit-msg.sh
170
160
  - lib/github_pivotal_flow/project.rb
171
161
  - lib/github_pivotal_flow/shell.rb
172
162
  - lib/github_pivotal_flow/start.rb
173
163
  - lib/github_pivotal_flow/story.rb
174
- - lib/github_pivotal_flow.rb
175
- - lib/github_pivotal_flow/prepare-commit-msg.sh
176
- - bin/git-finish
177
- - bin/git-start
164
+ - lib/github_pivotal_flow/version.rb
165
+ - spec/github_pivotal_flow/command_spec.rb
178
166
  - spec/github_pivotal_flow/configuration_spec.rb
179
167
  - spec/github_pivotal_flow/finish_spec.rb
180
168
  - spec/github_pivotal_flow/git_spec.rb
@@ -201,11 +189,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
201
189
  version: '0'
202
190
  requirements: []
203
191
  rubyforge_project:
204
- rubygems_version: 2.1.9
192
+ rubygems_version: 2.2.1
205
193
  signing_key:
206
194
  specification_version: 4
207
195
  summary: Git commands for integration with Pivotal Tracker and Github pull requests
208
196
  test_files:
197
+ - spec/github_pivotal_flow/command_spec.rb
209
198
  - spec/github_pivotal_flow/configuration_spec.rb
210
199
  - spec/github_pivotal_flow/finish_spec.rb
211
200
  - spec/github_pivotal_flow/git_spec.rb