gitx 2.13.1

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