pv 0.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/.gitignore +17 -0
- data/.rspec +1 -0
- data/Gemfile +10 -0
- data/LICENSE.txt +22 -0
- data/README.md +73 -0
- data/Rakefile +1 -0
- data/bin/pv +7 -0
- data/lib/pv.rb +15 -0
- data/lib/pv/bug_tracker.rb +27 -0
- data/lib/pv/command.rb +58 -0
- data/lib/pv/configuration.rb +20 -0
- data/lib/pv/story.rb +23 -0
- data/lib/pv/version.rb +3 -0
- data/lib/templates/story.txt.erb +8 -0
- data/pv.gemspec +24 -0
- data/spec/bug_tracker_spec.rb +22 -0
- data/spec/configuration_spec.rb +35 -0
- data/spec/fixtures/cassettes/pivotal_client_connection.yml +74 -0
- data/spec/fixtures/cassettes/pivotal_find_story.yml +586 -0
- data/spec/fixtures/cassettes/pivotal_project_stories_search.yml +515 -0
- data/spec/fixtures/pv.yml +3 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/story_spec.rb +21 -0
- metadata +141 -0
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--colour --format=progress
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Tom Scott
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
# PV
|
2
|
+
|
3
|
+
Use Pivotal Tracker in the shell.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Install the binary:
|
8
|
+
|
9
|
+
$ gem install pv
|
10
|
+
|
11
|
+
Then create `~/.pivotal.yml` with your deets:
|
12
|
+
|
13
|
+
```yaml
|
14
|
+
username: user@email.com
|
15
|
+
password: your-super-secret-password
|
16
|
+
project: your-awesome-project-name
|
17
|
+
```
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
Here are all of the commands Piv can do:
|
22
|
+
|
23
|
+
### pv
|
24
|
+
|
25
|
+
Simply running `piv` will open your `$PAGER` with the stories
|
26
|
+
in your "My Work" tab. Each story is one line and includes the
|
27
|
+
title and the ID:
|
28
|
+
|
29
|
+
123456 My spoon is too big. (A Banana)
|
30
|
+
|
31
|
+
### pv show 123456
|
32
|
+
|
33
|
+
Show the entire story's details, including tasks. Again, this is
|
34
|
+
viewed in your `$PAGER`...
|
35
|
+
|
36
|
+
Feature 123456 (6 points)
|
37
|
+
Requester: A Banana <banana@work.com>
|
38
|
+
Owner: John Doe <someone@home.com>
|
39
|
+
Status: NOT STARTED
|
40
|
+
|
41
|
+
My spoon is too big.
|
42
|
+
|
43
|
+
I AM A BANANA!
|
44
|
+
|
45
|
+
|
46
|
+
### pv edit 123456 -s {start|finish|deliver|accept|reject|restart|close} -m "message"
|
47
|
+
|
48
|
+
Edits the status of a given story on Pivotal Tracker, with an optional message. The
|
49
|
+
message is appended to the story in comments.
|
50
|
+
|
51
|
+
### pv {start|finish|deliver|accept|reject|restart|close} 123456 -m "message"
|
52
|
+
|
53
|
+
Easier-to-remember aliases for the above command.
|
54
|
+
|
55
|
+
### pv create "Story title" --points=3
|
56
|
+
|
57
|
+
Creates a story on Pivotal Tracker. Opens your `$EDITOR` so you can provide more information
|
58
|
+
than simply the title, unless you pass --minimal. Stories are created as features by default,
|
59
|
+
but you can override this by passing `--bug` or `--chore`. Note: `--points` values, while
|
60
|
+
required if creating a feature, are ignored when creating bugs or chores. They are also not
|
61
|
+
required on the command.
|
62
|
+
|
63
|
+
### pv help
|
64
|
+
|
65
|
+
Show a command manual.
|
66
|
+
|
67
|
+
## Contributing
|
68
|
+
|
69
|
+
1. Fork it
|
70
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
71
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
72
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
73
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/pv
ADDED
data/lib/pv.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'pv/configuration'
|
2
|
+
require 'pv/command'
|
3
|
+
require 'pv/version'
|
4
|
+
require 'pv/bug_tracker'
|
5
|
+
require 'pv/story'
|
6
|
+
|
7
|
+
module Pv
|
8
|
+
def self.config
|
9
|
+
@config ||= Pv::Configuration.new
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.tracker
|
13
|
+
@tracker ||= Pv::BugTracker.new
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'pivotal_tracker'
|
2
|
+
|
3
|
+
module Pv
|
4
|
+
class BugTracker
|
5
|
+
attr_reader :username, :password, :token
|
6
|
+
|
7
|
+
def initialize with_username=nil, and_password=nil
|
8
|
+
@username = with_username || Pv.config.username
|
9
|
+
@password = and_password || Pv.config.password
|
10
|
+
@token = PivotalTracker::Client.token(username, password)
|
11
|
+
|
12
|
+
PivotalTracker::Client.use_ssl = true
|
13
|
+
end
|
14
|
+
|
15
|
+
def connected?
|
16
|
+
@token != nil
|
17
|
+
end
|
18
|
+
|
19
|
+
def stories for_project_id=nil, and_full_name=nil
|
20
|
+
project_id = for_project_id || Pv.config.project_id
|
21
|
+
user = and_full_name || Pv.config.name
|
22
|
+
|
23
|
+
project = PivotalTracker::Project.all.select { |p| p.id == project_id }.first
|
24
|
+
project.stories.all(owned_by: user)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/pv/command.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'active_support/all'
|
3
|
+
|
4
|
+
module Pv
|
5
|
+
class Command < Thor
|
6
|
+
include Thor::Actions
|
7
|
+
|
8
|
+
default_task :log
|
9
|
+
desc :log, "Show every story assigned to you on this project."
|
10
|
+
def log
|
11
|
+
Pv.tracker.stories.each do |story|
|
12
|
+
say "[#{story.id}] #{story.name} < #{story.requested_by} >"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
desc "show STORY_ID", "Show the full text and attributes of a story on this project."
|
17
|
+
def show story_id
|
18
|
+
story = File.write "/tmp/story", Story.find(story_id).render
|
19
|
+
run "less -R /tmp/story"
|
20
|
+
end
|
21
|
+
|
22
|
+
desc "edit STORY_ID", "Edit a story's status on this project."
|
23
|
+
method_option :status, default: '', alias: 's'
|
24
|
+
method_option :message, default: "", alias: 'm'
|
25
|
+
def edit story_id
|
26
|
+
story = Story.find story_id
|
27
|
+
story.update(status: options[:status]) unless options[:status].blank?
|
28
|
+
story.comment! options[:message] unless options[:message].blank?
|
29
|
+
end
|
30
|
+
|
31
|
+
%w(start finish deliver accept reject restart).each do |status|
|
32
|
+
desc "#{status} STORY_ID", "#{status.titleize} a story on this project."
|
33
|
+
define_method(status) do |story_id|
|
34
|
+
options[:status] = status
|
35
|
+
edit(story_id)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
desc :help, "Show all commands"
|
40
|
+
def help
|
41
|
+
say <<-TEXT
|
42
|
+
|
43
|
+
pv: pivotal tracker in the shell
|
44
|
+
|
45
|
+
A simple command-line interface for Pivotal Tracker.
|
46
|
+
Configure in ~/.pv:
|
47
|
+
|
48
|
+
username: 'your-username'
|
49
|
+
password: 'secret'
|
50
|
+
project_id: 123456
|
51
|
+
|
52
|
+
Then use the following commands to manage your project:
|
53
|
+
TEXT
|
54
|
+
say "\n"
|
55
|
+
super
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module Pv
|
4
|
+
class Configuration
|
5
|
+
attr_reader :username, :password, :attributes, :project_id, :name
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@path = File.expand_path "~/.pv"
|
9
|
+
@attributes = YAML::load_file @path
|
10
|
+
@username = @attributes['username']
|
11
|
+
@password = @attributes['password']
|
12
|
+
@project_id = @attributes['project_id']
|
13
|
+
@name = @attributes['name']
|
14
|
+
end
|
15
|
+
|
16
|
+
def present?
|
17
|
+
File.exists? @path
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/pv/story.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
module Pv
|
2
|
+
class Story
|
3
|
+
attr_accessor :story_type, :requested_by, :owned_by, :current_state,
|
4
|
+
:name, :description, :estimate, :id
|
5
|
+
|
6
|
+
def initialize from_pivotal_story
|
7
|
+
%w(id story_type requested_by owned_by current_state name description estimate).each do |attr|
|
8
|
+
self.send "#{attr}=", from_pivotal_story.send(attr)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.find by_id
|
13
|
+
new Pv.tracker.stories.find(by_id).first
|
14
|
+
end
|
15
|
+
|
16
|
+
# Render this Story in plain text.
|
17
|
+
def render
|
18
|
+
source = IO.read "./lib/templates/story.txt.erb"
|
19
|
+
template = ERB.new(source)
|
20
|
+
template.result(binding)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/pv/version.rb
ADDED
data/pv.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'pv/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "pv"
|
8
|
+
gem.version = Pv::VERSION
|
9
|
+
gem.authors = ["Tom Scott"]
|
10
|
+
gem.email = ["tubbo@psychedeli.ca"]
|
11
|
+
gem.description = %q{A command-line interface to Pivotal Tracker.}
|
12
|
+
gem.summary = %q{A command-line interface to Pivotal Tracker.}
|
13
|
+
gem.homepage = "http://github.com/tubbo/pv"
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
|
20
|
+
gem.add_dependency 'pivotal-tracker'
|
21
|
+
gem.add_dependency 'thor'
|
22
|
+
gem.add_dependency 'active_support'
|
23
|
+
gem.add_dependency 'i18n'
|
24
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'pv/bug_tracker'
|
3
|
+
|
4
|
+
module Pv
|
5
|
+
describe BugTracker do
|
6
|
+
subject do
|
7
|
+
VCR.use_cassette :pivotal_client_connection do
|
8
|
+
Pv::BugTracker.new("john@doe.com", "password")
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
it "connects to pivotal tracker" do
|
13
|
+
subject.should be_connected
|
14
|
+
end
|
15
|
+
|
16
|
+
it "gathers the list of stories this user has to accomplish" do
|
17
|
+
VCR.use_cassette :pivotal_project_stories_search do
|
18
|
+
subject.stories(461255, "Tom Scott").should_not be_empty
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'pv/configuration'
|
2
|
+
|
3
|
+
module Pv
|
4
|
+
describe Configuration do
|
5
|
+
before do
|
6
|
+
if File.exists? File.expand_path("~/.pv")
|
7
|
+
`cp ~/.pv ~/.pv.safe`
|
8
|
+
end
|
9
|
+
|
10
|
+
`cp spec/fixtures/pv.yml ~/.pv`
|
11
|
+
end
|
12
|
+
|
13
|
+
subject { Pv::Configuration.new }
|
14
|
+
|
15
|
+
it "reads from ~/.pv YAML" do
|
16
|
+
subject.should be_present
|
17
|
+
subject.attributes.should_not be_empty
|
18
|
+
subject.attributes.count.should == 3
|
19
|
+
end
|
20
|
+
|
21
|
+
it "parses a username" do
|
22
|
+
subject.username.should == "john@doe.com"
|
23
|
+
end
|
24
|
+
|
25
|
+
it "parses a password" do
|
26
|
+
subject.password.should == "password"
|
27
|
+
end
|
28
|
+
|
29
|
+
after do
|
30
|
+
if File.exists? File.expand_path("~/.pv.safe")
|
31
|
+
`cp ~/.pv.safe ~/.pv`
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: post
|
5
|
+
uri: https://www.pivotaltracker.com/services/v3/tokens/active
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: username=necromancer&password=2AUcBXdL3EYq8E87vJA3qAUNN
|
9
|
+
headers:
|
10
|
+
Accept:
|
11
|
+
- ! '*/*; q=0.5, application/xml'
|
12
|
+
Accept-Encoding:
|
13
|
+
- gzip, deflate
|
14
|
+
Content-Length:
|
15
|
+
- '55'
|
16
|
+
Content-Type:
|
17
|
+
- application/x-www-form-urlencoded
|
18
|
+
User-Agent:
|
19
|
+
- Ruby
|
20
|
+
response:
|
21
|
+
status:
|
22
|
+
code: 200
|
23
|
+
message: !binary |-
|
24
|
+
T0s=
|
25
|
+
headers:
|
26
|
+
!binary "Q29udGVudC1UeXBl":
|
27
|
+
- !binary |-
|
28
|
+
YXBwbGljYXRpb24veG1sOyBjaGFyc2V0PXV0Zi04
|
29
|
+
!binary "VHJhbnNmZXItRW5jb2Rpbmc=":
|
30
|
+
- !binary |-
|
31
|
+
Y2h1bmtlZA==
|
32
|
+
!binary "Q29ubmVjdGlvbg==":
|
33
|
+
- !binary |-
|
34
|
+
Y2xvc2U=
|
35
|
+
!binary "U3RhdHVz":
|
36
|
+
- !binary |-
|
37
|
+
MjAw
|
38
|
+
!binary "WC1Qb3dlcmVkLUJ5":
|
39
|
+
- !binary |-
|
40
|
+
UGh1c2lvbiBQYXNzZW5nZXIgKG1vZF9yYWlscy9tb2RfcmFjaykgMy4wLjE0
|
41
|
+
!binary "Q2FjaGUtQ29udHJvbA==":
|
42
|
+
- !binary |-
|
43
|
+
cHJpdmF0ZSwgbWF4LWFnZT0wLCBtdXN0LXJldmFsaWRhdGU=
|
44
|
+
!binary "WC1SdW50aW1l":
|
45
|
+
- !binary |-
|
46
|
+
MTE3
|
47
|
+
!binary "U2V0LUNvb2tpZQ==":
|
48
|
+
- !binary |-
|
49
|
+
dF9zZXNzaW9uPUJBaDdCem9QWlhod2FYSmxjMTloZEVsMU9nbFVhVzFsRFNZ
|
50
|
+
ckhJRHJNR2RWQmpvZlFHMWhjbk5vWVd4ZmQybDBhRjkxZEdOZlkyOWxjbU5w
|
51
|
+
YjI1R09nOXpaWE56YVc5dVgybGtJaVZoTUdRNE1tTmpNbUk0TldVMk1EaG1Z
|
52
|
+
ekpqTkdOak9XVXhOR1V3T1RGak9RJTNEJTNELS0zZmJiYmY3MjA5MjMwNTI1
|
53
|
+
YTQ3N2U3ZmViNTdiNzRkZjQxZDk0ZGFmOyBwYXRoPS87IHNlY3VyZTsgSHR0
|
54
|
+
cE9ubHk=
|
55
|
+
!binary "RXRhZw==":
|
56
|
+
- !binary |-
|
57
|
+
ImQzNzdhNzkwNTE3MTNiYTE3MDliNjNhMzEzNWU5MWY1Ig==
|
58
|
+
!binary "U2VydmVy":
|
59
|
+
- !binary |-
|
60
|
+
bmdpbngvMS4yLjIgKyBQaHVzaW9uIFBhc3NlbmdlciAzLjAuMTQgKG1vZF9y
|
61
|
+
YWlscy9tb2RfcmFjayk=
|
62
|
+
!binary "Q29udGVudC1FbmNvZGluZw==":
|
63
|
+
- !binary |-
|
64
|
+
Z3ppcA==
|
65
|
+
body:
|
66
|
+
encoding: ASCII-8BIT
|
67
|
+
string: !binary |-
|
68
|
+
H4sIAAAAAAAAAy3MQQ7CIBAAwHtfQbgr3cJWTIDefIF+wO5CiAqmotHfmxrv
|
69
|
+
k3HT+3YVL14euRYvYdtLwWWulEvy8nQ8bKycQudavXAJnRAuPTMFBNTnmUY0
|
70
|
+
cW8sIEc2o8aBIpHesXXqx1afSbTPnb3MpXHiRQaAfkBwagVO/ecvVvIujYkA
|
71
|
+
AAA=
|
72
|
+
http_version:
|
73
|
+
recorded_at: Sun, 18 Nov 2012 06:21:24 GMT
|
74
|
+
recorded_with: VCR 2.3.0
|