git_workflow 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. data/README.markdown +77 -0
  2. data/Rakefile +33 -0
  3. data/bin/git-finish +4 -0
  4. data/bin/git-start +4 -0
  5. data/bin/git-workflow-setup +4 -0
  6. data/features/core_functionality/4049578_need_the_ability_to_start_a_new_branch.feature +15 -0
  7. data/features/core_functionality/4049611_need_the_ability_to_finish_a_specified_branch.feature +17 -0
  8. data/features/core_functionality/4056359_start_with_local_branch_already_present.feature +16 -0
  9. data/features/core_functionality/4056363_finish_the_current_branch.feature +17 -0
  10. data/features/core_functionality/4056389_changes_to_pt_story_name_cause_branch_issues.feature +25 -0
  11. data/features/core_functionality/4135658_control_output_verbosity_using_v_switch.feature +32 -0
  12. data/features/core_functionality/4468313_get_config_value_for_is_producing_an_error_log.feature +43 -0
  13. data/features/extensions/4058326_display_story_description_on_start.feature +14 -0
  14. data/features/git_branching/4135501_allow_branch_start_point_to_be_specified_from_command_line.feature +27 -0
  15. data/features/hooks/4069174_investigate_the_effort_required_to_add_pre_amp_post_hooks.feature +24 -0
  16. data/features/hooks/4199841_need_hooks_for_my_workflow.feature +51 -0
  17. data/features/hooks/4199845_need_hooks_for_sanger_workflow.feature +66 -0
  18. data/features/hooks/4205913_output_from_rake_tests_should_be_seen.feature +19 -0
  19. data/features/hooks/4424828_git_start_is_missing_pt_integration_for_my_hooks.feature +15 -0
  20. data/features/pivotal_tracker_api/4133291_chores_go_straight_to_accepted_not_to_finished.feature +15 -0
  21. data/features/release/4132264_fix_the_libxml_warnings_from_nokogiri.feature +20 -0
  22. data/features/step_definitions/aruba_extensions.rb +4 -0
  23. data/features/step_definitions/configuration_steps.rb +38 -0
  24. data/features/step_definitions/git_steps.rb +102 -0
  25. data/features/step_definitions/misc_steps.rb +3 -0
  26. data/features/step_definitions/pivotal_tracker_steps.rb +43 -0
  27. data/features/step_definitions/project_code_steps.rb +35 -0
  28. data/features/support/aruba.rb +1 -0
  29. data/features/support/hooks/configuration.rb +4 -0
  30. data/features/support/hooks/environment.rb +49 -0
  31. data/features/support/hooks/pivotal_tracker.rb +170 -0
  32. data/lib/git_workflow.rb +5 -0
  33. data/lib/git_workflow/callbacks.rb +16 -0
  34. data/lib/git_workflow/callbacks/pivotal_tracker_support.rb +64 -0
  35. data/lib/git_workflow/callbacks/remote_git_branch_support.rb +9 -0
  36. data/lib/git_workflow/callbacks/styles/debug.rb +58 -0
  37. data/lib/git_workflow/callbacks/styles/default.rb +43 -0
  38. data/lib/git_workflow/callbacks/styles/mine.rb +52 -0
  39. data/lib/git_workflow/callbacks/styles/sanger.rb +48 -0
  40. data/lib/git_workflow/callbacks/test_code_support.rb +29 -0
  41. data/lib/git_workflow/command_line.rb +46 -0
  42. data/lib/git_workflow/commands.rb +4 -0
  43. data/lib/git_workflow/commands/base.rb +31 -0
  44. data/lib/git_workflow/commands/finish.rb +36 -0
  45. data/lib/git_workflow/commands/setup.rb +157 -0
  46. data/lib/git_workflow/commands/start.rb +30 -0
  47. data/lib/git_workflow/configuration.rb +93 -0
  48. data/lib/git_workflow/core_ext.rb +106 -0
  49. data/lib/git_workflow/git.rb +143 -0
  50. data/lib/git_workflow/logger.yaml +27 -0
  51. data/lib/git_workflow/logging.rb +77 -0
  52. data/lib/git_workflow/story.rb +96 -0
  53. data/spec/core_functionality/4056539_support_http_proxy_spec.rb +68 -0
  54. data/spec/core_functionality/4058365_bad_request_on_story_update_spec.rb +13 -0
  55. data/spec/core_functionality/4058394_make_commands_more_verbose_spec.rb +41 -0
  56. data/spec/core_functionality/4058719_error_if_none_of_the_required_configuration_values_are_set_spec.rb +21 -0
  57. data/spec/core_functionality/4058861_git_config_returns_empty_strings_which_should_be_nil_spec.rb +59 -0
  58. data/spec/core_functionality/4172431_add_git_workflow_setup_command_to_make_setup_easier_spec.rb +148 -0
  59. data/spec/core_functionality/4199841_need_callbacks_for_my_workflow_spec.rb +97 -0
  60. data/spec/extensions/4199841_need_callbacks_for_my_workflow_spec.rb +167 -0
  61. data/spec/extensions/4205913_output_from_rake_tests_should_be_seen_spec.rb +34 -0
  62. data/spec/git_branching/4058723_branches_end_in_if_the_last_character_is_invalid_spec.rb +40 -0
  63. data/spec/git_branching/4059824_code_is_not_using_workflow_localbranchconvention_to_decode_branch_names_on_finish_spec.rb +115 -0
  64. data/spec/git_branching/4216087_support_pushing_branches_spec.rb +44 -0
  65. data/spec/hooks/4199845_need_hooks_for_sanger_workflow_spec.rb +27 -0
  66. data/spec/pivotal_tracker_api/4056381_pt_api_token_not_being_passed_in_headers_spec.rb +40 -0
  67. data/spec/pivotal_tracker_api/4056638_library_code_using_localhost_spec.rb +16 -0
  68. data/spec/pivotal_tracker_api/4056661_content_type_header_should_be_text_xml_on_updates_spec.rb +16 -0
  69. data/spec/pivotal_tracker_api/4058718_if_pt_username_is_not_set_use_user_name_spec.rb +26 -0
  70. data/spec/pivotal_tracker_api/4146016_decode_the_xml_encoded_text_of_description_and_name_spec.rb +63 -0
  71. data/spec/shared_examples/configuration.rb +10 -0
  72. data/spec/shared_examples/story.rb +17 -0
  73. data/spec/spec_helper.rb +18 -0
  74. metadata +220 -0
@@ -0,0 +1,97 @@
1
+ require 'spec_helper'
2
+
3
+ describe GitWorkflow::Git do
4
+ before(:each) do
5
+ @git, @repository = Class.new, mock('Repository')
6
+ @git.send(:extend, GitWorkflow::Git)
7
+ @git.stub(:repository).and_return(@repository)
8
+
9
+ @git.stub(:info).with(anything)
10
+ end
11
+
12
+ describe '#in_git_branch' do
13
+ before(:each) do
14
+ @repository.should_receive(:current_branch).ordered.and_return('current')
15
+ end
16
+
17
+ it 'does not switch back if an error occurs' do
18
+ @repository.should_receive(:checkout).with('new_branch').ordered
19
+
20
+ lambda do
21
+ @git.in_git_branch('new_branch') { raise StandardError, 'Failed' }
22
+ end.should raise_error(StandardError)
23
+ end
24
+
25
+ context 'with a successful block' do
26
+ before(:each) do
27
+ @callback = mock('callback')
28
+ @callback.should_receive(:called)
29
+ end
30
+
31
+ after(:each) do
32
+ @git.in_git_branch(@target_branch) { @callback.called }
33
+ end
34
+
35
+ it 'simply yields if the current branch is the requested branch' do
36
+ @target_branch = 'current'
37
+ end
38
+
39
+ it 'switches to the new branch and back again for success' do
40
+ @target_branch = 'new_branch'
41
+
42
+ @repository.should_receive(:checkout).with('new_branch').ordered
43
+ @repository.should_receive(:checkout).with('current').ordered
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ class GitWorkflow::Git::Repository
50
+ def self.for_testing
51
+ new
52
+ end
53
+
54
+ def internal_current_branch
55
+ @current_branch
56
+ end
57
+ end
58
+
59
+ describe GitWorkflow::Git::Repository do
60
+ before(:each) do
61
+ @repository = described_class.for_testing
62
+ end
63
+
64
+ describe '#current_branch' do
65
+ it 'finds the current branch from the list' do
66
+ @repository.should_receive(:execute_command).with('git branch').and_return([ ' branch_1', '* branch_2', ' branch_3' ].join("\n"))
67
+ @repository.current_branch.should == 'branch_2'
68
+ end
69
+
70
+ it 'raises if the current branch cannot be found in the list' do
71
+ @repository.should_receive(:execute_command).with('git branch').and_return([ ' branch_1', ' branch_2', ' branch_3' ].join("\n"))
72
+ lambda { @repository.current_branch }.should raise_error(GitWorkflow::Git::Repository::BranchError)
73
+ end
74
+ end
75
+
76
+ describe '#checkout' do
77
+ it 'raises CheckoutError on command failure' do
78
+ @repository.should_receive(:execute_command).with(anything).and_raise(Execution::CommandFailure.new('command', :fail))
79
+ lambda { @repository.checkout('target') }.should raise_error(GitWorkflow::Git::Repository::CheckoutError)
80
+ end
81
+
82
+ context 'on successful checkout' do
83
+ before(:each) do
84
+ @repository.should_receive(:execute_command).with('git checkout target')
85
+ @repository.checkout('target')
86
+ end
87
+
88
+ it 'checks out the requested branch' do
89
+ # Nothing needed here
90
+ end
91
+
92
+ it 'maintains the current branch for performance' do
93
+ @repository.internal_current_branch.should == 'target'
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,167 @@
1
+ require 'spec_helper'
2
+ require 'git_workflow/callbacks/styles/mine'
3
+
4
+ describe String do
5
+ describe '#camelize' do
6
+ it 'camelizes an underscored word' do
7
+ 'foo_bar_baz'.camelize.should == 'FooBarBaz'
8
+ end
9
+
10
+ it 'modularizes a path' do
11
+ 'foo/bar/baz'.camelize.should == 'Foo::Bar::Baz'
12
+ end
13
+ end
14
+
15
+ describe '#underscore' do
16
+ it 'underscores a modular string' do
17
+ 'Foo::Bar::Baz'.underscore.should == 'foo/bar/baz'
18
+ end
19
+
20
+ it 'underscores a camelized word' do
21
+ 'FooBarBaz'.underscore.should == 'foo_bar_baz'
22
+ end
23
+ end
24
+ end
25
+
26
+ describe GitWorkflow::Callbacks::Styles::Mine::FinishBehaviour do
27
+ before(:each) do
28
+ @callbacks = Class.new
29
+ @callbacks.send(:extend, GitWorkflow::Callbacks::Styles::Mine::FinishBehaviour)
30
+ end
31
+
32
+ describe '#finish' do
33
+ before(:each) do
34
+ @story = mock('Story')
35
+ @story.stub(:branch_name).and_return('story_branch')
36
+
37
+ @callbacks.should_receive(:in_git_branch).with('story_branch').and_yield.ordered
38
+ @callbacks.should_receive(:run_tests!).with(:spec, :features).ordered
39
+ end
40
+
41
+ after(:each) do
42
+ @callbacks.finish(@story, @target_branch)
43
+ end
44
+
45
+ it 'runs the tests but not the push if non-master branch' do
46
+ @target_branch = 'non-master'
47
+
48
+ @callbacks.should_receive(:in_git_branch).with(@target_branch).and_yield.ordered
49
+ @callbacks.should_receive(:merge_branch).with('story_branch', anything).ordered
50
+ @callbacks.should_receive(:run_tests_with_recovery!).with(:spec, :features).ordered
51
+
52
+ @callbacks.should_not_receive(:push_current_branch_to).with('non-master').ordered
53
+ end
54
+
55
+ it 'runs the tests and the push if master branch' do
56
+ @target_branch = 'master'
57
+
58
+ @callbacks.should_receive(:in_git_branch).with(@target_branch).and_yield.ordered
59
+ @callbacks.should_receive(:merge_branch).with('story_branch', anything).ordered
60
+ @callbacks.should_receive(:run_tests_with_recovery!).with(:spec, :features).ordered
61
+
62
+ @callbacks.should_receive(:push_current_branch_to).with('master').ordered
63
+ end
64
+ end
65
+ end
66
+
67
+ describe GitWorkflow::Callbacks::Styles::Mine do
68
+ describe '.setup' do
69
+ before(:each) do
70
+ @start, @finish = Class.new, Class.new
71
+
72
+ GitWorkflow::Callbacks::Styles::Mine.setup(@start, @finish)
73
+ end
74
+
75
+ context 'configured start' do
76
+ it 'has my callbacks installed' do
77
+ @start.included_modules.should include(GitWorkflow::Callbacks::Styles::Mine::StartBehaviour)
78
+ end
79
+ end
80
+
81
+ context 'configured finish' do
82
+ it 'has the Pivotal Tracker support' do
83
+ @finish.included_modules.should include(GitWorkflow::Callbacks::PivotalTrackerSupport)
84
+ end
85
+
86
+ it 'has the test code support' do
87
+ @finish.included_modules.should include(GitWorkflow::Callbacks::TestCodeSupport)
88
+ end
89
+
90
+ it 'has the remote git branch support' do
91
+ @finish.included_modules.should include(GitWorkflow::Callbacks::RemoteGitBranchSupport)
92
+ end
93
+
94
+ it 'has my callbacks installed' do
95
+ @finish.included_modules.should include(GitWorkflow::Callbacks::Styles::Mine::FinishBehaviour)
96
+ end
97
+ end
98
+ end
99
+ end
100
+
101
+ describe GitWorkflow::Callbacks::TestCodeSupport do
102
+ before(:each) do
103
+ @callbacks = Class.new
104
+ @callbacks.send(:extend, GitWorkflow::Callbacks::TestCodeSupport)
105
+ end
106
+
107
+ describe '#run_tests!' do
108
+ it 'does nothing if the tests pass' do
109
+ @callbacks.should_receive(:run_tests).with(:task1, :task2).once.and_return(true)
110
+
111
+ @callbacks.run_tests!(:task1, :task2)
112
+ end
113
+
114
+ it 'raises if the tests fail' do
115
+ @callbacks.should_receive(:run_tests).with(:task1, :task2).once.and_return(false)
116
+
117
+ lambda { @callbacks.run_tests!(:task1, :task2) }.should raise_error(GitWorkflow::Callbacks::TestCodeSupport::Failure)
118
+ end
119
+ end
120
+
121
+ describe '#run_tests_with_recovery!' do
122
+ it 'does nothing if the tests pass' do
123
+ @callbacks.should_receive(:run_tests).with(:task1, :task2).once.and_return(true)
124
+
125
+ @callbacks.run_tests_with_recovery!(:task1, :task2)
126
+ end
127
+
128
+ it 'tries to recover from failing tests' do
129
+ @callbacks.should_receive(:run_tests).with(:task1, :task2).twice.and_return(false, true)
130
+ @callbacks.should_receive(:spawn_shell_for_recovery).once.and_return(true)
131
+
132
+ @callbacks.run_tests_with_recovery!(:task1, :task2)
133
+ end
134
+
135
+ it 'raises if the recovery is not possible' do
136
+ @callbacks.should_receive(:run_tests).with(:task1, :task2).once.and_return(false)
137
+ @callbacks.should_receive(:spawn_shell_for_recovery).once.and_return(false)
138
+
139
+ lambda { @callbacks.run_tests_with_recovery!(:task1, :task2) }.should raise_error(GitWorkflow::Callbacks::TestCodeSupport::Failure)
140
+ end
141
+ end
142
+
143
+ describe '#run_tests' do
144
+ it 'executes the given rake tasks' do
145
+ @callbacks.should_receive(:system).with('rake', 'task1', 'task2').and_return(true)
146
+ @callbacks.send(:run_tests, :task1, :task2).should == true
147
+ end
148
+
149
+ it 'returns the result of execution' do
150
+ @callbacks.should_receive(:system).with('rake', 'task1', 'task2').and_return(false)
151
+ @callbacks.send(:run_tests, :task1, :task2).should == false
152
+ end
153
+ end
154
+
155
+ describe '#spawn_shell_for_recovery' do
156
+ end
157
+ end
158
+
159
+ describe GitWorkflow::Callbacks::RemoteGitBranchSupport do
160
+ before(:each) do
161
+ @callbacks = Class.new
162
+ @callbacks.send(:extend, GitWorkflow::Callbacks::RemoteGitBranchSupport)
163
+ end
164
+
165
+ describe '#push_current_branch_to' do
166
+ end
167
+ end
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+
3
+ describe Execution do
4
+ before(:each) do
5
+ @executor = Class.new
6
+ @executor.send(:extend, Execution)
7
+ @executor.stub(:debug).with(anything).and_yield
8
+ end
9
+
10
+ before(:each) do
11
+ @stdin, @stdout, @stderr = mock('stdin'), mock('stdout'), mock('stderr')
12
+ @outputs = [ @stdout, @stderr, @stdin ]
13
+ end
14
+
15
+ describe '#execute_command' do
16
+ it 'returns the output from the command' do
17
+ @stdout.should_receive(:read).and_return('Hello World')
18
+ @executor.should_receive(:execute_command_with_output_handling).with(:command).and_yield(*@outputs)
19
+ @executor.execute_command(:command).should == 'Hello World'
20
+ end
21
+ end
22
+
23
+ describe '#execute_command_with_output_handling' do
24
+ it 'does not raise if the command succeeds' do
25
+ @executor.execute_command_with_output_handling('sh -c "exit 0"') { |*args| }
26
+ end
27
+
28
+ it 'raises if the command fails' do
29
+ lambda do
30
+ @executor.execute_command_with_output_handling('sh -c "exit 1"') { |*args| }
31
+ end.should raise_error(Execution::CommandFailure)
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,40 @@
1
+ require 'spec_helper'
2
+
3
+ class GitWorkflow::Story
4
+ attr_writer :name
5
+ end
6
+
7
+ describe GitWorkflow::Story do
8
+ describe '#branch_name' do
9
+ it_should_behave_like 'it needs a working Story'
10
+
11
+ it 'delegates to the local branch convention' do
12
+ configuration, convention = mock('configuration'), mock('convention')
13
+ configuration.should_receive(:local_branch_convention).and_return(convention)
14
+ convention.should_receive(:to).with(@story).and_return(:ok)
15
+ GitWorkflow::Configuration.stub!(:instance).and_return(configuration)
16
+
17
+ @story.branch_name.should == :ok
18
+ end
19
+ end
20
+ end
21
+
22
+ describe GitWorkflow::Configuration::Convention do
23
+ describe '#to' do
24
+ before(:each) do
25
+ @convention = described_class.new('${number}_${name}')
26
+ end
27
+
28
+ after(:each) do
29
+ @convention.to(OpenStruct.new(:story_id => 12345, :name => @name)).should_not match(/_+$/)
30
+ end
31
+
32
+ it 'does not end in underscore with invalid character at the end' do
33
+ @name = 'ends in invalid character!'
34
+ end
35
+
36
+ it 'does not end in multiple underscores' do
37
+ @name = 'ends in invalid characters!!!!!!!'
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,115 @@
1
+ require 'spec_helper'
2
+
3
+ class GitWorkflow::Configuration
4
+ def self.instance_for_testing
5
+ new
6
+ end
7
+ end
8
+
9
+ describe GitWorkflow::Configuration do
10
+ describe '#local_branch_convention' do
11
+ before(:each) do
12
+ @configuration = GitWorkflow::Configuration.instance_for_testing
13
+ @configuration.should_receive(:get_config_value_for!).with('workflow.localbranchconvention').once.and_return('${number}_${name}')
14
+ end
15
+
16
+ it 'uses the workflow.localbranchconvention configuration value' do
17
+ @configuration.local_branch_convention
18
+ end
19
+
20
+ it 'caches the value' do
21
+ @configuration.local_branch_convention # Gets the value ...
22
+ @configuration.local_branch_convention # ... that should now be cached
23
+ end
24
+ end
25
+ end
26
+
27
+ class GitWorkflow::Commands::Finish
28
+ public :extract_story_from_branch
29
+ end
30
+
31
+ describe GitWorkflow::Commands::Finish do
32
+ before(:each) do
33
+ @configuration = mock('configuration')
34
+ @configuration.stub(:ignore_git_global?).and_return(false)
35
+ GitWorkflow::Configuration.stub!(:instance).and_return(@configuration)
36
+
37
+ @command = described_class.new([])
38
+ end
39
+
40
+ describe '#extract_story_from_branch' do
41
+ before(:each) do
42
+ @convention = mock('convention')
43
+ @configuration.stub!(:local_branch_convention).and_return(@convention)
44
+ end
45
+
46
+ it 'uses the branch convention' do
47
+ @convention.should_receive(:from).with('12345_this_branch_matches').and_return(12345)
48
+ @command.extract_story_from_branch('12345_this_branch_matches').should == 12345
49
+ end
50
+ end
51
+ end
52
+
53
+ describe GitWorkflow::Configuration::Convention do
54
+ describe '#initialize' do
55
+ it 'errors if the convention has no story_id' do
56
+ lambda { described_class.new('foo') }.should raise_error(StandardError)
57
+ end
58
+
59
+ it 'does not error if story_id included' do
60
+ described_class.new('${number}_foo')
61
+ end
62
+ end
63
+
64
+ shared_examples_for 'branch convention behaviour' do
65
+ before(:each) do
66
+ @branches = { :start => '12345_works_for_me', :end => 'works_for_me_12345' }
67
+ end
68
+
69
+ describe '#to' do
70
+ before(:each) do
71
+ @decoder = described_class.new(@convention)
72
+ @decoder.stub!(:use_existing_for).with(anything).and_return(nil)
73
+ end
74
+
75
+ it 'evaluates the convention string' do
76
+ story = OpenStruct.new(:story_id => 12345, :name => 'works for me')
77
+ @decoder.to(story).should == @branches[ @to_result ]
78
+ end
79
+ end
80
+
81
+ describe '#from' do
82
+ before(:each) do
83
+ @decoder = described_class.new(@convention)
84
+ end
85
+
86
+ it 'decodes the same branch that #to produces' do
87
+ @decoder.from(@branches[ @to_result ]).should == 12345
88
+ end
89
+
90
+ it 'errors if the branch does not conform to the convention' do
91
+ lambda { @decoder.from(@branches[ @from_mismatch ]) }.should raise_error(StandardError)
92
+ end
93
+ end
94
+ end
95
+
96
+ context 'after successful initialization' do
97
+ context 'with ${number}_${name}' do
98
+ it_should_behave_like 'branch convention behaviour'
99
+
100
+ before(:each) do
101
+ @convention = '${number}_${name}'
102
+ @to_result, @from_mismatch = :start, :end
103
+ end
104
+ end
105
+
106
+ context 'with ${name}_${number}' do
107
+ it_should_behave_like 'branch convention behaviour'
108
+
109
+ before(:each) do
110
+ @convention = '${name}_${number}'
111
+ @to_result, @from_mismatch = :end, :start
112
+ end
113
+ end
114
+ end
115
+ end