git_pivotal_tracker_x 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.rvmrc +1 -0
- data/Gemfile +18 -0
- data/LICENSE +21 -0
- data/README.md +123 -0
- data/Rakefile +54 -0
- data/VERSION +1 -0
- data/bin/commit-msg +13 -0
- data/bin/git-bug +7 -0
- data/bin/git-chore +7 -0
- data/bin/git-feature +7 -0
- data/bin/git-finish +7 -0
- data/bin/git-info +7 -0
- data/bin/git-story +7 -0
- data/cucumber.yml +8 -0
- data/features/diagnostics.feature +15 -0
- data/features/getting_started.feature +35 -0
- data/features/hooks/aruba.rb +3 -0
- data/features/step_definitions/git_steps.rb +14 -0
- data/features/support/env.rb +1 -0
- data/git_pivotal_tracker_x.gemspec +97 -0
- data/lib/git_pivotal_tracker.rb +9 -0
- data/lib/git_pivotal_tracker/base.rb +112 -0
- data/lib/git_pivotal_tracker/finish.rb +51 -0
- data/lib/git_pivotal_tracker/info.rb +19 -0
- data/lib/git_pivotal_tracker/story.rb +59 -0
- 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 +235 -0
- data/spec/git_pivotal_tracker/finish_spec.rb +100 -0
- data/spec/git_pivotal_tracker/info_spec.rb +18 -0
- data/spec/git_pivotal_tracker/story_spec.rb +130 -0
- data/spec/spec_helper.rb +27 -0
- metadata +188 -0
@@ -0,0 +1,112 @@
|
|
1
|
+
module GitPivotalTracker
|
2
|
+
class Base
|
3
|
+
GIT_DIR = ENV['GIT_DIR'] || '.git'
|
4
|
+
|
5
|
+
attr_reader :options, :repository
|
6
|
+
|
7
|
+
def initialize(*args)
|
8
|
+
directories = Dir.pwd.split(::File::SEPARATOR)
|
9
|
+
begin
|
10
|
+
break if File.directory?(File.join(directories, GIT_DIR))
|
11
|
+
end while directories.pop
|
12
|
+
|
13
|
+
raise "No #{GIT_DIR} directory found" if directories.empty?
|
14
|
+
root = File.join(directories, GIT_DIR)
|
15
|
+
@repository = Grit::Repo.new(root)
|
16
|
+
|
17
|
+
new_hook_path = File.join(root, 'hooks', 'commit-msg')
|
18
|
+
unless File.executable?(new_hook_path)
|
19
|
+
puts "Installing commit-msg hook..."
|
20
|
+
old_hook_path = File.join(File.dirname(__FILE__), '..', '..', 'bin', 'commit-msg')
|
21
|
+
FileUtils.cp(old_hook_path, new_hook_path, :preserve => true)
|
22
|
+
end
|
23
|
+
|
24
|
+
@options = {}
|
25
|
+
parse_gitconfig
|
26
|
+
parse_argv(*args)
|
27
|
+
end
|
28
|
+
|
29
|
+
def run!
|
30
|
+
unless options[:api_token] && options[:project_id]
|
31
|
+
puts "Pivotal Tracker API Token and Project ID are required"
|
32
|
+
return 1
|
33
|
+
end
|
34
|
+
|
35
|
+
PivotalTracker::Client.token = options[:api_token]
|
36
|
+
PivotalTracker::Client.use_ssl = options[:use_ssl]
|
37
|
+
|
38
|
+
nil
|
39
|
+
end
|
40
|
+
|
41
|
+
protected
|
42
|
+
|
43
|
+
def integration_branch
|
44
|
+
current_branch_suffix || options[:integration_branch] || 'master'
|
45
|
+
end
|
46
|
+
|
47
|
+
def current_branch_suffix
|
48
|
+
if current_branch =~ /.*-\d+?-(.*)/ and @repository.branches.any? { |branch| branch.name == $1 }
|
49
|
+
$1
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def current_branch
|
54
|
+
@current_branch ||= repository.head.name
|
55
|
+
end
|
56
|
+
|
57
|
+
def story_id
|
58
|
+
if current_branch =~ /-(\d+)-/
|
59
|
+
$1
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def project
|
64
|
+
@project ||= PivotalTracker::Project.find(options[:project_id])
|
65
|
+
end
|
66
|
+
|
67
|
+
def story
|
68
|
+
@story ||= project.stories.find(story_id)
|
69
|
+
end
|
70
|
+
|
71
|
+
def log(message)
|
72
|
+
puts message if options[:verbose]
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def parse_gitconfig
|
78
|
+
options[:api_token] = repository.config['pivotal.api-token']
|
79
|
+
options[:project_id] = repository.config['pivotal.project-id']
|
80
|
+
options[:integration_branch] = repository.config['pivotal.integration-branch']
|
81
|
+
options[:only_mine] = repository.config['pivotal.only-mine']
|
82
|
+
options[:include_rejected] = repository.config['pivotal.include-rejected']
|
83
|
+
options[:fast_forward] = repository.config['pivotal.fast-forward']
|
84
|
+
options[:rebase] = repository.config['pivotal.rebase']
|
85
|
+
options[:full_name] = repository.config['pivotal.full-name'] || repository.config['user.name']
|
86
|
+
options[:verbose] = repository.config['pivotal.verbose']
|
87
|
+
options[:use_ssl] = repository.config['pivotal.use-ssl']
|
88
|
+
options[:delete_branch] = repository.config['pivotal.delete-branch']
|
89
|
+
end
|
90
|
+
|
91
|
+
def parse_argv(*args)
|
92
|
+
OptionParser.new do |opts|
|
93
|
+
opts.banner = "Usage: git <feature|chore|bug> [options]"
|
94
|
+
opts.on("-t", "--api-token=", "Pivotal Tracker API key") { |k| options[:api_token] = k }
|
95
|
+
opts.on("-p", "--project-id=", "Pivotal Tracker project id") { |p| options[:project_id] = p }
|
96
|
+
opts.on("-b", "--integration-branch=", "The branch to merge finished stories back down onto") { |b| options[:integration_branch] = b }
|
97
|
+
opts.on("-n", "--full-name=", "Your Pivotal Tracker full name") { |n| options[:full_name] = n }
|
98
|
+
|
99
|
+
|
100
|
+
opts.on("-D", "--delete-branch", "Delete store branch after merging") { |d| options[:delete_branch] = d }
|
101
|
+
opts.on("-I", "--include-rejected", "Include rejected stories as well as unstarted ones") { |i| options[:include_rejected] = i }
|
102
|
+
opts.on("-O", "--only-mine", "Only include stories that are assigned to me") { |o| options[:only_mine] = o }
|
103
|
+
opts.on("-F", "--fast-forward", "Merge topic branch with fast forward") { |f| options[:fast_forward] = f }
|
104
|
+
opts.on("-S", "--use-ssl", "Use SSL for connection to Pivotal Tracker") { |s| options[:use_ssl] = s }
|
105
|
+
opts.on("-R", "--rebase", "Fetch and rebase the integration branch before merging") { |r| options[:rebase] = r }
|
106
|
+
opts.on("-V", "--verbose", "Verbose command logging") { |v| options[:verbose] = v }
|
107
|
+
opts.on_tail("-h", "--help", "This usage guide") { put opts.to_s; exit 0 }
|
108
|
+
end.parse!(args)
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module GitPivotalTracker
|
2
|
+
class Finish < Base
|
3
|
+
|
4
|
+
def run!
|
5
|
+
return 1 if super
|
6
|
+
|
7
|
+
unless story_id
|
8
|
+
puts "Branch name must contain a Pivotal Tracker story id"
|
9
|
+
return 1
|
10
|
+
end
|
11
|
+
|
12
|
+
if options[:rebase]
|
13
|
+
puts "Fetching origin and rebasing #{current_branch}"
|
14
|
+
log repository.git.checkout({:raise => true}, integration_branch)
|
15
|
+
log repository.git.pull({:raise => true})
|
16
|
+
log repository.git.rebase({:raise => true}, integration_branch, current_branch)
|
17
|
+
end
|
18
|
+
|
19
|
+
puts "Merging #{current_branch} into #{integration_branch}"
|
20
|
+
log repository.git.checkout({:raise => true}, integration_branch)
|
21
|
+
|
22
|
+
merge_options = {:raise => true}
|
23
|
+
merge_options[:no_ff] = true unless options[:fast_forward]
|
24
|
+
log repository.git.merge(merge_options, current_branch)
|
25
|
+
|
26
|
+
puts "Pushing #{integration_branch}"
|
27
|
+
log repository.git.push({:raise => true}, 'origin', integration_branch)
|
28
|
+
|
29
|
+
puts "Marking Story #{story_id} as finished..."
|
30
|
+
if story.update(:current_state => finished_state)
|
31
|
+
delete_current_branch if options[:delete_branch]
|
32
|
+
puts "Success"
|
33
|
+
return 0
|
34
|
+
else
|
35
|
+
puts "Unable to mark Story #{story_id} as finished"
|
36
|
+
return 1
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def finished_state
|
43
|
+
story.story_type == "chore" ? "accepted" : "finished"
|
44
|
+
end
|
45
|
+
|
46
|
+
def delete_current_branch
|
47
|
+
puts "Deleting #{current_branch}"
|
48
|
+
log repository.git.branch({:raise => true, :d => true}, current_branch)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module GitPivotalTracker
|
2
|
+
class Info < Base
|
3
|
+
|
4
|
+
def run!
|
5
|
+
return 1 if super
|
6
|
+
|
7
|
+
unless story_id
|
8
|
+
puts "Branch name must contain a Pivotal Tracker story id"
|
9
|
+
return 1
|
10
|
+
end
|
11
|
+
|
12
|
+
puts "URL: #{story.url}"
|
13
|
+
puts "Story: #{story.name}"
|
14
|
+
puts "Description: #{story.description}"
|
15
|
+
|
16
|
+
return 0
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module GitPivotalTracker
|
2
|
+
class Story < Base
|
3
|
+
|
4
|
+
def run!
|
5
|
+
return 1 if super
|
6
|
+
|
7
|
+
puts "Retrieving latest #{type} from Pivotal Tracker"
|
8
|
+
|
9
|
+
unless story = fetch_story
|
10
|
+
puts "No #{type} available!"
|
11
|
+
return 1
|
12
|
+
end
|
13
|
+
|
14
|
+
puts "URL: #{story.url}"
|
15
|
+
puts "Story: #{story.name}"
|
16
|
+
|
17
|
+
print "Enter branch name [#{branch_suffix story}]: "
|
18
|
+
suffix = gets.chomp
|
19
|
+
suffix = branch_suffix(story) if suffix == ""
|
20
|
+
|
21
|
+
branch = "#{story.story_type}-#{story.id}-#{suffix}"
|
22
|
+
puts "Checking out a new branch '#{branch}'"
|
23
|
+
log repository.git.checkout({:b => true, :raise => true}, branch)
|
24
|
+
|
25
|
+
puts "Updating #{type} status in Pivotal Tracker..."
|
26
|
+
if story.update(:owned_by => options[:full_name], :current_state => :started)
|
27
|
+
puts "Success"
|
28
|
+
return 0
|
29
|
+
else
|
30
|
+
puts "Unable to mark #{type} as started"
|
31
|
+
return 1
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def type
|
36
|
+
self.class.name.downcase.split(/::/).last
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def fetch_story
|
42
|
+
state = options[:include_rejected] ? "unstarted,rejected" : "unstarted"
|
43
|
+
conditions = { :current_state => state, :limit => 1 }
|
44
|
+
conditions[:owned_by] = "\"#{options[:full_name]}\"" if options[:only_mine]
|
45
|
+
conditions[:story_type] = type unless type == 'story'
|
46
|
+
project.stories.all(conditions).first
|
47
|
+
end
|
48
|
+
|
49
|
+
def branch_suffix(story)
|
50
|
+
story.name.sub(/^\W+/, '').sub(/\W+$/, '').gsub(/\W+/, '_').downcase
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class Bug < Story; end
|
55
|
+
|
56
|
+
class Feature < Story; end
|
57
|
+
|
58
|
+
class Chore < Story; end
|
59
|
+
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,235 @@
|
|
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
|
+
|
36
|
+
it "leaves include_rejected nil" do
|
37
|
+
subject.options[:include_rejected].should be_nil
|
38
|
+
end
|
39
|
+
|
40
|
+
it "leaves only_mine nil" do
|
41
|
+
subject.options[:only_mine].should be_nil
|
42
|
+
end
|
43
|
+
|
44
|
+
it "leaves delete_branch nil" do
|
45
|
+
subject.options[:delete_branch].should be_nil
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
it "sets the api_token" do
|
50
|
+
GitPivotalTracker::Base.new("--api-token", "8a8a8a8").options[:api_token].should == '8a8a8a8'
|
51
|
+
GitPivotalTracker::Base.new("-t", "8a8a8a8").options[:api_token].should == '8a8a8a8'
|
52
|
+
end
|
53
|
+
|
54
|
+
it "sets the project_id" do
|
55
|
+
GitPivotalTracker::Base.new("--project-id", "123").options[:project_id].should == '123'
|
56
|
+
GitPivotalTracker::Base.new("-p", "123").options[:project_id].should == '123'
|
57
|
+
end
|
58
|
+
|
59
|
+
it "sets the integration_branch" do
|
60
|
+
GitPivotalTracker::Base.new("--integration-branch", "development").options[:integration_branch].should == 'development'
|
61
|
+
GitPivotalTracker::Base.new("-b", "development").options[:integration_branch].should == 'development'
|
62
|
+
end
|
63
|
+
|
64
|
+
it "sets full_name" do
|
65
|
+
GitPivotalTracker::Base.new("--full-name", "Full Name").options[:full_name].should == 'Full Name'
|
66
|
+
GitPivotalTracker::Base.new("-n", "Full Name").options[:full_name].should == 'Full Name'
|
67
|
+
end
|
68
|
+
|
69
|
+
it "sets include_rejected" do
|
70
|
+
GitPivotalTracker::Base.new("--include-rejected").options[:include_rejected].should be
|
71
|
+
GitPivotalTracker::Base.new("-I").options[:include_rejected].should be
|
72
|
+
end
|
73
|
+
|
74
|
+
it "sets only_mine" do
|
75
|
+
GitPivotalTracker::Base.new("--only-mine").options[:only_mine].should be
|
76
|
+
GitPivotalTracker::Base.new("-O").options[:only_mine].should be
|
77
|
+
end
|
78
|
+
|
79
|
+
it "sets fast_forward" do
|
80
|
+
GitPivotalTracker::Base.new("--fast-forward").options[:fast_forward].should be
|
81
|
+
GitPivotalTracker::Base.new("-F").options[:fast_forward].should be
|
82
|
+
end
|
83
|
+
|
84
|
+
it "sets use_ssl" do
|
85
|
+
GitPivotalTracker::Base.new("--use-ssl").options[:use_ssl].should be
|
86
|
+
GitPivotalTracker::Base.new("-S").options[:use_ssl].should be
|
87
|
+
end
|
88
|
+
|
89
|
+
it "sets rebase" do
|
90
|
+
GitPivotalTracker::Base.new("--rebase").options[:rebase].should be
|
91
|
+
GitPivotalTracker::Base.new("-R").options[:rebase].should be
|
92
|
+
end
|
93
|
+
|
94
|
+
it "sets verbose" do
|
95
|
+
GitPivotalTracker::Base.new("--verbose").options[:verbose].should be
|
96
|
+
GitPivotalTracker::Base.new("-V").options[:verbose].should be
|
97
|
+
end
|
98
|
+
|
99
|
+
it "sets delete_branch" do
|
100
|
+
GitPivotalTracker::Base.new("--delete-branch").options[:delete_branch].should be
|
101
|
+
GitPivotalTracker::Base.new("-D").options[:delete_branch].should be
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
describe "#parse_gitconfig" do
|
106
|
+
context "with a full-name" do
|
107
|
+
before do
|
108
|
+
stub_git_config 'pivotal.full-name' => 'Full Name', 'user.name' => 'User Name'
|
109
|
+
subject = GitPivotalTracker::Base.new
|
110
|
+
end
|
111
|
+
|
112
|
+
it "sets the full_name to the full name" do
|
113
|
+
subject.options[:full_name].should == 'Full Name'
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
context "with no full-name" do
|
118
|
+
before do
|
119
|
+
stub_git_config({
|
120
|
+
'user.name' => 'User Name',
|
121
|
+
'pivotal.integration-branch' => 'development',
|
122
|
+
'pivotal.only-mine' => 1,
|
123
|
+
'pivotal.include-rejected' => 1,
|
124
|
+
'pivotal.fast-forward' => 1,
|
125
|
+
'pivotal.rebase' => 1,
|
126
|
+
'pivotal.verbose' => 1,
|
127
|
+
'pivotal.use-ssl' => 1
|
128
|
+
})
|
129
|
+
subject = GitPivotalTracker::Base.new
|
130
|
+
end
|
131
|
+
|
132
|
+
it "sets use_ssl" do
|
133
|
+
subject.options[:use_ssl].should be
|
134
|
+
end
|
135
|
+
|
136
|
+
it "sets the api_token" do
|
137
|
+
subject.options[:api_token].should == '8a8a8a8'
|
138
|
+
end
|
139
|
+
|
140
|
+
it "sets the project_id" do
|
141
|
+
subject.options[:project_id].should == '123'
|
142
|
+
end
|
143
|
+
|
144
|
+
it "sets only_mine" do
|
145
|
+
subject.options[:only_mine].should be
|
146
|
+
end
|
147
|
+
|
148
|
+
it "sets include_rejected" do
|
149
|
+
subject.options[:include_rejected].should be
|
150
|
+
end
|
151
|
+
|
152
|
+
it "sets fast_forward" do
|
153
|
+
subject.options[:fast_forward].should be
|
154
|
+
end
|
155
|
+
|
156
|
+
it "sets rebase" do
|
157
|
+
subject.options[:rebase].should be
|
158
|
+
end
|
159
|
+
|
160
|
+
it "sets verbose" do
|
161
|
+
subject.options[:verbose].should be
|
162
|
+
end
|
163
|
+
|
164
|
+
it "sets the full_name to the user name" do
|
165
|
+
subject.options[:full_name].should == 'User Name'
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
describe ".new" do
|
171
|
+
context "given an invalid git root" do
|
172
|
+
before do
|
173
|
+
@current_dir = Dir.pwd
|
174
|
+
Dir.chdir("/")
|
175
|
+
end
|
176
|
+
|
177
|
+
it "fails to initialize" do
|
178
|
+
expect { GitPivotalTracker::Base.new }.to raise_error "No .git directory found"
|
179
|
+
end
|
180
|
+
|
181
|
+
after do
|
182
|
+
Dir.chdir @current_dir
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
context "given no commit-msg hook" do
|
187
|
+
let(:file_name) { ".git/hooks/commit-msg" }
|
188
|
+
|
189
|
+
before do
|
190
|
+
File.delete file_name if File.exists? file_name
|
191
|
+
GitPivotalTracker::Base.new
|
192
|
+
end
|
193
|
+
|
194
|
+
it "installs the hook" do
|
195
|
+
File.executable?(file_name).should be
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
describe "#run!" do
|
201
|
+
context "given a config with an api token and a project id" do
|
202
|
+
before do
|
203
|
+
stub_git_config
|
204
|
+
subject = GitPivotalTracker::Base.new
|
205
|
+
PivotalTracker::Client.should_receive(:token=).with('8a8a8a8')
|
206
|
+
end
|
207
|
+
|
208
|
+
it "succeeds" do
|
209
|
+
subject.run!.should be_nil
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
context "given the config has no api token" do
|
214
|
+
before do
|
215
|
+
stub_git_config 'pivotal.api-token' => nil
|
216
|
+
subject = GitPivotalTracker::Base.new
|
217
|
+
end
|
218
|
+
|
219
|
+
it "fails" do
|
220
|
+
subject.run!.should == 1
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
context "given the config has no project id" do
|
225
|
+
before do
|
226
|
+
stub_git_config 'pivotal.project-id' => nil
|
227
|
+
subject = GitPivotalTracker::Base.new
|
228
|
+
end
|
229
|
+
|
230
|
+
it "fails" do
|
231
|
+
subject.run!.should == 1
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|