story_branch 0.1.8

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0f515e7349d6e9e3c97deb9036626e0b609a4961
4
+ data.tar.gz: 7cf773b74675fd4060b5c7f664b0dd4dcad6d87d
5
+ SHA512:
6
+ metadata.gz: 1146269635ae3f44f03aae8da7556fb566a65648997ae107aa59531958a800589eccf0129bc2c0661d80ddd7150b6a9910808890f8a780fbb770b9b889022e00
7
+ data.tar.gz: e39bee90b82d67fd713e9a8155c756b9b9b548f2552c9aaef89a694a9f305ad7561a4f97daa33483c31e5332f46a9d81d95c396b80c3f24b618663fc6428c093
data/LICENCE ADDED
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Jason Milkins, Gabe Hollombe, Rui Baltazar, Dominic Wong
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,114 @@
1
+ [![Gem Version](https://badge.fury.io/rb/story_branch.png)](http://badge.fury.io/rb/story_branch)
2
+
3
+ # Story Branch
4
+
5
+ Create a git feature branch with automatic reference to a Pivotal Tracker Story
6
+
7
+ By default `story_branch` will present a list of started stories from
8
+ your Pivotal Tracker project, you select one and then provide
9
+ a feature branch name for that story. The branch will be created and
10
+ the name will include the selected `story_id` as a suffix.
11
+
12
+ When picking a story, enter the selection number on the left, using the up
13
+ arrow (or C-p) will scroll through the selection numbers.
14
+
15
+ Once a story is selected, a feature branch name can be entered, a
16
+ suggestion is provided (press the up arrow (or C-p) to see it)
17
+
18
+ The feature branch name input has full
19
+ [Readline](http://tiswww.case.edu/php/chet/readline/rluserman.html#SEC5)
20
+ capability, to make it easy and pleasant to edit.
21
+
22
+ ## Setup
23
+
24
+ Install the gem:
25
+
26
+ gem install story_branch
27
+
28
+ Config the Pivotal API key and Project ID, either in the environment
29
+ or using a config YAML file. (`.story_branch` in the git root, or
30
+ `~/.story_branch`)
31
+
32
+ The environment variables to set are `PIVOTAL_API_KEY` and `PIVOTAL_PROJECT_ID`
33
+
34
+ The **Pivotal API** key is visble at the bottom of your Pivotal Tracker
35
+ Profile page when you're logged in. the **Project ID** is in the URL for
36
+ your pivotal project.
37
+
38
+ If you decide to use the `.story_branch` config file, it should look
39
+ something like:
40
+
41
+ project: 123456
42
+ api: REHTKHMYKEYISM328974Y32487AND_SO_ON
43
+
44
+ Or just:
45
+
46
+ project: 123456
47
+
48
+ Replace the values with your own. Any value not found in the config
49
+ will attempt to be set from the environment. An error is thrown for a
50
+ value that cannot be found anywhere.
51
+
52
+ Note, that only one config file will be used at the moment, values
53
+ **CANNOT** currently be split over `.story_branch` in the git root and
54
+ `~/.story_branch`
55
+
56
+ ## Usage
57
+
58
+ While checked out at your master branch, and located in the git root.
59
+
60
+ story_branch
61
+
62
+ Follow the directions on screen. When the process is finished, you'll
63
+ be switched automatically to the newly created branch.
64
+
65
+ ## Aliases
66
+
67
+ You can also run story branch using the following aliases.
68
+
69
+ git story
70
+
71
+ git story-branch
72
+
73
+ story-branch
74
+
75
+
76
+ ## Roadmap
77
+
78
+ Prepare a v1.0 release
79
+
80
+ ## Changelog
81
+
82
+ * Banish constraint of master as parent branch
83
+ * Verify API key / Project ID
84
+ * Update config method to use YAML .story_branch files (in git root or $HOME) see above.
85
+ * Build/Publish as Ruby gem
86
+ * Simple sanitization
87
+ * Begin test coverage
88
+ * Refactor to class
89
+ * Provide readline editing for inputs
90
+ * Present safe version of story name (dash-cased) for editing
91
+ * Readline history injection for story selection & branch name suggestion
92
+ * Validate that branchname is 'legal'
93
+ * Validate that branchname doesn't already exist (strip pivotal
94
+ tracker ids suffix from existing names when present)
95
+ * Use Levenshtein Distance to determine if name is (very) similar to
96
+ existing branch names
97
+ * Use Git gem
98
+ * Use ActiveSupport gem
99
+ * Use Levenschtein-ffi gem
100
+ * ~~Look for pivotal project id (.pivotal-id) in repo root (we assume
101
+ we're in project root.) fallback to `PIVOTAL_PROJECT_ID` environment
102
+ var~~ **PLEASE NOTE:** Now uses `.story_branch` or `~/.story_branch`
103
+ as config file, containing YAML. Only one is used, the local root
104
+ `.story_branch` is favoured.
105
+
106
+ ## Contributing
107
+
108
+ If you'd like to contribute to `story_branch` please follow the steps below.
109
+
110
+ * Fork, start a feature branch and check it out
111
+ * Write tests / Pass them
112
+ * Send a pull request
113
+
114
+ **Note:** Pull requests require full test coverage to be accepted.
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ require 'story_branch'
3
+ # require_relative '../lib/story_branch'
4
+
5
+ sb = StoryBranch.new()
6
+ sb.connect
data/bin/git-story ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ require 'story_branch'
3
+ # require_relative '../lib/story_branch'
4
+
5
+ sb = StoryBranch.new()
6
+ sb.connect
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ require 'story_branch'
3
+ # require_relative '../lib/story_branch'
4
+
5
+ sb = StoryBranch.new()
6
+ sb.connect
data/bin/story-branch ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ require 'story_branch'
3
+ # require_relative '../lib/story_branch'
4
+
5
+ sb = StoryBranch.new()
6
+ sb.connect
data/bin/story_branch ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ require 'story_branch'
3
+ # require_relative '../lib/story_branch'
4
+
5
+ sb = StoryBranch.new()
6
+ sb.connect
@@ -0,0 +1,214 @@
1
+ # Name: story_branch (recommend: setting a git alias as "git story")
2
+ #
3
+ # Authors: Jason Milkins <jason@opsmanager.com>
4
+ # Rui Baltazar <rui.p.baltazar@gmail.com>
5
+ # Gabe Hollombe <gabe@neo.com>
6
+ # Dominic Wong <dominic.wong.617@gmail.com>
7
+ #
8
+ # Version: 0.1.8
9
+ #
10
+ # Description:
11
+ #
12
+ # Create a git branch with automatic reference to a Pivotal Tracker
13
+ # Story ID
14
+ #
15
+ # Commentary:
16
+ #
17
+ # By default story_branch will present a list of started stories from
18
+ # your active PivotalTracker project, you select one and then provide
19
+ # a feature branch name for that story. The branch will be created and
20
+ # the name will include the story_id as a suffix.
21
+ #
22
+ # When picking a story, enter the selection number on the left (up
23
+ # arrow / C-p will scroll through the numbers)
24
+ #
25
+ # Once a story is selected, a feature branch name must be entered, a
26
+ # suggestion is shown if you press up arrow / C-p
27
+ #
28
+ # Usage:
29
+ #
30
+ # Note: Run story_branch from the project root folder, with the
31
+ # master branch checked out, or an error will be thrown.
32
+ #
33
+ # You must have a PIVOTAL_API_KEY environment variable set to your
34
+ # Pivotal api key, and either a .pivotal-id file or PIVOTAL_PROJECT_ID
35
+ # environment variable set, (the file will supersede the environment
36
+ # variable)
37
+ #
38
+
39
+ require 'yaml'
40
+ require 'pivotal-tracker'
41
+ require 'readline'
42
+ require 'git'
43
+ require 'levenshtein'
44
+
45
+ class StoryBranch
46
+
47
+ # Config file = .pivotal or ~/.pivotal
48
+ # contains YAML
49
+ # project: pivotal-id
50
+ # api: pivotal api key
51
+
52
+ # NOTE: Is this Windows friendly? Await freak-outs from those users... *crickets*
53
+ PIVOTAL_CONFIG_FILES = ['.story_branch',"#{ENV['HOME']}/.story_branch"]
54
+
55
+ def initialize
56
+ if config_file
57
+ @pivotal_info = YAML.load_file config_file
58
+ end
59
+
60
+ @api_key = config_value "api", 'PIVOTAL_API_KEY'
61
+ @project_id = config_value "project", 'PIVOTAL_PROJECT_ID'
62
+ end
63
+
64
+ def config_file
65
+ PIVOTAL_CONFIG_FILES.select{|conf| File.exists? conf}.first
66
+ end
67
+
68
+ def config_value key, env
69
+ value = @pivotal_info[key] if @pivotal_info and @pivotal_info[key]
70
+ value ||= env_required env
71
+ value
72
+ end
73
+
74
+ def connect
75
+ begin
76
+ pivotal_story_branch @api_key, @project_id
77
+ rescue RestClient::Unauthorized
78
+ puts "Pivotal API key or Project ID invalid"
79
+ exit
80
+ end
81
+ end
82
+
83
+ def valid?
84
+ return (not @api_key.strip.empty? and not @project_id.strip.empty?)
85
+ end
86
+
87
+ private
88
+ def env_required var_name
89
+ if ENV[var_name].nil?
90
+ puts "$#{var_name} must be set or in .story_branch file"
91
+ exit
92
+ end
93
+ ENV[var_name]
94
+ end
95
+
96
+ def readline prompt, history=[]
97
+ if history.length > 0
98
+ history.each {|i| Readline::HISTORY.push i}
99
+ end
100
+ begin
101
+ Readline.readline(prompt, false)
102
+ rescue Interrupt
103
+ exit
104
+ end
105
+ end
106
+
107
+ def dashed s
108
+ s.tr(' _,./:;', '-')
109
+ end
110
+
111
+ def simple_sanitize s
112
+ s.tr '\'"%!@#$(){}[]*\\?', ''
113
+ end
114
+
115
+ # Branch name validation
116
+ def validate_branch_name name
117
+ unless valid_branch_name? name
118
+ puts "Error: #{name}\nis an invalid name."
119
+ return false
120
+ end
121
+ existing_name_score = is_existing_branch?(name)
122
+ unless existing_name_score == -1
123
+ puts <<-END.strip_heredoc
124
+ Name Collision Error:
125
+
126
+ #{name}
127
+
128
+ This is too similar to the name of an existing
129
+ branch, a more unique name is required
130
+ END
131
+ end
132
+ end
133
+
134
+ def valid_branch_name? name
135
+ # Valid names begin with a letter and are followed by alphanumeric
136
+ # with _ . - as allowed punctuation
137
+ valid = /[a-zA-Z][-._0-9a-zA-Z]*/
138
+ name.match valid
139
+ end
140
+
141
+ # Git operations
142
+
143
+ def is_existing_branch? name
144
+ # we don't use the Git gem's is_local_branch? because we want to
145
+ # ignore the id suffix while still avoiding name collisions
146
+ git_branch_names.each do |n|
147
+ normalised_branch_name = simple_sanitize(dashed(n.match(/(^.*)(-[1-9][0-9]+$)?/)[1]))
148
+ levenshtein_distance = Levenshtein.distance normalised_branch_name, name
149
+ if levenshtein_distance < 2
150
+ return levenshtein_distance
151
+ end
152
+ end
153
+ return -1
154
+ end
155
+
156
+ def git_branch_names
157
+ g = Git.open "."
158
+ g.branches.map(&:name)
159
+ end
160
+
161
+ def git_current_branch
162
+ g = Git.open "."
163
+ g.current_branch
164
+ end
165
+
166
+ def git_create_branch name
167
+ g = Git.open "."
168
+ g.branch(name).create
169
+ g.branch(name).checkout
170
+ end
171
+
172
+ # Use Pivotal tracker API to get Stories
173
+
174
+ def list_pivotal_stories api_key, project_id
175
+ PivotalTracker::Client.token = api_key
176
+ project = PivotalTracker::Project.find(project_id.to_i)
177
+ stories = project.stories.all({current_state: :started})
178
+ stories.each_with_index{|s,i| puts "[#{i+1}] ##{s.id} : #{s.name}"}
179
+ stories
180
+ end
181
+
182
+ def select_story stories
183
+ story_selection = nil
184
+ while story_selection == nil or story_selection == 0 or story_selection > stories.length + 1
185
+ puts "invalid selection" if story_selection != nil
186
+ story_selection = readline("Select a story: ", Range.new(1,stories.length).to_a.map(&:to_s)).to_i
187
+ end
188
+ story = stories[story_selection - 1]
189
+ puts "Selected : ##{story.id} : #{story.name}"
190
+ return story
191
+ end
192
+
193
+ def create_feature_branch story
194
+ current_branch = git_current_branch
195
+ dashed_story_name = simple_sanitize((dashed story.name).downcase).squeeze("-")
196
+ feature_branch_name = nil
197
+ puts "You are checked out at: #{current_branch}"
198
+ while feature_branch_name == nil or feature_branch_name == ""
199
+ puts "Provide a new branch name..." if [nil, ""].include? feature_branch_name
200
+ feature_branch_name = readline("Name of feature branch: ", [dashed_story_name])
201
+ end
202
+ feature_branch_name.chomp!
203
+ validate_branch_name feature_branch_name
204
+ feature_branch_name_with_story_id = "#{feature_branch_name}-#{story.id}"
205
+ puts "Creating: #{feature_branch_name_with_story_id} with #{current_branch} as parent"
206
+ git_create_branch feature_branch_name_with_story_id
207
+ end
208
+
209
+ def pivotal_story_branch api_key, project_id
210
+ stories = list_pivotal_stories api_key, project_id
211
+ story = select_story stories
212
+ create_feature_branch story
213
+ end
214
+ end
metadata ADDED
@@ -0,0 +1,121 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: story_branch
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.8
5
+ platform: ruby
6
+ authors:
7
+ - Jason Milkins
8
+ - Gabe Hollombe
9
+ - Rui Baltazar
10
+ - Dominic Wong
11
+ autorequire:
12
+ bindir: bin
13
+ cert_chain: []
14
+ date: 2014-06-24 00:00:00.000000000 Z
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: pivotal-tracker
18
+ requirement: !ruby/object:Gem::Requirement
19
+ requirements:
20
+ - - "~>"
21
+ - !ruby/object:Gem::Version
22
+ version: '0.5'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '0.5'
30
+ - !ruby/object:Gem::Dependency
31
+ name: git
32
+ requirement: !ruby/object:Gem::Requirement
33
+ requirements:
34
+ - - "~>"
35
+ - !ruby/object:Gem::Version
36
+ version: '1.2'
37
+ type: :runtime
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - "~>"
42
+ - !ruby/object:Gem::Version
43
+ version: '1.2'
44
+ - !ruby/object:Gem::Dependency
45
+ name: levenshtein-ffi
46
+ requirement: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - "~>"
49
+ - !ruby/object:Gem::Version
50
+ version: '1.0'
51
+ type: :runtime
52
+ prerelease: false
53
+ version_requirements: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - "~>"
56
+ - !ruby/object:Gem::Version
57
+ version: '1.0'
58
+ - !ruby/object:Gem::Dependency
59
+ name: rspec
60
+ requirement: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: '0'
65
+ type: :development
66
+ prerelease: false
67
+ version_requirements: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: '0'
72
+ description: Simple gem that fetches the available stories in your pivotaltracker
73
+ project and allows you to create a git branch with the name based on the selected
74
+ story
75
+ email:
76
+ - jasonm23@gmail.com
77
+ - gabe@neo.com
78
+ - rui.p.baltazar@gmail.com
79
+ - dominic.wong.617@gmail.com
80
+ executables:
81
+ - story_branch
82
+ - story-branch
83
+ - git-story
84
+ - git-story-branch
85
+ - git-pivotal-story
86
+ extensions: []
87
+ extra_rdoc_files: []
88
+ files:
89
+ - LICENCE
90
+ - README.md
91
+ - bin/git-pivotal-story
92
+ - bin/git-story
93
+ - bin/git-story-branch
94
+ - bin/story-branch
95
+ - bin/story_branch
96
+ - lib/story_branch.rb
97
+ homepage: https://github.com/jasonm23/pivotal-story-branch
98
+ licenses:
99
+ - MIT
100
+ metadata: {}
101
+ post_install_message:
102
+ rdoc_options: []
103
+ require_paths:
104
+ - lib
105
+ required_ruby_version: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: 1.9.3
110
+ required_rubygems_version: !ruby/object:Gem::Requirement
111
+ requirements:
112
+ - - ">="
113
+ - !ruby/object:Gem::Version
114
+ version: '0'
115
+ requirements: []
116
+ rubyforge_project:
117
+ rubygems_version: 2.2.2
118
+ signing_key:
119
+ specification_version: 4
120
+ summary: Story Branch - create git branches based on pivotal tracker stories
121
+ test_files: []