git_pivotal_tracker 0.0.2 → 0.0.3
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 +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
|