gitx 2.13.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +28 -0
  3. data/.rspec +2 -0
  4. data/.ruby-version +1 -0
  5. data/.travis.yml +6 -0
  6. data/CONTRIBUTING.md +67 -0
  7. data/Gemfile +4 -0
  8. data/Guardfile +8 -0
  9. data/LICENSE.txt +22 -0
  10. data/README.md +87 -0
  11. data/Rakefile +5 -0
  12. data/bin/git-buildtag +6 -0
  13. data/bin/git-cleanup +7 -0
  14. data/bin/git-integrate +6 -0
  15. data/bin/git-nuke +6 -0
  16. data/bin/git-release +6 -0
  17. data/bin/git-review +6 -0
  18. data/bin/git-share +6 -0
  19. data/bin/git-start +6 -0
  20. data/bin/git-track +6 -0
  21. data/bin/git-update +6 -0
  22. data/gitx.gemspec +37 -0
  23. data/lib/gitx.rb +8 -0
  24. data/lib/gitx/cli/base_command.rb +56 -0
  25. data/lib/gitx/cli/buildtag_command.rb +39 -0
  26. data/lib/gitx/cli/cleanup_command.rb +45 -0
  27. data/lib/gitx/cli/integrate_command.rb +94 -0
  28. data/lib/gitx/cli/nuke_command.rb +62 -0
  29. data/lib/gitx/cli/release_command.rb +40 -0
  30. data/lib/gitx/cli/review_command.rb +93 -0
  31. data/lib/gitx/cli/share_command.rb +15 -0
  32. data/lib/gitx/cli/start_command.rb +37 -0
  33. data/lib/gitx/cli/track_command.rb +14 -0
  34. data/lib/gitx/cli/update_command.rb +35 -0
  35. data/lib/gitx/configuration.rb +44 -0
  36. data/lib/gitx/extensions/string.rb +12 -0
  37. data/lib/gitx/extensions/thor.rb +39 -0
  38. data/lib/gitx/github.rb +178 -0
  39. data/lib/gitx/version.rb +3 -0
  40. data/spec/fixtures/vcr_cassettes/pull_request_does_exist_with_failure_status.yml +135 -0
  41. data/spec/fixtures/vcr_cassettes/pull_request_does_exist_with_success_status.yml +149 -0
  42. data/spec/fixtures/vcr_cassettes/pull_request_does_not_exist.yml +133 -0
  43. data/spec/gitx/cli/base_command_spec.rb +41 -0
  44. data/spec/gitx/cli/buildtag_command_spec.rb +70 -0
  45. data/spec/gitx/cli/cleanup_command_spec.rb +37 -0
  46. data/spec/gitx/cli/integrate_command_spec.rb +290 -0
  47. data/spec/gitx/cli/nuke_command_spec.rb +165 -0
  48. data/spec/gitx/cli/release_command_spec.rb +172 -0
  49. data/spec/gitx/cli/review_command_spec.rb +356 -0
  50. data/spec/gitx/cli/share_command_spec.rb +32 -0
  51. data/spec/gitx/cli/start_command_spec.rb +96 -0
  52. data/spec/gitx/cli/track_command_spec.rb +31 -0
  53. data/spec/gitx/cli/update_command_spec.rb +79 -0
  54. data/spec/spec_helper.rb +86 -0
  55. data/spec/support/global_config.rb +26 -0
  56. data/spec/support/home_env.rb +11 -0
  57. data/spec/support/matchers/meet_expectations_matcher.rb +7 -0
  58. data/spec/support/pry.rb +1 -0
  59. data/spec/support/stub_execution.rb +14 -0
  60. data/spec/support/timecop.rb +9 -0
  61. data/spec/support/vcr.rb +6 -0
  62. data/spec/support/webmock.rb +1 -0
  63. metadata +348 -0
@@ -0,0 +1,165 @@
1
+ require 'spec_helper'
2
+ require 'gitx/cli/nuke_command'
3
+
4
+ describe Gitx::Cli::NukeCommand do
5
+ let(:args) { [] }
6
+ let(:options) { {} }
7
+ let(:config) do
8
+ {
9
+ pretend: true
10
+ }
11
+ end
12
+ let(:cli) { Gitx::Cli::NukeCommand.new(args, options, config) }
13
+ let(:branch) { double('fake branch', name: 'feature-branch') }
14
+
15
+ before do
16
+ allow(cli).to receive(:current_branch).and_return(branch)
17
+ end
18
+
19
+ describe '#nuke' do
20
+ context 'when target branch == prototype and --destination == master' do
21
+ let(:options) do
22
+ {
23
+ destination: good_branch
24
+ }
25
+ end
26
+ let(:good_branch) { 'master' }
27
+ let(:bad_branch) { 'prototype' }
28
+ let(:buildtag) { 'build-master-2013-10-01-01' }
29
+ before do
30
+ expect(cli).to receive(:yes?).and_return(true)
31
+
32
+ expect(cli).to receive(:current_build_tag).with(good_branch).and_return(buildtag)
33
+
34
+ expect(cli).to receive(:run_cmd).with('git checkout master').ordered
35
+ expect(cli).to receive(:run_cmd).with('git branch -D prototype', allow_failure: true).ordered
36
+ expect(cli).to receive(:run_cmd).with('git push origin --delete prototype', allow_failure: true).ordered
37
+ expect(cli).to receive(:run_cmd).with('git checkout -b prototype build-master-2013-10-01-01').ordered
38
+ expect(cli).to receive(:run_cmd).with('git push origin prototype').ordered
39
+ expect(cli).to receive(:run_cmd).with('git branch --set-upstream-to origin/prototype').ordered
40
+ expect(cli).to receive(:run_cmd).with('git checkout master').ordered
41
+
42
+ cli.nuke bad_branch
43
+ end
44
+ it 'runs expected commands' do
45
+ should meet_expectations
46
+ end
47
+ end
48
+ context 'when target branch == prototype and destination prompt == nil' do
49
+ let(:good_branch) { 'master' }
50
+ let(:bad_branch) { 'prototype' }
51
+ let(:buildtag) { 'build-master-2013-10-01-01' }
52
+ before do
53
+ expect(cli).to receive(:ask).and_return(good_branch)
54
+ expect(cli).to receive(:yes?).and_return(true)
55
+
56
+ expect(cli).to receive(:current_build_tag).with(good_branch).and_return(buildtag)
57
+
58
+ expect(cli).to receive(:run_cmd).with('git checkout master').ordered
59
+ expect(cli).to receive(:run_cmd).with('git branch -D prototype', allow_failure: true).ordered
60
+ expect(cli).to receive(:run_cmd).with('git push origin --delete prototype', allow_failure: true).ordered
61
+ expect(cli).to receive(:run_cmd).with('git checkout -b prototype build-master-2013-10-01-01').ordered
62
+ expect(cli).to receive(:run_cmd).with('git push origin prototype').ordered
63
+ expect(cli).to receive(:run_cmd).with('git branch --set-upstream-to origin/prototype').ordered
64
+ expect(cli).to receive(:run_cmd).with('git checkout master').ordered
65
+
66
+ cli.nuke 'prototype'
67
+ end
68
+ it 'defaults to prototype and should run expected commands' do
69
+ should meet_expectations
70
+ end
71
+ end
72
+ context 'when user does not confirm nuking the target branch' do
73
+ let(:buildtag) { 'build-master-2013-10-01-01' }
74
+ before do
75
+ expect(cli).to receive(:ask).and_return('master')
76
+ expect(cli).to receive(:yes?).and_return(false)
77
+
78
+ expect(cli).to receive(:current_build_tag).with('master').and_return(buildtag)
79
+
80
+ expect(cli).to_not receive(:run_cmd)
81
+
82
+ cli.nuke 'prototype'
83
+ end
84
+ it 'runs expected commands' do
85
+ should meet_expectations
86
+ end
87
+ end
88
+ context 'when no valid buildtag is found' do
89
+ let(:options) do
90
+ {
91
+ destination: good_branch
92
+ }
93
+ end
94
+ let(:good_branch) { 'master' }
95
+ let(:bad_branch) { 'prototype' }
96
+ let(:buildtags) { '' }
97
+ it 'raises error' do
98
+ expect(cli).to receive(:run_cmd).with('git fetch --tags').ordered
99
+ expect(cli).to receive(:run_cmd).with("git tag -l 'build-master-*'").and_return(buildtags).ordered
100
+
101
+ expect { cli.nuke('prototype') }.to raise_error(/No known good tag found for branch/)
102
+ end
103
+ end
104
+ context 'when database migrations exist and user cancels operation' do
105
+ let(:buildtag) { 'build-master-2013-10-01-01' }
106
+ let(:good_branch) { 'master' }
107
+ let(:bad_branch) { 'prototype' }
108
+ let(:migrations) do
109
+ %w( db/migrate/20140715194946_create_users.rb db/migrate/20140730063034_update_user_account.rb ).join("\n")
110
+ end
111
+ before do
112
+ FileUtils.mkdir_p('db/migrate')
113
+
114
+ expect(cli).to receive(:current_build_tag).with(good_branch).and_return(buildtag)
115
+
116
+ expect(cli).to receive(:ask).and_return(good_branch)
117
+ expect(cli).to receive(:yes?).with('Reset prototype to build-master-2013-10-01-01? (y/n)', :green).and_return(true)
118
+ expect(cli).to receive(:run_cmd).with('git diff build-master-2013-10-01-01...prototype --name-only db/migrate').and_return(migrations)
119
+ expect(cli).to receive(:yes?).with('Are you sure you want to nuke prototype? (y/n) ', :green).and_return(false)
120
+
121
+ cli.nuke 'prototype'
122
+ end
123
+ after do
124
+ FileUtils.rm_rf('db/migrate')
125
+ end
126
+ it 'prompts for nuke confirmation' do
127
+ should meet_expectations
128
+ end
129
+ end
130
+ context 'when database migrations exist and user approves operation' do
131
+ let(:buildtag) { 'build-master-2013-10-01-01' }
132
+ let(:good_branch) { 'master' }
133
+ let(:bad_branch) { 'prototype' }
134
+ let(:migrations) do
135
+ %w( db/migrate/20140715194946_create_users.rb db/migrate/20140730063034_update_user_account.rb ).join("\n")
136
+ end
137
+ before do
138
+ FileUtils.mkdir_p('db/migrate')
139
+
140
+ expect(cli).to receive(:current_build_tag).with(good_branch).and_return(buildtag)
141
+
142
+ expect(cli).to receive(:ask).and_return(good_branch)
143
+ expect(cli).to receive(:yes?).with('Reset prototype to build-master-2013-10-01-01? (y/n)', :green).and_return(true)
144
+ expect(cli).to receive(:run_cmd).with('git diff build-master-2013-10-01-01...prototype --name-only db/migrate').and_return(migrations)
145
+ expect(cli).to receive(:yes?).with('Are you sure you want to nuke prototype? (y/n) ', :green).and_return(true)
146
+
147
+ expect(cli).to receive(:run_cmd).with('git checkout master').ordered
148
+ expect(cli).to receive(:run_cmd).with('git branch -D prototype', allow_failure: true).ordered
149
+ expect(cli).to receive(:run_cmd).with('git push origin --delete prototype', allow_failure: true).ordered
150
+ expect(cli).to receive(:run_cmd).with('git checkout -b prototype build-master-2013-10-01-01').ordered
151
+ expect(cli).to receive(:run_cmd).with('git push origin prototype').ordered
152
+ expect(cli).to receive(:run_cmd).with('git branch --set-upstream-to origin/prototype').ordered
153
+ expect(cli).to receive(:run_cmd).with('git checkout master').ordered
154
+
155
+ cli.nuke 'prototype'
156
+ end
157
+ after do
158
+ FileUtils.rm_rf('db/migrate')
159
+ end
160
+ it 'prompts for nuke confirmation' do
161
+ should meet_expectations
162
+ end
163
+ end
164
+ end
165
+ end
@@ -0,0 +1,172 @@
1
+ require 'spec_helper'
2
+ require 'gitx/cli/release_command'
3
+
4
+ describe Gitx::Cli::ReleaseCommand do
5
+ let(:args) { [] }
6
+ let(:options) { {} }
7
+ let(:config) do
8
+ {
9
+ pretend: true
10
+ }
11
+ end
12
+ let(:cli) { described_class.new(args, options, config) }
13
+ let(:branch) { double('fake branch', name: 'feature-branch') }
14
+ let(:authorization_token) { '123123' }
15
+
16
+ before do
17
+ allow(cli).to receive(:current_branch).and_return(branch)
18
+ end
19
+
20
+ describe '#release' do
21
+ context 'when user rejects release' do
22
+ before do
23
+ expect(cli).to receive(:yes?).and_return(false)
24
+ expect(cli).to_not receive(:run_cmd)
25
+
26
+ cli.release
27
+ end
28
+ it 'only runs update commands' do
29
+ should meet_expectations
30
+ end
31
+ end
32
+ context 'when user confirms release and pull request exists with non-success status' do
33
+ before do
34
+ expect(cli).to receive(:execute_command).with(Gitx::Cli::UpdateCommand, :update)
35
+ expect(cli).to_not receive(:execute_command).with(Gitx::Cli::IntegrateCommand, :integrate, 'staging')
36
+ expect(cli).to_not receive(:execute_command).with(Gitx::Cli::CleanupCommand, :cleanup)
37
+
38
+ expect(cli).to receive(:yes?).with('Release feature-branch to production? (y/n)', :green).and_return(true)
39
+ expect(cli).to receive(:yes?).with('Branch status is currently: failure. Proceed with release? (y/n)', :red).and_return(false)
40
+ allow(cli).to receive(:authorization_token).and_return(authorization_token)
41
+
42
+ expect(cli).to_not receive(:run_cmd).with('git checkout master')
43
+ expect(cli).to_not receive(:run_cmd).with('git pull origin master')
44
+ expect(cli).to_not receive(:run_cmd).with('git merge --no-ff feature-branch')
45
+ expect(cli).to_not receive(:run_cmd).with('git push origin HEAD')
46
+
47
+ VCR.use_cassette('pull_request_does_exist_with_failure_status') do
48
+ cli.release
49
+ end
50
+ end
51
+ it 'runs expected commands' do
52
+ should meet_expectations
53
+ end
54
+ end
55
+ context 'when user confirms release and pull request exists with success status' do
56
+ before do
57
+ expect(cli).to receive(:execute_command).with(Gitx::Cli::UpdateCommand, :update)
58
+ expect(cli).to receive(:execute_command).with(Gitx::Cli::IntegrateCommand, :integrate, 'staging')
59
+ expect(cli).to_not receive(:execute_command).with(Gitx::Cli::CleanupCommand, :cleanup)
60
+
61
+ expect(cli).to receive(:yes?).and_return(true)
62
+ allow(cli).to receive(:authorization_token).and_return(authorization_token)
63
+
64
+ expect(cli).to receive(:run_cmd).with('git checkout feature-branch').ordered
65
+ expect(cli).to receive(:run_cmd).with('git checkout master').ordered
66
+ expect(cli).to receive(:run_cmd).with('git pull origin master').ordered
67
+ expect(cli).to receive(:run_cmd).with('git merge --no-ff feature-branch').ordered
68
+ expect(cli).to receive(:run_cmd).with('git push origin HEAD').ordered
69
+
70
+ VCR.use_cassette('pull_request_does_exist_with_success_status') do
71
+ cli.release
72
+ end
73
+ end
74
+ it 'runs expected commands' do
75
+ should meet_expectations
76
+ end
77
+ end
78
+ context 'when target_branch is not nil and user confirms release and pull request exists with success status' do
79
+ before do
80
+ expect(cli).to receive(:execute_command).with(Gitx::Cli::UpdateCommand, :update)
81
+ expect(cli).to receive(:execute_command).with(Gitx::Cli::IntegrateCommand, :integrate, 'staging')
82
+ expect(cli).to_not receive(:execute_command).with(Gitx::Cli::CleanupCommand, :cleanup)
83
+
84
+ expect(cli).to receive(:yes?).and_return(true)
85
+ allow(cli).to receive(:authorization_token).and_return(authorization_token)
86
+
87
+ expect(cli).to receive(:run_cmd).with('git checkout feature-branch').ordered
88
+ expect(cli).to receive(:run_cmd).with('git checkout master').ordered
89
+ expect(cli).to receive(:run_cmd).with('git pull origin master').ordered
90
+ expect(cli).to receive(:run_cmd).with('git merge --no-ff feature-branch').ordered
91
+ expect(cli).to receive(:run_cmd).with('git push origin HEAD').ordered
92
+
93
+ VCR.use_cassette('pull_request_does_exist_with_success_status') do
94
+ cli.release 'feature-branch'
95
+ end
96
+ end
97
+ it 'runs expected commands' do
98
+ should meet_expectations
99
+ end
100
+ end
101
+ context 'when user confirms release and pull request does not exist' do
102
+ let(:new_pull_request) do
103
+ {
104
+ html_url: 'https://path/to/html/pull/request',
105
+ issue_url: 'https://api/path/to/issue/url',
106
+ number: 10,
107
+ head: {
108
+ ref: 'branch_name'
109
+ }
110
+ }
111
+ end
112
+ before do
113
+ allow(cli).to receive(:authorization_token).and_return(authorization_token)
114
+ allow(cli).to receive(:ask_editor).and_return('description')
115
+
116
+ expect(cli).to receive(:execute_command).with(Gitx::Cli::UpdateCommand, :update).twice
117
+ expect(cli).to receive(:execute_command).with(Gitx::Cli::IntegrateCommand, :integrate, 'staging')
118
+ expect(cli).to_not receive(:execute_command).with(Gitx::Cli::CleanupCommand, :cleanup)
119
+
120
+ expect(cli).to receive(:yes?).with('Release feature-branch to production? (y/n)', :green).and_return(true)
121
+ expect(cli).to receive(:yes?).with('Branch status is currently: pending. Proceed with release? (y/n)', :red).and_return(true)
122
+
123
+ expect(cli).to receive(:run_cmd).with('git checkout feature-branch').ordered
124
+ expect(cli).to receive(:run_cmd).with('git checkout feature-branch').ordered
125
+ expect(cli).to receive(:run_cmd).with("git log master...feature-branch --reverse --no-merges --pretty=format:'* %s%n%b'").and_return('2013-01-01 did some stuff').ordered
126
+ expect(cli).to receive(:run_cmd).with('git checkout master').ordered
127
+ expect(cli).to receive(:run_cmd).with('git pull origin master').ordered
128
+ expect(cli).to receive(:run_cmd).with('git merge --no-ff feature-branch').ordered
129
+ expect(cli).to receive(:run_cmd).with('git push origin HEAD').ordered
130
+
131
+ stub_request(:post, 'https://api.github.com/repos/wireframe/gitx/pulls').to_return(:status => 201, :body => new_pull_request.to_json, :headers => {'Content-Type' => 'application/json'})
132
+ VCR.use_cassette('pull_request_does_not_exist') do
133
+ cli.release
134
+ end
135
+ end
136
+ it 'creates pull request on github' do
137
+ should meet_expectations
138
+ end
139
+ it 'runs expected commands' do
140
+ should meet_expectations
141
+ end
142
+ end
143
+ context 'when --cleanup flag passed' do
144
+ let(:options) do
145
+ {
146
+ cleanup: true
147
+ }
148
+ end
149
+ before do
150
+ expect(cli).to receive(:execute_command).with(Gitx::Cli::UpdateCommand, :update)
151
+ expect(cli).to receive(:execute_command).with(Gitx::Cli::IntegrateCommand, :integrate, 'staging')
152
+ expect(cli).to receive(:execute_command).with(Gitx::Cli::CleanupCommand, :cleanup)
153
+
154
+ expect(cli).to receive(:yes?).and_return(true)
155
+ allow(cli).to receive(:authorization_token).and_return(authorization_token)
156
+
157
+ expect(cli).to receive(:run_cmd).with('git checkout feature-branch').ordered
158
+ expect(cli).to receive(:run_cmd).with('git checkout master').ordered
159
+ expect(cli).to receive(:run_cmd).with('git pull origin master').ordered
160
+ expect(cli).to receive(:run_cmd).with('git merge --no-ff feature-branch').ordered
161
+ expect(cli).to receive(:run_cmd).with('git push origin HEAD').ordered
162
+
163
+ VCR.use_cassette('pull_request_does_exist_with_success_status') do
164
+ cli.release
165
+ end
166
+ end
167
+ it 'runs expected commands' do
168
+ should meet_expectations
169
+ end
170
+ end
171
+ end
172
+ end
@@ -0,0 +1,356 @@
1
+ require 'spec_helper'
2
+ require 'gitx/cli/review_command'
3
+
4
+ describe Gitx::Cli::ReviewCommand do
5
+ let(:args) { [] }
6
+ let(:options) { {} }
7
+ let(:config) do
8
+ {
9
+ pretend: true
10
+ }
11
+ end
12
+ let(:cli) { described_class.new(args, options, config) }
13
+ let(:repo) { double('fake repo', config: repo_config) }
14
+ let(:repo_config) do
15
+ {
16
+ 'remote.origin.url' => 'https://github.com/wireframe/gitx'
17
+ }
18
+ end
19
+ let(:branch) { double('fake branch', name: 'feature-branch') }
20
+
21
+ before do
22
+ allow(cli).to receive(:repo).and_return(repo)
23
+ allow(cli).to receive(:current_branch).and_return(branch)
24
+ allow(cli).to receive(:ask_editor).and_return('description')
25
+ end
26
+
27
+ describe '#review' do
28
+ context 'when pull request does not exist' do
29
+ let(:authorization_token) { '123123' }
30
+ let(:changelog) { '* made some fixes' }
31
+ let(:fake_update_command) { double('fake update command', update: nil) }
32
+ let(:new_pull_request) do
33
+ {
34
+ html_url: 'https://path/to/html/pull/request',
35
+ issue_url: 'https://api/path/to/issue/url',
36
+ number: 10,
37
+ head: {
38
+ ref: 'branch_name'
39
+ }
40
+ }
41
+ end
42
+ before do
43
+ expect(Gitx::Cli::UpdateCommand).to receive(:new).and_return(fake_update_command)
44
+
45
+ allow(cli).to receive(:authorization_token).and_return(authorization_token)
46
+ expect(cli).to receive(:run_cmd).with('git checkout feature-branch').ordered
47
+ expect(cli).to receive(:run_cmd).with("git log master...feature-branch --reverse --no-merges --pretty=format:'* %s%n%b'").and_return("* old commit\n\n* new commit").ordered
48
+ expect(cli).to receive(:ask_editor).with("### Changelog\n* old commit\n\n* new commit\n#{Gitx::Github::PULL_REQUEST_FOOTER}", anything).and_return('description')
49
+
50
+ stub_request(:post, 'https://api.github.com/repos/wireframe/gitx/pulls').to_return(:status => 201, :body => new_pull_request.to_json, :headers => {'Content-Type' => 'application/json'})
51
+
52
+ VCR.use_cassette('pull_request_does_not_exist') do
53
+ cli.review
54
+ end
55
+ end
56
+ it 'creates github pull request' do
57
+ should meet_expectations
58
+ end
59
+ it 'runs expected commands' do
60
+ should meet_expectations
61
+ end
62
+ end
63
+ context 'when target branch is not nil and pull request does not exist' do
64
+ let(:authorization_token) { '123123' }
65
+ let(:changelog) { '* made some fixes' }
66
+ let(:fake_update_command) { double('fake update command', update: nil) }
67
+ let(:new_pull_request) do
68
+ {
69
+ html_url: 'https://path/to/html/pull/request',
70
+ issue_url: 'https://api/path/to/issue/url',
71
+ number: 10,
72
+ head: {
73
+ ref: 'branch_name'
74
+ }
75
+ }
76
+ end
77
+ before do
78
+ expect(Gitx::Cli::UpdateCommand).to receive(:new).and_return(fake_update_command)
79
+
80
+ allow(cli).to receive(:authorization_token).and_return(authorization_token)
81
+ expect(cli).to receive(:run_cmd).with('git checkout feature-branch').ordered
82
+ expect(cli).to receive(:run_cmd).with("git log master...feature-branch --reverse --no-merges --pretty=format:'* %s%n%b'").and_return("* old commit\n\n* new commit").ordered
83
+ expect(cli).to receive(:ask_editor).with("### Changelog\n* old commit\n\n* new commit\n#{Gitx::Github::PULL_REQUEST_FOOTER}", anything).and_return('description')
84
+
85
+ stub_request(:post, 'https://api.github.com/repos/wireframe/gitx/pulls').to_return(:status => 201, :body => new_pull_request.to_json, :headers => {'Content-Type' => 'application/json'})
86
+
87
+ VCR.use_cassette('pull_request_does_not_exist') do
88
+ cli.review 'feature-branch'
89
+ end
90
+ end
91
+ it 'creates github pull request' do
92
+ should meet_expectations
93
+ end
94
+ it 'runs expected commands' do
95
+ should meet_expectations
96
+ end
97
+ end
98
+ context 'when authorization_token is missing' do
99
+ let(:authorization_token) { nil }
100
+ it do
101
+ allow(cli).to receive(:authorization_token).and_return(authorization_token)
102
+ expect { cli.review }.to raise_error(/token not found/)
103
+ end
104
+ end
105
+ context 'when pull request already exists' do
106
+ let(:authorization_token) { '123123' }
107
+ before do
108
+ allow(cli).to receive(:authorization_token).and_return(authorization_token)
109
+ expect(cli).to_not receive(:create_pull_request)
110
+
111
+ VCR.use_cassette('pull_request_does_exist_with_success_status') do
112
+ cli.review
113
+ end
114
+ end
115
+ it 'does not create new pull request' do
116
+ should meet_expectations
117
+ end
118
+ end
119
+ context 'when --assignee option passed' do
120
+ let(:options) do
121
+ {
122
+ assignee: 'johndoe'
123
+ }
124
+ end
125
+ let(:authorization_token) { '123123' }
126
+ before do
127
+ allow(cli).to receive(:authorization_token).and_return(authorization_token)
128
+
129
+ stub_request(:patch, /.*api.github.com.*/).to_return(:status => 200)
130
+
131
+ VCR.use_cassette('pull_request_does_exist_with_success_status') do
132
+ cli.review
133
+ end
134
+ end
135
+ it 'updates github pull request' do
136
+ expect(WebMock).to have_requested(:patch, 'https://api.github.com/repos/wireframe/gitx/issues/10')
137
+ end
138
+ end
139
+ context 'when --open flag passed' do
140
+ let(:options) do
141
+ {
142
+ open: true
143
+ }
144
+ end
145
+ let(:authorization_token) { '123123' }
146
+ before do
147
+ allow(cli).to receive(:authorization_token).and_return(authorization_token)
148
+ expect(cli).to receive(:run_cmd).with('open https://path/to/html/pull/request').ordered
149
+ VCR.use_cassette('pull_request_does_exist_with_success_status') do
150
+ cli.review
151
+ end
152
+ end
153
+ it 'runs open command with pull request url' do
154
+ should meet_expectations
155
+ end
156
+ end
157
+ context 'when --bump flag is passed' do
158
+ let(:options) do
159
+ {
160
+ bump: true
161
+ }
162
+ end
163
+ let(:authorization_token) { '123123' }
164
+ let(:reference) { double('fake reference', target_id: 'e12da4') }
165
+ before do
166
+ allow(cli).to receive(:authorization_token).and_return(authorization_token)
167
+ expect(cli).to receive(:ask_editor).and_return('comment description')
168
+ allow(repo).to receive(:head).and_return(reference)
169
+ stub_request(:post, /.*api.github.com.*/).to_return(:status => 201)
170
+
171
+ VCR.use_cassette('pull_request_does_exist_with_success_status') do
172
+ cli.review
173
+ end
174
+ end
175
+ it 'posts comment to github' do
176
+ expect(WebMock).to have_requested(:post, 'https://api.github.com/repos/wireframe/gitx/issues/10/comments').
177
+ with(body: {body: "[gitx] review bump :tada:\n\ncomment description"})
178
+ end
179
+ it 'creates pending build status for latest commit' do
180
+ expect(WebMock).to have_requested(:post, 'https://api.github.com/repos/wireframe/gitx/statuses/e12da4').
181
+ with(body: {state: 'pending', context: 'peer_review', description: 'Peer review in progress'})
182
+ end
183
+ end
184
+ context 'when --reject flag is passed' do
185
+ let(:options) do
186
+ {
187
+ reject: true
188
+ }
189
+ end
190
+ let(:authorization_token) { '123123' }
191
+ let(:reference) { double('fake reference', target_id: 'e12da4') }
192
+ before do
193
+ allow(cli).to receive(:authorization_token).and_return(authorization_token)
194
+ expect(cli).to receive(:ask_editor).and_return('comment body')
195
+ allow(repo).to receive(:head).and_return(reference)
196
+ stub_request(:post, /.*api.github.com.*/).to_return(:status => 201)
197
+
198
+ VCR.use_cassette('pull_request_does_exist_with_success_status') do
199
+ cli.review
200
+ end
201
+ end
202
+ it 'posts comment to github' do
203
+ expect(WebMock).to have_requested(:post, 'https://api.github.com/repos/wireframe/gitx/issues/10/comments').
204
+ with(body: {body: "[gitx] review rejected\n\ncomment body"})
205
+ end
206
+ it 'creates failure build status for latest commit' do
207
+ expect(WebMock).to have_requested(:post, 'https://api.github.com/repos/wireframe/gitx/statuses/e12da4').
208
+ with(body: {state: 'failure', context: 'peer_review', description: 'Peer review rejected'})
209
+ end
210
+ end
211
+ context 'when --approve flag is passed' do
212
+ let(:options) do
213
+ {
214
+ approve: true
215
+ }
216
+ end
217
+ let(:authorization_token) { '123123' }
218
+ let(:reference) { double('fake reference', target_id: 'e12da4') }
219
+ before do
220
+ allow(cli).to receive(:authorization_token).and_return(authorization_token)
221
+ expect(cli).to receive(:ask_editor).and_return('comment body')
222
+ allow(repo).to receive(:head).and_return(reference)
223
+ stub_request(:post, /.*api.github.com.*/).to_return(:status => 201)
224
+
225
+ VCR.use_cassette('pull_request_does_exist_with_success_status') do
226
+ cli.review
227
+ end
228
+ end
229
+ it 'posts comment to github' do
230
+ expect(WebMock).to have_requested(:post, 'https://api.github.com/repos/wireframe/gitx/issues/10/comments').
231
+ with(body: {body: "[gitx] review approved :shipit:\n\ncomment body"})
232
+ end
233
+ it 'creates success build status for latest commit' do
234
+ expect(WebMock).to have_requested(:post, 'https://api.github.com/repos/wireframe/gitx/statuses/e12da4').
235
+ with(body: {state: 'success', context: 'peer_review', description: 'Peer review approved'})
236
+ end
237
+ end
238
+ end
239
+
240
+ describe '#authorization_token' do
241
+ context 'when github.user is not configured' do
242
+ it 'raises error' do
243
+ expect do
244
+ cli.send(:authorization_token)
245
+ end.to raise_error(/Github user not configured/)
246
+ end
247
+ end
248
+ context 'when ENV[GITX_GITHUB_TOKEN] is set' do
249
+ let(:auth_token) { '123123' }
250
+ before do
251
+ ENV['GITX_GITHUB_TOKEN'] = auth_token
252
+ expect(cli).to_not receive(:ask)
253
+ @auth_token = cli.send(:authorization_token)
254
+ end
255
+ after do
256
+ ENV.delete('GITX_GITHUB_TOKEN')
257
+ end
258
+ it { expect(@auth_token).to eq auth_token }
259
+ it { is_expected.to meet_expectations }
260
+ end
261
+ context 'when global config token is nil' do
262
+ let(:repo_config) do
263
+ {
264
+ 'remote.origin.url' => 'https://github.com/wireframe/gitx',
265
+ 'github.user' => 'ryan@codecrate.com'
266
+ }
267
+ end
268
+ let(:github_password) { 'secretz' }
269
+ let(:authorization_token) { '123981239123' }
270
+ before do
271
+ stub_request(:post, 'https://ryan@codecrate.com:secretz@api.github.com/authorizations').
272
+ to_return(:status => 200, :body => JSON.dump(token: authorization_token), :headers => {'Content-Type' => 'application/json'})
273
+
274
+ expect(cli).to receive(:ask).with('Github password for ryan@codecrate.com: ', {:echo => false}).and_return(github_password)
275
+ expect(cli).to receive(:ask).with('Github two factor authorization token (if enabled): ', {:echo => false}).and_return(nil)
276
+
277
+ @auth_token = cli.send(:authorization_token)
278
+ end
279
+ it 'stores authorization_token in global config' do
280
+ expect(global_config).to include('token' => authorization_token)
281
+ end
282
+ it { expect(@auth_token).to eq authorization_token }
283
+ end
284
+ context 'when global authorization_token is nil and first request fails' do
285
+ let(:repo_config) do
286
+ {
287
+ 'remote.origin.url' => 'https://github.com/wireframe/gitx',
288
+ 'github.user' => 'ryan@codecrate.com'
289
+ }
290
+ end
291
+ let(:github_password) { 'secretz' }
292
+ let(:authorization_token) { '123981239123' }
293
+ before do
294
+ stub_request(:post, 'https://ryan@codecrate.com:secretz@api.github.com/authorizations').
295
+ to_return(:status => 401, :body => JSON.dump(token: authorization_token), :headers => {'Content-Type' => 'application/json'}).
296
+ then.
297
+ to_return(:status => 200, :body => JSON.dump(token: authorization_token), :headers => {'Content-Type' => 'application/json'})
298
+
299
+ expect(cli).to receive(:ask).with('Github password for ryan@codecrate.com: ', {:echo => false}).and_return(github_password).twice
300
+ expect(cli).to receive(:ask).with('Github two factor authorization token (if enabled): ', {:echo => false}).and_return(nil).twice
301
+
302
+ @auth_token = cli.send(:authorization_token)
303
+ end
304
+ it 'stores authorization_token in global config' do
305
+ expect(global_config).to include('token' => authorization_token)
306
+ end
307
+ it { expect(@auth_token).to eq authorization_token }
308
+ end
309
+ context 'when the global config has an existing token' do
310
+ let(:authorization_token) { '123981239123' }
311
+ let(:repo_config) do
312
+ {
313
+ 'remote.origin.url' => 'https://github.com/wireframe/gitx',
314
+ 'github.user' => 'ryan@codecrate.com'
315
+ }
316
+ end
317
+ let(:config) do
318
+ {
319
+ 'token' => authorization_token
320
+ }
321
+ end
322
+ before do
323
+ File.open(global_config_file, 'w') do |file|
324
+ file.write(config.to_yaml)
325
+ end
326
+ @auth_token = cli.send(:authorization_token)
327
+ end
328
+ it { expect(@auth_token).to eq authorization_token }
329
+ end
330
+ context 'when two factor authorization token given' do
331
+ let(:repo_config) do
332
+ {
333
+ 'remote.origin.url' => 'https://github.com/wireframe/gitx',
334
+ 'github.user' => 'ryan@codecrate.com'
335
+ }
336
+ end
337
+ let(:github_password) { 'secretz' }
338
+ let(:authorization_token) { '123981239123' }
339
+ let(:two_factor_auth_token) { '456456' }
340
+ before do
341
+ stub_request(:post, 'https://ryan@codecrate.com:secretz@api.github.com/authorizations').
342
+ with(headers: {'X-GitHub-OTP' => two_factor_auth_token}).
343
+ to_return(:status => 200, :body => JSON.dump(token: authorization_token), :headers => {'Content-Type' => 'application/json'})
344
+
345
+ expect(cli).to receive(:ask).with('Github password for ryan@codecrate.com: ', {:echo => false}).and_return(github_password)
346
+ expect(cli).to receive(:ask).with('Github two factor authorization token (if enabled): ', {:echo => false}).and_return(two_factor_auth_token)
347
+
348
+ @auth_token = cli.send(:authorization_token)
349
+ end
350
+ it 'stores authorization_token in global config' do
351
+ expect(global_config).to include('token' => authorization_token)
352
+ end
353
+ it { expect(@auth_token).to eq authorization_token }
354
+ end
355
+ end
356
+ end