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.
- checksums.yaml +7 -0
- data/.gitignore +28 -0
- data/.rspec +2 -0
- data/.ruby-version +1 -0
- data/.travis.yml +6 -0
- data/CONTRIBUTING.md +67 -0
- data/Gemfile +4 -0
- data/Guardfile +8 -0
- data/LICENSE.txt +22 -0
- data/README.md +87 -0
- data/Rakefile +5 -0
- data/bin/git-buildtag +6 -0
- data/bin/git-cleanup +7 -0
- data/bin/git-integrate +6 -0
- data/bin/git-nuke +6 -0
- data/bin/git-release +6 -0
- data/bin/git-review +6 -0
- data/bin/git-share +6 -0
- data/bin/git-start +6 -0
- data/bin/git-track +6 -0
- data/bin/git-update +6 -0
- data/gitx.gemspec +37 -0
- data/lib/gitx.rb +8 -0
- data/lib/gitx/cli/base_command.rb +56 -0
- data/lib/gitx/cli/buildtag_command.rb +39 -0
- data/lib/gitx/cli/cleanup_command.rb +45 -0
- data/lib/gitx/cli/integrate_command.rb +94 -0
- data/lib/gitx/cli/nuke_command.rb +62 -0
- data/lib/gitx/cli/release_command.rb +40 -0
- data/lib/gitx/cli/review_command.rb +93 -0
- data/lib/gitx/cli/share_command.rb +15 -0
- data/lib/gitx/cli/start_command.rb +37 -0
- data/lib/gitx/cli/track_command.rb +14 -0
- data/lib/gitx/cli/update_command.rb +35 -0
- data/lib/gitx/configuration.rb +44 -0
- data/lib/gitx/extensions/string.rb +12 -0
- data/lib/gitx/extensions/thor.rb +39 -0
- data/lib/gitx/github.rb +178 -0
- data/lib/gitx/version.rb +3 -0
- data/spec/fixtures/vcr_cassettes/pull_request_does_exist_with_failure_status.yml +135 -0
- data/spec/fixtures/vcr_cassettes/pull_request_does_exist_with_success_status.yml +149 -0
- data/spec/fixtures/vcr_cassettes/pull_request_does_not_exist.yml +133 -0
- data/spec/gitx/cli/base_command_spec.rb +41 -0
- data/spec/gitx/cli/buildtag_command_spec.rb +70 -0
- data/spec/gitx/cli/cleanup_command_spec.rb +37 -0
- data/spec/gitx/cli/integrate_command_spec.rb +290 -0
- data/spec/gitx/cli/nuke_command_spec.rb +165 -0
- data/spec/gitx/cli/release_command_spec.rb +172 -0
- data/spec/gitx/cli/review_command_spec.rb +356 -0
- data/spec/gitx/cli/share_command_spec.rb +32 -0
- data/spec/gitx/cli/start_command_spec.rb +96 -0
- data/spec/gitx/cli/track_command_spec.rb +31 -0
- data/spec/gitx/cli/update_command_spec.rb +79 -0
- data/spec/spec_helper.rb +86 -0
- data/spec/support/global_config.rb +26 -0
- data/spec/support/home_env.rb +11 -0
- data/spec/support/matchers/meet_expectations_matcher.rb +7 -0
- data/spec/support/pry.rb +1 -0
- data/spec/support/stub_execution.rb +14 -0
- data/spec/support/timecop.rb +9 -0
- data/spec/support/vcr.rb +6 -0
- data/spec/support/webmock.rb +1 -0
- 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
|