git-pivotal-tracker 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. data/.gitignore +5 -0
  2. data/.rspec +2 -0
  3. data/.rvmrc +1 -0
  4. data/.travis.yml +3 -0
  5. data/CHANGELOG +45 -0
  6. data/Gemfile +3 -0
  7. data/Gemfile.lock +74 -0
  8. data/LICENSE +21 -0
  9. data/Rakefile +24 -0
  10. data/VERSION +1 -0
  11. data/bin/git-accept +7 -0
  12. data/bin/git-block +7 -0
  13. data/bin/git-finish +7 -0
  14. data/bin/git-info +7 -0
  15. data/bin/git-start +8 -0
  16. data/bin/git-unblock +7 -0
  17. data/features/accept.feature +140 -0
  18. data/features/block.feature +94 -0
  19. data/features/finish.feature +119 -0
  20. data/features/info.feature +99 -0
  21. data/features/start.feature +113 -0
  22. data/features/step_definitions/steps.rb +114 -0
  23. data/features/support/dsl/assertions.rb +11 -0
  24. data/features/support/dsl/data.rb +11 -0
  25. data/features/support/dsl/pivotal.rb +76 -0
  26. data/features/support/env.rb +6 -0
  27. data/features/support/git-pivotal.rb +69 -0
  28. data/features/test_repo/origin.git/COMMIT_EDITMSG +1 -0
  29. data/features/test_repo/origin.git/HEAD +1 -0
  30. data/features/test_repo/origin.git/config +8 -0
  31. data/features/test_repo/origin.git/description +1 -0
  32. data/features/test_repo/origin.git/hooks/applypatch-msg.sample +15 -0
  33. data/features/test_repo/origin.git/hooks/commit-msg.sample +24 -0
  34. data/features/test_repo/origin.git/hooks/post-commit.sample +8 -0
  35. data/features/test_repo/origin.git/hooks/post-receive.sample +15 -0
  36. data/features/test_repo/origin.git/hooks/post-update.sample +8 -0
  37. data/features/test_repo/origin.git/hooks/pre-applypatch.sample +14 -0
  38. data/features/test_repo/origin.git/hooks/pre-commit.sample +46 -0
  39. data/features/test_repo/origin.git/hooks/pre-rebase.sample +169 -0
  40. data/features/test_repo/origin.git/hooks/prepare-commit-msg.sample +36 -0
  41. data/features/test_repo/origin.git/hooks/update.sample +128 -0
  42. data/features/test_repo/origin.git/index +0 -0
  43. data/features/test_repo/origin.git/info/exclude +6 -0
  44. data/features/test_repo/origin.git/logs/HEAD +1 -0
  45. data/features/test_repo/origin.git/logs/refs/heads/master +1 -0
  46. data/features/test_repo/origin.git/objects/0c/6f7b1384910d1a2f137590095f008a06c7e00c +0 -0
  47. data/features/test_repo/origin.git/objects/10/ecf2b7ce989f01f3f7266e712b48d9275f2635 +0 -0
  48. data/features/test_repo/origin.git/objects/a5/71d56305df09fb060f6ccb730b46080d305beb +0 -0
  49. data/features/test_repo/origin.git/refs/heads/master +1 -0
  50. data/features/test_repo/readme +1 -0
  51. data/features/unblock.feature +68 -0
  52. data/git-pivotal-tracker.gemspec +27 -0
  53. data/lib/commands/accept.rb +76 -0
  54. data/lib/commands/base.rb +128 -0
  55. data/lib/commands/block.rb +59 -0
  56. data/lib/commands/bug.rb +19 -0
  57. data/lib/commands/card.rb +32 -0
  58. data/lib/commands/chore.rb +19 -0
  59. data/lib/commands/feature.rb +19 -0
  60. data/lib/commands/finish.rb +59 -0
  61. data/lib/commands/info.rb +58 -0
  62. data/lib/commands/map.rb +10 -0
  63. data/lib/commands/pick.rb +76 -0
  64. data/lib/commands/start.rb +35 -0
  65. data/lib/commands/unblock.rb +55 -0
  66. data/lib/git-pivotal-tracker.rb +11 -0
  67. data/readme.markdown +95 -0
  68. data/spec/commands/base_spec.rb +151 -0
  69. data/spec/commands/bug_spec.rb +24 -0
  70. data/spec/commands/chore_spec.rb +24 -0
  71. data/spec/commands/feature_spec.rb +24 -0
  72. data/spec/commands/finish_spec.rb +125 -0
  73. data/spec/commands/map_spec.rb +14 -0
  74. data/spec/commands/start_spec.rb +29 -0
  75. data/spec/factories.rb +13 -0
  76. data/spec/factory.rb +26 -0
  77. data/spec/spec_helper.rb +24 -0
  78. metadata +251 -0
@@ -0,0 +1,11 @@
1
+ $:.unshift(File.dirname(__FILE__))
2
+
3
+ require File.join('commands', 'base')
4
+ require File.join('commands', 'pick')
5
+ require File.join('commands', 'card')
6
+ require File.join('commands', 'feature')
7
+ require File.join('commands', 'bug')
8
+ require File.join('commands', 'chore')
9
+ require File.join('commands', 'finish')
10
+ require File.join('commands', 'info')
11
+ require File.join('commands', 'start')
@@ -0,0 +1,95 @@
1
+ [![Build Status](https://secure.travis-ci.org/zdennis/git-pivotal.png)](http://travis-ci.org/#!/zdennis/git-pivotal)
2
+
3
+ This is based on https://github.com/trydionel/git-pivotal but has an extended vision and set of goals to achieve.
4
+
5
+ Things like:
6
+
7
+ * Make the existing API more intuitive:
8
+ * git-bug, git-chore, git-feature should not be separate CLI commands. The common action is start, so goal here is to support "git-start _card\_type_"
9
+ * Users should be able to interact with specific cards when possible.
10
+ * e.g. "git start 123456" should start card 123456.
11
+ * being generic (interacting with next available or specific should apply to all applicable commands)
12
+ * Add more commands to interact with Pivotal:
13
+ * git block
14
+ * git unblock
15
+ * git comments
16
+ * git label
17
+ * git unstart
18
+ * git accept
19
+ * Add verbosity and dry-run support to commands to communicate to the users what commands will be run
20
+ * Add before/after hooks extension points so people do not have to modify the project in order to do something custom.
21
+ * e.g. if you want to build a changelog as card's are finished, this should be able to be done by hooking into the "git finish" command and not require altering the code-base
22
+ * better support for handling merge conflicts (_this may be nothing more than communicating better to the user if a merge conflict happens when issuing a command_)
23
+
24
+ The main vision for this is simple: Encourage and support good practices and be flexible.
25
+
26
+ More README to come. See ISSUES for things I want to tackle in this project.
27
+
28
+
29
+ ### git start - Starting the next available Feature/Bug/Chore
30
+
31
+ git start <card_type>
32
+
33
+ Replace card\_type in the above command to start the next available card in your Pivotal project, e.g.:
34
+
35
+ 1 git-pivotal:master % git start feature
36
+ Collecting latest stories from Pivotal Tracker...
37
+ Story: Test git pivotal
38
+ URL: http://www.pivotaltracker.com/story/show/1234567
39
+ Updating story status in Pivotal Tracker...
40
+ Enter branch name (will be prepended by 1234567) [feature]: testing
41
+ Creating 1234567-testing branch...
42
+ 2 git-pivotal:1234567-testing %
43
+
44
+
45
+ ### git finish
46
+ When on a feature branch, this command will close the associated story in Pivotal Tracker, merge the branch into your integration branch (`master` by default) and remove the feature branch.
47
+
48
+ 3 git-pivotal:1234567-testing % git finish
49
+ Marking Story 1234567 as finished...
50
+ Merging 1234567-testing into master
51
+ Removing 1234567-testing branch
52
+ 4 git-pivotal:master %
53
+
54
+ ### git info
55
+ When on a feature/bug/chore branch, this command will display the story information as recorded in Pivotal Tracker.
56
+
57
+ 5 git-pivotal:1234567-testing % git info
58
+ Story: Test git pivotal
59
+ URL: http://www.pivotaltracker.com/story/show/1234567
60
+ Description: The awesome story description
61
+ 6 git-pivotal:1234567-testing %
62
+
63
+ ## Installation
64
+
65
+ _This section is out of date and applies to the original project. It needs to be updated._
66
+
67
+ To install git-pivotal, simply run
68
+
69
+ [sudo] gem install git-pivotal
70
+
71
+ ## Configuration
72
+
73
+ Once installed, git pivotal needs three bits of info: your Pivotal Tracker API Token, your name as it appears in Pivotal Tracker and your Pivotal Tracker project id. The former two are best set as a global git config options:
74
+
75
+ git config --global pivotal.api-token 9a9a9a9a9a9a9a9a9a9a
76
+ git config --global pivotal.full-name "Jeff Tucker"
77
+
78
+ If you prefer to merge back to a branch other than master when you've finished a story, you can configure that:
79
+
80
+ git config --global pivotal.integration-branch develop
81
+
82
+ If you only want to pick up bugs/features/chores that are already assigned to you, set:
83
+
84
+ git config --global pivotal.only-mine true
85
+
86
+ The project id is best placed within your project's git config:
87
+
88
+ git config -f .git/config pivotal.project-id 88888
89
+
90
+ If you would rather have the story id appended to the branch name (feature-123456) instead of prepending (123456-feature), you can configue that:
91
+
92
+ git config -f .git/config pivotal.append-name true
93
+
94
+ If you're not interested in storing these options in git, you can pass them into git pivotal as command line arguments. See the usage guides for more details.
95
+
@@ -0,0 +1,151 @@
1
+ require 'spec_helper'
2
+
3
+ describe Commands::Base do
4
+
5
+ before(:each) do
6
+ @input = mock('input')
7
+ @output = mock('output')
8
+
9
+ # stub out git config requests
10
+ Commands::Base.any_instance.stubs(:get).with { |v| v =~ /git config/ }.returns("")
11
+ end
12
+
13
+ it "should set the api key with the -k option" do
14
+ @pick = Commands::Base.new("-k", "1234").with(@input, @output)
15
+ @pick.options[:api_token].should == "1234"
16
+ end
17
+
18
+ it "should set the api key with the --api-token= option" do
19
+ @pick = Commands::Base.new("--api-key=1234").with(@input, @output)
20
+ @pick.options[:api_token].should == "1234"
21
+ end
22
+
23
+ it "should set the project id with the -p option" do
24
+ @pick = Commands::Base.new("-p", "1").with(@input, @output)
25
+ @pick.options[:project_id].should == "1"
26
+ end
27
+
28
+ it "should set the project id with the --project-id= option" do
29
+ @pick = Commands::Base.new("--project-id=1").with(@input, @output)
30
+ @pick.options[:project_id].should == "1"
31
+ end
32
+
33
+ it "should set the full name with the -n option" do
34
+ @pick = Commands::Base.new("-n", "Jeff Tucker").with(@input, @output)
35
+ @pick.options[:full_name].should == "Jeff Tucker"
36
+ end
37
+
38
+ it "should set the full name with the --full-name= option" do
39
+ @pick = Commands::Base.new(@input, @output,"--full-name=Jeff Tucker")
40
+ @pick.options[:full_name].should == "Jeff Tucker"
41
+ end
42
+
43
+ it "should set the quiet flag with the -q option" do
44
+ @pick = Commands::Base.new("-q").with(@input, @output)
45
+ @pick.options[:quiet].should be_true
46
+ end
47
+
48
+ it "should set the quiet flag with the --quiet option" do
49
+ @pick = Commands::Base.new("--quiet").with(@input, @output)
50
+ @pick.options[:quiet].should be_true
51
+ end
52
+
53
+ it "should set the verbose flag with the -v option" do
54
+ @pick = Commands::Base.new("-v").with(@input, @output)
55
+ @pick.options[:verbose].should be_true
56
+ end
57
+
58
+ it "should set the verbose flag with the --verbose option" do
59
+ @pick = Commands::Base.new("--verbose").with(@input, @output)
60
+ @pick.options[:verbose].should be_true
61
+ end
62
+
63
+ it "should unset the verbose flag with the --no-verbose option" do
64
+ @pick = Commands::Base.new("--no-verbose").with(@input, @output)
65
+ @pick.options[:verbose].should be_false
66
+ end
67
+
68
+ it "should set the integration branch with the -b option" do
69
+ @pick = Commands::Base.new("-b", "integration").with(@input, @output)
70
+ @pick.send(:integration_branch).should == "integration"
71
+ end
72
+
73
+ it "should set the integration branch from git config" do
74
+ Commands::Base.any_instance.stubs(:get).with("git config --get pivotal.integration-branch").returns("chickens")
75
+ @pick = Commands::Base.new
76
+ @pick.send(:integration_branch).should == "chickens"
77
+ end
78
+
79
+ it "should set the integration branch with the --integration-branch= option" do
80
+ @pick = Commands::Base.new("--integration-branch=integration").with(@input, @output)
81
+ @pick.send(:integration_branch).should == "integration"
82
+ end
83
+
84
+ it "should default the integration branch to master if none is specified" do
85
+ @pick = Commands::Base.new
86
+ @pick.send(:integration_branch).should == "master"
87
+ end
88
+
89
+ it "should print a message if the API token is missing" do
90
+ @output.expects(:print).with("Pivotal Tracker API Token and Project ID are required\n")
91
+
92
+ @pick = Commands::Base.new("-p", "1").with(@input, @output)
93
+ @pick.run!
94
+ end
95
+
96
+ it "should print a message if the project ID is missing" do
97
+ @output.expects(:print).with("Pivotal Tracker API Token and Project ID are required\n")
98
+
99
+ @pick = Commands::Base.new("-k", "1").with(@input, @output)
100
+ @pick.run!
101
+ end
102
+
103
+ it "should set the append name flag with the -a option" do
104
+ @pick = Commands::Base.new("-a").with(@input, @output)
105
+ @pick.options[:append_name].should be_true
106
+ end
107
+
108
+ it "should set the append name flag from git config" do
109
+ Commands::Base.any_instance.stubs(:get).with("git config --get pivotal.append-name").returns("true")
110
+ @pick = Commands::Base.new
111
+ @pick.options[:append_name].should be_true
112
+ end
113
+
114
+ it "should set the append name flag with the --append-name" do
115
+ @pick = Commands::Base.new("--append-name").with(@input, @output)
116
+ @pick.options[:append_name].should be_true
117
+ end
118
+
119
+ it "should default the append name flag if none is specified" do
120
+ @pick = Commands::Base.new
121
+ @pick.options[:append_name].should be_false
122
+ end
123
+
124
+ it "should set use_ssl to true with --use-ssl" do
125
+ @pick = Commands::Base.new("--use-ssl").with(@input, @output)
126
+ @pick.options[:use_ssl].should be_true
127
+ end
128
+
129
+ it "should set use_ssl to true with -S" do
130
+ @pick = Commands::Base.new("-S").with(@input, @output)
131
+ @pick.options[:use_ssl].should be_true
132
+ end
133
+
134
+ it "should set use_ssl to false by default" do
135
+ @pick = Commands::Base.new("").with(@input, @output)
136
+ @pick.options[:use_ssl].should be_false
137
+ end
138
+
139
+ it "should respect use_ssl from git config if it's set true (case insensitive)" do
140
+ Commands::Base.any_instance.stubs(:get).with("git config --get pivotal.use-ssl").returns("truE")
141
+ @pick = Commands::Base.new
142
+ @pick.options[:use_ssl].should be_true
143
+ end
144
+
145
+ it "should respect use_ssl from git config if it's set to anything but true" do
146
+ Commands::Base.any_instance.stubs(:get).with("git config --get pivotal.use-ssl").returns("not true")
147
+ @pick = Commands::Base.new
148
+ @pick.options[:use_ssl].should be_false
149
+ end
150
+
151
+ end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ describe Commands::Bug do
4
+
5
+ before(:each) do
6
+ # stub out git config requests
7
+ Commands::Bug.any_instance.stubs(:get).with { |v| v =~ /git config/ }.returns("")
8
+
9
+ @bug = Commands::Bug.new
10
+ end
11
+
12
+ it "should specify its story type" do
13
+ @bug.type.should == "bug"
14
+ end
15
+
16
+ it "should specify a plural for its story types" do
17
+ @bug.plural_type.should == "bugs"
18
+ end
19
+
20
+ it "should specify its branch suffix" do
21
+ @bug.branch_suffix.should == "bugfix"
22
+ end
23
+
24
+ end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ describe Commands::Chore do
4
+
5
+ before(:each) do
6
+ # stub out git config requests
7
+ Commands::Chore.any_instance.stubs(:get).with { |v| v =~ /git config/ }.returns("")
8
+
9
+ @chore = Commands::Chore.new
10
+ end
11
+
12
+ it "should specify its story type" do
13
+ @chore.type.should == "chore"
14
+ end
15
+
16
+ it "should specify a plural for its story types" do
17
+ @chore.plural_type.should == "chores"
18
+ end
19
+
20
+ it "should specify its branch suffix" do
21
+ @chore.branch_suffix.should == "chore"
22
+ end
23
+
24
+ end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ describe Commands::Feature do
4
+
5
+ before(:each) do
6
+ # stub out git config requests
7
+ Commands::Feature.any_instance.stubs(:get).with { |v| v =~ /git config/ }.returns("")
8
+
9
+ @feature = Commands::Feature.new
10
+ end
11
+
12
+ it "should specify its story type" do
13
+ @feature.type.should == "feature"
14
+ end
15
+
16
+ it "should specify a plural for its story types" do
17
+ @feature.plural_type.should == "features"
18
+ end
19
+
20
+ it "should specify its branch suffix" do
21
+ @feature.branch_suffix.should == "feature"
22
+ end
23
+
24
+ end
@@ -0,0 +1,125 @@
1
+ require 'spec_helper'
2
+
3
+ describe Commands::Finish do
4
+
5
+ def mock_project_id
6
+ @mock_project_id ||= '4321'
7
+ end
8
+
9
+ def mock_projects
10
+ if @mock_projects.nil?
11
+ @mock_projects = mock("mock project")
12
+ @mock_projects.stubs(:stories).returns(mock_stories)
13
+ end
14
+ @mock_projects
15
+ end
16
+
17
+ def mock_stories
18
+ if @mock_stories.nil?
19
+ @mock_stories = mock("mock story list")
20
+ @mock_stories.stubs(:find).with(mock_story_id).returns(mock_story)
21
+ end
22
+ @mock_stories
23
+ end
24
+
25
+ def mock_story_id
26
+ @mock_story_id ||= 1234
27
+ end
28
+
29
+ def mock_story
30
+ if @mock_story.nil?
31
+ @mock_story = mock("mock story")
32
+ end
33
+ @mock_story
34
+ end
35
+
36
+ def branch_name
37
+ "#{mock_story_id}-feature-branch-name"
38
+ end
39
+
40
+ before(:each) do
41
+ # stub out git config requests
42
+ Commands::Finish.any_instance.stubs(:get).with { |v| v =~ /git config/ }.returns("")
43
+
44
+ PivotalTracker::Project.stubs(:find).returns(mock_projects)
45
+
46
+ @finish = Commands::Finish.new(nil, nil, "-p", mock_project_id)
47
+ @finish.stubs(:sys)
48
+ @finish.stubs(:put)
49
+ end
50
+
51
+ context "where the branch name does not contain a valid story id" do
52
+ before(:each) do
53
+ # stub out git status request to identify the branch
54
+ branch_name = "invalid-branch-name-without-story-id"
55
+ Commands::Finish.any_instance.stubs(:get).with { |v| v =~ /git status/ }.returns("# On branch #{branch_name}")
56
+ Commands::Finish.any_instance.stubs(:get).with { |v| v == "git symbolic-ref HEAD" }.returns(branch_name)
57
+ end
58
+
59
+ it "should return an exit status of one" do
60
+ @finish.run!.should == 1
61
+ end
62
+
63
+ it "should print an error message" do
64
+ @finish.expects(:put).with("Branch name must contain a Pivotal Tracker story id").once
65
+ @finish.run!
66
+ end
67
+ end
68
+
69
+ context "where the branch name does contain a valid story id" do
70
+
71
+ before(:each) do
72
+ # stub out git status request to identify the branch
73
+ Commands::Finish.any_instance.stubs(:get).with { |v| v =~ /git status/ }.returns("# On branch #{branch_name}")
74
+ Commands::Finish.any_instance.stubs(:get).with { |v| v == "git symbolic-ref HEAD" }.returns(branch_name)
75
+ end
76
+
77
+ it "should attempt to update the story status to the stories finished_state" do
78
+ mock_story.stubs(:story_type).returns("finished")
79
+ mock_story.expects(:update).with(:current_state => "finished")
80
+ @finish.run!
81
+ end
82
+
83
+ context "and the story is successfully marked as finished in PT" do
84
+ before(:each) do
85
+ mock_story.stubs(:story_type).returns("finished")
86
+ mock_story.stubs(:update).with(:current_state => "finished").returns(true)
87
+ end
88
+
89
+ it "should succeed with an exist status of zero" do
90
+ @finish.run!.should == 0
91
+ end
92
+
93
+ it "should checkout the developer's acceptance branch" do
94
+ @finish.expects(:sys).with("git checkout acceptance")
95
+ @finish.run!
96
+ end
97
+
98
+ it "should pull down the latest changes into the developer's acceptance branch" do
99
+ @finish.expects(:sys).with("git pull")
100
+ @finish.run!
101
+ end
102
+
103
+ it "should merge in the story branch" do
104
+ @finish.expects(:sys).with("git merge --no-ff #{branch_name}")
105
+ @finish.run!
106
+ end
107
+ end
108
+
109
+ context "and the story fails to be marked as finished in PT" do
110
+ before(:each) do
111
+ mock_story.stubs(:story_type).returns("finished")
112
+ mock_story.stubs(:update).with(:current_state => "finished").returns(false)
113
+ end
114
+
115
+ it "should fail with an exist status of one" do
116
+ @finish.run!.should == 1
117
+ end
118
+
119
+ it "should print an error message" do
120
+ @finish.expects(:put).with("Unable to mark Story #{mock_story_id} as finished").once
121
+ @finish.run!
122
+ end
123
+ end
124
+ end
125
+ end