git_pivotal_tracker 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/README.md +7 -3
- data/git_pivotal_tracker.gemspec +1 -0
- data/lib/git_pivotal_tracker/base.rb +12 -5
- data/lib/git_pivotal_tracker/finish.rb +8 -7
- data/lib/git_pivotal_tracker/story.rb +4 -5
- data/lib/git_pivotal_tracker/version.rb +1 -1
- data/spec/fixtures/finish_story.xml +17 -0
- data/spec/fixtures/no_stories.xml +3 -0
- data/spec/fixtures/one_story.xml +19 -0
- data/spec/fixtures/project.xml +37 -0
- data/spec/fixtures/start_story.xml +17 -0
- data/spec/fixtures/story.xml +18 -0
- data/spec/git_pivotal_tracker/base_spec.rb +196 -0
- data/spec/git_pivotal_tracker/finish_spec.rb +84 -0
- data/spec/git_pivotal_tracker/story_spec.rb +99 -0
- data/spec/spec_helper.rb +17 -2
- metadata +34 -7
- data/spec/git_pivotal_tracker_spec.rb +0 -4
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
git_pivotal_tracker
|
2
2
|
===========
|
3
3
|
|
4
4
|
Inspired by [Hashrocket's blend of git and Pivotal Tracker](http://reinh.com/blog/2009/03/02/a-git-workflow-for-agile-teams.html) and [Carbon Five's article on effective git workflows](http://blog.carbonfive.com/2010/11/01/integrating-topic-branches-in-git/) and [the git-pivotal gem](https://github.com/trydionel/git-pivotal), I wanted a tool that makes the git workflow fun and simple.
|
@@ -18,7 +18,7 @@ These commands collect the first available story from your Pivotal Tracker proje
|
|
18
18
|
|
19
19
|
* `git-finish`
|
20
20
|
|
21
|
-
When on a topic branch, this command will fetch the latest integration branch ('master' by default), rebase your topic branch from it, merge the branch into the integration branch with no-fast-
|
21
|
+
When on a topic branch, this command will fetch the latest integration branch ('master' by default), rebase your topic branch from it, merge the branch into the integration branch with no-fast-forward and push the integration branch to origin.
|
22
22
|
|
23
23
|
* `git-info`
|
24
24
|
|
@@ -49,9 +49,13 @@ The project id is best placed within your project's git config:
|
|
49
49
|
|
50
50
|
``git config -f .git/config pivotal.project-id 88888``
|
51
51
|
|
52
|
+
If you project's access is setup to use HTTPS:
|
53
|
+
|
54
|
+
``git config -f .git/config pivotal.use-ssl 1``
|
55
|
+
|
52
56
|
If you prefer to merge back to a branch other than master when you've finished a story, you can configure that:
|
53
57
|
|
54
|
-
``git config
|
58
|
+
``git config -f .git/config pivotal.integration-branch develop``
|
55
59
|
|
56
60
|
If you prefer to fetch and rebase from origin before merging (default is no):
|
57
61
|
|
data/git_pivotal_tracker.gemspec
CHANGED
@@ -33,6 +33,8 @@ module GitPivotalTracker
|
|
33
33
|
end
|
34
34
|
|
35
35
|
PivotalTracker::Client.token = options[:api_token]
|
36
|
+
PivotalTracker::Client.use_ssl = options[:use_ssl]
|
37
|
+
|
36
38
|
nil
|
37
39
|
end
|
38
40
|
|
@@ -47,7 +49,9 @@ module GitPivotalTracker
|
|
47
49
|
end
|
48
50
|
|
49
51
|
def story_id
|
50
|
-
|
52
|
+
if current_branch =~ /-(\d+)-/
|
53
|
+
$1
|
54
|
+
end
|
51
55
|
end
|
52
56
|
|
53
57
|
def project
|
@@ -72,18 +76,21 @@ module GitPivotalTracker
|
|
72
76
|
options[:rebase] = repository.config['pivotal.rebase']
|
73
77
|
options[:full_name] = repository.config['pivotal.full-name'] || repository.config['user.name']
|
74
78
|
options[:verbose] = repository.config['pivotal.verbose']
|
79
|
+
options[:use_ssl] = repository.config['pivotal.use-ssl']
|
75
80
|
end
|
76
81
|
|
77
82
|
def parse_argv(*args)
|
78
83
|
OptionParser.new do |opts|
|
79
84
|
opts.banner = "Usage: git <feature|chore|bug> [options]"
|
80
|
-
opts.on("-
|
85
|
+
opts.on("-t", "--api-token=", "Pivotal Tracker API key") { |k| options[:api_token] = k }
|
81
86
|
opts.on("-p", "--project-id=", "Pivotal Tracker project id") { |p| options[:project_id] = p }
|
82
87
|
opts.on("-b", "--integration-branch=", "The branch to merge finished stories back down onto") { |b| options[:integration_branch] = b }
|
83
|
-
opts.on("-f", "--fast-forward=", "Merge topic branch with fast forward") { |f| options[:fast_foward] = f }
|
84
88
|
opts.on("-n", "--full-name=", "Your Pivotal Tracker full name") { |n| options[:full_name] = n }
|
85
|
-
|
86
|
-
opts.on("-
|
89
|
+
|
90
|
+
opts.on("-F", "--fast-forward", "Merge topic branch with fast forward") { |f| options[:fast_forward] = f }
|
91
|
+
opts.on("-S", "--use-ssl", "Use SSL for connection to Pivotal Tracker") { |s| options[:use_ssl] = s }
|
92
|
+
opts.on("-R", "--rebase", "Fetch and rebase the integration branch before merging") { |r| options[:rebase] = r }
|
93
|
+
opts.on("-V", "--verbose", "Verbose command logging") { |v| options[:verbose] = v }
|
87
94
|
opts.on_tail("-h", "--help", "This usage guide") { put opts.to_s; exit 0 }
|
88
95
|
end.parse!(args)
|
89
96
|
end
|
@@ -5,24 +5,25 @@ module GitPivotalTracker
|
|
5
5
|
return 1 if super
|
6
6
|
|
7
7
|
unless story_id
|
8
|
-
|
8
|
+
puts "Branch name must contain a Pivotal Tracker story id"
|
9
9
|
return 1
|
10
10
|
end
|
11
11
|
|
12
12
|
if options[:rebase]
|
13
13
|
puts "Fetching origin and rebasing #{current_branch}"
|
14
|
-
log repository.git.
|
15
|
-
log repository.git.rebase({:raise => true},
|
14
|
+
log repository.git.pull({:raise => true})
|
15
|
+
log repository.git.rebase({:raise => true}, integration_branch)
|
16
16
|
end
|
17
17
|
|
18
18
|
puts "Merging #{current_branch} into #{integration_branch}"
|
19
19
|
log repository.git.checkout({:raise => true}, integration_branch)
|
20
20
|
|
21
|
-
|
22
|
-
|
21
|
+
merge_options = {:raise => true}
|
22
|
+
merge_options[:no_ff] = true unless options[:fast_forward]
|
23
|
+
log repository.git.merge(merge_options, current_branch)
|
23
24
|
|
24
|
-
puts "Pushing #{integration_branch}
|
25
|
-
log repository.git.push({:raise => true}
|
25
|
+
puts "Pushing #{integration_branch}"
|
26
|
+
log repository.git.push({:raise => true})
|
26
27
|
|
27
28
|
puts "Marking Story #{story_id} as finished..."
|
28
29
|
if story.update(:current_state => finished_state)
|
@@ -24,7 +24,7 @@ module GitPivotalTracker
|
|
24
24
|
log repository.git.checkout({:b => true, :raise => true}, branch)
|
25
25
|
|
26
26
|
puts "Updating #{type} status in Pivotal Tracker..."
|
27
|
-
if story.update(:owned_by => options[:
|
27
|
+
if story.update(:owned_by => options[:full_name], :current_state => :started)
|
28
28
|
puts "Success"
|
29
29
|
return 0
|
30
30
|
else
|
@@ -33,15 +33,14 @@ module GitPivotalTracker
|
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
|
-
private
|
37
|
-
|
38
36
|
def type
|
39
37
|
self.class.name.downcase.split(/::/).last
|
40
38
|
end
|
41
39
|
|
40
|
+
private
|
41
|
+
|
42
42
|
def fetch_story
|
43
|
-
|
44
|
-
conditions = { :current_state => "unstarted", :limit => 1, :offset => 0 }
|
43
|
+
conditions = { :current_state => "unstarted", :limit => 1 }
|
45
44
|
conditions[:story_type] = type unless type == 'story'
|
46
45
|
project.stories.all(conditions).first
|
47
46
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
<?xml version="1.0"?>
|
2
|
+
<story>
|
3
|
+
<name>Pause the film</name>
|
4
|
+
<description>As a moderator,
|
5
|
+
I can pause the film
|
6
|
+
In order to allow another activity to take place (discussion, etc).</description>
|
7
|
+
<story_type>feature</story_type>
|
8
|
+
<estimate>2</estimate>
|
9
|
+
<current_state>finished</current_state>
|
10
|
+
<requested_by>Ben Lindsey</requested_by>
|
11
|
+
<owned_by>Ben Lindsey</owned_by>
|
12
|
+
<labels>moderate,2_needs_design</labels>
|
13
|
+
<project_id>123</project_id>
|
14
|
+
<other_id></other_id>
|
15
|
+
<integration_id></integration_id>
|
16
|
+
<created_at>2011-07-14T23:06:28+00:00</created_at>
|
17
|
+
</story>
|
@@ -0,0 +1,19 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<stories type="array" count="1" total="2" limit="1">
|
3
|
+
<story>
|
4
|
+
<id type="integer">1234567890</id>
|
5
|
+
<project_id type="integer">123</project_id>
|
6
|
+
<story_type>feature</story_type>
|
7
|
+
<url>http://www.pivotaltracker.com/story/show/1234567890</url>
|
8
|
+
<estimate type="integer">2</estimate>
|
9
|
+
<current_state>unstarted</current_state>
|
10
|
+
<description>As a moderator,
|
11
|
+
I can pause the film
|
12
|
+
In order to allow another activity to take place (discussion, etc).</description>
|
13
|
+
<name>Pause the film</name>
|
14
|
+
<requested_by>Ben Lindsey</requested_by>
|
15
|
+
<created_at type="datetime">2011/07/14 23:06:28 UTC</created_at>
|
16
|
+
<updated_at type="datetime">2011/07/14 23:13:24 UTC</updated_at>
|
17
|
+
<labels>moderate,2_needs_design</labels>
|
18
|
+
</story>
|
19
|
+
</stories>
|
@@ -0,0 +1,37 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<project>
|
3
|
+
<id>123</id>
|
4
|
+
<name>Test Project</name>
|
5
|
+
<iteration_length type="integer">1</iteration_length>
|
6
|
+
<week_start_day>Monday</week_start_day>
|
7
|
+
<point_scale>0,1,2,3,5,8</point_scale>
|
8
|
+
<account>Carbon Five</account>
|
9
|
+
<start_date type="date">2011/07/18</start_date>
|
10
|
+
<first_iteration_start_time type="datetime">2011/07/18 07:00:00 UTC</first_iteration_start_time>
|
11
|
+
<current_iteration_number type="integer">1</current_iteration_number>
|
12
|
+
<enable_tasks type="boolean">true</enable_tasks>
|
13
|
+
<velocity_scheme>Average of 3 iterations</velocity_scheme>
|
14
|
+
<current_velocity>10</current_velocity>
|
15
|
+
<initial_velocity>10</initial_velocity>
|
16
|
+
<number_of_done_iterations_to_show>12</number_of_done_iterations_to_show>
|
17
|
+
<labels>1_needs_definition,2_needs_design,discuss,moderate</labels>
|
18
|
+
<last_activity_at type="datetime">2011/07/14 23:16:29 UTC</last_activity_at>
|
19
|
+
<allow_attachments>true</allow_attachments>
|
20
|
+
<public>false</public>
|
21
|
+
<use_https>false</use_https>
|
22
|
+
<bugs_and_chores_are_estimatable>false</bugs_and_chores_are_estimatable>
|
23
|
+
<commit_mode>false</commit_mode>
|
24
|
+
<memberships type="array">
|
25
|
+
<membership>
|
26
|
+
<id>1058581</id>
|
27
|
+
<person>
|
28
|
+
<email>ben@carbonfive.com</email>
|
29
|
+
<name>Ben Lindsey</name>
|
30
|
+
<initials>BL</initials>
|
31
|
+
</person>
|
32
|
+
<role>Owner</role>
|
33
|
+
</membership>
|
34
|
+
</memberships>
|
35
|
+
<integrations type="array">
|
36
|
+
</integrations>
|
37
|
+
</project>
|
@@ -0,0 +1,17 @@
|
|
1
|
+
<?xml version="1.0"?>
|
2
|
+
<story>
|
3
|
+
<name>Pause the film</name>
|
4
|
+
<description>As a moderator,
|
5
|
+
I can pause the film
|
6
|
+
In order to allow another activity to take place (discussion, etc).</description>
|
7
|
+
<story_type>feature</story_type>
|
8
|
+
<estimate>2</estimate>
|
9
|
+
<current_state>started</current_state>
|
10
|
+
<requested_by>Ben Lindsey</requested_by>
|
11
|
+
<owned_by>Ben Lindsey</owned_by>
|
12
|
+
<labels>moderate,2_needs_design</labels>
|
13
|
+
<project_id>123</project_id>
|
14
|
+
<other_id></other_id>
|
15
|
+
<integration_id></integration_id>
|
16
|
+
<created_at>2011-07-14T23:06:28+00:00</created_at>
|
17
|
+
</story>
|
@@ -0,0 +1,18 @@
|
|
1
|
+
<?xml version="1.0"?>
|
2
|
+
<story>
|
3
|
+
<id>1234567890</id>
|
4
|
+
<name>Pause the film</name>
|
5
|
+
<description>As a moderator,
|
6
|
+
I can pause the film
|
7
|
+
In order to allow another activity to take place (discussion, etc).</description>
|
8
|
+
<story_type>feature</story_type>
|
9
|
+
<estimate>2</estimate>
|
10
|
+
<current_state>started</current_state>
|
11
|
+
<requested_by>Ben Lindsey</requested_by>
|
12
|
+
<owned_by>Ben Lindsey</owned_by>
|
13
|
+
<labels>moderate,2_needs_design</labels>
|
14
|
+
<project_id>123</project_id>
|
15
|
+
<other_id></other_id>
|
16
|
+
<integration_id></integration_id>
|
17
|
+
<created_at>2011-07-14T23:06:28+00:00</created_at>
|
18
|
+
</story>
|
@@ -0,0 +1,196 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'spec_helper')
|
2
|
+
|
3
|
+
describe GitPivotalTracker::Base do
|
4
|
+
describe "#parse_argv" do
|
5
|
+
|
6
|
+
context "by default" do
|
7
|
+
before do
|
8
|
+
stub_git_config
|
9
|
+
subject = GitPivotalTracker::Base.new
|
10
|
+
end
|
11
|
+
|
12
|
+
it "leaves integration_branch nil" do
|
13
|
+
subject.options[:integration_branch].should be_nil
|
14
|
+
end
|
15
|
+
|
16
|
+
it "leaves fast_forward nil" do
|
17
|
+
subject.options[:fast_forward].should be_nil
|
18
|
+
end
|
19
|
+
|
20
|
+
it "leaves rebase nil" do
|
21
|
+
subject.options[:rebase].should be_nil
|
22
|
+
end
|
23
|
+
|
24
|
+
it "leaves verbose nil" do
|
25
|
+
subject.options[:verbose].should be_nil
|
26
|
+
end
|
27
|
+
|
28
|
+
it "leaves use_ssl nil" do
|
29
|
+
subject.options[:use_ssl].should be_nil
|
30
|
+
end
|
31
|
+
|
32
|
+
it "leaves full_name nil" do
|
33
|
+
subject.options[:full_name].should be_nil
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
it "sets the api_token" do
|
38
|
+
GitPivotalTracker::Base.new("--api-token", "8a8a8a8").options[:api_token].should == '8a8a8a8'
|
39
|
+
GitPivotalTracker::Base.new("-t", "8a8a8a8").options[:api_token].should == '8a8a8a8'
|
40
|
+
end
|
41
|
+
|
42
|
+
it "sets the project_id" do
|
43
|
+
GitPivotalTracker::Base.new("--project-id", "123").options[:project_id].should == '123'
|
44
|
+
GitPivotalTracker::Base.new("-p", "123").options[:project_id].should == '123'
|
45
|
+
end
|
46
|
+
|
47
|
+
it "sets the integration_branch" do
|
48
|
+
GitPivotalTracker::Base.new("--integration-branch", "development").options[:integration_branch].should == 'development'
|
49
|
+
GitPivotalTracker::Base.new("-b", "development").options[:integration_branch].should == 'development'
|
50
|
+
end
|
51
|
+
|
52
|
+
it "sets full_name" do
|
53
|
+
GitPivotalTracker::Base.new("--full-name", "Full Name").options[:full_name].should == 'Full Name'
|
54
|
+
GitPivotalTracker::Base.new("-n", "Full Name").options[:full_name].should == 'Full Name'
|
55
|
+
end
|
56
|
+
|
57
|
+
it "sets fast_forward" do
|
58
|
+
GitPivotalTracker::Base.new("--fast-forward").options[:fast_forward].should be
|
59
|
+
GitPivotalTracker::Base.new("-F").options[:fast_forward].should be
|
60
|
+
end
|
61
|
+
|
62
|
+
it "sets use_ssl" do
|
63
|
+
GitPivotalTracker::Base.new("--use-ssl").options[:use_ssl].should be
|
64
|
+
GitPivotalTracker::Base.new("-S").options[:use_ssl].should be
|
65
|
+
end
|
66
|
+
|
67
|
+
it "sets rebase" do
|
68
|
+
GitPivotalTracker::Base.new("--rebase").options[:rebase].should be
|
69
|
+
GitPivotalTracker::Base.new("-R").options[:rebase].should be
|
70
|
+
end
|
71
|
+
|
72
|
+
it "sets verbose" do
|
73
|
+
GitPivotalTracker::Base.new("--verbose").options[:verbose].should be
|
74
|
+
GitPivotalTracker::Base.new("-V").options[:verbose].should be
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe "#parse_gitconfig" do
|
79
|
+
context "with a full-name" do
|
80
|
+
before do
|
81
|
+
stub_git_config 'pivotal.full-name' => 'Full Name', 'user.name' => 'User Name'
|
82
|
+
subject = GitPivotalTracker::Base.new
|
83
|
+
end
|
84
|
+
|
85
|
+
it "sets the full_name to the full name" do
|
86
|
+
subject.options[:full_name].should == 'Full Name'
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
context "with no full-name" do
|
91
|
+
before do
|
92
|
+
stub_git_config({
|
93
|
+
'user.name' => 'User Name',
|
94
|
+
'pivotal.integration-branch' => 'development',
|
95
|
+
'pivotal.fast-forward' => 1,
|
96
|
+
'pivotal.rebase' => 1,
|
97
|
+
'pivotal.verbose' => 1,
|
98
|
+
'pivotal.use-ssl' => 1
|
99
|
+
})
|
100
|
+
subject = GitPivotalTracker::Base.new
|
101
|
+
end
|
102
|
+
|
103
|
+
it "sets use_ssl" do
|
104
|
+
subject.options[:use_ssl].should be
|
105
|
+
end
|
106
|
+
|
107
|
+
it "sets the api_token" do
|
108
|
+
subject.options[:api_token].should == '8a8a8a8'
|
109
|
+
end
|
110
|
+
|
111
|
+
it "sets the project_id" do
|
112
|
+
subject.options[:project_id].should == '123'
|
113
|
+
end
|
114
|
+
|
115
|
+
it "sets fast_forward" do
|
116
|
+
subject.options[:fast_forward].should be
|
117
|
+
end
|
118
|
+
|
119
|
+
it "sets rebase" do
|
120
|
+
subject.options[:rebase].should be
|
121
|
+
end
|
122
|
+
|
123
|
+
it "sets verbose" do
|
124
|
+
subject.options[:verbose].should be
|
125
|
+
end
|
126
|
+
|
127
|
+
it "sets the full_name to the user name" do
|
128
|
+
subject.options[:full_name].should == 'User Name'
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
describe ".new" do
|
134
|
+
context "given an invalid git root" do
|
135
|
+
before do
|
136
|
+
@current_dir = Dir.pwd
|
137
|
+
Dir.chdir("/")
|
138
|
+
end
|
139
|
+
|
140
|
+
it "fails to initialize" do
|
141
|
+
expect { GitPivotalTracker::Base.new }.to raise_error "No .git directory found"
|
142
|
+
end
|
143
|
+
|
144
|
+
after do
|
145
|
+
Dir.chdir @current_dir
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
context "given no prepare-commit-msg hook" do
|
150
|
+
before do
|
151
|
+
File.delete ".git/hooks/prepare-commit-msg"
|
152
|
+
GitPivotalTracker::Base.new
|
153
|
+
end
|
154
|
+
|
155
|
+
it "installs the hook" do
|
156
|
+
File.executable?(".git/hooks/prepare-commit-msg").should be
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
describe "#run!" do
|
162
|
+
context "given a config with an api token and a project id" do
|
163
|
+
before do
|
164
|
+
stub_git_config
|
165
|
+
subject = GitPivotalTracker::Base.new
|
166
|
+
PivotalTracker::Client.should_receive(:token=).with('8a8a8a8')
|
167
|
+
end
|
168
|
+
|
169
|
+
it "succeeds" do
|
170
|
+
subject.run!.should be_nil
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
context "given the config has no api token" do
|
175
|
+
before do
|
176
|
+
stub_git_config 'pivotal.api-token' => nil
|
177
|
+
subject = GitPivotalTracker::Base.new
|
178
|
+
end
|
179
|
+
|
180
|
+
it "fails" do
|
181
|
+
subject.run!.should == 1
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
context "given the config has no project id" do
|
186
|
+
before do
|
187
|
+
stub_git_config 'pivotal.project-id' => nil
|
188
|
+
subject = GitPivotalTracker::Base.new
|
189
|
+
end
|
190
|
+
|
191
|
+
it "fails" do
|
192
|
+
subject.run!.should == 1
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'spec_helper')
|
2
|
+
|
3
|
+
describe GitPivotalTracker::Finish do
|
4
|
+
|
5
|
+
before do
|
6
|
+
stub_request(:get, 'http://www.pivotaltracker.com/services/v3/projects/123').
|
7
|
+
to_return :body => File.read("#{FIXTURES_PATH}/project.xml")
|
8
|
+
stub_request(:get, 'http://www.pivotaltracker.com/services/v3/projects/123/stories/1234567890').
|
9
|
+
to_return :body => File.read("#{FIXTURES_PATH}/story.xml")
|
10
|
+
@current_head = finish.repository.head
|
11
|
+
end
|
12
|
+
|
13
|
+
after do
|
14
|
+
puts finish.repository.git.checkout({}, @current_head.name)
|
15
|
+
puts finish.repository.git.reset({}, @current_head.commit.sha)
|
16
|
+
end
|
17
|
+
|
18
|
+
let(:finish) { GitPivotalTracker::Finish.new("-t", "8a8a8a8", "-p", "123") }
|
19
|
+
|
20
|
+
context "given I am not on a topic branch" do
|
21
|
+
before do
|
22
|
+
@new_branch = 'invalid-topic-branch'
|
23
|
+
finish.repository.git.branch({:D => true}, @new_branch)
|
24
|
+
finish.repository.git.checkout({:b => true, :raise => true}, @new_branch)
|
25
|
+
end
|
26
|
+
|
27
|
+
it "fails" do
|
28
|
+
finish.run!.should == 1
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context "given I am on a topic branch with a commit" do
|
33
|
+
before do
|
34
|
+
finish.options[:integration_branch] = @current_head.name
|
35
|
+
|
36
|
+
finish_xml = File.read("#{FIXTURES_PATH}/finish_story.xml")
|
37
|
+
stub_request(:put, 'http://www.pivotaltracker.com/services/v3/projects/123/stories/1234567890').
|
38
|
+
with(:body => finish_xml).to_return(:body => finish_xml)
|
39
|
+
|
40
|
+
@repo = finish.repository
|
41
|
+
@new_branch = "testing-1234567890-branch_name"
|
42
|
+
@repo.git.branch({:D => true}, @new_branch)
|
43
|
+
@repo.git.checkout({:b => true, :raise => true}, @new_branch)
|
44
|
+
|
45
|
+
index = @repo.index
|
46
|
+
index.read_tree @new_branch
|
47
|
+
message = "Test commit: #{rand()}"
|
48
|
+
index.add('test.txt', message)
|
49
|
+
@sha = index.commit(message, [@repo.heads.detect { |h| h.name == @new_branch }.commit], nil, nil, @new_branch)
|
50
|
+
|
51
|
+
@repo.git.should_receive(:push).with(:raise => true)
|
52
|
+
end
|
53
|
+
|
54
|
+
it "merges the topic branch into the integration branch with a merge commit" do
|
55
|
+
finish.run!.should == 0
|
56
|
+
@repo.head.name.should == @current_head.branch
|
57
|
+
@repo.commits.first.parents.should have(2).items
|
58
|
+
@repo.heads.detect { |h| h.name == @current_head.name }.commit.sha.should == @sha
|
59
|
+
end
|
60
|
+
|
61
|
+
context "when I have rebase turned on" do
|
62
|
+
before do
|
63
|
+
finish.options[:rebase] = 1
|
64
|
+
finish.repository.git.should_receive(:pull).with(:raise => true)
|
65
|
+
finish.repository.git.should_receive(:rebase).with({:raise => true}, @current_head.name)
|
66
|
+
end
|
67
|
+
|
68
|
+
it "succeeds" do
|
69
|
+
finish.run!.should == 0
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
context "when I have fast forward turned on" do
|
74
|
+
before do
|
75
|
+
finish.options[:fast_forward] = 1
|
76
|
+
end
|
77
|
+
|
78
|
+
it "merges the topic branch without a merge commit" do
|
79
|
+
finish.run!.should == 0
|
80
|
+
@repo.heads.detect { |h| h.name == @current_head.name }.commit.sha.should == @sha
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'spec_helper')
|
2
|
+
|
3
|
+
describe GitPivotalTracker::Story do
|
4
|
+
describe "#run!" do
|
5
|
+
before do
|
6
|
+
stub_request(:get, 'http://www.pivotaltracker.com/services/v3/projects/123').
|
7
|
+
to_return :body => File.read("#{FIXTURES_PATH}/project.xml")
|
8
|
+
end
|
9
|
+
|
10
|
+
let(:story) { GitPivotalTracker::Story.new("-t", "8a8a8a8", "-p", "123") }
|
11
|
+
|
12
|
+
context "given there are no stories" do
|
13
|
+
before do
|
14
|
+
stub_request(:get, 'http://www.pivotaltracker.com/services/v3/projects/123/stories?filter=current_state:unstarted&limit=1').
|
15
|
+
to_return :body => File.read("#{FIXTURES_PATH}/no_stories.xml")
|
16
|
+
end
|
17
|
+
|
18
|
+
it "fails" do
|
19
|
+
story.run!.should == 1
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context "given there is a story" do
|
24
|
+
before do
|
25
|
+
start_xml = File.read("#{FIXTURES_PATH}/start_story.xml")
|
26
|
+
stub_request(:get, 'http://www.pivotaltracker.com/services/v3/projects/123/stories?filter=current_state:unstarted&limit=1').
|
27
|
+
to_return :body => File.read("#{FIXTURES_PATH}/one_story.xml")
|
28
|
+
stub_request(:put, 'http://www.pivotaltracker.com/services/v3/projects/123/stories/1234567890').
|
29
|
+
with(:body => start_xml).to_return(:body => start_xml)
|
30
|
+
|
31
|
+
@current_branch = story.repository.head.name
|
32
|
+
end
|
33
|
+
|
34
|
+
context "then I accept the default branch suffix" do
|
35
|
+
before do
|
36
|
+
@expected_branch = 'feature-1234567890-pause_the_film'
|
37
|
+
story.repository.git.branch({:D => true}, @expected_branch)
|
38
|
+
|
39
|
+
story.should_receive(:gets).and_return "\n"
|
40
|
+
story.run!
|
41
|
+
end
|
42
|
+
|
43
|
+
it "creates a new branch" do
|
44
|
+
story.repository.head.name.should == @expected_branch
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context "then I customize the branch suffix" do
|
49
|
+
before do
|
50
|
+
@expected_branch = 'feature-1234567890-new_name'
|
51
|
+
story.repository.git.branch({:D => true}, @expected_branch)
|
52
|
+
|
53
|
+
story.should_receive(:gets).and_return "new_name\n"
|
54
|
+
story.run!
|
55
|
+
end
|
56
|
+
|
57
|
+
it "creates a new branch" do
|
58
|
+
story.repository.head.name.should == @expected_branch
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
after do
|
63
|
+
story.repository.git.checkout({:raise => true}, @current_branch)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
describe "#type" do
|
70
|
+
it "is a story" do
|
71
|
+
GitPivotalTracker::Story.new.type.should == 'story'
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe GitPivotalTracker::Bug do
|
77
|
+
describe "#type" do
|
78
|
+
it "is a bug" do
|
79
|
+
GitPivotalTracker::Bug.new.type.should == 'bug'
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe GitPivotalTracker::Feature do
|
85
|
+
describe "#type" do
|
86
|
+
it "is a feature" do
|
87
|
+
GitPivotalTracker::Feature.new.type.should == 'feature'
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe GitPivotalTracker::Chore do
|
93
|
+
describe "#type" do
|
94
|
+
it "is a chore" do
|
95
|
+
GitPivotalTracker::Chore.new.type.should == 'chore'
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
data/spec/spec_helper.rb
CHANGED
@@ -1,6 +1,13 @@
|
|
1
|
-
|
1
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
2
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
3
|
|
3
|
-
|
4
|
+
require 'git_pivotal_tracker'
|
5
|
+
require 'rspec'
|
6
|
+
require 'webmock/rspec'
|
7
|
+
|
8
|
+
FIXTURES_PATH = File.join(File.dirname(__FILE__), 'fixtures')
|
9
|
+
|
10
|
+
RSpec.configure do |config|
|
4
11
|
# == Mock Framework
|
5
12
|
#
|
6
13
|
# RSpec uses it's own mocking framework by default. If you prefer to
|
@@ -10,3 +17,11 @@ Spec::Runner.configure do |config|
|
|
10
17
|
# config.mock_with :flexmock
|
11
18
|
# config.mock_with :rr
|
12
19
|
end
|
20
|
+
|
21
|
+
def stub_git_config(opts = {})
|
22
|
+
git_options = {
|
23
|
+
"pivotal.api-token" => "8a8a8a8",
|
24
|
+
"pivotal.project-id" => "123"
|
25
|
+
}.merge opts
|
26
|
+
Grit::Repo.stub!(:new).and_return mock('Grit::Repo', :config => git_options)
|
27
|
+
end
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: git_pivotal_tracker
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.0.
|
5
|
+
version: 0.0.3
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Ben Lindsey
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-07-
|
13
|
+
date: 2011-07-17 00:00:00 -07:00
|
14
14
|
default_executable:
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
@@ -25,7 +25,7 @@ dependencies:
|
|
25
25
|
type: :development
|
26
26
|
version_requirements: *id001
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: webmock
|
29
29
|
prerelease: false
|
30
30
|
requirement: &id002 !ruby/object:Gem::Requirement
|
31
31
|
none: false
|
@@ -33,10 +33,10 @@ dependencies:
|
|
33
33
|
- - ">="
|
34
34
|
- !ruby/object:Gem::Version
|
35
35
|
version: "0"
|
36
|
-
type: :
|
36
|
+
type: :development
|
37
37
|
version_requirements: *id002
|
38
38
|
- !ruby/object:Gem::Dependency
|
39
|
-
name:
|
39
|
+
name: grit
|
40
40
|
prerelease: false
|
41
41
|
requirement: &id003 !ruby/object:Gem::Requirement
|
42
42
|
none: false
|
@@ -46,6 +46,17 @@ dependencies:
|
|
46
46
|
version: "0"
|
47
47
|
type: :runtime
|
48
48
|
version_requirements: *id003
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: pivotal-tracker
|
51
|
+
prerelease: false
|
52
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: "0"
|
58
|
+
type: :runtime
|
59
|
+
version_requirements: *id004
|
49
60
|
description: provides a set of git workflow tools to start and finish Pivotal Tracker stories in topic branches
|
50
61
|
email:
|
51
62
|
- ben@carbonfive.com
|
@@ -82,7 +93,15 @@ files:
|
|
82
93
|
- lib/git_pivotal_tracker/info.rb
|
83
94
|
- lib/git_pivotal_tracker/story.rb
|
84
95
|
- lib/git_pivotal_tracker/version.rb
|
85
|
-
- spec/
|
96
|
+
- spec/fixtures/finish_story.xml
|
97
|
+
- spec/fixtures/no_stories.xml
|
98
|
+
- spec/fixtures/one_story.xml
|
99
|
+
- spec/fixtures/project.xml
|
100
|
+
- spec/fixtures/start_story.xml
|
101
|
+
- spec/fixtures/story.xml
|
102
|
+
- spec/git_pivotal_tracker/base_spec.rb
|
103
|
+
- spec/git_pivotal_tracker/finish_spec.rb
|
104
|
+
- spec/git_pivotal_tracker/story_spec.rb
|
86
105
|
- spec/spec_helper.rb
|
87
106
|
has_rdoc: true
|
88
107
|
homepage: https://github.com/blindsey/git_pivotal_tracker
|
@@ -113,5 +132,13 @@ signing_key:
|
|
113
132
|
specification_version: 3
|
114
133
|
summary: A git workflow integrated with Pivotal Tracker
|
115
134
|
test_files:
|
116
|
-
- spec/
|
135
|
+
- spec/fixtures/finish_story.xml
|
136
|
+
- spec/fixtures/no_stories.xml
|
137
|
+
- spec/fixtures/one_story.xml
|
138
|
+
- spec/fixtures/project.xml
|
139
|
+
- spec/fixtures/start_story.xml
|
140
|
+
- spec/fixtures/story.xml
|
141
|
+
- spec/git_pivotal_tracker/base_spec.rb
|
142
|
+
- spec/git_pivotal_tracker/finish_spec.rb
|
143
|
+
- spec/git_pivotal_tracker/story_spec.rb
|
117
144
|
- spec/spec_helper.rb
|