story_branch 0.2.5 → 0.2.6
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 +15 -7
- data/lib/story_branch.rb +61 -81
- metadata +14 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 27b478138908ff36d93a8c9c641136de6b789f1d
|
4
|
+
data.tar.gz: b7435c40b8257bb544f1379436a7559115100f2c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: aa08dfe184feb00523a32995ea457952ef7881d34473d2aaf3423c4dbf2aedfa83050786090d1f84b2651bcf64061c2809ea0ef01eed3f052d53947f629af4e3
|
7
|
+
data.tar.gz: acec4e4033a5b0a6deff313be9b34253e89c379b614a0374c4e0b9a91d424e6377fb03644ecaf9867c14b964721a13d7314ab736509308bd6f44745bd8f6409f
|
data/README.md
CHANGED
@@ -12,12 +12,17 @@ Pivotal Tracker Story. It will get started stories from your active
|
|
12
12
|
project. You can enter text and press TAB to search for a story
|
13
13
|
name, or TAB to show the full list. It will then suggest an editable
|
14
14
|
branch name. When the branch is created the `story_id` will
|
15
|
-
be appended to it.
|
15
|
+
be appended to it. Enter a blank to exit.
|
16
16
|
|
17
17
|
`git start`: Start a story in Pivotal Tracker from the terminal.
|
18
18
|
It'll get all unstarted stories in your current project. You can
|
19
19
|
enter text and press TAB to search for a story name, or TAB to show
|
20
|
-
the full list.
|
20
|
+
the full list. Enter blank to exit.
|
21
|
+
|
22
|
+
`git unstart`: Unstart a started story in Pivotal Tracker from the terminal.
|
23
|
+
It'll get all started stories in your current project. You can
|
24
|
+
enter text and press TAB to search for a story name, or TAB to show
|
25
|
+
the full list. Enter blank to exit.
|
21
26
|
|
22
27
|
`git finish`: Create commit/message for the staged changes, e.g:
|
23
28
|
"[Finishes #1234567] My Story Title" - optionally Finishes the story
|
@@ -48,7 +53,7 @@ Can be saved to `~/` or `./`
|
|
48
53
|
|
49
54
|
You run story_branch from the git/project root folder.
|
50
55
|
|
51
|
-
`start`, `
|
56
|
+
`start`, `unstart` and `story`, are run interactively and will display a
|
52
57
|
list of stories to work with.
|
53
58
|
|
54
59
|
`finish` will scan the current branch name for a story id (as its
|
@@ -56,6 +61,8 @@ suffix) and if a valid, active story is found on pivotal tracker it
|
|
56
61
|
will create a commit with a message to trigger pivotal's git
|
57
62
|
integraton features.
|
58
63
|
|
64
|
+
All text entry supports Readline keyboard shortcuts / bindings, (word fwd/back, undo etc.)
|
65
|
+
|
59
66
|
### Command names
|
60
67
|
|
61
68
|
It's possible to the commands in a few ways, we have deprecated the
|
@@ -63,10 +70,11 @@ old commmand names, and now encourage only the use of the `git`
|
|
63
70
|
style usage.
|
64
71
|
|
65
72
|
git style | deprecated
|
66
|
-
|
67
|
-
git story | story_branch
|
68
|
-
git start | story_start
|
69
|
-
git
|
73
|
+
------------+---------------+--------------
|
74
|
+
git story | story_branch | story-branch
|
75
|
+
git start | story_start | story-start
|
76
|
+
git unstart| story_unstart | story-unstart
|
77
|
+
git finish | story_finish | story-finish
|
70
78
|
|
71
79
|
## Contributing
|
72
80
|
|
data/lib/story_branch.rb
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
# Dominic Wong <dominic.wong.617@gmail.com>
|
6
6
|
# Gabe Hollombe <gabe@neo.com>
|
7
7
|
#
|
8
|
-
# Version: 0.2.
|
8
|
+
# Version: 0.2.6
|
9
9
|
#
|
10
10
|
# ## Description
|
11
11
|
#
|
@@ -86,50 +86,45 @@ require 'pivotal-tracker'
|
|
86
86
|
require 'rb-readline'
|
87
87
|
require 'readline'
|
88
88
|
require 'git'
|
89
|
-
require 'active_support/core_ext/string/inflections'
|
90
89
|
require 'levenshtein'
|
91
90
|
|
92
91
|
trap('INT') {exit}
|
93
92
|
|
94
93
|
module StoryBranch
|
95
|
-
|
96
94
|
class Main
|
97
|
-
|
98
95
|
ERRORS = {
|
99
|
-
|
96
|
+
'Stories in the started state must be estimated.' =>
|
100
97
|
"Error: Pivotal won't allow you to start an unestimated story"
|
101
98
|
}
|
102
99
|
|
103
|
-
PIVOTAL_CONFIG_FILES = ['.story_branch',"#{ENV['HOME']}/.story_branch"]
|
100
|
+
PIVOTAL_CONFIG_FILES = ['.story_branch', "#{ENV['HOME']}/.story_branch"]
|
101
|
+
PIVOTAL_API_CONFIG_FILES = ['.pivotal_api_key', "#{ENV['HOME']}/.pivotal_api_key"]
|
104
102
|
|
105
103
|
attr_accessor :p
|
106
104
|
|
107
105
|
def initialize
|
108
|
-
if config_file
|
109
|
-
|
110
|
-
|
111
|
-
@p
|
112
|
-
@p.
|
113
|
-
@p.project_id = config_value "project", 'PIVOTAL_PROJECT_ID'
|
106
|
+
@pivotal_info = YAML.load_file config_file(PIVOTAL_CONFIG_FILES) if config_file(PIVOTAL_CONFIG_FILES)
|
107
|
+
@pivotal_api_info = YAML.load_file config_file(PIVOTAL_API_CONFIG_FILES) if config_file(PIVOTAL_API_CONFIG_FILES)
|
108
|
+
@p = PivotalUtils.new
|
109
|
+
@p.api_key = api_config_value
|
110
|
+
@p.project_id = config_value 'project', 'PIVOTAL_PROJECT_ID'
|
114
111
|
exit unless @p.valid?
|
115
112
|
end
|
116
113
|
|
117
114
|
def create_story_branch
|
118
115
|
begin
|
119
|
-
puts
|
116
|
+
puts 'Connecting with Pivotal Tracker'
|
120
117
|
@p.get_project
|
121
|
-
puts
|
118
|
+
puts 'Getting stories...'
|
122
119
|
stories = @p.display_stories :started, false
|
123
120
|
if stories.length < 1
|
124
|
-
puts
|
121
|
+
puts 'No stories started, exiting'
|
125
122
|
exit
|
126
123
|
end
|
127
124
|
story = @p.select_story stories
|
128
|
-
if story
|
129
|
-
@p.create_feature_branch story
|
130
|
-
end
|
125
|
+
@p.create_feature_branch story if story
|
131
126
|
rescue RestClient::Unauthorized
|
132
|
-
$stderr.puts
|
127
|
+
$stderr.puts 'Pivotal API key or Project ID invalid'
|
133
128
|
return nil
|
134
129
|
end
|
135
130
|
end
|
@@ -178,39 +173,44 @@ module StoryBranch
|
|
178
173
|
end
|
179
174
|
|
180
175
|
if GitUtils.has_status? :untracked or GitUtils.has_status? :modified
|
181
|
-
puts
|
182
|
-
puts
|
183
|
-
puts
|
176
|
+
puts 'There are unstaged changes'
|
177
|
+
puts 'Use git add to stage changes before running git finish'
|
178
|
+
puts 'Use git stash if you want to hide changes for this commit'
|
184
179
|
return
|
185
180
|
end
|
186
181
|
|
187
182
|
unless GitUtils.has_status? :added or GitUtils.has_status? :staged
|
188
|
-
puts
|
189
|
-
puts
|
183
|
+
puts 'There are no staged changes.'
|
184
|
+
puts 'Nothing to do'
|
190
185
|
return
|
191
186
|
end
|
192
187
|
|
193
|
-
puts
|
188
|
+
puts 'Use standard finishing commit message: [y/N]?'
|
194
189
|
commit_message = "[Finishes ##{GitUtils.current_branch_story_parts[:id]}] #{StringUtils.undashed GitUtils.current_branch_story_parts[:title]}"
|
195
190
|
puts commit_message
|
196
191
|
|
197
|
-
if gets.chomp!.downcase ==
|
192
|
+
if gets.chomp!.downcase == 'y'
|
198
193
|
GitUtils.commit commit_message
|
199
194
|
else
|
200
195
|
puts "Aborted"
|
201
196
|
end
|
202
197
|
rescue RestClient::Unauthorized
|
203
|
-
$stderr.puts
|
198
|
+
$stderr.puts 'Pivotal API key or Project ID invalid'
|
204
199
|
return nil
|
205
200
|
end
|
206
201
|
end
|
207
202
|
|
208
|
-
def config_file
|
209
|
-
|
203
|
+
def config_file file_set
|
204
|
+
file_set.select{|conf| File.exists? conf}.first
|
205
|
+
end
|
206
|
+
|
207
|
+
def api_config_value
|
208
|
+
return @pivotal_api_info['api'] if @pivotal_api_info && @pivotal_api_info['api']
|
209
|
+
config_value 'api', 'PIVOTAL_API_KEY'
|
210
210
|
end
|
211
211
|
|
212
212
|
def config_value key, env
|
213
|
-
value = @pivotal_info[key] if @pivotal_info
|
213
|
+
value = @pivotal_info[key] if @pivotal_info && @pivotal_info[key]
|
214
214
|
value ||= env_required env
|
215
215
|
value
|
216
216
|
end
|
@@ -222,62 +222,52 @@ module StoryBranch
|
|
222
222
|
end
|
223
223
|
ENV[var_name]
|
224
224
|
end
|
225
|
-
|
226
225
|
end
|
227
226
|
|
228
227
|
class StringUtils
|
229
|
-
|
230
228
|
def self.dashed s
|
231
229
|
s.tr(' _,./:;+&', '-')
|
232
230
|
end
|
233
231
|
|
234
232
|
def self.simple_sanitize s
|
235
|
-
strip_newlines
|
233
|
+
strip_newlines s.tr '\'"%!@#$(){}[]*\\?', ''
|
236
234
|
end
|
237
235
|
|
238
236
|
def self.normalised_branch_name s
|
239
|
-
StringUtils.simple_sanitize
|
237
|
+
StringUtils.simple_sanitize StringUtils.dashed(s).downcase.squeeze('-')
|
240
238
|
end
|
241
239
|
|
242
240
|
def self.strip_newlines s
|
243
|
-
s.tr "\n",
|
241
|
+
s.tr "\n", '-'
|
244
242
|
end
|
245
243
|
|
246
244
|
def self.undashed s
|
247
245
|
s.underscore.humanize
|
248
246
|
end
|
249
|
-
|
250
247
|
end
|
251
248
|
|
252
249
|
class GitUtils
|
253
|
-
|
254
250
|
def self.g
|
255
|
-
Git.open
|
251
|
+
Git.open '.'
|
256
252
|
end
|
257
253
|
|
258
254
|
def self.is_existing_branch? name
|
259
255
|
branch_names.each do |n|
|
260
|
-
if Levenshtein.distance(n, name) < 3
|
261
|
-
return true
|
262
|
-
end
|
256
|
+
return true if Levenshtein.distance(n, name) < 3
|
263
257
|
existing_branch_name = n.match(/(.*)(-[1-9][0-9]+$)/)
|
264
258
|
if existing_branch_name
|
265
259
|
levenshtein_distance = Levenshtein.distance existing_branch_name[1], name
|
266
|
-
if levenshtein_distance < 3
|
267
|
-
return true
|
268
|
-
end
|
260
|
+
return true if levenshtein_distance < 3
|
269
261
|
end
|
270
262
|
end
|
271
|
-
|
263
|
+
false
|
272
264
|
end
|
273
265
|
|
274
266
|
def self.is_existing_story? id
|
275
267
|
branch_names.each do |n|
|
276
268
|
branch_id = n.match(/-[1-9][0-9]+$/)
|
277
269
|
if branch_id
|
278
|
-
if branch_id.to_s == "-#{id}"
|
279
|
-
return true
|
280
|
-
end
|
270
|
+
return true if branch_id.to_s == "-#{id}"
|
281
271
|
end
|
282
272
|
end
|
283
273
|
false
|
@@ -297,11 +287,7 @@ module StoryBranch
|
|
297
287
|
|
298
288
|
def self.current_branch_story_parts
|
299
289
|
matches = current_branch.match(/(.*)-(\d+$)/)
|
300
|
-
if matches.length == 3
|
301
|
-
{ title: matches[1], id: matches[2] }
|
302
|
-
else
|
303
|
-
nil
|
304
|
-
end
|
290
|
+
{ title: matches[1], id: matches[2] } if matches.length == 3
|
305
291
|
end
|
306
292
|
|
307
293
|
def self.create_branch name
|
@@ -310,11 +296,7 @@ module StoryBranch
|
|
310
296
|
end
|
311
297
|
|
312
298
|
def self.status_collect status, regex
|
313
|
-
status.select{|e|
|
314
|
-
e.match(regex)
|
315
|
-
}.map{|e|
|
316
|
-
e.match(regex)[1]
|
317
|
-
}
|
299
|
+
status.select { |s| s.match(regex) }.map { |e| e.match(regex)[1] }
|
318
300
|
end
|
319
301
|
|
320
302
|
def self.status
|
@@ -340,11 +322,9 @@ module StoryBranch
|
|
340
322
|
def self.commit message
|
341
323
|
g.commit(message)
|
342
324
|
end
|
343
|
-
|
344
325
|
end
|
345
326
|
|
346
327
|
class PivotalUtils
|
347
|
-
|
348
328
|
attr_accessor :api_key, :project_id, :project
|
349
329
|
|
350
330
|
def valid?
|
@@ -357,11 +337,10 @@ module StoryBranch
|
|
357
337
|
end
|
358
338
|
|
359
339
|
def is_current_branch_a_story?
|
360
|
-
GitUtils.current_story
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
include? GitUtils.current_story[2].to_i
|
340
|
+
GitUtils.current_story && GitUtils.current_story.length == 3 &&
|
341
|
+
filtered_stories_list(:started, true)
|
342
|
+
.map(&:id)
|
343
|
+
.include?(GitUtils.current_story[2].to_i)
|
365
344
|
end
|
366
345
|
|
367
346
|
def story_from_current_branch
|
@@ -377,9 +356,9 @@ module StoryBranch
|
|
377
356
|
stories = project.stories.all({current_state: state})
|
378
357
|
if estimated
|
379
358
|
stories.select{|s|
|
380
|
-
s.story_type ==
|
381
|
-
s.story_type ==
|
382
|
-
(s.story_type ==
|
359
|
+
s.story_type == 'bug' ||
|
360
|
+
s.story_type == 'chore' ||
|
361
|
+
(s.story_type == 'feature' && s.estimate && s.estimate >= 0)}
|
383
362
|
else
|
384
363
|
stories
|
385
364
|
end
|
@@ -421,16 +400,17 @@ module StoryBranch
|
|
421
400
|
dashed_story_name = StringUtils.normalised_branch_name story.name
|
422
401
|
feature_branch_name = nil
|
423
402
|
puts "You are checked out at: #{GitUtils.current_branch}"
|
424
|
-
while feature_branch_name == nil
|
425
|
-
puts
|
426
|
-
feature_branch_name = readline(
|
403
|
+
while feature_branch_name == nil || feature_branch_name == ""
|
404
|
+
puts 'Provide a new branch name... (TAB for suggested name)' if [nil, ''].include? feature_branch_name
|
405
|
+
feature_branch_name = readline('Name of feature branch: ', [dashed_story_name])
|
427
406
|
end
|
428
407
|
feature_branch_name.chomp!
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
408
|
+
|
409
|
+
return unless validate_branch_name feature_branch_name, story.id
|
410
|
+
|
411
|
+
feature_branch_name_with_story_id = "#{feature_branch_name}-#{story.id}"
|
412
|
+
puts "Creating: #{feature_branch_name_with_story_id} with #{GitUtils.current_branch} as parent"
|
413
|
+
GitUtils.create_branch feature_branch_name_with_story_id
|
434
414
|
end
|
435
415
|
|
436
416
|
# Branch name validation
|
@@ -440,7 +420,7 @@ module StoryBranch
|
|
440
420
|
return false
|
441
421
|
end
|
442
422
|
if GitUtils.is_existing_branch? name
|
443
|
-
puts "Error:
|
423
|
+
puts "Error: The name: #{name}, is very similar to an existing branch. To avoid confusion use a more unique name."
|
444
424
|
return false
|
445
425
|
end
|
446
426
|
unless valid_branch_name? name
|
@@ -461,10 +441,10 @@ module StoryBranch
|
|
461
441
|
# Store the state of the terminal
|
462
442
|
RbReadline.clear_history
|
463
443
|
if completions.length > 0
|
464
|
-
completions.each {|i| Readline::HISTORY.push i}
|
465
|
-
RbReadline.rl_completer_word_break_characters =
|
466
|
-
Readline.completion_proc = proc {|s| completions.grep(/#{Regexp.escape(s)}/) }
|
467
|
-
Readline.completion_append_character =
|
444
|
+
completions.each { |i| Readline::HISTORY.push i}
|
445
|
+
RbReadline.rl_completer_word_break_characters = ''
|
446
|
+
Readline.completion_proc = proc { |s| completions.grep(/#{Regexp.escape(s)}/) }
|
447
|
+
Readline.completion_append_character = ''
|
468
448
|
end
|
469
449
|
Readline.readline(prompt, false)
|
470
450
|
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.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jason Milkins
|
@@ -17,70 +17,70 @@ dependencies:
|
|
17
17
|
name: pivotal-tracker
|
18
18
|
requirement: !ruby/object:Gem::Requirement
|
19
19
|
requirements:
|
20
|
-
- - ~>
|
20
|
+
- - "~>"
|
21
21
|
- !ruby/object:Gem::Version
|
22
22
|
version: '0.5'
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
26
26
|
requirements:
|
27
|
-
- - ~>
|
27
|
+
- - "~>"
|
28
28
|
- !ruby/object:Gem::Version
|
29
29
|
version: '0.5'
|
30
30
|
- !ruby/object:Gem::Dependency
|
31
31
|
name: git
|
32
32
|
requirement: !ruby/object:Gem::Requirement
|
33
33
|
requirements:
|
34
|
-
- - ~>
|
34
|
+
- - "~>"
|
35
35
|
- !ruby/object:Gem::Version
|
36
36
|
version: '1.2'
|
37
37
|
type: :runtime
|
38
38
|
prerelease: false
|
39
39
|
version_requirements: !ruby/object:Gem::Requirement
|
40
40
|
requirements:
|
41
|
-
- - ~>
|
41
|
+
- - "~>"
|
42
42
|
- !ruby/object:Gem::Version
|
43
43
|
version: '1.2'
|
44
44
|
- !ruby/object:Gem::Dependency
|
45
45
|
name: levenshtein-ffi
|
46
46
|
requirement: !ruby/object:Gem::Requirement
|
47
47
|
requirements:
|
48
|
-
- - ~>
|
48
|
+
- - "~>"
|
49
49
|
- !ruby/object:Gem::Version
|
50
50
|
version: '1.0'
|
51
51
|
type: :runtime
|
52
52
|
prerelease: false
|
53
53
|
version_requirements: !ruby/object:Gem::Requirement
|
54
54
|
requirements:
|
55
|
-
- - ~>
|
55
|
+
- - "~>"
|
56
56
|
- !ruby/object:Gem::Version
|
57
57
|
version: '1.0'
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: rb-readline
|
60
60
|
requirement: !ruby/object:Gem::Requirement
|
61
61
|
requirements:
|
62
|
-
- - ~>
|
62
|
+
- - "~>"
|
63
63
|
- !ruby/object:Gem::Version
|
64
64
|
version: '0.5'
|
65
65
|
type: :runtime
|
66
66
|
prerelease: false
|
67
67
|
version_requirements: !ruby/object:Gem::Requirement
|
68
68
|
requirements:
|
69
|
-
- - ~>
|
69
|
+
- - "~>"
|
70
70
|
- !ruby/object:Gem::Version
|
71
71
|
version: '0.5'
|
72
72
|
- !ruby/object:Gem::Dependency
|
73
73
|
name: rspec
|
74
74
|
requirement: !ruby/object:Gem::Requirement
|
75
75
|
requirements:
|
76
|
-
- - ~>
|
76
|
+
- - "~>"
|
77
77
|
- !ruby/object:Gem::Version
|
78
78
|
version: '3.0'
|
79
79
|
type: :development
|
80
80
|
prerelease: false
|
81
81
|
version_requirements: !ruby/object:Gem::Requirement
|
82
82
|
requirements:
|
83
|
-
- - ~>
|
83
|
+
- - "~>"
|
84
84
|
- !ruby/object:Gem::Version
|
85
85
|
version: '3.0'
|
86
86
|
description: Simple gem that fetches the available stories in your pivotaltracker
|
@@ -142,17 +142,17 @@ require_paths:
|
|
142
142
|
- lib
|
143
143
|
required_ruby_version: !ruby/object:Gem::Requirement
|
144
144
|
requirements:
|
145
|
-
- -
|
145
|
+
- - ">="
|
146
146
|
- !ruby/object:Gem::Version
|
147
147
|
version: 1.9.3
|
148
148
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
149
149
|
requirements:
|
150
|
-
- -
|
150
|
+
- - ">="
|
151
151
|
- !ruby/object:Gem::Version
|
152
152
|
version: '0'
|
153
153
|
requirements: []
|
154
154
|
rubyforge_project:
|
155
|
-
rubygems_version: 2.
|
155
|
+
rubygems_version: 2.4.3
|
156
156
|
signing_key:
|
157
157
|
specification_version: 4
|
158
158
|
summary: Story Branch - create git branches based on pivotal tracker stories
|