Commit 5c4957c88a013a9e4916130980d339739160c60c

Authored by Diego Camarinha
2 parents f585ae1e 123f9c4a

Merge pull request #298 from mezuro/git_lab_hooks

Git lab hooks
.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 &lt; 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
... ...
features/repository/notify_push.feature 0 → 100644
... ... @@ -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 &quot;(.*?)&quot; 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 &quot;(.*?)&quot;$/) 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 =&gt; :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 =&gt; :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 =&gt; :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 =&gt; :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 =&gt; :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
... ... @@ -20,5 +20,10 @@ FactoryGirl.define do
20 20 name "Public Kalibro Configuration"
21 21 description "Public Configuration."
22 22 end
  23 +
  24 + factory :ruby_configuration do
  25 + name "Ruby"
  26 + description "Code metrics for Ruby."
  27 + end
23 28 end
24 29 end
... ...
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 =&gt; :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
... ...