github-pivotal-flow 0.0.8 → 0.0.9

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 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