pivotal-slacker 1.0.1
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/bin/pivotal-slacker +199 -0
- data/lib/app_config.rb +40 -0
- data/lib/formatter.rb +65 -0
- metadata +49 -0
data/bin/pivotal-slacker
ADDED
@@ -0,0 +1,199 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
require "rubygems"
|
5
|
+
require "pivotal-tracker"
|
6
|
+
require "rainbow"
|
7
|
+
require "action_view"
|
8
|
+
require "launchy"
|
9
|
+
require "pp"
|
10
|
+
require "app_config"
|
11
|
+
require "formatter"
|
12
|
+
include ActionView::Helpers::DateHelper
|
13
|
+
|
14
|
+
# ----------------------------------------------------------
|
15
|
+
# Load config
|
16
|
+
|
17
|
+
config = nil
|
18
|
+
begin
|
19
|
+
config = AppConfig.load
|
20
|
+
rescue Exception => e
|
21
|
+
puts e
|
22
|
+
exit 1
|
23
|
+
end
|
24
|
+
|
25
|
+
# ----------------------------------------------------------
|
26
|
+
# Init
|
27
|
+
|
28
|
+
PivotalTracker::Client.token = config.api_key
|
29
|
+
project = PivotalTracker::Project.find config.project
|
30
|
+
|
31
|
+
# ----------------------------------------------------------
|
32
|
+
# Main app code
|
33
|
+
|
34
|
+
require "commander/import"
|
35
|
+
require "pivotal-tracker"
|
36
|
+
|
37
|
+
program :version, "1.0"
|
38
|
+
program :description, "Pivotal Tracker command line client."
|
39
|
+
|
40
|
+
command :mine do |c|
|
41
|
+
c.syntax = "pivotal-slacker mine [options]"
|
42
|
+
c.description = "List your stories in pivotal tracker."
|
43
|
+
c.example "description", "command example"
|
44
|
+
c.option "--some-switch", "Some switch that does something"
|
45
|
+
c.action do |args, options|
|
46
|
+
# Do something or c.when_called Pivotal-slacker::Commands::Mine
|
47
|
+
|
48
|
+
states = %w{unstarted started finished accepted rejected}
|
49
|
+
owned_by = config.user
|
50
|
+
|
51
|
+
puts "Looking for stories owned by #{owned_by} in states: #{states}".color("#444444")
|
52
|
+
|
53
|
+
stories = project.stories.all(:owned_by => owned_by, :current_state => states)
|
54
|
+
stories.sort! { |a, b| a.created_at <=> b.created_at }
|
55
|
+
stories.each do |story|
|
56
|
+
id = Formatter.story_id(story.id)
|
57
|
+
name = Formatter.story_name(story.name)
|
58
|
+
state = Formatter.state(story.current_state)
|
59
|
+
requested_by = Formatter.requested_by(story.requested_by)
|
60
|
+
created_at = Formatter.time_ago(story.created_at)
|
61
|
+
|
62
|
+
puts "[#{id}] #{name} ~ #{state}, from #{requested_by}, created #{created_at}"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
command :open do |c|
|
68
|
+
c.syntax = "pivotal-slacker open story_id"
|
69
|
+
c.description = "Open a specific Pivotal Tracker story in the browser."
|
70
|
+
c.example "Open a story with ID 123", "pivotal-slacker open 123"
|
71
|
+
c.action do |args, options|
|
72
|
+
story = project.stories.find(args[0].to_i)
|
73
|
+
Launchy.open story.url
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
command :show do |c|
|
78
|
+
c.syntax = "pivotal-slacker show story_id"
|
79
|
+
c.description = "Show the details of a given Pivotal Tracker story."
|
80
|
+
c.example "Show a story with ID 123", "pivotal-slacker show 123"
|
81
|
+
c.action do |args, options|
|
82
|
+
story = project.stories.find(args[0].to_i)
|
83
|
+
id = Formatter.story_id(story.id)
|
84
|
+
state = Formatter.state(story.current_state)
|
85
|
+
requested_by = Formatter.requested_by(story.requested_by)
|
86
|
+
created_at = Formatter.time_ago(story.created_at)
|
87
|
+
|
88
|
+
puts ""
|
89
|
+
puts "[#{id}] #{Formatter.story_name(story.name, :heading => true)}"
|
90
|
+
puts "#{state}, from #{requested_by}, created #{created_at}"
|
91
|
+
puts ""
|
92
|
+
puts Formatter.description(story.description)
|
93
|
+
puts ""
|
94
|
+
|
95
|
+
notes = story.notes.all.sort { |a, b| a.noted_at <=> b.noted_at }
|
96
|
+
notes.each do |note|
|
97
|
+
author = Formatter.note_author(note.author)
|
98
|
+
noted_at = Formatter.time_ago(note.noted_at)
|
99
|
+
note_text = Formatter.note_text(note.text)
|
100
|
+
puts "#{author} (#{noted_at}): #{note_text}"
|
101
|
+
end
|
102
|
+
|
103
|
+
# Extra padding line after notes.
|
104
|
+
if notes != nil and notes.size > 0
|
105
|
+
puts ""
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
command :start do |c|
|
111
|
+
c.syntax = "pivotal-slacker start story_id"
|
112
|
+
c.description = "Mark a given Pivotal Tracker story as \"started\"."
|
113
|
+
c.example "Start a story with ID 123", "pivotal-slacker start 123"
|
114
|
+
c.action do |args, options|
|
115
|
+
story_id = args[0].to_i
|
116
|
+
|
117
|
+
story = project.stories.find(story_id)
|
118
|
+
story.update :current_state => "started"
|
119
|
+
|
120
|
+
puts Formatter.story_action(Formatter.state("started"), story_id, story.name) + "."
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
command :comment do |c|
|
125
|
+
c.syntax = "pivotal-slacker comment story_id comment"
|
126
|
+
c.description = "Comment on a given Pivotal Tracker story."
|
127
|
+
c.example "Comment on story 123", "pivotal-slacker comment 123 \"Due to my skills I will destroy this task.\""
|
128
|
+
c.action do |args, options|
|
129
|
+
story_id = args[0].to_i
|
130
|
+
comment = args[1]
|
131
|
+
|
132
|
+
story = project.stories.find(story_id)
|
133
|
+
story.notes.create :text => comment
|
134
|
+
|
135
|
+
comment = Formatter.note_text(comment)
|
136
|
+
puts "#{Formatter.story_action "Commented on", story_id, story.name}:"
|
137
|
+
puts "#{Formatter.note_author(config.user)}: #{comment}"
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
command :finish do |c|
|
142
|
+
c.syntax = "pivotal-slacker finish story_id"
|
143
|
+
c.description = "Mark a given Pivotal Tracker story as \"finished\"."
|
144
|
+
c.example "Finish a story with ID 123", "pivotal-slacker finish 123"
|
145
|
+
c.action do |args, options|
|
146
|
+
story_id = args[0].to_i
|
147
|
+
|
148
|
+
story = project.stories.find(story_id)
|
149
|
+
story.update :current_state => "finished"
|
150
|
+
|
151
|
+
puts Formatter.story_action(Formatter.state("finished"), story_id, story.name) + "."
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
command :create do |c|
|
156
|
+
c.syntax = "pivotal-slacker create [options]"
|
157
|
+
c.description = "Create a new task in Pivotal Tracker."
|
158
|
+
c.example "Create a new task", "pivotal-slacker create --chore --owner \"Jimmy Winkerbean\""
|
159
|
+
c.option "--feature", "Designate the story as a feature"
|
160
|
+
c.option "--bug", "Designate the story as a bug"
|
161
|
+
c.option "--chore", "Designate the story as a chore"
|
162
|
+
c.option "--release", "Designate the story as a release"
|
163
|
+
c.option "--owner STRING", String, "Assigns the story to a user"
|
164
|
+
c.option "--name STRING", String, "Name of story"
|
165
|
+
c.option "--description STRING", String, "Description of story"
|
166
|
+
c.action do |args, options|
|
167
|
+
options.default :chore => false, :owner => config.user, :name => nil, :description => nil
|
168
|
+
|
169
|
+
raise "--name is required" if options.name == nil
|
170
|
+
raise "--description is required" if options.description == nil
|
171
|
+
|
172
|
+
story_type = "feature" if options.feature
|
173
|
+
story_type = "bug" if options.bug
|
174
|
+
story_type = "chore" if options.chore
|
175
|
+
story_type = "release" if options.release
|
176
|
+
|
177
|
+
puts ""
|
178
|
+
puts Formatter.story_name(options.name, :heading => true)
|
179
|
+
puts "#{Formatter.attr_descriptor('type is', Formatter.story_type(story_type))}"
|
180
|
+
puts "#{Formatter.attr_descriptor('requested by', Formatter.owner(config.user))}"
|
181
|
+
puts "#{Formatter.attr_descriptor('owned by', Formatter.owner(options.owner))}"
|
182
|
+
puts "#{Formatter.attr_descriptor('description is', Formatter.description(options.description))}"
|
183
|
+
puts ""
|
184
|
+
|
185
|
+
if agree "Really create? (y/n)"
|
186
|
+
story = project.stories.create(
|
187
|
+
:name => options.name,
|
188
|
+
:story_type => story_type,
|
189
|
+
:description => options.description,
|
190
|
+
:requested_by => config.user,
|
191
|
+
:owned_by => options.owner
|
192
|
+
)
|
193
|
+
puts Formatter.story_action("Created", story.id, story.name)
|
194
|
+
else
|
195
|
+
puts "Didn't create story."
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
data/lib/app_config.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
|
3
|
+
class AppConfig
|
4
|
+
FILENAME = ".pt.yaml"
|
5
|
+
|
6
|
+
def self.load
|
7
|
+
require "yaml"
|
8
|
+
|
9
|
+
# Read YAML config or quit.
|
10
|
+
config = nil
|
11
|
+
begin
|
12
|
+
config = YAML.load_file FILENAME
|
13
|
+
rescue Exception => e
|
14
|
+
raise "No #{FILENAME} file found in current directory; please create one."
|
15
|
+
end
|
16
|
+
|
17
|
+
AppConfig.new :config => config
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize opts={:config => Hash.new}
|
21
|
+
@config = opts[:config]
|
22
|
+
|
23
|
+
raise "Config must include \"project\" key" if not @config.has_key? "project"
|
24
|
+
raise "Config must include \"api_key\" key" if not @config.has_key? "api_key"
|
25
|
+
raise "Config must include \"user\" key" if not @config.has_key? "user"
|
26
|
+
end
|
27
|
+
|
28
|
+
def project
|
29
|
+
@config["project"]
|
30
|
+
end
|
31
|
+
|
32
|
+
def api_key
|
33
|
+
@config["api_key"]
|
34
|
+
end
|
35
|
+
|
36
|
+
def user
|
37
|
+
@config["user"]
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
data/lib/formatter.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "rubygems"
|
3
|
+
require "rainbow"
|
4
|
+
|
5
|
+
class Formatter
|
6
|
+
def self.story_id id
|
7
|
+
id.to_s.color("#404040")
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.state state
|
11
|
+
if state == "finished"
|
12
|
+
# state = state.color('009900').bright #background('009900').color('ffffff')
|
13
|
+
" ✓ #{state} ".background('#4C9900').bright.underline
|
14
|
+
elsif state == "accepted"
|
15
|
+
" ✓ #{state} ".background('#0080ff').color(:black).underline
|
16
|
+
else
|
17
|
+
state = state.bright # .background(:green).underline
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.requested_by requested_by
|
22
|
+
requested_by.color("#3399ff")
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.owner owner
|
26
|
+
self.requested_by owner
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.story_name story_name, opts={:heading => false}
|
30
|
+
if opts[:heading] == true
|
31
|
+
story_name.bright.underline
|
32
|
+
else
|
33
|
+
story_name
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.time_ago time
|
38
|
+
(distance_of_time_in_words(Time.now, time) + " ago").color('#404040')
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.note_author author
|
42
|
+
self.requested_by(author)
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.note_text text
|
46
|
+
text.bright
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.story_action comment, story_id, story_name
|
50
|
+
story_name = Formatter.story_name(story_name, :heading => true)
|
51
|
+
"#{comment} #{Formatter.story_id(story_id)}, #{story_name}"
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.story_type type
|
55
|
+
type.color '#99004C'
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.attr_descriptor descriptor, attr
|
59
|
+
"#{descriptor} → ".color('#333333') + attr
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.description description
|
63
|
+
description
|
64
|
+
end
|
65
|
+
end
|
metadata
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pivotal-slacker
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Sean Gilbertson
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-02-04 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: If you're overwhelmed with or tired of using the Pivotal Tracker web
|
15
|
+
UI, use this!
|
16
|
+
email: sean.gilbertson@gmail.com
|
17
|
+
executables:
|
18
|
+
- pivotal-slacker
|
19
|
+
extensions: []
|
20
|
+
extra_rdoc_files: []
|
21
|
+
files:
|
22
|
+
- lib/formatter.rb
|
23
|
+
- lib/app_config.rb
|
24
|
+
- bin/pivotal-slacker
|
25
|
+
homepage: https://github.com/dreki/pivotal-slacker
|
26
|
+
licenses: []
|
27
|
+
post_install_message:
|
28
|
+
rdoc_options: []
|
29
|
+
require_paths:
|
30
|
+
- lib
|
31
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
32
|
+
none: false
|
33
|
+
requirements:
|
34
|
+
- - ! '>='
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: '0'
|
37
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
38
|
+
none: false
|
39
|
+
requirements:
|
40
|
+
- - ! '>='
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '0'
|
43
|
+
requirements: []
|
44
|
+
rubyforge_project:
|
45
|
+
rubygems_version: 1.8.25
|
46
|
+
signing_key:
|
47
|
+
specification_version: 3
|
48
|
+
summary: A command-line client for Pivotal Tracker.
|
49
|
+
test_files: []
|