story_branch 0.2.7 → 0.2.8
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 +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
|