story_branch 0.2.7 → 0.2.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +2 -2
- data/lib/story_branch.rb +85 -76
- metadata +19 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dfb03c2e2971ef4d7821325027d126506ec4d02c
|
4
|
+
data.tar.gz: de96f85c513ad4ec77f7bd058e7954edd0658446
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9a326ba3d7ff1913aa8ba33af7519548b512d111a74c64899170190d533b5608726ee4800343210a22076cb908974e57cb7377cb9c3e82be88507bbebba58281
|
7
|
+
data.tar.gz: 2d8d4811aa52d7eb47bed0fa185f7424a54137f905b9d0e70b602586dc82856f489895acf7d3b5839e213e2bf587fb924f38d961d8af13f6f9cdebce58f93a52
|
data/README.md
CHANGED
@@ -19,10 +19,10 @@ e.g. `my-story-name-1234567`
|
|
19
19
|
|
20
20
|
`git finish`: Creates a git commit message for the staged changes.
|
21
21
|
|
22
|
-
e.g: `[Finishes #1234567] My
|
22
|
+
e.g: `[Finishes #1234567] My story name`
|
23
23
|
|
24
24
|
You must stage all changes (or stash them) first. Note the commit will not
|
25
|
-
be pushed. Note: You'll be able to bail out
|
25
|
+
be pushed. Note: You'll be able to bail out of the commit.
|
26
26
|
|
27
27
|
`git start`: Start a story in Pivotal Tracker from the terminal.
|
28
28
|
It'll get all unstarted stories in your current project. You can
|
data/lib/story_branch.rb
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
# Ranhiru Cooray <ranhiru@gmail.com>
|
7
7
|
# Gabe Hollombe <gabe@neo.com>
|
8
8
|
#
|
9
|
-
# Version: 0.2.
|
9
|
+
# Version: 0.2.8
|
10
10
|
#
|
11
11
|
# ## Description
|
12
12
|
# A small collection of tools for working with git branches and Pivotal
|
@@ -26,10 +26,10 @@
|
|
26
26
|
#
|
27
27
|
# `git finish`: Creates a git commit message for the staged changes.
|
28
28
|
#
|
29
|
-
# e.g: `[Finishes #1234567] My
|
29
|
+
# e.g: `[Finishes #1234567] My story name`
|
30
30
|
#
|
31
31
|
# You must stage all changes (or stash them) first. Note the commit will not
|
32
|
-
# be pushed. Note: You'll be able to bail out
|
32
|
+
# be pushed. Note: You'll be able to bail out of the commit.
|
33
33
|
#
|
34
34
|
# `git start`: Start a story in Pivotal Tracker from the terminal.
|
35
35
|
# It'll get all unstarted stories in your current project. You can
|
@@ -81,85 +81,83 @@
|
|
81
81
|
#
|
82
82
|
# Code:
|
83
83
|
|
84
|
+
require 'byebug'
|
84
85
|
require 'yaml'
|
85
|
-
require '
|
86
|
+
require 'blanket'
|
86
87
|
require 'rb-readline'
|
87
88
|
require 'readline'
|
88
89
|
require 'git'
|
89
90
|
require 'levenshtein'
|
90
91
|
|
91
|
-
trap('INT') {exit}
|
92
|
+
trap('INT') { exit }
|
92
93
|
|
93
94
|
module StoryBranch
|
94
|
-
|
95
95
|
class Main
|
96
|
-
|
97
96
|
ERRORS = {
|
98
|
-
|
97
|
+
'Stories in the started state must be estimated.' =>
|
99
98
|
"Error: Pivotal won't allow you to start an unestimated story"
|
100
99
|
}
|
101
100
|
|
102
|
-
PIVOTAL_CONFIG_FILES = ['.story_branch',"#{ENV['HOME']}/.story_branch"]
|
101
|
+
PIVOTAL_CONFIG_FILES = ['.story_branch', "#{ENV['HOME']}/.story_branch"]
|
103
102
|
|
104
103
|
attr_accessor :p
|
105
104
|
|
106
105
|
def initialize
|
107
|
-
if config_file
|
108
|
-
|
109
|
-
|
110
|
-
@p
|
111
|
-
@p.api_key = config_value "api", 'PIVOTAL_API_KEY'
|
112
|
-
@p.project_id = config_value "project", 'PIVOTAL_PROJECT_ID'
|
106
|
+
@pivotal_info = YAML.load_file config_file if config_file
|
107
|
+
@p = PivotalUtils.new
|
108
|
+
@p.api_key = config_value 'api', 'PIVOTAL_API_KEY'
|
109
|
+
@p.project_id = config_value 'project', 'PIVOTAL_PROJECT_ID'
|
113
110
|
exit unless @p.valid?
|
114
111
|
end
|
115
112
|
|
113
|
+
def unauthorised_message
|
114
|
+
$stderr.puts 'Pivotal API key or Project ID invalid'
|
115
|
+
end
|
116
|
+
|
116
117
|
def create_story_branch
|
117
118
|
begin
|
118
|
-
puts
|
119
|
+
puts 'Connecting with Pivotal Tracker'
|
119
120
|
@p.get_project
|
120
|
-
puts
|
121
|
+
puts 'Getting stories...'
|
121
122
|
stories = @p.display_stories :started, false
|
122
123
|
if stories.length < 1
|
123
|
-
puts
|
124
|
+
puts 'No stories started, exiting'
|
124
125
|
exit
|
125
126
|
end
|
126
127
|
story = @p.select_story stories
|
127
128
|
if story
|
128
129
|
@p.create_feature_branch story
|
129
130
|
end
|
130
|
-
rescue
|
131
|
-
|
131
|
+
rescue Blanket::Unauthorized
|
132
|
+
unauthorised_message
|
132
133
|
return nil
|
133
134
|
end
|
134
135
|
end
|
135
136
|
|
136
137
|
def pick_and_update filter, hash, msg, is_estimated
|
137
138
|
begin
|
138
|
-
puts
|
139
|
+
puts 'Connecting with Pivotal Tracker'
|
139
140
|
@p.get_project
|
140
|
-
puts
|
141
|
+
puts 'Getting stories...'
|
141
142
|
stories = @p.filtered_stories_list filter, is_estimated
|
142
143
|
story = @p.select_story stories
|
143
144
|
if story
|
144
|
-
result =
|
145
|
-
|
146
|
-
puts result.errors.to_a.uniq.map{|e| ERRORS[e] }
|
147
|
-
return nil
|
148
|
-
end
|
145
|
+
result = @p.story_update story, hash
|
146
|
+
fail result.error if result.error
|
149
147
|
puts "#{story.id} #{msg}"
|
150
148
|
end
|
151
|
-
rescue
|
152
|
-
|
149
|
+
rescue Blanket::Unauthorized
|
150
|
+
unauthorised_message
|
153
151
|
return nil
|
154
152
|
end
|
155
153
|
end
|
156
154
|
|
157
155
|
def story_start
|
158
|
-
pick_and_update(:unstarted, {:
|
156
|
+
pick_and_update(:unstarted, { current_state: 'started' }, 'started', true)
|
159
157
|
end
|
160
158
|
|
161
159
|
def story_unstart
|
162
|
-
pick_and_update(:started, {:
|
160
|
+
pick_and_update(:started, { current_state: 'unstarted' }, 'unstarted', false)
|
163
161
|
end
|
164
162
|
|
165
163
|
def story_estimate
|
@@ -168,7 +166,7 @@ module StoryBranch
|
|
168
166
|
|
169
167
|
def story_finish
|
170
168
|
begin
|
171
|
-
puts
|
169
|
+
puts 'Connecting with Pivotal Tracker'
|
172
170
|
@p.get_project
|
173
171
|
|
174
172
|
unless @p.is_current_branch_a_story?
|
@@ -177,29 +175,29 @@ module StoryBranch
|
|
177
175
|
end
|
178
176
|
|
179
177
|
if GitUtils.has_status? :untracked or GitUtils.has_status? :modified
|
180
|
-
puts
|
181
|
-
puts
|
182
|
-
puts
|
178
|
+
puts 'There are unstaged changes'
|
179
|
+
puts 'Use git add to stage changes before running git finish'
|
180
|
+
puts 'Use git stash if you want to hide changes for this commit'
|
183
181
|
return nil
|
184
182
|
end
|
185
183
|
|
186
184
|
unless GitUtils.has_status? :added or GitUtils.has_status? :staged
|
187
|
-
puts
|
188
|
-
puts
|
185
|
+
puts 'There are no staged changes.'
|
186
|
+
puts 'Nothing to do'
|
189
187
|
return nil
|
190
188
|
end
|
191
189
|
|
192
|
-
puts
|
190
|
+
puts 'Use standard finishing commit message: [y/N]?'
|
193
191
|
commit_message = "[Finishes ##{GitUtils.current_branch_story_parts[:id]}] #{StringUtils.undashed GitUtils.current_branch_story_parts[:title]}"
|
194
192
|
puts commit_message
|
195
193
|
|
196
|
-
if gets.chomp!.downcase ==
|
194
|
+
if gets.chomp!.downcase == 'y'
|
197
195
|
GitUtils.commit commit_message
|
198
196
|
else
|
199
|
-
puts
|
197
|
+
puts 'Aborted'
|
200
198
|
end
|
201
|
-
rescue
|
202
|
-
|
199
|
+
rescue Blanket::Unauthorized
|
200
|
+
unauthorised_message
|
203
201
|
return nil
|
204
202
|
end
|
205
203
|
end
|
@@ -235,23 +233,22 @@ module StoryBranch
|
|
235
233
|
end
|
236
234
|
|
237
235
|
def self.normalised_branch_name s
|
238
|
-
simple_sanitize((dashed s).downcase).squeeze(
|
236
|
+
simple_sanitize((dashed s).downcase).squeeze('-')
|
239
237
|
end
|
240
238
|
|
241
239
|
def self.strip_newlines s
|
242
|
-
s.tr
|
240
|
+
s.tr '\n', '-'
|
243
241
|
end
|
244
242
|
|
245
243
|
def self.undashed s
|
246
|
-
s.gsub(/-/,
|
244
|
+
s.gsub(/-/, ' ').capitalize
|
247
245
|
end
|
248
246
|
|
249
247
|
end
|
250
248
|
|
251
249
|
class GitUtils
|
252
|
-
|
253
250
|
def self.g
|
254
|
-
Git.open
|
251
|
+
Git.open '.'
|
255
252
|
end
|
256
253
|
|
257
254
|
def self.is_existing_branch? name
|
@@ -321,7 +318,7 @@ module StoryBranch
|
|
321
318
|
untracked_rx = /^\?\? (.*)/
|
322
319
|
staged_rx = /^M (.*)/
|
323
320
|
added_rx = /^A (.*)/
|
324
|
-
status = g.lib.send(:command,
|
321
|
+
status = g.lib.send(:command, 'status', '-s').lines
|
325
322
|
return nil if status.length == 0
|
326
323
|
{
|
327
324
|
modified: status_collect(status, modified_rx),
|
@@ -343,16 +340,25 @@ module StoryBranch
|
|
343
340
|
end
|
344
341
|
|
345
342
|
class PivotalUtils
|
346
|
-
|
347
|
-
attr_accessor :api_key, :project_id
|
343
|
+
API_URL = 'https://www.pivotaltracker.com/services/v5/'
|
344
|
+
attr_accessor :api_key, :project_id
|
348
345
|
|
349
346
|
def valid?
|
350
|
-
|
347
|
+
!@api_key.nil? && !@project_id.nil?
|
348
|
+
end
|
349
|
+
|
350
|
+
def api
|
351
|
+
fail 'API key must be specified' unless @api_key
|
352
|
+
Blanket.wrap API_URL, headers: { 'X-TrackerToken' => @api_key }
|
351
353
|
end
|
352
354
|
|
353
355
|
def get_project
|
354
|
-
|
355
|
-
|
356
|
+
fail 'Project ID must be set' unless @project_id
|
357
|
+
api.projects(@project_id.to_i)
|
358
|
+
end
|
359
|
+
|
360
|
+
def story_accessor
|
361
|
+
get_project.stories
|
356
362
|
end
|
357
363
|
|
358
364
|
def is_current_branch_a_story?
|
@@ -364,21 +370,22 @@ module StoryBranch
|
|
364
370
|
end
|
365
371
|
|
366
372
|
def story_from_current_branch
|
367
|
-
|
373
|
+
story_accessor.get(GitUtils.current_story[2].to_i) if GitUtils.current_story.length == 3
|
368
374
|
end
|
369
375
|
|
370
|
-
# TODO:
|
371
|
-
# Filtering on where a story lives (Backlog, IceBox)
|
372
|
-
# Filtering on
|
373
|
-
|
376
|
+
# TODO: Maybe add some other predicates
|
377
|
+
# - Filtering on where a story lives (Backlog, IceBox)
|
378
|
+
# - Filtering on labels
|
379
|
+
# as the need arises...
|
380
|
+
#
|
374
381
|
def filtered_stories_list state, estimated
|
375
|
-
|
376
|
-
stories =
|
382
|
+
options = { with_state: state.to_s }
|
383
|
+
stories = [* story_accessor.get(params: options).payload]
|
377
384
|
if estimated
|
378
|
-
stories.select
|
379
|
-
s.story_type ==
|
380
|
-
|
381
|
-
|
385
|
+
stories.select do |s|
|
386
|
+
s.story_type == 'bug' || s.story_type == 'chore' ||
|
387
|
+
(s.story_type == 'feature' && s.estimate && s.estimate >= 0)
|
388
|
+
end
|
382
389
|
else
|
383
390
|
stories
|
384
391
|
end
|
@@ -394,11 +401,9 @@ module StoryBranch
|
|
394
401
|
|
395
402
|
def select_story stories
|
396
403
|
story_texts = stories.map{|s| one_line_story s }
|
397
|
-
puts
|
398
|
-
story_selection = readline(
|
399
|
-
if story_selection ==
|
400
|
-
return nil
|
401
|
-
end
|
404
|
+
puts 'Leave blank to exit, use <up>/<down> to scroll through stories, TAB to list all and auto-complete'
|
405
|
+
story_selection = readline('Select a story: ', story_texts)
|
406
|
+
return nil if story_selection == '' or story_selection.nil?
|
402
407
|
story = stories.select{|s| story_matcher s, story_selection }.first
|
403
408
|
if story.nil?
|
404
409
|
puts "Not found: #{story_selection}"
|
@@ -409,6 +414,10 @@ module StoryBranch
|
|
409
414
|
end
|
410
415
|
end
|
411
416
|
|
417
|
+
def story_update story, hash
|
418
|
+
get_project.stories(story.id).put(body: hash).payload
|
419
|
+
end
|
420
|
+
|
412
421
|
def story_matcher story, selection
|
413
422
|
m = selection.match(/^(\d*) /)
|
414
423
|
return false unless m
|
@@ -420,9 +429,9 @@ module StoryBranch
|
|
420
429
|
dashed_story_name = StringUtils.normalised_branch_name story.name
|
421
430
|
feature_branch_name = nil
|
422
431
|
puts "You are checked out at: #{GitUtils.current_branch}"
|
423
|
-
while feature_branch_name == nil or feature_branch_name ==
|
424
|
-
puts
|
425
|
-
feature_branch_name = readline(
|
432
|
+
while feature_branch_name == nil or feature_branch_name == ''
|
433
|
+
puts 'Provide a new branch name... (TAB for suggested name)' if [nil, ''].include? feature_branch_name
|
434
|
+
feature_branch_name = readline('Name of feature branch: ', [dashed_story_name])
|
426
435
|
end
|
427
436
|
feature_branch_name.chomp!
|
428
437
|
if validate_branch_name feature_branch_name, story.id
|
@@ -439,7 +448,7 @@ module StoryBranch
|
|
439
448
|
return false
|
440
449
|
end
|
441
450
|
if GitUtils.is_existing_branch? name
|
442
|
-
puts
|
451
|
+
puts 'Error: This name is very similar to an existing branch. Avoid confusion and use a more unique name.'
|
443
452
|
return false
|
444
453
|
end
|
445
454
|
unless valid_branch_name? name
|
@@ -461,9 +470,9 @@ module StoryBranch
|
|
461
470
|
RbReadline.clear_history
|
462
471
|
if completions.length > 0
|
463
472
|
completions.each {|i| Readline::HISTORY.push i}
|
464
|
-
RbReadline.rl_completer_word_break_characters =
|
465
|
-
Readline.completion_proc = proc {|s| completions.grep(/#{Regexp.escape(s)}/) }
|
466
|
-
Readline.completion_append_character =
|
473
|
+
RbReadline.rl_completer_word_break_characters = ''
|
474
|
+
Readline.completion_proc = proc { |s| completions.grep(/#{Regexp.escape(s)}/) }
|
475
|
+
Readline.completion_append_character = ''
|
467
476
|
end
|
468
477
|
Readline.readline(prompt, false)
|
469
478
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: story_branch
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jason Milkins
|
@@ -12,22 +12,22 @@ authors:
|
|
12
12
|
autorequire:
|
13
13
|
bindir: bin
|
14
14
|
cert_chain: []
|
15
|
-
date: 2015-
|
15
|
+
date: 2015-05-12 00:00:00.000000000 Z
|
16
16
|
dependencies:
|
17
17
|
- !ruby/object:Gem::Dependency
|
18
|
-
name:
|
18
|
+
name: blanket_wrapper
|
19
19
|
requirement: !ruby/object:Gem::Requirement
|
20
20
|
requirements:
|
21
21
|
- - "~>"
|
22
22
|
- !ruby/object:Gem::Version
|
23
|
-
version: '0
|
23
|
+
version: '3.0'
|
24
24
|
type: :runtime
|
25
25
|
prerelease: false
|
26
26
|
version_requirements: !ruby/object:Gem::Requirement
|
27
27
|
requirements:
|
28
28
|
- - "~>"
|
29
29
|
- !ruby/object:Gem::Version
|
30
|
-
version: '0
|
30
|
+
version: '3.0'
|
31
31
|
- !ruby/object:Gem::Dependency
|
32
32
|
name: git
|
33
33
|
requirement: !ruby/object:Gem::Requirement
|
@@ -84,6 +84,20 @@ dependencies:
|
|
84
84
|
- - "~>"
|
85
85
|
- !ruby/object:Gem::Version
|
86
86
|
version: '3.0'
|
87
|
+
- !ruby/object:Gem::Dependency
|
88
|
+
name: byebug
|
89
|
+
requirement: !ruby/object:Gem::Requirement
|
90
|
+
requirements:
|
91
|
+
- - "~>"
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '4.0'
|
94
|
+
type: :development
|
95
|
+
prerelease: false
|
96
|
+
version_requirements: !ruby/object:Gem::Requirement
|
97
|
+
requirements:
|
98
|
+
- - "~>"
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: '4.0'
|
87
101
|
description: Simple gem that fetches the available stories in your pivotaltracker
|
88
102
|
project and allows you to create a git branch with the name based on the selected
|
89
103
|
story
|