git-pivotal-tracker 0.9.0

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.
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