pivotal-github 0.9.1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.api_token +1 -0
- data/.project_id +1 -0
- data/Gemfile +2 -0
- data/README.md +23 -4
- data/bin/git-story-accept +5 -0
- data/lib/pivotal-github.rb +1 -0
- data/lib/pivotal-github/story_accept.rb +105 -0
- data/lib/pivotal-github/version.rb +1 -1
- data/spec/commands/story_accept_spec.rb +56 -0
- metadata +9 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3b1327c6aeb3947b802849284a92b1f8f613454f
|
4
|
+
data.tar.gz: d149aee10919ff595cec2185b42981cd022fec2b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ec6de0af73b760f7db2cf8f966df795cc69af8564cac7c637a3e23803603c98a25c8e7be8a08fb7f836389364d6207343ce0a93e0de0afa11bffc4ac1a120977
|
7
|
+
data.tar.gz: 43737ee77614a033099f5097694f70ac78da05b41b32a37d79e75650dadbfdc63f70d552f27a9afee529be5e02fc337668dea0532f8c640f3d2274c7248b8062
|
data/.api_token
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
faed06d1e5f6dfd030e451c34b24ab68
|
data/.project_id
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
745955
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -125,6 +125,23 @@ As with `git story-merge`, by default `git story-pull-request` exits with a warn
|
|
125
125
|
-o, --override override unfinished story warning
|
126
126
|
-h, --help this usage guide
|
127
127
|
|
128
|
+
### git story-accept
|
129
|
+
|
130
|
+
`git story-accept` examines the repository log and changes every **Delivered** story to **Accepted**. This makes it possible to accept a pull request by merging into master and then mark all the associated stories **Accepted** by running `git story-accept`. This saves having to manually keep track of the correspondences.
|
131
|
+
|
132
|
+
The purpose of `git story-accept` is to accept stories that have been merged into `master`, so by default it works only on the master branch. This requirement can be overridden by the `--override` option.
|
133
|
+
|
134
|
+
In order to avoid reading the entire Git log every time it's run, by default `git story-accept` stops immediately after finding a story that has already been accepted. The assumption is that `git story-accept` is run immediately after merging a pull request into a master branch that is always up-to-date, so that there are no delivered but unaccepted stories further down in the log.
|
135
|
+
|
136
|
+
`git story-accept` requires the existence of `.api_token` and `.project_id` files containing the Pivotal Tracker API token and project id, respectively. The user is prompted to create them if they are not present. (They aren't read from the command line using `gets` due to an incompatibility with options passing.)
|
137
|
+
|
138
|
+
#### Options
|
139
|
+
|
140
|
+
Usage: git story-accept
|
141
|
+
-o, --override override master branch requirement
|
142
|
+
-a, --all process all stories (entire log)
|
143
|
+
-h, --help this usage guide
|
144
|
+
|
128
145
|
### story-open
|
129
146
|
|
130
147
|
The `story-open` command (no `git`) opens the current story in the default browser (OS X–only):
|
@@ -134,13 +151,14 @@ The `story-open` command (no `git`) opens the current story in the default brows
|
|
134
151
|
|
135
152
|
## Configuration
|
136
153
|
|
137
|
-
In order to use the `pivotal-github` gem, you need to configure a post-receive hook for your repository. At GitHub, navigate to `Settings > Service Hooks > Pivotal Tracker` and paste in your Pivotal Tracker API token. (To find your Pivotal Tracker API token, go to your user profile and scroll to the bottom.) Be sure to check the **Active** box to activate the post-receive hook. At Bitbucket, click on the gear icon to view the settings, click on `Services`, select `Pivotal Tracker`, and paste in your Pivotal Tracker API key.
|
154
|
+
In order to use the `pivotal-github` gem, you need to configure a post-receive hook for your repository. At GitHub, navigate to `Settings > Service Hooks > Pivotal Tracker` and paste in your Pivotal Tracker API token. (To find your Pivotal Tracker API token, go to your user profile and scroll to the bottom.) Be sure to check the **Active** box to activate the post-receive hook. At Bitbucket, click on the gear icon to view the settings, click on `Services`, select `Pivotal Tracker`, and paste in your Pivotal Tracker API key. In addition, the `git story-accept` command requires the existence of `.api_token` and `.project_id` files containing the Pivotal Tracker API token and project id, respectively.
|
138
155
|
|
139
156
|
The `pivotal-github` command names follow the Git convention of being verbose (e.g., unlike Subversion, Git doesn't natively support `co` for `checkout`), but I recommend setting up aliases as necessary. Here are some suggestions, formatted so that they can be pasted directly into a terminal window:
|
140
157
|
|
141
158
|
git config --global alias.sc story-commit
|
142
159
|
git config --global alias.sm story-merge
|
143
160
|
git config --global alias.spr story-pull-request
|
161
|
+
git config --global alias.sa story-accept
|
144
162
|
|
145
163
|
I also recommend setting up an alias for `git push-branch` from [git-utils](https://github.com/mhartl/git-utils):
|
146
164
|
|
@@ -161,12 +179,13 @@ A single-developer workflow would then look like this:
|
|
161
179
|
$ git sync
|
162
180
|
$ git rebase master
|
163
181
|
$ git sm
|
182
|
+
$ git sa
|
164
183
|
|
165
|
-
Here `git sync` is
|
184
|
+
Here `git sync` is from [git-utils](https://github.com/mhartl/git-utils).
|
166
185
|
|
167
186
|
## Workflow with integrated code reivew
|
168
187
|
|
169
|
-
The `pivotal-github` gem is
|
188
|
+
The `pivotal-github` gem is designed to support a workflow involving integrated code review, which has the usual benefits: at least two pairs of eyes see any committed code, and at least two brains know basically what the committed code does. The cost is that having a second developer involved can slow you down. I suggest using your judgment to determine which workflow makes the most sense on a story-by-story basis.
|
170
189
|
|
171
190
|
Here's the process in detail:
|
172
191
|
|
@@ -192,7 +211,7 @@ Rather than immediately submitting a pull request, Alice can also continue by br
|
|
192
211
|
### Developer #2 (Bob)
|
193
212
|
|
194
213
|
1. Select **Pull Requests** at GitHub and review the pull request diffs
|
195
|
-
2. If acceptable, merge the
|
214
|
+
2. If acceptable, merge the pull request into master, run `git pull` on `master` to pull in the changes, and run `git story-accept` to mark the corresponding stories accepted
|
196
215
|
3. If not acceptable, manually change the state at Pivotal Tracker to **Rejected** and leave a note (at GitHub or at Pivotal Tracker) indicating the reason
|
197
216
|
4. If the branch can't be automatically merged, mark the story as **Rejected**
|
198
217
|
|
data/lib/pivotal-github.rb
CHANGED
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'pivotal-github/command'
|
2
|
+
require 'git'
|
3
|
+
require 'net/http'
|
4
|
+
require 'uri'
|
5
|
+
require 'nokogiri'
|
6
|
+
|
7
|
+
class StoryAccept < Command
|
8
|
+
|
9
|
+
def parser
|
10
|
+
OptionParser.new do |opts|
|
11
|
+
opts.banner = "Usage: git story-accept"
|
12
|
+
opts.on("-o", "--override", "override master branch requirement") do |opt|
|
13
|
+
self.options.override = opt
|
14
|
+
end
|
15
|
+
opts.on("-a", "--all", "process all stories (entire log)") do |opt|
|
16
|
+
self.options.all = opt
|
17
|
+
end
|
18
|
+
opts.on_tail("-h", "--help", "this usage guide") do
|
19
|
+
puts opts.to_s; exit 0
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Returns the ids to accept.
|
25
|
+
# These ids are of the form [Delivers #<story id>] or
|
26
|
+
# [Delivers #<story id> #<another story id>].
|
27
|
+
def ids_to_accept
|
28
|
+
delivered_regex = /\[Deliver(?:s|ed) (.*?)\]/
|
29
|
+
Git.open('.').log.inject([]) do |delivered_ids, commit|
|
30
|
+
message = commit.message
|
31
|
+
delivered = message.scan(delivered_regex).flatten
|
32
|
+
commit_ids = delivered.inject([]) do |ids, element|
|
33
|
+
ids.concat(element.scan(/[0-9]{8,}/).flatten)
|
34
|
+
ids
|
35
|
+
end
|
36
|
+
commit_ids.each do |commit_id|
|
37
|
+
return delivered_ids if already_accepted?(commit_id) && !options['all']
|
38
|
+
delivered_ids << commit_id
|
39
|
+
delivered_ids.uniq!
|
40
|
+
end
|
41
|
+
delivered_ids
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Returns true if a story has already been accepted.
|
46
|
+
def already_accepted?(story_id)
|
47
|
+
data = { 'X-TrackerToken' => api_token,
|
48
|
+
'Content-type' => "application/xml" }
|
49
|
+
response = Net::HTTP.start(story_uri.host, story_uri.port) do |http|
|
50
|
+
http.get(story_uri.path, data)
|
51
|
+
end
|
52
|
+
Nokogiri::XML(response.body).at_css('current_state').content == "accepted"
|
53
|
+
end
|
54
|
+
|
55
|
+
def api_token
|
56
|
+
api_filename = '.api_token'
|
57
|
+
if File.exist?(api_filename)
|
58
|
+
@api_token ||= File.read(api_filename).strip
|
59
|
+
else
|
60
|
+
puts "Please create a file called '#{api_filename}'"
|
61
|
+
puts "containing your Pivotal Tracker API token."
|
62
|
+
exit 1
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def project_id
|
67
|
+
project_id_filename = '.project_id'
|
68
|
+
if File.exist?(project_id_filename)
|
69
|
+
@project_id_filename ||= File.read(project_id_filename)
|
70
|
+
else
|
71
|
+
puts "Please create a file called '#{project_id}'"
|
72
|
+
puts "containing the Pivotal Tracker project number."
|
73
|
+
exit 1
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Changes a story's state to **Accepted**.
|
78
|
+
def accept!(story_id)
|
79
|
+
accepted = "<story><current_state>accepted</current_state></story>"
|
80
|
+
data = { 'X-TrackerToken' => api_token,
|
81
|
+
'Content-type' => "application/xml" }
|
82
|
+
Net::HTTP.start(story_uri.host, story_uri.port) do |http|
|
83
|
+
http.put(story_uri.path, accepted, data)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def run!
|
88
|
+
if story_branch != 'master' && !options['override']
|
89
|
+
puts "Runs only on the master branch by default"
|
90
|
+
puts "Use --override to override"
|
91
|
+
exit 1
|
92
|
+
end
|
93
|
+
ids_to_accept.each { |id| accept!(id) }
|
94
|
+
end
|
95
|
+
|
96
|
+
private
|
97
|
+
|
98
|
+
def api_base
|
99
|
+
'http://www.pivotaltracker.com/services/v3'
|
100
|
+
end
|
101
|
+
|
102
|
+
def story_uri
|
103
|
+
URI.parse("#{api_base}/projects/#{project_id}/stories/#{story_id}")
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe StoryAccept do
|
4
|
+
|
5
|
+
let(:command) { StoryAccept.new(['-o', '-a']) }
|
6
|
+
before do
|
7
|
+
command.stub(:story_branch).and_return('62831853-tau-manifesto')
|
8
|
+
command.stub(:already_accepted?).and_return(false)
|
9
|
+
end
|
10
|
+
subject { command }
|
11
|
+
|
12
|
+
it { should respond_to(:ids_to_accept) }
|
13
|
+
|
14
|
+
describe "ids_to_accept" do
|
15
|
+
let(:ids) { command.ids_to_accept }
|
16
|
+
subject { ids }
|
17
|
+
|
18
|
+
it { should_not be_empty }
|
19
|
+
it { should include("51204529") }
|
20
|
+
it { should include("51106181") }
|
21
|
+
it { should include("50566167") }
|
22
|
+
|
23
|
+
it "should not have duplicate ids" do
|
24
|
+
expect(ids).to eq ids.uniq
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
its(:api_token) { should_not be_empty }
|
30
|
+
|
31
|
+
describe "accept!" do
|
32
|
+
before do
|
33
|
+
command.stub(:accept!)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should accept each id" do
|
37
|
+
number_accepted = command.ids_to_accept.length
|
38
|
+
command.should_receive(:accept!).exactly(number_accepted).times
|
39
|
+
command.run!
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe "when stopping upon reading the first accepted id" do
|
44
|
+
let(:command) { StoryAccept.new(['-o']) }
|
45
|
+
before do
|
46
|
+
command.stub(:story_branch).and_return('62831853-tau-manifesto')
|
47
|
+
command.stub(:already_accepted?).and_return(false)
|
48
|
+
command.stub(:already_accepted?).with("50566167").and_return(true)
|
49
|
+
end
|
50
|
+
subject { command }
|
51
|
+
|
52
|
+
its(:ids_to_accept) { should include("51204529") }
|
53
|
+
its(:ids_to_accept) { should include("51106181") }
|
54
|
+
its(:ids_to_accept) { should_not include("50566167") }
|
55
|
+
end
|
56
|
+
end
|
metadata
CHANGED
@@ -1,19 +1,20 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pivotal-github
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Hartl
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-06-
|
11
|
+
date: 2013-06-18 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Add commands for Pivotal Tracker-GitHub integration
|
14
14
|
email:
|
15
15
|
- michael@michaelhartl.com
|
16
16
|
executables:
|
17
|
+
- git-story-accept
|
17
18
|
- git-story-commit
|
18
19
|
- git-story-merge
|
19
20
|
- git-story-pull-request
|
@@ -21,7 +22,9 @@ executables:
|
|
21
22
|
extensions: []
|
22
23
|
extra_rdoc_files: []
|
23
24
|
files:
|
25
|
+
- .api_token
|
24
26
|
- .gitignore
|
27
|
+
- .project_id
|
25
28
|
- .rspec
|
26
29
|
- .ruby-gemset
|
27
30
|
- .ruby-version
|
@@ -29,6 +32,7 @@ files:
|
|
29
32
|
- LICENSE.txt
|
30
33
|
- README.md
|
31
34
|
- Rakefile
|
35
|
+
- bin/git-story-accept
|
32
36
|
- bin/git-story-commit
|
33
37
|
- bin/git-story-merge
|
34
38
|
- bin/git-story-pull-request
|
@@ -37,6 +41,7 @@ files:
|
|
37
41
|
- lib/pivotal-github/command.rb
|
38
42
|
- lib/pivotal-github/finished_command.rb
|
39
43
|
- lib/pivotal-github/options.rb
|
44
|
+
- lib/pivotal-github/story_accept.rb
|
40
45
|
- lib/pivotal-github/story_commit.rb
|
41
46
|
- lib/pivotal-github/story_merge.rb
|
42
47
|
- lib/pivotal-github/story_open.rb
|
@@ -44,6 +49,7 @@ files:
|
|
44
49
|
- lib/pivotal-github/version.rb
|
45
50
|
- pivotal-github.gemspec
|
46
51
|
- spec/commands/command_spec.rb
|
52
|
+
- spec/commands/story_accept_spec.rb
|
47
53
|
- spec/commands/story_commit_spec.rb
|
48
54
|
- spec/commands/story_merge_spec.rb
|
49
55
|
- spec/commands/story_open_spec.rb
|
@@ -75,6 +81,7 @@ specification_version: 4
|
|
75
81
|
summary: See the README for full documentation
|
76
82
|
test_files:
|
77
83
|
- spec/commands/command_spec.rb
|
84
|
+
- spec/commands/story_accept_spec.rb
|
78
85
|
- spec/commands/story_commit_spec.rb
|
79
86
|
- spec/commands/story_merge_spec.rb
|
80
87
|
- spec/commands/story_open_spec.rb
|