Commit 5c4957c88a013a9e4916130980d339739160c60c
Exists in
colab
and in
4 other branches
Merge pull request #298 from mezuro/git_lab_hooks
Git lab hooks
Showing
12 changed files
with
179 additions
and
26 deletions
Show diff stats
.travis.yml
... | ... | @@ -9,8 +9,8 @@ before_script: |
9 | 9 | - pushd kalibro_install |
10 | 10 | # Remove bugged libzmq3 package, see https://github.com/travis-ci/travis-ci/issues/982 and https://github.com/travis-ci/travis-ci/issues/1715 for details |
11 | 11 | - sudo apt-get remove libzmq3 |
12 | - - export KALIBRO_PROCESSOR_VERSION=v1.1.2 | |
13 | - - export KALIBRO_CONFIGURATIONS_VERSION=v1.2.0 | |
12 | + - export KALIBRO_PROCESSOR_VERSION=v1.1.4 | |
13 | + - export KALIBRO_CONFIGURATIONS_VERSION=v1.2.4 | |
14 | 14 | - bash install.sh |
15 | 15 | - popd |
16 | 16 | - cp config/database.yml.sample config/database.yml | ... | ... |
app/controllers/repositories_controller.rb
1 | 1 | include OwnershipAuthentication |
2 | 2 | |
3 | 3 | class RepositoriesController < ApplicationController |
4 | - before_action :authenticate_user!, except: [:show, :state, :state_with_date, :index] | |
4 | + before_action :authenticate_user!, except: [:show, :state, :state_with_date, :index, :notify_push] | |
5 | 5 | before_action :project_owner?, only: [:new, :create], unless: Proc.new { params[:project_id].nil? } |
6 | 6 | before_action :repository_owner?, only: [:edit, :update, :destroy, :process_repository] |
7 | 7 | before_action :set_repository, only: [:show, :edit, :update, :destroy, :state, :state_with_date, :process_repository] |
... | ... | @@ -96,7 +96,18 @@ class RepositoriesController < ApplicationController |
96 | 96 | end |
97 | 97 | end |
98 | 98 | |
99 | -private | |
99 | + def notify_push | |
100 | + gitlab_event = request.headers['X-Gitlab-Event'] | |
101 | + if gitlab_event.nil? || !gitlab_event.end_with?('Push Hook') | |
102 | + return render nothing: true, status: :unprocessable_entity | |
103 | + end | |
104 | + set_repository | |
105 | + @repository.cancel_processing_of_repository unless %w(READY ERROR).include? @repository.last_processing_state | |
106 | + @repository.process | |
107 | + render nothing: true, status: :ok | |
108 | + end | |
109 | + | |
110 | + private | |
100 | 111 | def set_project_id_repository_types_and_configurations |
101 | 112 | @project_id = params[:project_id] |
102 | 113 | @repository_types = Repository.repository_types | ... | ... |
config/routes.rb
... | ... | @@ -14,9 +14,12 @@ Rails.application.routes.draw do |
14 | 14 | put '/repositories/:id' => 'repositories#update', as: :repository_update |
15 | 15 | # This route should be a POST to be semantically correct. But, RepositoriesController#create relies on a redirect to it which is not possible with a POST |
16 | 16 | get '/repositories/:id/process' => 'repositories#process_repository', as: :repository_process |
17 | - | |
18 | 17 | get '/repository_branches' => 'repositories#branches', as: :repository_branches |
19 | 18 | |
19 | + scope :format => false, :constraints => { :format => 'json' } do | |
20 | + post '/repositories/:id/notify_push' => 'repositories#notify_push', as: :repository_notify_push, format: :json | |
21 | + end | |
22 | + | |
20 | 23 | resources :kalibro_configurations do |
21 | 24 | get '/metric_configurations/choose_metric' => 'metric_configurations#choose_metric', as: :choose_metric |
22 | 25 | resources :metric_configurations, except: [:update, :new] do | ... | ... |
... | ... | @@ -0,0 +1,32 @@ |
1 | +Feature: Notify push to repository | |
2 | + In order to automatically process a repository | |
3 | + As a regular user | |
4 | + I want to use a webhook in my repository to notify Mezuro of new pushes | |
5 | + | |
6 | + @kalibro_configuration_restart @kalibro_processor_restart | |
7 | + Scenario: Valid repository | |
8 | + Given I am a regular user | |
9 | + And I have a sample configuration with hotspot metrics | |
10 | + And I have a sample repository | |
11 | + And I start to process that repository | |
12 | + And I wait up for a ready processing | |
13 | + When I push some commits to the repository | |
14 | + Then Mezuro should process the repository again | |
15 | + | |
16 | + @kalibro_configuration_restart @kalibro_processor_restart | |
17 | + Scenario: Invalid repository | |
18 | + Given I am a regular user | |
19 | + When I push some commits to an invalid repository | |
20 | + Then I should get a not found error | |
21 | + | |
22 | + @kalibro_configuration_restart @kalibro_processor_restart | |
23 | + Scenario: Repository with an errored processing | |
24 | + Given I am a regular user | |
25 | + And I have a sample reading group | |
26 | + And I have a sample configuration with the Saikuro native metric | |
27 | + And I have a compound metric configuration with script "rtrnaqdfwqefwqr213r2145211234ed a = b=2" within the given mezuro configuration | |
28 | + And I have a sample repository | |
29 | + And I start to process that repository | |
30 | + And I wait up for an error processing | |
31 | + When I push some commits to the repository | |
32 | + Then Mezuro should process the repository again | ... | ... |
features/repository/show/hotspot_metric_results.feature
... | ... | @@ -37,7 +37,7 @@ Feature: Repository hotspot metric results |
37 | 37 | And I have a sample configuration with native metrics |
38 | 38 | And I have a sample of an invalid repository within the sample project |
39 | 39 | And I start to process that repository |
40 | - And I wait up for a error processing | |
40 | + And I wait up for an error processing | |
41 | 41 | When I visit the repository show page |
42 | 42 | And I click the "Hotspot Metric Results" h3 |
43 | 43 | Then I should see "Repository process returned with error. There are no hotspot metric results." | ... | ... |
features/repository/show/metric_results.feature
... | ... | @@ -46,7 +46,7 @@ Feature: Repository metric results |
46 | 46 | And I have a sample configuration with native metrics |
47 | 47 | And I have a sample of an invalid repository within the sample project |
48 | 48 | And I start to process that repository |
49 | - And I wait up for a error processing | |
49 | + And I wait up for an error processing | |
50 | 50 | When I visit the repository show page |
51 | 51 | And I click the "Tree Metric Results" h3 |
52 | 52 | Then I should see "Repository process returned with error. There are no tree metric results." | ... | ... |
features/step_definitions/compound_metric_configuration_steps.rb
... | ... | @@ -14,6 +14,10 @@ Given(/^I have another compound metric configuration with code "(.*?)" within th |
14 | 14 | @another_compound_metric_configuration = FactoryGirl.create(:compound_metric_configuration, {kalibro_configuration_id: @kalibro_configuration.id, metric: FactoryGirl.build(:compound_metric, code: code), reading_group_id: @reading_group.id}) |
15 | 15 | end |
16 | 16 | |
17 | +Given(/^I have a compound metric configuration with script "(.+)" within the given mezuro configuration$/) do |script| | |
18 | + @another_compound_metric_configuration = FactoryGirl.create(:compound_metric_configuration, kalibro_configuration_id: @kalibro_configuration.id, metric: FactoryGirl.build(:compound_metric, script: script), reading_group_id: @reading_group.id) | |
19 | +end | |
20 | + | |
17 | 21 | When(/^I visit the sample compound metric configuration edit page$/) do |
18 | 22 | visit edit_kalibro_configuration_compound_metric_configuration_path(kalibro_configuration_id: @compound_metric_configuration.kalibro_configuration_id, id: @compound_metric_configuration.id) |
19 | 23 | end | ... | ... |
features/step_definitions/repository_steps.rb
... | ... | @@ -9,15 +9,15 @@ Given(/^I have a sample configuration with native metrics but without ranges$/) |
9 | 9 | end |
10 | 10 | |
11 | 11 | Given(/^I have a sample configuration with native metrics$/) do |
12 | - reading_group = FactoryGirl.create(:reading_group) | |
13 | - reading = FactoryGirl.create(:reading, {reading_group_id: reading_group.id}) | |
12 | + @reading_group = FactoryGirl.create(:reading_group) | |
13 | + reading = FactoryGirl.create(:reading, {reading_group_id: @reading_group.id}) | |
14 | 14 | |
15 | 15 | @kalibro_configuration = FactoryGirl.create(:kalibro_configuration) |
16 | 16 | FactoryGirl.create(:kalibro_configuration_attributes, {id: nil, user_id: @user.id, kalibro_configuration_id: @kalibro_configuration.id}) |
17 | 17 | |
18 | 18 | metric_configuration = FactoryGirl.create(:metric_configuration, |
19 | 19 | {metric: FactoryGirl.build(:loc), |
20 | - reading_group_id: reading_group.id, | |
20 | + reading_group_id: @reading_group.id, | |
21 | 21 | kalibro_configuration_id: @kalibro_configuration.id}) |
22 | 22 | range = FactoryGirl.build(:kalibro_range, {reading_id: reading.id, beginning: '-INF', :end => 'INF', metric_configuration_id: metric_configuration.id}) |
23 | 23 | range.save |
... | ... | @@ -69,7 +69,7 @@ Given(/^I wait up for the last processing to get ready$/) do |
69 | 69 | end |
70 | 70 | end |
71 | 71 | |
72 | -Given(/^I wait up for a error processing$/) do | |
72 | +Given(/^I wait up for an error processing$/) do | |
73 | 73 | while @repository.last_processing_state != "ERROR" |
74 | 74 | sleep(10) |
75 | 75 | end |
... | ... | @@ -119,6 +119,17 @@ Given(/^I am at the All Repositories page$/) do |
119 | 119 | visit repositories_path |
120 | 120 | end |
121 | 121 | |
122 | +Given(/^I have a sample configuration with the (\w+) native metric$/) do |metric| | |
123 | + metric_configuration_factory = (metric + "_metric_configuration").downcase | |
124 | + metric_factory = metric.downcase | |
125 | + @kalibro_configuration = FactoryGirl.create(:ruby_configuration) | |
126 | + metric_configuration = FactoryGirl.create(metric_configuration_factory.to_sym, | |
127 | + {id: 4, | |
128 | + metric: FactoryGirl.build(metric_factory.to_sym), | |
129 | + reading_group_id: @reading_group.id, | |
130 | + kalibro_configuration_id: @kalibro_configuration.id}) | |
131 | +end | |
132 | + | |
122 | 133 | When(/^I click on the sample metric's name$/) do |
123 | 134 | find_link(@metric_results.first.metric_configuration.metric.name).trigger('click') |
124 | 135 | end |
... | ... | @@ -150,6 +161,14 @@ When(/^I get the Creation Date information as "(.*?)"$/) do |variable| |
150 | 161 | eval ("@#{variable} = DateTime.parse('#{val}')") |
151 | 162 | end |
152 | 163 | |
164 | +When(/^I push some commits to the repository$/) do | |
165 | + post repository_notify_push_path(id: @repository.id), {}, {'HTTP_X_GITLAB_EVENT' => 'Push Hook'} | |
166 | +end | |
167 | + | |
168 | +When(/^I push some commits to an invalid repository$/) do | |
169 | + @response = post repository_notify_push_path(id: 0), {}, {'HTTP_X_GITLAB_EVENT' => 'Push Hook'} | |
170 | +end | |
171 | + | |
153 | 172 | Then(/^I should see the sample metric's name$/) do |
154 | 173 | expect(page).to have_content(@metric_results.first.metric_configuration.metric.name) |
155 | 174 | end |
... | ... | @@ -245,3 +264,11 @@ Then(/^I should see the hotspot metric results file names$/) do |
245 | 264 | expect(page).to have_content(metric_result.module_result.kalibro_module.short_name) |
246 | 265 | end |
247 | 266 | end |
267 | + | |
268 | +Then(/^Mezuro should process the repository again$/) do | |
269 | + expect(@repository.last_processing).not_to eq(@repository.first_processing) | |
270 | +end | |
271 | + | |
272 | +Then(/^I should get a not found error$/) do | |
273 | + expect(@response.status).to eq(404) | |
274 | +end | ... | ... |
spec/controllers/repositories_controller_spec.rb
... | ... | @@ -171,7 +171,6 @@ describe RepositoriesController, :type => :controller do |
171 | 171 | end |
172 | 172 | |
173 | 173 | context 'for an specific module_result' do |
174 | - | |
175 | 174 | before :each do |
176 | 175 | KalibroConfiguration.expects(:find).with(repository.id).returns(FactoryGirl.build(:kalibro_configuration, :with_id)) |
177 | 176 | Repository.expects(:find).with(repository.id).returns(repository) |
... | ... | @@ -209,8 +208,8 @@ describe RepositoriesController, :type => :controller do |
209 | 208 | delete :destroy, id: repository.id |
210 | 209 | end |
211 | 210 | |
212 | - it { is_expected.to redirect_to(projects_url) } | |
213 | - it { is_expected.to respond_with(:redirect) } | |
211 | + it { is_expected.to redirect_to(projects_url) } | |
212 | + it { is_expected.to respond_with(:redirect) } | |
214 | 213 | end |
215 | 214 | end |
216 | 215 | |
... | ... | @@ -346,7 +345,7 @@ describe RepositoriesController, :type => :controller do |
346 | 345 | context 'with another state then READY' do |
347 | 346 | let(:processing) { FactoryGirl.build(:processing, state: 'ANALYZING') } |
348 | 347 | |
349 | - before :each do | |
348 | + before :each do | |
350 | 349 | repository.expects(:last_processing).returns(processing) |
351 | 350 | Repository.expects(:find).at_least_once.with(repository.id).returns(repository) |
352 | 351 | |
... | ... | @@ -400,16 +399,16 @@ describe RepositoriesController, :type => :controller do |
400 | 399 | end |
401 | 400 | |
402 | 401 | describe 'process_repository' do |
403 | - let(:repository) { FactoryGirl.build(:repository) } | |
404 | - before :each do | |
405 | - sign_in FactoryGirl.create(:user) | |
406 | - subject.expects(:repository_owner?).returns true | |
407 | - repository.expects(:process) | |
408 | - Repository.expects(:find).at_least_once.with(repository.id).returns(repository) | |
409 | - KalibroConfiguration.expects(:find).with(repository.id).returns(FactoryGirl.build(:kalibro_configuration, :with_id)) | |
410 | - get :process_repository, id: repository.id | |
411 | - end | |
412 | - it { is_expected.to redirect_to(repository_path(repository.id)) } | |
402 | + let(:repository) { FactoryGirl.build(:repository) } | |
403 | + before :each do | |
404 | + sign_in FactoryGirl.create(:user) | |
405 | + subject.expects(:repository_owner?).returns true | |
406 | + repository.expects(:process) | |
407 | + Repository.expects(:find).at_least_once.with(repository.id).returns(repository) | |
408 | + KalibroConfiguration.expects(:find).with(repository.id).returns(FactoryGirl.build(:kalibro_configuration, :with_id)) | |
409 | + get :process_repository, id: repository.id | |
410 | + end | |
411 | + it { is_expected.to redirect_to(repository_path(repository.id)) } | |
413 | 412 | end |
414 | 413 | |
415 | 414 | describe 'branches' do |
... | ... | @@ -474,4 +473,67 @@ describe RepositoriesController, :type => :controller do |
474 | 473 | end |
475 | 474 | end |
476 | 475 | end |
476 | + | |
477 | + describe 'notify_push' do | |
478 | + let(:repository) { FactoryGirl.build(:repository) } | |
479 | + | |
480 | + def post_push | |
481 | + @request.env['HTTP_X_GITLAB_EVENT'] = ['Push Hook', 'Tag Push Hook'].sample | |
482 | + post :notify_push, id: repository.id, format: :json | |
483 | + end | |
484 | + | |
485 | + context 'with a valid repository' do | |
486 | + before :each do | |
487 | + Repository.expects(:find).with(repository.id).returns(repository) | |
488 | + end | |
489 | + | |
490 | + context 'when the repository is being processed' do | |
491 | + before do | |
492 | + repository.expects(:last_processing_state).returns('INTERPRETING') | |
493 | + repository.expects(:cancel_processing_of_repository).once | |
494 | + repository.expects(:process).once | |
495 | + post_push | |
496 | + end | |
497 | + | |
498 | + it { is_expected.to respond_with(:ok) } | |
499 | + end | |
500 | + | |
501 | + context "when the repository's processing resulted in an error" do | |
502 | + before do | |
503 | + repository.expects(:last_processing_state).returns('ERROR') | |
504 | + repository.expects(:process).once | |
505 | + post_push | |
506 | + end | |
507 | + | |
508 | + it { is_expected.to respond_with(:ok) } | |
509 | + end | |
510 | + | |
511 | + context 'when the repository is not being processed' do | |
512 | + before do | |
513 | + repository.expects(:last_processing_state).returns('READY') | |
514 | + repository.expects(:process).once | |
515 | + post_push | |
516 | + end | |
517 | + | |
518 | + it { is_expected.to respond_with(:ok) } | |
519 | + end | |
520 | + end | |
521 | + | |
522 | + context 'with an invalid repository' do | |
523 | + before :each do | |
524 | + Repository.expects(:find).with(repository.id).raises(KalibroClient::Errors::RecordNotFound) | |
525 | + post_push | |
526 | + end | |
527 | + | |
528 | + it { is_expected.to respond_with(:not_found) } | |
529 | + end | |
530 | + | |
531 | + context 'with an invalid header' do | |
532 | + before :each do | |
533 | + post :notify_push, id: repository.id, format: :json | |
534 | + end | |
535 | + | |
536 | + it { is_expected.to respond_with(:unprocessable_entity) } | |
537 | + end | |
538 | + end | |
477 | 539 | end | ... | ... |
spec/factories/kalibro_configurations.rb
spec/factories/metric_configurations.rb
... | ... | @@ -39,4 +39,10 @@ FactoryGirl.define do |
39 | 39 | metric { FactoryGirl.build(:hotspot_metric) } |
40 | 40 | kalibro_configuration_id 1 |
41 | 41 | end |
42 | + | |
43 | + factory :saikuro_metric_configuration, class: MetricConfiguration do | |
44 | + metric { FactoryGirl.build(:saikuro) } | |
45 | + weight 1 | |
46 | + aggregation_form "MEAN" | |
47 | + end | |
42 | 48 | end | ... | ... |
spec/routing/repositories_routing_spec.rb
... | ... | @@ -30,5 +30,8 @@ describe RepositoriesController, :type => :routing do |
30 | 30 | to(controller: :repositories, action: :new, project_id: 1) } |
31 | 31 | it { is_expected.to route(:post, '/projects/1/repositories'). |
32 | 32 | to(controller: :repositories, action: :create, project_id: 1) } |
33 | - end | |
33 | + it { is_expected.to route(:post, '/repositories/1/notify_push'). | |
34 | + to(controller: :repositories, action: :notify_push, id: 1) } | |
35 | + it { expect(post: '/repositories/1/notify_push.html').not_to be_routable } | |
36 | + end | |
34 | 37 | end | ... | ... |