hone-git_pivot 0.0.0 → 0.1.0
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.
- data/README.rdoc +27 -3
- data/Rakefile +1 -0
- data/VERSION +1 -0
- data/git_pivot.gemspec +7 -2
- data/git_pivot.yml.example +3 -0
- data/lib/git_pivot.rb +73 -2
- data/lib/runner.rb +193 -11
- metadata +15 -4
data/README.rdoc
CHANGED
@@ -2,13 +2,37 @@
|
|
2
2
|
|
3
3
|
Tool that allows you to work with Pivotal Tracker from the command line.
|
4
4
|
|
5
|
+
== Installation and Usage
|
6
|
+
|
7
|
+
To install:
|
8
|
+
|
9
|
+
$ gem install hone-git_pivot -s http://gems.github.com
|
10
|
+
|
11
|
+
Now you'll need to add a <tt>git_pivot.yml</tt> to the root directory of your project:
|
12
|
+
|
13
|
+
project_id: 213992
|
14
|
+
token: 3a9390ed9309ba304903939ae948298
|
15
|
+
owner: Matt Buck
|
16
|
+
use_git: true
|
17
|
+
|
18
|
+
... where <tt>owner</tt> is your name as it appears in the Pivotal Tracker interface. Set <tt>use_git</tt> to <tt>true</tt> if you want GitPivot to automatically create (local) topic branches when you start a story.
|
19
|
+
|
20
|
+
== Current Features
|
21
|
+
* display stories in current
|
22
|
+
* display stories under "My Work"
|
23
|
+
* start a story (optional git support to start a topic branch)
|
24
|
+
* display a story with associated "Notes"
|
25
|
+
* add a note to a story
|
26
|
+
* finish a story
|
27
|
+
* list tasks for a story
|
28
|
+
* add a task to a story
|
29
|
+
* complete tasks
|
30
|
+
|
5
31
|
== Planned Features
|
6
32
|
|
7
33
|
Planned features include:
|
8
|
-
* display stories in current sprint
|
9
|
-
* start working on a ticket that creates a new branch in Git, starts ticket in Pivotal Tracker
|
10
|
-
* close ticket in Pivotal Tracker
|
11
34
|
* search for stories
|
35
|
+
* backlog stories
|
12
36
|
|
13
37
|
== Note on Patches/Pull Requests
|
14
38
|
|
data/Rakefile
CHANGED
@@ -12,6 +12,7 @@ begin
|
|
12
12
|
gem.authors = ["hone"]
|
13
13
|
gem.add_development_dependency "hone-pivotal-tracker"
|
14
14
|
gem.add_development_dependency "rupot"
|
15
|
+
gem.add_development_dependency "git"
|
15
16
|
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
16
17
|
end
|
17
18
|
rescue LoadError
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
data/git_pivot.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{git_pivot}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.1.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["hone"]
|
12
|
-
s.date = %q{2009-08
|
12
|
+
s.date = %q{2009-09-08}
|
13
13
|
s.default_executable = %q{git_pivot}
|
14
14
|
s.description = %q{TODO: longer description of your gem}
|
15
15
|
s.email = %q{hone02@gmail.com}
|
@@ -24,8 +24,10 @@ Gem::Specification.new do |s|
|
|
24
24
|
"LICENSE",
|
25
25
|
"README.rdoc",
|
26
26
|
"Rakefile",
|
27
|
+
"VERSION",
|
27
28
|
"bin/git_pivot",
|
28
29
|
"git_pivot.gemspec",
|
30
|
+
"git_pivot.yml.example",
|
29
31
|
"lib/git_pivot.rb",
|
30
32
|
"lib/runner.rb"
|
31
33
|
]
|
@@ -43,12 +45,15 @@ Gem::Specification.new do |s|
|
|
43
45
|
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
44
46
|
s.add_development_dependency(%q<hone-pivotal-tracker>, [">= 0"])
|
45
47
|
s.add_development_dependency(%q<rupot>, [">= 0"])
|
48
|
+
s.add_development_dependency(%q<git>, [">= 0"])
|
46
49
|
else
|
47
50
|
s.add_dependency(%q<hone-pivotal-tracker>, [">= 0"])
|
48
51
|
s.add_dependency(%q<rupot>, [">= 0"])
|
52
|
+
s.add_dependency(%q<git>, [">= 0"])
|
49
53
|
end
|
50
54
|
else
|
51
55
|
s.add_dependency(%q<hone-pivotal-tracker>, [">= 0"])
|
52
56
|
s.add_dependency(%q<rupot>, [">= 0"])
|
57
|
+
s.add_dependency(%q<git>, [">= 0"])
|
53
58
|
end
|
54
59
|
end
|
data/lib/git_pivot.rb
CHANGED
@@ -19,14 +19,16 @@
|
|
19
19
|
|
20
20
|
require 'ruport'
|
21
21
|
require 'pivotal-tracker'
|
22
|
+
require 'git'
|
22
23
|
|
23
24
|
module GitPivot
|
24
25
|
class GitPivot
|
25
26
|
|
26
27
|
# ssl should default to yes since http basic auth is insecure
|
27
|
-
def initialize(project_id, token, owner, use_ssl = true)
|
28
|
+
def initialize(project_id, token, owner, git = nil, use_ssl = true)
|
28
29
|
@owner = owner
|
29
30
|
@tracker = PivotalTracker.new(project_id, token, {:use_ssl => use_ssl })
|
31
|
+
@g = Git.open('.') if git
|
30
32
|
end
|
31
33
|
|
32
34
|
# list stories in current sprint
|
@@ -61,10 +63,11 @@ module GitPivot
|
|
61
63
|
end
|
62
64
|
|
63
65
|
# start story
|
64
|
-
def start_story(id)
|
66
|
+
def start_story(id, name = nil)
|
65
67
|
story = @tracker.find_story(id)
|
66
68
|
story.current_state = "started"
|
67
69
|
@tracker.update_story(story)
|
70
|
+
create_topic_branch(topic_branch_name(story, name))
|
68
71
|
|
69
72
|
display_story(id)
|
70
73
|
end
|
@@ -82,6 +85,36 @@ module GitPivot
|
|
82
85
|
display_story(id)
|
83
86
|
end
|
84
87
|
|
88
|
+
# add a new note
|
89
|
+
def add_note(id, note_text)
|
90
|
+
note = Note.new(:text => note_text)
|
91
|
+
@tracker.create_note(id, note)
|
92
|
+
|
93
|
+
display_story(id)
|
94
|
+
end
|
95
|
+
|
96
|
+
def tasks(id)
|
97
|
+
tasks = @tracker.tasks(id)
|
98
|
+
display_tasks(id, tasks)
|
99
|
+
end
|
100
|
+
|
101
|
+
def add_task(id, task_text)
|
102
|
+
task = Task.new(:description => task_text)
|
103
|
+
@tracker.create_task(id, task)
|
104
|
+
|
105
|
+
tasks = @tracker.tasks(id)
|
106
|
+
display_tasks(id, tasks)
|
107
|
+
end
|
108
|
+
|
109
|
+
def complete_task(id, task_id)
|
110
|
+
task = @tracker.find_task(id, task_id)
|
111
|
+
task.complete = true
|
112
|
+
@tracker.update_task(id, task)
|
113
|
+
|
114
|
+
tasks = @tracker.tasks(id)
|
115
|
+
display_tasks(id, tasks)
|
116
|
+
end
|
117
|
+
|
85
118
|
private
|
86
119
|
def display_stories(stories)
|
87
120
|
data = stories.collect do |story|
|
@@ -91,5 +124,43 @@ module GitPivot
|
|
91
124
|
puts Table(:data => data, :column_names => ["ID", "Type", "Owner", "State", "Name"])
|
92
125
|
end
|
93
126
|
|
127
|
+
def display_tasks(story_id, tasks)
|
128
|
+
data = tasks.collect do |task|
|
129
|
+
[task.id, task.position, task.complete, task.created_at, task.description]
|
130
|
+
end
|
131
|
+
|
132
|
+
puts "Story ID: #{story_id}"
|
133
|
+
puts Table(:data => data,
|
134
|
+
:column_names => ["ID", "Position", "Complete", "Created At", "Description"])
|
135
|
+
end
|
136
|
+
|
137
|
+
def create_topic_branch(name)
|
138
|
+
if @g
|
139
|
+
# always branch from master
|
140
|
+
unless @g.lib.branch_current == "master"
|
141
|
+
puts 'Switching to master branch'
|
142
|
+
@g.branch('master').checkout
|
143
|
+
end
|
144
|
+
|
145
|
+
puts "Switching to #{name} branch"
|
146
|
+
@g.branch(name).checkout
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def topic_branch_name(story, name = nil)
|
151
|
+
name_to_use =
|
152
|
+
if name
|
153
|
+
name
|
154
|
+
else
|
155
|
+
story.name
|
156
|
+
end
|
157
|
+
# convert spaces to underscores and remove all punctation
|
158
|
+
"#{story.id}_#{transform_name(name_to_use)}"
|
159
|
+
end
|
160
|
+
|
161
|
+
def transform_name(name)
|
162
|
+
name.downcase.gsub(' ', '_').gsub(/[^\w]/, '')
|
163
|
+
end
|
164
|
+
|
94
165
|
end
|
95
166
|
end
|
data/lib/runner.rb
CHANGED
@@ -17,32 +17,214 @@
|
|
17
17
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
18
18
|
=end
|
19
19
|
|
20
|
-
require '
|
20
|
+
require 'trollop'
|
21
21
|
require 'date'
|
22
22
|
|
23
23
|
module GitPivot
|
24
24
|
class Runner
|
25
|
+
SUB_COMMANDS = %w{current work display start finish note stack push tasks task complete}
|
26
|
+
STATE_FILE = "git_pivot.state"
|
27
|
+
|
25
28
|
def initialize(args)
|
26
|
-
@
|
27
|
-
|
29
|
+
@method, @cmd, @cmd_opts = process_args(args)
|
30
|
+
|
28
31
|
# configuration stuff
|
29
32
|
configuration = YAML.load_file("git_pivot.yml")
|
33
|
+
if File.exist?(STATE_FILE)
|
34
|
+
File.open(STATE_FILE) do |file|
|
35
|
+
@states = Marshal.load(file)
|
36
|
+
if @states.is_a?(Array)
|
37
|
+
@states.uniq!
|
38
|
+
else
|
39
|
+
@states = nil
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
30
43
|
|
31
|
-
@git_pivot = GitPivot.new(configuration["project_id"], configuration["token"], configuration["owner"])
|
44
|
+
@git_pivot = GitPivot.new(configuration["project_id"], configuration["token"], configuration["owner"], configuration["use_git"])
|
32
45
|
end
|
33
46
|
|
34
47
|
def run
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
48
|
+
args = [@method]
|
49
|
+
if @method and @git_pivot.method(@method).arity.abs > 0
|
50
|
+
if @cmd_opts[:id]
|
51
|
+
args << @cmd_opts[:id]
|
52
|
+
elsif @states.any?
|
53
|
+
args << @states.first
|
54
|
+
else
|
55
|
+
Trollop::die "Need to specify a story id"
|
56
|
+
end
|
57
|
+
|
58
|
+
if @cmd_opts[:name]
|
59
|
+
args << @cmd_opts[:name].join(' ')
|
60
|
+
end
|
61
|
+
if @cmd_opts[:text]
|
62
|
+
args << @cmd_opts[:text].join(' ')
|
63
|
+
end
|
64
|
+
if @cmd_opts[:"task-id"]
|
65
|
+
args << @cmd_opts[:"task-id"]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
if @method == :start_story
|
70
|
+
add_story_to_states(@cmd_opts[:id])
|
71
|
+
end
|
72
|
+
|
73
|
+
if @method
|
74
|
+
@git_pivot.send(*args)
|
75
|
+
end
|
76
|
+
|
77
|
+
# must do this after, in case finish fails, don't want to pop
|
78
|
+
if @method == :finish_story
|
79
|
+
if @states and @states.first == args[1]
|
80
|
+
@states.shift
|
81
|
+
|
82
|
+
save_state
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
case @cmd
|
87
|
+
when "stack"
|
88
|
+
puts @states
|
89
|
+
when "push"
|
90
|
+
add_story_to_states(@cmd_opts[:id])
|
91
|
+
puts @states
|
39
92
|
end
|
40
93
|
end
|
41
94
|
|
42
95
|
private
|
43
|
-
def
|
44
|
-
@
|
45
|
-
|
96
|
+
def save_state
|
97
|
+
File.open(STATE_FILE, 'w') {|file| Marshal.dump(@states, file) }
|
98
|
+
end
|
99
|
+
|
100
|
+
def add_story_to_states(story_id)
|
101
|
+
if @states.nil?
|
102
|
+
@states = [story_id]
|
103
|
+
else
|
104
|
+
@states.delete(story_id)
|
105
|
+
@states.unshift(story_id)
|
106
|
+
end
|
107
|
+
|
108
|
+
save_state
|
109
|
+
end
|
110
|
+
|
111
|
+
def process_args(args)
|
112
|
+
global_opts = Trollop::options do
|
113
|
+
banner <<-BANNER
|
114
|
+
A command-line interface for Pivotal Tracker.
|
115
|
+
|
116
|
+
Subcommands:
|
117
|
+
current - Lists the stories that are part of the current iteration.
|
118
|
+
work - Lists the stories that you own.
|
119
|
+
display - Displays information about a specific story.
|
120
|
+
start - Marks a story as started.
|
121
|
+
finish - Marks a story as finished.
|
122
|
+
note - Add a new note to an existing story
|
123
|
+
stack - Current Stack of Story ids
|
124
|
+
push - Push a story to the top of the Story Stack
|
125
|
+
tasks - Display tasks associated to a story
|
126
|
+
task - Add a task to the given story
|
127
|
+
complete - Mark a task as completed
|
128
|
+
|
129
|
+
BANNER
|
130
|
+
stop_on SUB_COMMANDS
|
131
|
+
end
|
132
|
+
|
133
|
+
command = nil
|
134
|
+
cmd = args.shift
|
135
|
+
cmd_opts = case cmd
|
136
|
+
when "current"
|
137
|
+
command = :current_sprint
|
138
|
+
|
139
|
+
Trollop::options(args) do
|
140
|
+
banner "Lists the stories that are part of the current iteration."
|
141
|
+
end
|
142
|
+
when "work"
|
143
|
+
command = :my_work
|
144
|
+
|
145
|
+
# FIXME: This help message doesn't appear with a 'git_pivot work -h', but the 'git_pivot current -h' message does.
|
146
|
+
Trollop::options(args) do
|
147
|
+
banner "Lists the stories that you own."
|
148
|
+
end
|
149
|
+
when "display"
|
150
|
+
command = :display_story
|
151
|
+
|
152
|
+
Trollop::options(args) do
|
153
|
+
banner "Display information about a specific story."
|
154
|
+
|
155
|
+
opt :id, "The id of the story to display.", :type => Integer
|
156
|
+
end
|
157
|
+
when "start"
|
158
|
+
command = :start_story
|
159
|
+
|
160
|
+
Trollop::options(args) do
|
161
|
+
banner "Marks a specific story as started."
|
162
|
+
|
163
|
+
opt :id, "The id of the story to start.", :required => true, :type => Integer
|
164
|
+
opt :name, "Topic branch name", :type => :strings
|
165
|
+
end
|
166
|
+
when "finish"
|
167
|
+
command = :finish_story
|
168
|
+
|
169
|
+
Trollop::options(args) do
|
170
|
+
banner "Marks a specific story as finished."
|
171
|
+
|
172
|
+
opt :id, "The id of the story to finish.", :type => Integer
|
173
|
+
end
|
174
|
+
when "note"
|
175
|
+
command = :add_note
|
176
|
+
|
177
|
+
Trollop::options(args) do
|
178
|
+
banner "Adds a note to the story."
|
179
|
+
|
180
|
+
opt :id, "The id of the story to finish.", :type => Integer
|
181
|
+
opt :text, "The text of the note.", :required => true, :type => :strings
|
182
|
+
end
|
183
|
+
when "stack"
|
184
|
+
command = nil
|
185
|
+
|
186
|
+
Trollop::options(args) do
|
187
|
+
banner "Displays the stack of story ids."
|
188
|
+
end
|
189
|
+
when "push"
|
190
|
+
command = nil
|
191
|
+
|
192
|
+
Trollop::options(args) do
|
193
|
+
banner "Push story to the top of the story stack."
|
194
|
+
|
195
|
+
opt :id, "The id of the story.", :type => Integer
|
196
|
+
end
|
197
|
+
when "tasks"
|
198
|
+
command = :tasks
|
199
|
+
|
200
|
+
Trollop::options(args) do
|
201
|
+
banner "Tasks for a given story."
|
202
|
+
|
203
|
+
opt :id, "The id of the story.", :type => Integer
|
204
|
+
end
|
205
|
+
when "task"
|
206
|
+
command = :add_task
|
207
|
+
|
208
|
+
Trollop::options(args) do
|
209
|
+
banner "Add a task to the story."
|
210
|
+
|
211
|
+
opt :id, "The id of the story.", :type => Integer
|
212
|
+
opt :text, "The text of the task.", :required => true, :type => :strings
|
213
|
+
end
|
214
|
+
when "complete"
|
215
|
+
command = :complete_task
|
216
|
+
|
217
|
+
Trollop::options(args) do
|
218
|
+
banner "Complete a task"
|
219
|
+
|
220
|
+
opt :id, "The id of the story.", :type => Integer
|
221
|
+
opt :"task-id", "The id of the task.", :required => true, :type => Integer
|
222
|
+
end
|
223
|
+
else
|
224
|
+
Trollop::die "unknown subcommand #{cmd.inspect}"
|
225
|
+
end
|
226
|
+
|
227
|
+
[command, cmd, cmd_opts]
|
46
228
|
end
|
47
229
|
end
|
48
230
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hone-git_pivot
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- hone
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-08
|
12
|
+
date: 2009-09-08 00:00:00 -07:00
|
13
13
|
default_executable: git_pivot
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -32,6 +32,16 @@ dependencies:
|
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: "0"
|
34
34
|
version:
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: git
|
37
|
+
type: :development
|
38
|
+
version_requirement:
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: "0"
|
44
|
+
version:
|
35
45
|
description: "TODO: longer description of your gem"
|
36
46
|
email: hone02@gmail.com
|
37
47
|
executables:
|
@@ -47,13 +57,14 @@ files:
|
|
47
57
|
- LICENSE
|
48
58
|
- README.rdoc
|
49
59
|
- Rakefile
|
60
|
+
- VERSION
|
50
61
|
- bin/git_pivot
|
51
62
|
- git_pivot.gemspec
|
63
|
+
- git_pivot.yml.example
|
52
64
|
- lib/git_pivot.rb
|
53
65
|
- lib/runner.rb
|
54
66
|
has_rdoc: true
|
55
67
|
homepage: http://github.com/hone/git_pivot
|
56
|
-
licenses:
|
57
68
|
post_install_message:
|
58
69
|
rdoc_options:
|
59
70
|
- --charset=UTF-8
|
@@ -74,7 +85,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
74
85
|
requirements: []
|
75
86
|
|
76
87
|
rubyforge_project:
|
77
|
-
rubygems_version: 1.
|
88
|
+
rubygems_version: 1.2.0
|
78
89
|
signing_key:
|
79
90
|
specification_version: 2
|
80
91
|
summary: "TODO: one-line summary of your gem"
|