Migrate CI::Services and CI::WebHooks to Services and WebHooks
This commit is contained in:
parent
4e5897f51e
commit
2988e1fbf5
|
@ -16,6 +16,7 @@ v 8.3.0 (unreleased)
|
|||
- Fire update hook from GitLab
|
||||
- Style warning about mentioning many people in a comment
|
||||
- Fix: sort milestones by due date once again (Greg Smethells)
|
||||
- Migrate all CI::Services and CI::WebHooks to Services and WebHooks
|
||||
- Don't show project fork event as "imported"
|
||||
- Add API endpoint to fetch merge request commits list
|
||||
- Expose events API with comment information and author info
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
class Projects::CiServicesController < Projects::ApplicationController
|
||||
before_action :ci_project
|
||||
before_action :authorize_admin_project!
|
||||
|
||||
layout "project_settings"
|
||||
|
||||
def index
|
||||
@ci_project.build_missing_services
|
||||
@services = @ci_project.services.reload
|
||||
end
|
||||
|
||||
def edit
|
||||
service
|
||||
end
|
||||
|
||||
def update
|
||||
if service.update_attributes(service_params)
|
||||
redirect_to edit_namespace_project_ci_service_path(@project.namespace, @project, service.to_param)
|
||||
else
|
||||
render 'edit'
|
||||
end
|
||||
end
|
||||
|
||||
def test
|
||||
last_build = @project.ci_builds.last
|
||||
|
||||
if service.execute(last_build)
|
||||
message = { notice: 'We successfully tested the service' }
|
||||
else
|
||||
message = { alert: 'We tried to test the service but error occurred' }
|
||||
end
|
||||
|
||||
redirect_back_or_default(options: message)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def service
|
||||
@service ||= @ci_project.services.find { |service| service.to_param == params[:id] }
|
||||
end
|
||||
|
||||
def service_params
|
||||
params.require(:service).permit(
|
||||
:type, :active, :webhook, :notify_only_broken_builds,
|
||||
:email_recipients, :email_only_broken_builds, :email_add_pusher,
|
||||
:hipchat_token, :hipchat_room, :hipchat_server
|
||||
)
|
||||
end
|
||||
end
|
|
@ -1,45 +0,0 @@
|
|||
class Projects::CiWebHooksController < Projects::ApplicationController
|
||||
before_action :ci_project
|
||||
before_action :authorize_admin_project!
|
||||
|
||||
layout "project_settings"
|
||||
|
||||
def index
|
||||
@web_hooks = @ci_project.web_hooks
|
||||
@web_hook = Ci::WebHook.new
|
||||
end
|
||||
|
||||
def create
|
||||
@web_hook = @ci_project.web_hooks.new(web_hook_params)
|
||||
@web_hook.save
|
||||
|
||||
if @web_hook.valid?
|
||||
redirect_to namespace_project_ci_web_hooks_path(@project.namespace, @project)
|
||||
else
|
||||
@web_hooks = @ci_project.web_hooks.select(&:persisted?)
|
||||
render :index
|
||||
end
|
||||
end
|
||||
|
||||
def test
|
||||
Ci::TestHookService.new.execute(hook, current_user)
|
||||
|
||||
redirect_back_or_default(default: { action: 'index' })
|
||||
end
|
||||
|
||||
def destroy
|
||||
hook.destroy
|
||||
|
||||
redirect_to namespace_project_ci_web_hooks_path(@project.namespace, @project)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def hook
|
||||
@web_hook ||= @ci_project.web_hooks.find(params[:id])
|
||||
end
|
||||
|
||||
def web_hook_params
|
||||
params.require(:web_hook).permit(:url)
|
||||
end
|
||||
end
|
|
@ -53,6 +53,7 @@ class Projects::HooksController < Projects::ApplicationController
|
|||
|
||||
def hook_params
|
||||
params.require(:hook).permit(:url, :push_events, :issues_events,
|
||||
:merge_requests_events, :tag_push_events, :note_events, :enable_ssl_verification)
|
||||
:merge_requests_events, :tag_push_events, :note_events,
|
||||
:build_events, :enable_ssl_verification)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,7 +6,9 @@ class Projects::ServicesController < Projects::ApplicationController
|
|||
:description, :issues_url, :new_issue_url, :restrict_to_branch, :channel,
|
||||
:colorize_messages, :channels,
|
||||
:push_events, :issues_events, :merge_requests_events, :tag_push_events,
|
||||
:note_events, :send_from_committer_email, :disable_diffs, :external_wiki_url,
|
||||
:note_events, :build_events,
|
||||
:notify_only_broken_builds, :add_pusher,
|
||||
:send_from_committer_email, :disable_diffs, :external_wiki_url,
|
||||
:notify, :color,
|
||||
:server_host, :server_port, :default_irc_uri, :enable_ssl_verification]
|
||||
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
module Ci
|
||||
module Emails
|
||||
module Builds
|
||||
def build_fail_email(build_id, to)
|
||||
@build = Ci::Build.find(build_id)
|
||||
@project = @build.project
|
||||
mail(to: to, subject: subject("Build failed for #{@project.name}", @build.short_sha))
|
||||
end
|
||||
|
||||
def build_success_email(build_id, to)
|
||||
@build = Ci::Build.find(build_id)
|
||||
@project = @build.project
|
||||
mail(to: to, subject: subject("Build success for #{@project.name}", @build.short_sha))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,46 +0,0 @@
|
|||
module Ci
|
||||
class Notify < ActionMailer::Base
|
||||
include Ci::Emails::Builds
|
||||
|
||||
add_template_helper Ci::GitlabHelper
|
||||
|
||||
default_url_options[:host] = Gitlab.config.gitlab.host
|
||||
default_url_options[:protocol] = Gitlab.config.gitlab.protocol
|
||||
default_url_options[:port] = Gitlab.config.gitlab.port unless Gitlab.config.gitlab_on_standard_port?
|
||||
default_url_options[:script_name] = Gitlab.config.gitlab.relative_url_root
|
||||
|
||||
default from: Gitlab.config.gitlab.email_from
|
||||
|
||||
# Just send email with 3 seconds delay
|
||||
def self.delay
|
||||
delay_for(2.seconds)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Formats arguments into a String suitable for use as an email subject
|
||||
#
|
||||
# extra - Extra Strings to be inserted into the subject
|
||||
#
|
||||
# Examples
|
||||
#
|
||||
# >> subject('Lorem ipsum')
|
||||
# => "GitLab-CI | Lorem ipsum"
|
||||
#
|
||||
# # Automatically inserts Project name when @project is set
|
||||
# >> @project = Project.last
|
||||
# => #<Project id: 1, name: "Ruby on Rails", path: "ruby_on_rails", ...>
|
||||
# >> subject('Lorem ipsum')
|
||||
# => "GitLab-CI | Ruby on Rails | Lorem ipsum "
|
||||
#
|
||||
# # Accepts multiple arguments
|
||||
# >> subject('Lorem ipsum', 'Dolor sit amet')
|
||||
# => "GitLab-CI | Lorem ipsum | Dolor sit amet"
|
||||
def subject(*extra)
|
||||
subject = "GitLab-CI"
|
||||
subject << (@project ? " | #{@project.name}" : "")
|
||||
subject << " | " + extra.join(' | ') if extra.present?
|
||||
subject
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,15 @@
|
|||
module Emails
|
||||
module Builds
|
||||
def build_fail_email(build_id, to)
|
||||
@build = Ci::Build.find(build_id)
|
||||
@project = @build.project
|
||||
mail(to: to, subject: subject("Build failed for #{@project.name}", @build.short_sha))
|
||||
end
|
||||
|
||||
def build_success_email(build_id, to)
|
||||
@build = Ci::Build.find(build_id)
|
||||
@project = @build.project
|
||||
mail(to: to, subject: subject("Build success for #{@project.name}", @build.short_sha))
|
||||
end
|
||||
end
|
||||
end
|
|
@ -7,6 +7,7 @@ class Notify < BaseMailer
|
|||
include Emails::Projects
|
||||
include Emails::Profile
|
||||
include Emails::Groups
|
||||
include Emails::Builds
|
||||
|
||||
add_template_helper MergeRequestsHelper
|
||||
add_template_helper EmailsHelper
|
||||
|
|
|
@ -96,21 +96,21 @@ module Ci
|
|||
end
|
||||
|
||||
state_machine :status, initial: :pending do
|
||||
after_transition pending: :running do |build, transition|
|
||||
build.execute_hooks
|
||||
end
|
||||
|
||||
after_transition any => [:success, :failed, :canceled] do |build, transition|
|
||||
return unless build.gl_project
|
||||
|
||||
project = build.project
|
||||
|
||||
if project.web_hooks?
|
||||
Ci::WebHookService.new.build_end(build)
|
||||
end
|
||||
|
||||
build.commit.create_next_builds(build)
|
||||
project.execute_services(build)
|
||||
|
||||
if project.coverage_enabled?
|
||||
build.update_coverage
|
||||
end
|
||||
|
||||
build.commit.create_next_builds(build)
|
||||
build.execute_hooks
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -275,6 +275,12 @@ module Ci
|
|||
end
|
||||
end
|
||||
|
||||
def execute_hooks
|
||||
build_data = Gitlab::BuildDataBuilder.build(self)
|
||||
gl_project.execute_hooks(build_data.dup, :build_hooks)
|
||||
gl_project.execute_services(build_data.dup, :build_hooks)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def yaml_variables
|
||||
|
|
|
@ -178,6 +178,10 @@ module Ci
|
|||
duration_array.reduce(:+).to_i
|
||||
end
|
||||
|
||||
def started_at
|
||||
@started_at ||= statuses.order('started_at ASC').first.try(:started_at)
|
||||
end
|
||||
|
||||
def finished_at
|
||||
@finished_at ||= statuses.order('finished_at DESC').first.try(:finished_at)
|
||||
end
|
||||
|
|
|
@ -35,17 +35,10 @@ module Ci
|
|||
|
||||
has_many :runner_projects, dependent: :destroy, class_name: 'Ci::RunnerProject'
|
||||
has_many :runners, through: :runner_projects, class_name: 'Ci::Runner'
|
||||
has_many :web_hooks, dependent: :destroy, class_name: 'Ci::WebHook'
|
||||
has_many :events, dependent: :destroy, class_name: 'Ci::Event'
|
||||
has_many :variables, dependent: :destroy, class_name: 'Ci::Variable'
|
||||
has_many :triggers, dependent: :destroy, class_name: 'Ci::Trigger'
|
||||
|
||||
# Project services
|
||||
has_many :services, dependent: :destroy, class_name: 'Ci::Service'
|
||||
has_one :hip_chat_service, dependent: :destroy, class_name: 'Ci::HipChatService'
|
||||
has_one :slack_service, dependent: :destroy, class_name: 'Ci::SlackService'
|
||||
has_one :mail_service, dependent: :destroy, class_name: 'Ci::MailService'
|
||||
|
||||
accepts_nested_attributes_for :variables, allow_destroy: true
|
||||
|
||||
delegate :name_with_namespace, :path_with_namespace, :web_url, :http_url_to_repo, :ssh_url_to_repo, to: :gl_project
|
||||
|
@ -122,14 +115,6 @@ module Ci
|
|||
email_add_pusher || email_recipients.present?
|
||||
end
|
||||
|
||||
def web_hooks?
|
||||
web_hooks.any?
|
||||
end
|
||||
|
||||
def services?
|
||||
services.any?
|
||||
end
|
||||
|
||||
def timeout_in_minutes
|
||||
timeout / 60
|
||||
end
|
||||
|
@ -151,32 +136,6 @@ module Ci
|
|||
end
|
||||
end
|
||||
|
||||
def available_services_names
|
||||
%w(slack mail hip_chat)
|
||||
end
|
||||
|
||||
def build_missing_services
|
||||
available_services_names.each do |service_name|
|
||||
service = services.find { |service| service.to_param == service_name }
|
||||
|
||||
# If service is available but missing in db
|
||||
# we should create an instance. Ex `create_gitlab_ci_service`
|
||||
self.send :"create_#{service_name}_service" if service.nil?
|
||||
end
|
||||
end
|
||||
|
||||
def execute_services(data)
|
||||
services.each do |service|
|
||||
|
||||
# Call service hook only if it is active
|
||||
begin
|
||||
service.execute(data) if service.active && service.can_execute?(data)
|
||||
rescue => e
|
||||
logger.error(e)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def setup_finished?
|
||||
commits.any?
|
||||
end
|
||||
|
|
|
@ -1,105 +0,0 @@
|
|||
# == Schema Information
|
||||
#
|
||||
# Table name: ci_services
|
||||
#
|
||||
# id :integer not null, primary key
|
||||
# type :string(255)
|
||||
# title :string(255)
|
||||
# project_id :integer not null
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# active :boolean default(FALSE), not null
|
||||
# properties :text
|
||||
#
|
||||
|
||||
# To add new service you should build a class inherited from Service
|
||||
# and implement a set of methods
|
||||
module Ci
|
||||
class Service < ActiveRecord::Base
|
||||
extend Ci::Model
|
||||
|
||||
serialize :properties, JSON
|
||||
|
||||
default_value_for :active, false
|
||||
|
||||
after_initialize :initialize_properties
|
||||
|
||||
belongs_to :project, class_name: 'Ci::Project'
|
||||
|
||||
validates :project_id, presence: true
|
||||
|
||||
def activated?
|
||||
active
|
||||
end
|
||||
|
||||
def category
|
||||
:common
|
||||
end
|
||||
|
||||
def initialize_properties
|
||||
self.properties = {} if properties.nil?
|
||||
end
|
||||
|
||||
def title
|
||||
# implement inside child
|
||||
end
|
||||
|
||||
def description
|
||||
# implement inside child
|
||||
end
|
||||
|
||||
def help
|
||||
# implement inside child
|
||||
end
|
||||
|
||||
def to_param
|
||||
# implement inside child
|
||||
end
|
||||
|
||||
def fields
|
||||
# implement inside child
|
||||
[]
|
||||
end
|
||||
|
||||
def can_test?
|
||||
project.builds.any?
|
||||
end
|
||||
|
||||
def can_execute?(build)
|
||||
true
|
||||
end
|
||||
|
||||
def execute(build)
|
||||
# implement inside child
|
||||
end
|
||||
|
||||
# Provide convenient accessor methods
|
||||
# for each serialized property.
|
||||
def self.prop_accessor(*args)
|
||||
args.each do |arg|
|
||||
class_eval %{
|
||||
def #{arg}
|
||||
(properties || {})['#{arg}']
|
||||
end
|
||||
|
||||
def #{arg}=(value)
|
||||
self.properties ||= {}
|
||||
self.properties['#{arg}'] = value
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def self.boolean_accessor(*args)
|
||||
self.prop_accessor(*args)
|
||||
|
||||
args.each do |arg|
|
||||
class_eval %{
|
||||
def #{arg}?
|
||||
ActiveRecord::ConnectionAdapters::Column::TRUE_VALUES.include?(#{arg})
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,43 +0,0 @@
|
|||
# == Schema Information
|
||||
#
|
||||
# Table name: ci_web_hooks
|
||||
#
|
||||
# id :integer not null, primary key
|
||||
# url :string(255) not null
|
||||
# project_id :integer not null
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
#
|
||||
|
||||
module Ci
|
||||
class WebHook < ActiveRecord::Base
|
||||
extend Ci::Model
|
||||
|
||||
include HTTParty
|
||||
|
||||
belongs_to :project, class_name: 'Ci::Project'
|
||||
|
||||
# HTTParty timeout
|
||||
default_timeout 10
|
||||
|
||||
validates :url, presence: true, url: true
|
||||
|
||||
def execute(data)
|
||||
parsed_url = URI.parse(url)
|
||||
if parsed_url.userinfo.blank?
|
||||
Ci::WebHook.post(url, body: data.to_json, headers: { "Content-Type" => "application/json" }, verify: false)
|
||||
else
|
||||
post_url = url.gsub("#{parsed_url.userinfo}@", "")
|
||||
auth = {
|
||||
username: URI.decode(parsed_url.user),
|
||||
password: URI.decode(parsed_url.password),
|
||||
}
|
||||
Ci::WebHook.post(post_url,
|
||||
body: data.to_json,
|
||||
headers: { "Content-Type" => "application/json" },
|
||||
verify: false,
|
||||
basic_auth: auth)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -25,4 +25,5 @@ class ProjectHook < WebHook
|
|||
scope :issue_hooks, -> { where(issues_events: true) }
|
||||
scope :note_hooks, -> { where(note_events: true) }
|
||||
scope :merge_request_hooks, -> { where(merge_requests_events: true) }
|
||||
scope :build_hooks, -> { where(build_events: true) }
|
||||
end
|
||||
|
|
|
@ -26,6 +26,7 @@ class WebHook < ActiveRecord::Base
|
|||
default_value_for :note_events, false
|
||||
default_value_for :merge_requests_events, false
|
||||
default_value_for :tag_push_events, false
|
||||
default_value_for :build_events, false
|
||||
default_value_for :enable_ssl_verification, true
|
||||
|
||||
# HTTParty timeout
|
||||
|
|
|
@ -81,6 +81,7 @@ class Project < ActiveRecord::Base
|
|||
has_one :campfire_service, dependent: :destroy
|
||||
has_one :drone_ci_service, dependent: :destroy
|
||||
has_one :emails_on_push_service, dependent: :destroy
|
||||
has_one :builds_email_service, dependent: :destroy
|
||||
has_one :irker_service, dependent: :destroy
|
||||
has_one :pivotaltracker_service, dependent: :destroy
|
||||
has_one :hipchat_service, dependent: :destroy
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
# == Schema Information
|
||||
#
|
||||
# Table name: services
|
||||
#
|
||||
# id :integer not null, primary key
|
||||
# type :string(255)
|
||||
# title :string(255)
|
||||
# project_id :integer
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# active :boolean default(FALSE), not null
|
||||
# properties :text
|
||||
# template :boolean default(FALSE)
|
||||
# push_events :boolean default(TRUE)
|
||||
# issues_events :boolean default(TRUE)
|
||||
# merge_requests_events :boolean default(TRUE)
|
||||
# tag_push_events :boolean default(TRUE)
|
||||
# note_events :boolean default(TRUE), not null
|
||||
#
|
||||
|
||||
class BuildsEmailService < Service
|
||||
prop_accessor :recipients
|
||||
boolean_accessor :add_pusher
|
||||
boolean_accessor :notify_only_broken_builds
|
||||
validates :recipients, presence: true, if: :activated?
|
||||
|
||||
def title
|
||||
'Builds emails'
|
||||
end
|
||||
|
||||
def description
|
||||
'Email the builds status to a list of recipients.'
|
||||
end
|
||||
|
||||
def to_param
|
||||
'builds_email'
|
||||
end
|
||||
|
||||
def supported_events
|
||||
%w(build)
|
||||
end
|
||||
|
||||
def execute(push_data)
|
||||
return unless supported_events.include?(push_data[:object_kind])
|
||||
|
||||
if should_build_be_notified?(push_data)
|
||||
BuildEmailWorker.perform_async(
|
||||
push_data[:build_id],
|
||||
all_recipients(push_data),
|
||||
push_data,
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def fields
|
||||
[
|
||||
{ type: 'textarea', name: 'recipients', placeholder: 'Emails separated by whitespace' },
|
||||
{ type: 'checkbox', name: 'add_pusher', label: 'Add pusher to recipients list' },
|
||||
{ type: 'checkbox', name: 'notify_only_broken_builds' },
|
||||
]
|
||||
end
|
||||
|
||||
def should_build_be_notified?(data)
|
||||
case data[:build_status]
|
||||
when 'success'
|
||||
!notify_only_broken_builds?
|
||||
when 'failed'
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def all_recipients(data)
|
||||
if add_pusher? && data[:user][:email]
|
||||
recipients + " #{data[:user][:email]}"
|
||||
else
|
||||
recipients
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,73 +0,0 @@
|
|||
module Ci
|
||||
class HipChatMessage
|
||||
include Gitlab::Application.routes.url_helpers
|
||||
|
||||
attr_reader :build
|
||||
|
||||
def initialize(build)
|
||||
@build = build
|
||||
end
|
||||
|
||||
def to_s
|
||||
lines = Array.new
|
||||
lines.push("<a href=\"#{ci_project_url(project)}\">#{project.name}</a> - ")
|
||||
lines.push("<a href=\"#{builds_namespace_project_commit_url(commit.gl_project.namespace, commit.gl_project, commit.sha)}\">Commit ##{commit.id}</a></br>")
|
||||
lines.push("#{commit.short_sha} #{commit.git_author_name} - #{commit.git_commit_message}</br>")
|
||||
lines.push("#{humanized_status(commit_status)} in #{commit.duration} second(s).")
|
||||
lines.join('')
|
||||
end
|
||||
|
||||
def status_color(build_or_commit=nil)
|
||||
build_or_commit ||= commit_status
|
||||
case build_or_commit
|
||||
when :success
|
||||
'green'
|
||||
when :failed, :canceled
|
||||
'red'
|
||||
else # :pending, :running or unknown
|
||||
'yellow'
|
||||
end
|
||||
end
|
||||
|
||||
def notify?
|
||||
[:failed, :canceled].include?(commit_status)
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
|
||||
def commit
|
||||
build.commit
|
||||
end
|
||||
|
||||
def project
|
||||
commit.project
|
||||
end
|
||||
|
||||
def build_status
|
||||
build.status.to_sym
|
||||
end
|
||||
|
||||
def commit_status
|
||||
commit.status.to_sym
|
||||
end
|
||||
|
||||
def humanized_status(build_or_commit=nil)
|
||||
build_or_commit ||= commit_status
|
||||
case build_or_commit
|
||||
when :pending
|
||||
"Pending"
|
||||
when :running
|
||||
"Running"
|
||||
when :failed
|
||||
"Failed"
|
||||
when :success
|
||||
"Successful"
|
||||
when :canceled
|
||||
"Canceled"
|
||||
else
|
||||
"Unknown"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,93 +0,0 @@
|
|||
# == Schema Information
|
||||
#
|
||||
# Table name: ci_services
|
||||
#
|
||||
# id :integer not null, primary key
|
||||
# type :string(255)
|
||||
# title :string(255)
|
||||
# project_id :integer not null
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# active :boolean default(FALSE), not null
|
||||
# properties :text
|
||||
#
|
||||
|
||||
module Ci
|
||||
class HipChatService < Ci::Service
|
||||
prop_accessor :hipchat_token, :hipchat_room, :hipchat_server
|
||||
boolean_accessor :notify_only_broken_builds
|
||||
validates :hipchat_token, presence: true, if: :activated?
|
||||
validates :hipchat_room, presence: true, if: :activated?
|
||||
default_value_for :notify_only_broken_builds, true
|
||||
|
||||
def title
|
||||
"HipChat"
|
||||
end
|
||||
|
||||
def description
|
||||
"Private group chat, video chat, instant messaging for teams"
|
||||
end
|
||||
|
||||
def help
|
||||
end
|
||||
|
||||
def to_param
|
||||
'hip_chat'
|
||||
end
|
||||
|
||||
def fields
|
||||
[
|
||||
{ type: 'text', name: 'hipchat_token', label: 'Token', placeholder: '' },
|
||||
{ type: 'text', name: 'hipchat_room', label: 'Room', placeholder: '' },
|
||||
{ type: 'text', name: 'hipchat_server', label: 'Server', placeholder: 'https://hipchat.example.com', help: 'Leave blank for default' },
|
||||
{ type: 'checkbox', name: 'notify_only_broken_builds', label: 'Notify only broken builds' }
|
||||
]
|
||||
end
|
||||
|
||||
def can_execute?(build)
|
||||
return if build.allow_failure?
|
||||
|
||||
commit = build.commit
|
||||
return unless commit
|
||||
return unless commit.latest_builds.include? build
|
||||
|
||||
case commit.status.to_sym
|
||||
when :failed
|
||||
true
|
||||
when :success
|
||||
true unless notify_only_broken_builds?
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def execute(build)
|
||||
msg = Ci::HipChatMessage.new(build)
|
||||
opts = default_options.merge(
|
||||
token: hipchat_token,
|
||||
room: hipchat_room,
|
||||
server: server_url,
|
||||
color: msg.status_color,
|
||||
notify: msg.notify?
|
||||
)
|
||||
Ci::HipChatNotifierWorker.perform_async(msg.to_s, opts)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def default_options
|
||||
{
|
||||
service_name: 'GitLab CI',
|
||||
message_format: 'html'
|
||||
}
|
||||
end
|
||||
|
||||
def server_url
|
||||
if hipchat_server.blank?
|
||||
'https://api.hipchat.com'
|
||||
else
|
||||
hipchat_server
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,84 +0,0 @@
|
|||
# == Schema Information
|
||||
#
|
||||
# Table name: ci_services
|
||||
#
|
||||
# id :integer not null, primary key
|
||||
# type :string(255)
|
||||
# title :string(255)
|
||||
# project_id :integer not null
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# active :boolean default(FALSE), not null
|
||||
# properties :text
|
||||
#
|
||||
|
||||
module Ci
|
||||
class MailService < Ci::Service
|
||||
delegate :email_recipients, :email_recipients=,
|
||||
:email_add_pusher, :email_add_pusher=,
|
||||
:email_only_broken_builds, :email_only_broken_builds=, to: :project, prefix: false
|
||||
|
||||
before_save :update_project
|
||||
|
||||
default_value_for :active, true
|
||||
|
||||
def title
|
||||
'Mail'
|
||||
end
|
||||
|
||||
def description
|
||||
'Email notification'
|
||||
end
|
||||
|
||||
def to_param
|
||||
'mail'
|
||||
end
|
||||
|
||||
def fields
|
||||
[
|
||||
{ type: 'text', name: 'email_recipients', label: 'Recipients', help: 'Whitespace-separated list of recipient addresses' },
|
||||
{ type: 'checkbox', name: 'email_add_pusher', label: 'Add pusher to recipients list' },
|
||||
{ type: 'checkbox', name: 'email_only_broken_builds', label: 'Notify only broken builds' }
|
||||
]
|
||||
end
|
||||
|
||||
def can_execute?(build)
|
||||
return if build.allow_failure?
|
||||
|
||||
# it doesn't make sense to send emails for retried builds
|
||||
commit = build.commit
|
||||
return unless commit
|
||||
return unless commit.latest_builds.include?(build)
|
||||
|
||||
case build.status.to_sym
|
||||
when :failed
|
||||
true
|
||||
when :success
|
||||
true unless email_only_broken_builds
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def execute(build)
|
||||
build.project_recipients.each do |recipient|
|
||||
case build.status.to_sym
|
||||
when :success
|
||||
mailer.build_success_email(build.id, recipient).deliver_later
|
||||
when :failed
|
||||
mailer.build_fail_email(build.id, recipient).deliver_later
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def update_project
|
||||
project.save!
|
||||
end
|
||||
|
||||
def mailer
|
||||
Ci::Notify
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,92 +0,0 @@
|
|||
require 'slack-notifier'
|
||||
|
||||
module Ci
|
||||
class SlackMessage
|
||||
include Gitlab::Application.routes.url_helpers
|
||||
|
||||
def initialize(commit)
|
||||
@commit = commit
|
||||
end
|
||||
|
||||
def pretext
|
||||
''
|
||||
end
|
||||
|
||||
def color
|
||||
attachment_color
|
||||
end
|
||||
|
||||
def fallback
|
||||
format(attachment_message)
|
||||
end
|
||||
|
||||
def attachments
|
||||
fields = []
|
||||
|
||||
commit.latest_builds.each do |build|
|
||||
next if build.allow_failure?
|
||||
next unless build.failed?
|
||||
fields << {
|
||||
title: build.name,
|
||||
value: "Build <#{namespace_project_build_url(build.gl_project.namespace, build.gl_project, build)}|\##{build.id}> failed in #{build.duration.to_i} second(s)."
|
||||
}
|
||||
end
|
||||
|
||||
[{
|
||||
text: attachment_message,
|
||||
color: attachment_color,
|
||||
fields: fields
|
||||
}]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :commit
|
||||
|
||||
def attachment_message
|
||||
out = "<#{ci_project_url(project)}|#{project_name}>: "
|
||||
out << "Commit <#{builds_namespace_project_commit_url(commit.gl_project.namespace, commit.gl_project, commit.sha)}|\##{commit.id}> "
|
||||
out << "(<#{commit_sha_link}|#{commit.short_sha}>) "
|
||||
out << "of <#{commit_ref_link}|#{commit.ref}> "
|
||||
out << "by #{commit.git_author_name} " if commit.git_author_name
|
||||
out << "#{commit_status} in "
|
||||
out << "#{commit.duration} second(s)"
|
||||
end
|
||||
|
||||
def format(string)
|
||||
Slack::Notifier::LinkFormatter.format(string)
|
||||
end
|
||||
|
||||
def project
|
||||
commit.project
|
||||
end
|
||||
|
||||
def project_name
|
||||
project.name
|
||||
end
|
||||
|
||||
def commit_sha_link
|
||||
"#{project.gitlab_url}/commit/#{commit.sha}"
|
||||
end
|
||||
|
||||
def commit_ref_link
|
||||
"#{project.gitlab_url}/commits/#{commit.ref}"
|
||||
end
|
||||
|
||||
def attachment_color
|
||||
if commit.success?
|
||||
'good'
|
||||
else
|
||||
'danger'
|
||||
end
|
||||
end
|
||||
|
||||
def commit_status
|
||||
if commit.success?
|
||||
'succeeded'
|
||||
else
|
||||
'failed'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,81 +0,0 @@
|
|||
# == Schema Information
|
||||
#
|
||||
# Table name: ci_services
|
||||
#
|
||||
# id :integer not null, primary key
|
||||
# type :string(255)
|
||||
# title :string(255)
|
||||
# project_id :integer not null
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# active :boolean default(FALSE), not null
|
||||
# properties :text
|
||||
#
|
||||
|
||||
module Ci
|
||||
class SlackService < Ci::Service
|
||||
prop_accessor :webhook
|
||||
boolean_accessor :notify_only_broken_builds
|
||||
validates :webhook, presence: true, if: :activated?
|
||||
|
||||
default_value_for :notify_only_broken_builds, true
|
||||
|
||||
def title
|
||||
'Slack'
|
||||
end
|
||||
|
||||
def description
|
||||
'A team communication tool for the 21st century'
|
||||
end
|
||||
|
||||
def to_param
|
||||
'slack'
|
||||
end
|
||||
|
||||
def help
|
||||
'Visit https://www.slack.com/services/new/incoming-webhook. Then copy link and save project!' unless webhook.present?
|
||||
end
|
||||
|
||||
def fields
|
||||
[
|
||||
{ type: 'text', name: 'webhook', label: 'Webhook URL', placeholder: '' },
|
||||
{ type: 'checkbox', name: 'notify_only_broken_builds', label: 'Notify only broken builds' }
|
||||
]
|
||||
end
|
||||
|
||||
def can_execute?(build)
|
||||
return if build.allow_failure?
|
||||
|
||||
commit = build.commit
|
||||
return unless commit
|
||||
return unless commit.latest_builds.include?(build)
|
||||
|
||||
case commit.status.to_sym
|
||||
when :failed
|
||||
true
|
||||
when :success
|
||||
true unless notify_only_broken_builds?
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def execute(build)
|
||||
message = Ci::SlackMessage.new(build.commit)
|
||||
options = default_options.merge(
|
||||
color: message.color,
|
||||
fallback: message.fallback,
|
||||
attachments: message.attachments
|
||||
)
|
||||
Ci::SlackNotifierWorker.perform_async(webhook, message.pretext, options)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def default_options
|
||||
{
|
||||
username: 'GitLab CI'
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
|
@ -22,6 +22,7 @@ class HipchatService < Service
|
|||
MAX_COMMITS = 3
|
||||
|
||||
prop_accessor :token, :room, :server, :notify, :color, :api_version
|
||||
boolean_accessor :notify_only_broken_builds
|
||||
validates :token, presence: true, if: :activated?
|
||||
|
||||
def title
|
||||
|
@ -45,12 +46,13 @@ class HipchatService < Service
|
|||
{ type: 'text', name: 'api_version',
|
||||
placeholder: 'Leave blank for default (v2)' },
|
||||
{ type: 'text', name: 'server',
|
||||
placeholder: 'Leave blank for default. https://hipchat.example.com' }
|
||||
placeholder: 'Leave blank for default. https://hipchat.example.com' },
|
||||
{ type: 'checkbox', name: 'notify_only_broken_builds' },
|
||||
]
|
||||
end
|
||||
|
||||
def supported_events
|
||||
%w(push issue merge_request note tag_push)
|
||||
%w(push issue merge_request note tag_push build)
|
||||
end
|
||||
|
||||
def execute(data)
|
||||
|
@ -94,6 +96,8 @@ class HipchatService < Service
|
|||
create_merge_request_message(data) unless is_update?(data)
|
||||
when "note"
|
||||
create_note_message(data)
|
||||
when "build"
|
||||
create_build_message(data) if should_build_be_notified?(data)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -235,6 +239,20 @@ class HipchatService < Service
|
|||
message
|
||||
end
|
||||
|
||||
def create_build_message(data)
|
||||
ref_type = data[:tag] ? 'tag' : 'branch'
|
||||
ref = data[:ref]
|
||||
sha = data[:sha]
|
||||
user_name = data[:commit][:author_name]
|
||||
status = data[:commit][:status]
|
||||
duration = data[:commit][:duration]
|
||||
|
||||
branch_link = "<a href=\"#{project_url}/commits/#{URI.escape(ref)}\">#{ref}</a>"
|
||||
commit_link = "<a href=\"#{project_url}/commit/#{URI.escape(sha)}/builds\">#{Commit.truncate_sha(sha)}</a>"
|
||||
|
||||
"#{project_link}: Commit #{commit_link} of #{branch_link} #{ref_type} by #{user_name} #{humanized_status(status)} in #{duration} second(s)"
|
||||
end
|
||||
|
||||
def project_name
|
||||
project.name_with_namespace.gsub(/\s/, '')
|
||||
end
|
||||
|
@ -250,4 +268,24 @@ class HipchatService < Service
|
|||
def is_update?(data)
|
||||
data[:object_attributes][:action] == 'update'
|
||||
end
|
||||
|
||||
def humanized_status(status)
|
||||
case status
|
||||
when 'success'
|
||||
'passed'
|
||||
else
|
||||
status
|
||||
end
|
||||
end
|
||||
|
||||
def should_build_be_notified?(data)
|
||||
case data[:commit][:status]
|
||||
when 'success'
|
||||
!notify_only_broken_builds?
|
||||
when 'failed'
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
class SlackService < Service
|
||||
prop_accessor :webhook, :username, :channel
|
||||
boolean_accessor :notify_only_broken_builds
|
||||
validates :webhook, presence: true, if: :activated?
|
||||
|
||||
def title
|
||||
|
@ -45,12 +46,13 @@ class SlackService < Service
|
|||
{ type: 'text', name: 'webhook',
|
||||
placeholder: 'https://hooks.slack.com/services/...' },
|
||||
{ type: 'text', name: 'username', placeholder: 'username' },
|
||||
{ type: 'text', name: 'channel', placeholder: '#channel' }
|
||||
{ type: 'text', name: 'channel', placeholder: '#channel' },
|
||||
{ type: 'checkbox', name: 'notify_only_broken_builds' },
|
||||
]
|
||||
end
|
||||
|
||||
def supported_events
|
||||
%w(push issue merge_request note tag_push)
|
||||
%w(push issue merge_request note tag_push build)
|
||||
end
|
||||
|
||||
def execute(data)
|
||||
|
@ -78,6 +80,8 @@ class SlackService < Service
|
|||
MergeMessage.new(data) unless is_update?(data)
|
||||
when "note"
|
||||
NoteMessage.new(data)
|
||||
when "build"
|
||||
BuildMessage.new(data) if should_build_be_notified?(data)
|
||||
end
|
||||
|
||||
opt = {}
|
||||
|
@ -86,7 +90,7 @@ class SlackService < Service
|
|||
|
||||
if message
|
||||
notifier = Slack::Notifier.new(webhook, opt)
|
||||
notifier.ping(message.pretext, attachments: message.attachments)
|
||||
notifier.ping(message.pretext, attachments: message.attachments, fallback: message.fallback)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -103,9 +107,21 @@ class SlackService < Service
|
|||
def is_update?(data)
|
||||
data[:object_attributes][:action] == 'update'
|
||||
end
|
||||
|
||||
def should_build_be_notified?(data)
|
||||
case data[:commit][:status]
|
||||
when 'success'
|
||||
!notify_only_broken_builds?
|
||||
when 'failed'
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
require "slack_service/issue_message"
|
||||
require "slack_service/push_message"
|
||||
require "slack_service/merge_message"
|
||||
require "slack_service/note_message"
|
||||
require "slack_service/build_message"
|
||||
|
|
|
@ -10,6 +10,9 @@ class SlackService
|
|||
format(message)
|
||||
end
|
||||
|
||||
def fallback
|
||||
end
|
||||
|
||||
def attachments
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
class SlackService
|
||||
class BuildMessage < BaseMessage
|
||||
attr_reader :sha
|
||||
attr_reader :ref_type
|
||||
attr_reader :ref
|
||||
attr_reader :status
|
||||
attr_reader :project_name
|
||||
attr_reader :project_url
|
||||
attr_reader :user_name
|
||||
attr_reader :duration
|
||||
|
||||
def initialize(params, commit = true)
|
||||
@sha = params[:sha]
|
||||
@ref_type = params[:tag] ? 'tag' : 'branch'
|
||||
@ref = params[:ref]
|
||||
@project_name = params[:project_name]
|
||||
@project_url = params[:project_url]
|
||||
@status = params[:commit][:status]
|
||||
@user_name = params[:commit][:author_name]
|
||||
@duration = params[:commit][:duration]
|
||||
end
|
||||
|
||||
def pretext
|
||||
''
|
||||
end
|
||||
|
||||
def fallback
|
||||
format(message)
|
||||
end
|
||||
|
||||
def attachments
|
||||
[{ text: format(message), color: attachment_color }]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def message
|
||||
"#{project_link}: Commit #{commit_link} of #{branch_link} #{ref_type} by #{user_name} #{humanized_status} in #{duration} second(s)"
|
||||
end
|
||||
|
||||
def format(string)
|
||||
Slack::Notifier::LinkFormatter.format(string)
|
||||
end
|
||||
|
||||
def humanized_status
|
||||
case status
|
||||
when 'success'
|
||||
'passed'
|
||||
else
|
||||
status
|
||||
end
|
||||
end
|
||||
|
||||
def attachment_color
|
||||
if status == 'success'
|
||||
'good'
|
||||
else
|
||||
'danger'
|
||||
end
|
||||
end
|
||||
|
||||
def branch_url
|
||||
"#{project_url}/commits/#{ref}"
|
||||
end
|
||||
|
||||
def branch_link
|
||||
"[#{ref}](#{branch_url})"
|
||||
end
|
||||
|
||||
def project_link
|
||||
"[#{project_name}](#{project_url})"
|
||||
end
|
||||
|
||||
def commit_url
|
||||
"#{project_url}/commit/#{sha}/builds"
|
||||
end
|
||||
|
||||
def commit_link
|
||||
"[#{Commit.truncate_sha(sha)}](#{commit_url})"
|
||||
end
|
||||
end
|
||||
end
|
|
@ -30,6 +30,7 @@ class Service < ActiveRecord::Base
|
|||
default_value_for :merge_requests_events, true
|
||||
default_value_for :tag_push_events, true
|
||||
default_value_for :note_events, true
|
||||
default_value_for :build_events, true
|
||||
|
||||
after_initialize :initialize_properties
|
||||
|
||||
|
@ -47,6 +48,7 @@ class Service < ActiveRecord::Base
|
|||
scope :issue_hooks, -> { where(issues_events: true, active: true) }
|
||||
scope :merge_request_hooks, -> { where(merge_requests_events: true, active: true) }
|
||||
scope :note_hooks, -> { where(note_events: true, active: true) }
|
||||
scope :build_hooks, -> { where(build_events: true, active: true) }
|
||||
|
||||
def activated?
|
||||
active
|
||||
|
@ -133,6 +135,21 @@ class Service < ActiveRecord::Base
|
|||
end
|
||||
end
|
||||
|
||||
# Provide convenient boolean accessor methods
|
||||
# for each serialized property.
|
||||
# Also keep track of updated properties in a similar way as ActiveModel::Dirty
|
||||
def self.boolean_accessor(*args)
|
||||
self.prop_accessor(*args)
|
||||
|
||||
args.each do |arg|
|
||||
class_eval %{
|
||||
def #{arg}?
|
||||
ActiveRecord::ConnectionAdapters::Column::TRUE_VALUES.include?(#{arg})
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
# Returns a hash of the properties that have been assigned a new value since last save,
|
||||
# indicating their original values (attr => original value).
|
||||
# ActiveRecord does not provide a mechanism to track changes in serialized keys,
|
||||
|
@ -163,6 +180,7 @@ class Service < ActiveRecord::Base
|
|||
assembla
|
||||
bamboo
|
||||
buildkite
|
||||
builds_email
|
||||
campfire
|
||||
custom_issue_tracker
|
||||
drone_ci
|
||||
|
|
|
@ -31,7 +31,8 @@ module Ci
|
|||
trigger_request: trigger_request,
|
||||
user: user)
|
||||
|
||||
commit.builds.create!(build_attrs)
|
||||
build = commit.builds.create!(build_attrs)
|
||||
build.execute_hooks
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -50,18 +50,8 @@
|
|||
= icon('retweet fw')
|
||||
%span
|
||||
Triggers
|
||||
= nav_link path: 'ci_web_hooks#index' do
|
||||
= link_to namespace_project_ci_web_hooks_path(@project.namespace, @project), title: 'CI Web Hooks' do
|
||||
= icon('link fw')
|
||||
%span
|
||||
CI Web Hooks
|
||||
= nav_link path: 'ci_settings#edit' do
|
||||
= link_to edit_namespace_project_ci_settings_path(@project.namespace, @project), title: 'CI Settings' do
|
||||
= icon('building fw')
|
||||
%span
|
||||
CI Settings
|
||||
= nav_link controller: 'ci_services' do
|
||||
= link_to namespace_project_ci_services_path(@project.namespace, @project), title: 'CI Services' do
|
||||
= icon('share fw')
|
||||
%span
|
||||
CI Services
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
- content_for :header do
|
||||
%h1{style: "background: #c40834; color: #FFF; font: normal 20px Helvetica, Arial, sans-serif; margin: 0; padding: 5px 10px; line-height: 32px; font-size: 16px;"}
|
||||
GitLab CI (build failed)
|
||||
GitLab (build failed)
|
||||
%h3
|
||||
Project:
|
||||
= link_to ci_project_url(@project) do
|
|
@ -1,6 +1,6 @@
|
|||
- content_for :header do
|
||||
%h1{style: "background: #38CF5B; color: #FFF; font: normal 20px Helvetica, Arial, sans-serif; margin: 0; padding: 5px 10px; line-height: 32px; font-size: 16px;"}
|
||||
GitLab CI (build successful)
|
||||
GitLab (build successful)
|
||||
|
||||
%h3
|
||||
Project:
|
|
@ -1,54 +0,0 @@
|
|||
%h3.page-title
|
||||
= @service.title
|
||||
= boolean_to_icon @service.activated?
|
||||
|
||||
%p= @service.description
|
||||
|
||||
|
||||
%hr
|
||||
|
||||
= form_for(@service, as: :service, url: namespace_project_ci_service_path(@project.namespace, @project, @service.to_param), method: :put, html: { class: 'form-horizontal' }) do |f|
|
||||
- if @service.errors.any?
|
||||
.alert.alert-danger
|
||||
%ul
|
||||
- @service.errors.full_messages.each do |msg|
|
||||
%li= msg
|
||||
|
||||
- if @service.help.present?
|
||||
.bs-callout
|
||||
= @service.help
|
||||
|
||||
.form-group
|
||||
= f.label :active, "Active", class: "control-label"
|
||||
.col-sm-10
|
||||
= f.check_box :active
|
||||
|
||||
- @service.fields.each do |field|
|
||||
- name = field[:name]
|
||||
- label = field[:label] || name
|
||||
- value = @service.send(name)
|
||||
- type = field[:type]
|
||||
- placeholder = field[:placeholder]
|
||||
- choices = field[:choices]
|
||||
- default_choice = field[:default_choice]
|
||||
- help = field[:help]
|
||||
|
||||
.form-group
|
||||
= f.label label, class: "control-label"
|
||||
.col-sm-10
|
||||
- if type == 'text'
|
||||
= f.text_field name, class: "form-control", placeholder: placeholder
|
||||
- elsif type == 'textarea'
|
||||
= f.text_area name, rows: 5, class: "form-control", placeholder: placeholder
|
||||
- elsif type == 'checkbox'
|
||||
= f.check_box name
|
||||
- elsif type == 'select'
|
||||
= f.select name, options_for_select(choices, value ? value : default_choice), {}, { class: "form-control" }
|
||||
- if help
|
||||
.light #{help}
|
||||
|
||||
.form-actions
|
||||
= f.submit 'Save', class: 'btn btn-save'
|
||||
|
||||
- if @service.valid? && @service.activated? && @service.can_test?
|
||||
= link_to 'Test settings', test_namespace_project_ci_service_path(@project.namespace, @project, @service.to_param), class: 'btn'
|
|
@ -1,2 +0,0 @@
|
|||
- page_title @service.title, "CI Services"
|
||||
= render 'form'
|
|
@ -1,23 +0,0 @@
|
|||
- page_title "CI Services"
|
||||
%h3.page-title Project services
|
||||
%p.light Project services allow you to integrate GitLab CI with other applications
|
||||
|
||||
%table.table
|
||||
%thead
|
||||
%tr
|
||||
%th
|
||||
%th Service
|
||||
%th Description
|
||||
%th Last edit
|
||||
- @services.sort_by(&:title).each do |service|
|
||||
%tr
|
||||
%td
|
||||
= boolean_to_icon service.activated?
|
||||
%td
|
||||
= link_to edit_namespace_project_ci_service_path(@project.namespace, @project, service.to_param) do
|
||||
%strong= service.title
|
||||
%td
|
||||
= service.description
|
||||
%td.light
|
||||
= time_ago_in_words service.updated_at
|
||||
ago
|
|
@ -1,94 +0,0 @@
|
|||
- page_title "CI Web Hooks"
|
||||
%h3.page-title
|
||||
CI Web hooks
|
||||
|
||||
%p.light
|
||||
Web Hooks can be used for binding events when build completed.
|
||||
|
||||
%hr.clearfix
|
||||
|
||||
= form_for @web_hook, url: namespace_project_ci_web_hooks_path(@project.namespace, @project), html: { class: 'form-horizontal' } do |f|
|
||||
-if @web_hook.errors.any?
|
||||
.alert.alert-danger
|
||||
- @web_hook.errors.full_messages.each do |msg|
|
||||
%p= msg
|
||||
.form-group
|
||||
= f.label :url, "URL", class: 'control-label'
|
||||
.col-sm-10
|
||||
= f.text_field :url, class: "form-control", placeholder: 'http://example.com/trigger-ci.json'
|
||||
.form-actions
|
||||
= f.submit "Add Web Hook", class: "btn btn-create"
|
||||
|
||||
-if @web_hooks.any?
|
||||
%h4 Activated web hooks (#{@web_hooks.count})
|
||||
.table-holder
|
||||
%table.table
|
||||
- @web_hooks.each do |hook|
|
||||
%tr
|
||||
%td
|
||||
.clearfix
|
||||
%span.monospace= hook.url
|
||||
%td
|
||||
.pull-right
|
||||
- if @ci_project.commits.any?
|
||||
= link_to 'Test Hook', test_namespace_project_ci_web_hook_path(@project.namespace, @project, hook), class: "btn btn-sm btn-grouped"
|
||||
= link_to 'Remove', namespace_project_ci_web_hook_path(@project.namespace, @project, hook), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-remove btn-sm btn-grouped"
|
||||
|
||||
%h4 Web Hook data example
|
||||
|
||||
:erb
|
||||
<pre>
|
||||
<code>
|
||||
{
|
||||
"build_id": 2,
|
||||
"build_name":"rspec_linux"
|
||||
"build_status": "failed",
|
||||
"build_started_at": "2014-05-05T18:01:02.563Z",
|
||||
"build_finished_at": "2014-05-05T18:01:07.611Z",
|
||||
"project_id": 1,
|
||||
"project_name": "Brightbox \/ Brightbox Cli",
|
||||
"gitlab_url": "http:\/\/localhost:3000\/brightbox\/brightbox-cli",
|
||||
"ref": "master",
|
||||
"sha": "a26cf5de9ed9827746d4970872376b10d9325f40",
|
||||
"before_sha": "34f57f6ba3ed0c21c5e361bbb041c3591411176c",
|
||||
"push_data": {
|
||||
"before": "34f57f6ba3ed0c21c5e361bbb041c3591411176c",
|
||||
"after": "a26cf5de9ed9827746d4970872376b10d9325f40",
|
||||
"ref": "refs\/heads\/master",
|
||||
"user_id": 1,
|
||||
"user_name": "Administrator",
|
||||
"project_id": 5,
|
||||
"repository": {
|
||||
"name": "Brightbox Cli",
|
||||
"url": "dzaporozhets@localhost:brightbox\/brightbox-cli.git",
|
||||
"description": "Voluptatibus quae error consectetur voluptas dolores vel excepturi possimus.",
|
||||
"homepage": "http:\/\/localhost:3000\/brightbox\/brightbox-cli"
|
||||
},
|
||||
"commits": [
|
||||
{
|
||||
"id": "a26cf5de9ed9827746d4970872376b10d9325f40",
|
||||
"message": "Release v1.2.2",
|
||||
"timestamp": "2014-04-22T16:46:42+03:00",
|
||||
"url": "http:\/\/localhost:3000\/brightbox\/brightbox-cli\/commit\/a26cf5de9ed9827746d4970872376b10d9325f40",
|
||||
"author": {
|
||||
"name": "Paul Thornthwaite",
|
||||
"email": "tokengeek@gmail.com"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "34f57f6ba3ed0c21c5e361bbb041c3591411176c",
|
||||
"message": "Fix server user data update\n\nIncorrect condition was being used so Base64 encoding option was having\nopposite effect from desired.",
|
||||
"timestamp": "2014-04-11T18:17:26+03:00",
|
||||
"url": "http:\/\/localhost:3000\/brightbox\/brightbox-cli\/commit\/34f57f6ba3ed0c21c5e361bbb041c3591411176c",
|
||||
"author": {
|
||||
"name": "Paul Thornthwaite",
|
||||
"email": "tokengeek@gmail.com"
|
||||
}
|
||||
}
|
||||
],
|
||||
"total_commits_count": 2,
|
||||
"ci_yaml_file":"rspec_linux:\r\n script: ls\r\n"
|
||||
}
|
||||
}
|
||||
</code>
|
||||
</pre>
|
|
@ -55,6 +55,13 @@
|
|||
%strong Merge Request events
|
||||
%p.light
|
||||
This url will be triggered when a merge request is created
|
||||
%div
|
||||
= f.check_box :build_events, class: 'pull-left'
|
||||
.prepend-left-20
|
||||
= f.label :build_events, class: 'list-label' do
|
||||
%strong Build events
|
||||
%p.light
|
||||
This url will be triggered when the build status changes
|
||||
.form-group
|
||||
= f.label :enable_ssl_verification, "SSL verification", class: 'control-label checkbox'
|
||||
.col-sm-10
|
||||
|
@ -78,7 +85,7 @@
|
|||
.clearfix
|
||||
%span.monospace= hook.url
|
||||
%p
|
||||
- %w(push_events tag_push_events issues_events note_events merge_requests_events).each do |trigger|
|
||||
- %w(push_events tag_push_events issues_events note_events merge_requests_events build_events).each do |trigger|
|
||||
- if hook.send(trigger)
|
||||
%span.label.label-gray= trigger.titleize
|
||||
SSL Verification: #{hook.enable_ssl_verification ? "enabled" : "disabled"}
|
||||
|
|
|
@ -59,6 +59,15 @@
|
|||
%strong Merge Request events
|
||||
%p.light
|
||||
This url will be triggered when a merge request is created
|
||||
- if @service.supported_events.include?("build")
|
||||
%div
|
||||
= form.check_box :build_events, class: 'pull-left'
|
||||
.prepend-left-20
|
||||
= form.label :build_events, class: 'list-label' do
|
||||
%strong Build events
|
||||
%p.light
|
||||
This url will be triggered when a build status changes
|
||||
|
||||
|
||||
- @service.fields.each do |field|
|
||||
- type = field[:type]
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
class BuildEmailWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
def perform(build_id, recipients, push_data)
|
||||
recipients.split(' ').each do |recipient|
|
||||
begin
|
||||
case push_data['build_status']
|
||||
when 'success'
|
||||
Notify.build_success_email(build_id, recipient).deliver_now
|
||||
when 'failed'
|
||||
Notify.build_fail_email(build_id, recipient).deliver_now
|
||||
end
|
||||
# These are input errors and won't be corrected even if Sidekiq retries
|
||||
rescue Net::SMTPFatalError, Net::SMTPSyntaxError => e
|
||||
logger.info("Failed to send e-mail for project '#{push_data['project_name']}' to #{recipient}: #{e}")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,19 +0,0 @@
|
|||
module Ci
|
||||
class HipChatNotifierWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
def perform(message, options={})
|
||||
room = options.delete('room')
|
||||
token = options.delete('token')
|
||||
server = options.delete('server')
|
||||
name = options.delete('service_name')
|
||||
client_opts = {
|
||||
api_version: 'v2',
|
||||
server_url: server
|
||||
}
|
||||
|
||||
client = HipChat::Client.new(token, client_opts)
|
||||
client[room].send(name, message, options.symbolize_keys)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,10 +0,0 @@
|
|||
module Ci
|
||||
class SlackNotifierWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
def perform(webhook_url, message, options={})
|
||||
notifier = Slack::Notifier.new(webhook_url)
|
||||
notifier.ping(message, options)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,9 +0,0 @@
|
|||
module Ci
|
||||
class WebHookWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
def perform(hook_id, data)
|
||||
Ci::WebHook.find(hook_id).execute data
|
||||
end
|
||||
end
|
||||
end
|
|
@ -596,17 +596,6 @@ Rails.application.routes.draw do
|
|||
resource :variables, only: [:show, :update]
|
||||
resources :triggers, only: [:index, :create, :destroy]
|
||||
resource :ci_settings, only: [:edit, :update, :destroy]
|
||||
resources :ci_web_hooks, only: [:index, :create, :destroy] do
|
||||
member do
|
||||
get :test
|
||||
end
|
||||
end
|
||||
|
||||
resources :ci_services, constraints: { id: /[^\/]+/ }, only: [:index, :edit, :update] do
|
||||
member do
|
||||
get :test
|
||||
end
|
||||
end
|
||||
|
||||
resources :builds, only: [:index, :show] do
|
||||
collection do
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
class AddBuildEventsToServices < ActiveRecord::Migration
|
||||
def up
|
||||
add_column :services, :build_events, :boolean, default: false, null: false
|
||||
add_column :web_hooks, :build_events, :boolean, default: false, null: false
|
||||
end
|
||||
end
|
|
@ -11,7 +11,7 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 20151203162133) do
|
||||
ActiveRecord::Schema.define(version: 20151203162134) do
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "plpgsql"
|
||||
|
@ -706,6 +706,7 @@ ActiveRecord::Schema.define(version: 20151203162133) do
|
|||
t.boolean "merge_requests_events", default: true
|
||||
t.boolean "tag_push_events", default: true
|
||||
t.boolean "note_events", default: true, null: false
|
||||
t.boolean "build_events", default: false, null: false
|
||||
end
|
||||
|
||||
add_index "services", ["created_at", "id"], name: "index_services_on_created_at_and_id", using: :btree
|
||||
|
@ -854,6 +855,7 @@ ActiveRecord::Schema.define(version: 20151203162133) do
|
|||
t.boolean "tag_push_events", default: false
|
||||
t.boolean "note_events", default: false, null: false
|
||||
t.boolean "enable_ssl_verification", default: true
|
||||
t.boolean "build_events", default: false, null: false
|
||||
end
|
||||
|
||||
add_index "web_hooks", ["created_at", "id"], name: "index_web_hooks_on_created_at_and_id", using: :btree
|
||||
|
|
|
@ -55,11 +55,11 @@ Feature: Project Services
|
|||
And I fill Pushover settings
|
||||
Then I should see Pushover service settings saved
|
||||
|
||||
Scenario: Activate email on push service
|
||||
Scenario: Activate email service
|
||||
When I visit project "Shop" services page
|
||||
And I click email on push service link
|
||||
And I fill email on push settings
|
||||
Then I should see email on push service settings saved
|
||||
And I click email service link
|
||||
And I fill email settings
|
||||
Then I should see email service settings saved
|
||||
|
||||
Scenario: Activate Irker (IRC Gateway) service
|
||||
When I visit project "Shop" services page
|
||||
|
|
|
@ -32,6 +32,7 @@ class Spinach::Features::AdminSettings < Spinach::FeatureSteps
|
|||
page.check('Comments')
|
||||
page.check('Issues events')
|
||||
page.check('Merge Request events')
|
||||
page.check('Build events')
|
||||
click_on 'Save'
|
||||
end
|
||||
|
||||
|
@ -39,6 +40,7 @@ class Spinach::Features::AdminSettings < Spinach::FeatureSteps
|
|||
fill_in 'Webhook', with: 'http://localhost'
|
||||
fill_in 'Username', with: 'test_user'
|
||||
fill_in 'Channel', with: '#test_channel'
|
||||
page.check('Notify only broken builds')
|
||||
end
|
||||
|
||||
step 'I should see service template settings saved' do
|
||||
|
|
|
@ -118,16 +118,16 @@ class Spinach::Features::ProjectServices < Spinach::FeatureSteps
|
|||
expect(find_field('Restrict to branch').value).to eq 'master'
|
||||
end
|
||||
|
||||
step 'I click email on push service link' do
|
||||
click_link 'Emails on push'
|
||||
step 'I click email service link' do
|
||||
click_link 'Emails'
|
||||
end
|
||||
|
||||
step 'I fill email on push settings' do
|
||||
step 'I fill email settings' do
|
||||
fill_in 'Recipients', with: 'qa@company.name'
|
||||
click_button 'Save'
|
||||
end
|
||||
|
||||
step 'I should see email on push service settings saved' do
|
||||
step 'I should see email service settings saved' do
|
||||
expect(find_field('Recipients').value).to eq 'qa@company.name'
|
||||
end
|
||||
|
||||
|
|
|
@ -45,7 +45,8 @@ module API
|
|||
|
||||
class ProjectHook < Hook
|
||||
expose :project_id, :push_events
|
||||
expose :issues_events, :merge_requests_events, :tag_push_events, :note_events, :enable_ssl_verification
|
||||
expose :issues_events, :merge_requests_events, :tag_push_events, :note_events, :build_events
|
||||
expose :enable_ssl_verification
|
||||
end
|
||||
|
||||
class ForkedFromProject < Grape::Entity
|
||||
|
@ -252,7 +253,7 @@ module API
|
|||
|
||||
class ProjectService < Grape::Entity
|
||||
expose :id, :title, :created_at, :updated_at, :active
|
||||
expose :push_events, :issues_events, :merge_requests_events, :tag_push_events, :note_events
|
||||
expose :push_events, :issues_events, :merge_requests_events, :tag_push_events, :note_events, :build_events
|
||||
# Expose serialized properties
|
||||
expose :properties do |service, options|
|
||||
field_names = service.fields.
|
||||
|
|
|
@ -45,6 +45,7 @@ module API
|
|||
:merge_requests_events,
|
||||
:tag_push_events,
|
||||
:note_events,
|
||||
:build_events,
|
||||
:enable_ssl_verification
|
||||
]
|
||||
@hook = user_project.hooks.new(attrs)
|
||||
|
@ -77,6 +78,7 @@ module API
|
|||
:merge_requests_events,
|
||||
:tag_push_events,
|
||||
:note_events,
|
||||
:build_events,
|
||||
:enable_ssl_verification
|
||||
]
|
||||
|
||||
|
|
|
@ -5,30 +5,6 @@ module Ci
|
|||
before { authenticate! }
|
||||
|
||||
resource :projects do
|
||||
# Register new webhook for project
|
||||
#
|
||||
# Parameters
|
||||
# project_id (required) - The ID of a project
|
||||
# web_hook (required) - WebHook URL
|
||||
# Example Request
|
||||
# POST /projects/:project_id/webhooks
|
||||
post ":project_id/webhooks" do
|
||||
required_attributes! [:web_hook]
|
||||
|
||||
project = Ci::Project.find(params[:project_id])
|
||||
|
||||
unauthorized! unless can?(current_user, :admin_project, project.gl_project)
|
||||
|
||||
web_hook = project.web_hooks.new({ url: params[:web_hook] })
|
||||
|
||||
if web_hook.save
|
||||
present web_hook, with: Entities::WebHook
|
||||
else
|
||||
errors = web_hook.errors.full_messages.join(", ")
|
||||
render_api_error!(errors, 400)
|
||||
end
|
||||
end
|
||||
|
||||
# Retrieve all Gitlab CI projects that the user has access to
|
||||
#
|
||||
# Example Request:
|
||||
|
@ -121,20 +97,6 @@ module Ci
|
|||
end
|
||||
end
|
||||
|
||||
# Remove a Gitlab CI project
|
||||
#
|
||||
# Parameters:
|
||||
# id (required) - The ID of a project
|
||||
# Example Request:
|
||||
# DELETE /projects/:id
|
||||
delete ":id" do
|
||||
project = Ci::Project.find(params[:id])
|
||||
|
||||
unauthorized! unless can?(current_user, :admin_project, project.gl_project)
|
||||
|
||||
project.destroy
|
||||
end
|
||||
|
||||
# Link a Gitlab CI project to a runner
|
||||
#
|
||||
# Parameters:
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
module Gitlab
|
||||
class BuildDataBuilder
|
||||
class << self
|
||||
def build(build)
|
||||
project = build.gl_project
|
||||
commit = build.commit
|
||||
user = build.user
|
||||
|
||||
data = {
|
||||
object_kind: 'build',
|
||||
|
||||
ref: build.ref,
|
||||
tag: build.tag,
|
||||
before_sha: build.before_sha,
|
||||
sha: build.sha,
|
||||
|
||||
# TODO: should this be not prefixed with build_?
|
||||
# Leaving this way to have backward compatibility
|
||||
build_id: build.id,
|
||||
build_name: build.name,
|
||||
build_stage: build.stage,
|
||||
build_status: build.status,
|
||||
build_started_at: build.started_at,
|
||||
build_finished_at: build.finished_at,
|
||||
build_duration: build.duration,
|
||||
|
||||
# TODO: do we still need it?
|
||||
project_id: project.id,
|
||||
project_name: project.name_with_namespace,
|
||||
|
||||
user: {
|
||||
id: user.try(:id),
|
||||
name: user.try(:name),
|
||||
email: user.try(:email),
|
||||
},
|
||||
|
||||
commit: {
|
||||
id: commit.id,
|
||||
sha: commit.sha,
|
||||
message: commit.git_commit_message,
|
||||
author_name: commit.git_author_name,
|
||||
author_email: commit.git_author_email,
|
||||
status: commit.status,
|
||||
duration: commit.duration,
|
||||
started_at: commit.started_at,
|
||||
finished_at: commit.finished_at,
|
||||
},
|
||||
|
||||
repository: {
|
||||
name: project.name,
|
||||
url: project.url_to_repo,
|
||||
description: project.description,
|
||||
homepage: project.web_url,
|
||||
git_http_url: project.http_url_to_repo,
|
||||
git_ssh_url: project.ssh_url_to_repo,
|
||||
visibility_level: project.visibility_level
|
||||
},
|
||||
}
|
||||
|
||||
data
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,6 +0,0 @@
|
|||
FactoryGirl.define do
|
||||
factory :ci_web_hook, class: Ci::WebHook do
|
||||
sequence(:url) { FFaker::Internet.uri('http') }
|
||||
project factory: :ci_project
|
||||
end
|
||||
end
|
|
@ -1,27 +0,0 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe 'CI web hooks' do
|
||||
let(:user) { create(:user) }
|
||||
before { login_as(user) }
|
||||
|
||||
before do
|
||||
@project = FactoryGirl.create :ci_project
|
||||
@gl_project = @project.gl_project
|
||||
@gl_project.team << [user, :master]
|
||||
visit namespace_project_ci_web_hooks_path(@gl_project.namespace, @gl_project)
|
||||
end
|
||||
|
||||
context 'create a trigger' do
|
||||
before do
|
||||
fill_in 'web_hook_url', with: 'http://example.com'
|
||||
click_on 'Add Web Hook'
|
||||
end
|
||||
|
||||
it { expect(@project.web_hooks.count).to eq(1) }
|
||||
|
||||
it 'revokes the trigger' do
|
||||
click_on 'Remove'
|
||||
expect(@project.web_hooks.count).to eq(0)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,20 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe 'Gitlab::BuildDataBuilder' do
|
||||
let(:build) { create(:ci_build) }
|
||||
|
||||
describe :build do
|
||||
let(:data) do
|
||||
Gitlab::BuildDataBuilder.build(build)
|
||||
end
|
||||
|
||||
it { expect(data).to be_a(Hash) }
|
||||
it { expect(data[:ref]).to eq(build.ref) }
|
||||
it { expect(data[:sha]).to eq(build.sha) }
|
||||
it { expect(data[:tag]).to eq(build.tag) }
|
||||
it { expect(data[:build_id]).to eq(build.id) }
|
||||
it { expect(data[:build_status]).to eq(build.status) }
|
||||
it { expect(data[:project_id]).to eq(build.gl_project.id) }
|
||||
it { expect(data[:project_name]).to eq(build.gl_project.name_with_namespace) }
|
||||
end
|
||||
end
|
|
@ -1,35 +0,0 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Ci::Notify do
|
||||
include EmailSpec::Helpers
|
||||
include EmailSpec::Matchers
|
||||
|
||||
before do
|
||||
@commit = FactoryGirl.create :ci_commit
|
||||
@build = FactoryGirl.create :ci_build, commit: @commit
|
||||
end
|
||||
|
||||
describe 'build success' do
|
||||
subject { Ci::Notify.build_success_email(@build.id, 'wow@example.com') }
|
||||
|
||||
it 'has the correct subject' do
|
||||
should have_subject /Build success for/
|
||||
end
|
||||
|
||||
it 'contains name of project' do
|
||||
should have_body_text /build successful/
|
||||
end
|
||||
end
|
||||
|
||||
describe 'build fail' do
|
||||
subject { Ci::Notify.build_fail_email(@build.id, 'wow@example.com') }
|
||||
|
||||
it 'has the correct subject' do
|
||||
should have_subject /Build failed for/
|
||||
end
|
||||
|
||||
it 'contains name of project' do
|
||||
should have_body_text /build failed/
|
||||
end
|
||||
end
|
||||
end
|
|
@ -13,6 +13,7 @@ describe Notify do
|
|||
let(:gitlab_sender_reply_to) { Gitlab.config.gitlab.email_reply_to }
|
||||
let(:recipient) { create(:user, email: 'recipient@example.com') }
|
||||
let(:project) { create(:project) }
|
||||
let(:build) { create(:ci_build) }
|
||||
|
||||
before(:each) do
|
||||
ActionMailer::Base.deliveries.clear
|
||||
|
@ -865,4 +866,32 @@ describe Notify do
|
|||
is_expected.to have_body_text /#{diff_path}/
|
||||
end
|
||||
end
|
||||
|
||||
describe 'build success' do
|
||||
before { build.success }
|
||||
|
||||
subject { Notify.build_success_email(build.id, 'wow@example.com') }
|
||||
|
||||
it 'has the correct subject' do
|
||||
should have_subject /Build success for/
|
||||
end
|
||||
|
||||
it 'contains name of project' do
|
||||
should have_body_text build.project_name
|
||||
end
|
||||
end
|
||||
|
||||
describe 'build fail' do
|
||||
before { build.drop }
|
||||
|
||||
subject { Notify.build_fail_email(build.id, 'wow@example.com') }
|
||||
|
||||
it 'has the correct subject' do
|
||||
should have_subject /Build failed for/
|
||||
end
|
||||
|
||||
it 'contains name of project' do
|
||||
should have_body_text build.project_name
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Ci::HipChatMessage, models: true do
|
||||
subject { Ci::HipChatMessage.new(build) }
|
||||
|
||||
let(:commit) { FactoryGirl.create(:ci_commit_with_two_jobs) }
|
||||
|
||||
let(:build) do
|
||||
commit.builds.first
|
||||
end
|
||||
|
||||
context 'when all matrix builds succeed' do
|
||||
it 'returns a successful message' do
|
||||
commit.create_builds('master', false, nil)
|
||||
commit.builds.update_all(status: "success")
|
||||
commit.reload
|
||||
|
||||
expect(subject.status_color).to eq 'green'
|
||||
expect(subject.notify?).to be_falsey
|
||||
expect(subject.to_s).to match(/Commit #\d+/)
|
||||
expect(subject.to_s).to match(/Successful in \d+ second\(s\)\./)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when at least one matrix build fails' do
|
||||
it 'returns a failure message' do
|
||||
commit.create_builds('master', false, nil)
|
||||
first_build = commit.builds.first
|
||||
second_build = commit.builds.last
|
||||
first_build.update(status: "success")
|
||||
second_build.update(status: "failed")
|
||||
|
||||
expect(subject.status_color).to eq 'red'
|
||||
expect(subject.notify?).to be_truthy
|
||||
expect(subject.to_s).to match(/Commit #\d+/)
|
||||
expect(subject.to_s).to match(/Failed in \d+ second\(s\)\./)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,73 +0,0 @@
|
|||
# == Schema Information
|
||||
#
|
||||
# Table name: services
|
||||
#
|
||||
# id :integer not null, primary key
|
||||
# type :string(255)
|
||||
# title :string(255)
|
||||
# project_id :integer not null
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# active :boolean default(FALSE), not null
|
||||
# properties :text
|
||||
#
|
||||
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Ci::HipChatService, models: true do
|
||||
|
||||
describe "Validations" do
|
||||
|
||||
context "active" do
|
||||
before do
|
||||
subject.active = true
|
||||
end
|
||||
|
||||
it { is_expected.to validate_presence_of :hipchat_room }
|
||||
it { is_expected.to validate_presence_of :hipchat_token }
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
describe "Execute" do
|
||||
|
||||
let(:service) { Ci::HipChatService.new }
|
||||
let(:commit) { FactoryGirl.create :ci_commit }
|
||||
let(:build) { FactoryGirl.create :ci_build, commit: commit, status: 'failed' }
|
||||
let(:api_url) { 'https://api.hipchat.com/v2/room/123/notification?auth_token=a1b2c3d4e5f6' }
|
||||
|
||||
before do
|
||||
allow(service).to receive_messages(
|
||||
project: commit.project,
|
||||
project_id: commit.project_id,
|
||||
notify_only_broken_builds: false,
|
||||
hipchat_room: 123,
|
||||
hipchat_token: 'a1b2c3d4e5f6'
|
||||
)
|
||||
|
||||
WebMock.stub_request(:post, api_url)
|
||||
end
|
||||
|
||||
|
||||
it "should call the HipChat API" do
|
||||
service.execute(build)
|
||||
Ci::HipChatNotifierWorker.drain
|
||||
|
||||
expect( WebMock ).to have_requested(:post, api_url).once
|
||||
end
|
||||
|
||||
it "calls the worker with expected arguments" do
|
||||
expect( Ci::HipChatNotifierWorker ).to receive(:perform_async) \
|
||||
.with(an_instance_of(String), hash_including(
|
||||
token: 'a1b2c3d4e5f6',
|
||||
room: 123,
|
||||
server: 'https://api.hipchat.com',
|
||||
color: 'red',
|
||||
notify: true
|
||||
))
|
||||
|
||||
service.execute(build)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,177 +0,0 @@
|
|||
# == Schema Information
|
||||
#
|
||||
# Table name: services
|
||||
#
|
||||
# id :integer not null, primary key
|
||||
# type :string(255)
|
||||
# title :string(255)
|
||||
# project_id :integer not null
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# active :boolean default(FALSE), not null
|
||||
# properties :text
|
||||
#
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Ci::MailService, models: true do
|
||||
describe "Associations" do
|
||||
it { is_expected.to belong_to :project }
|
||||
end
|
||||
|
||||
describe "Validations" do
|
||||
context "active" do
|
||||
before do
|
||||
subject.active = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'Sends email for' do
|
||||
let(:mail) { Ci::MailService.new }
|
||||
let(:user) { User.new(notification_email: 'git@example.com')}
|
||||
|
||||
describe 'failed build' do
|
||||
let(:project) { FactoryGirl.create(:ci_project, email_add_pusher: true) }
|
||||
let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) }
|
||||
let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) }
|
||||
let(:build) { FactoryGirl.create(:ci_build, status: 'failed', commit: commit, user: user) }
|
||||
|
||||
before do
|
||||
allow(mail).to receive_messages(
|
||||
project: project
|
||||
)
|
||||
end
|
||||
|
||||
it do
|
||||
perform_enqueued_jobs do
|
||||
expect{ mail.execute(build) }.to change{ ActionMailer::Base.deliveries.size }.by(1)
|
||||
expect(ActionMailer::Base.deliveries.last.to).to eq(["git@example.com"])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'successfull build' do
|
||||
let(:project) { FactoryGirl.create(:ci_project, email_add_pusher: true, email_only_broken_builds: false) }
|
||||
let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) }
|
||||
let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) }
|
||||
let(:build) { FactoryGirl.create(:ci_build, status: 'success', commit: commit, user: user) }
|
||||
|
||||
before do
|
||||
allow(mail).to receive_messages(
|
||||
project: project
|
||||
)
|
||||
end
|
||||
|
||||
it do
|
||||
perform_enqueued_jobs do
|
||||
expect{ mail.execute(build) }.to change{ ActionMailer::Base.deliveries.size }.by(1)
|
||||
expect(ActionMailer::Base.deliveries.last.to).to eq(["git@example.com"])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'successfull build and project has email_recipients' do
|
||||
let(:project) do
|
||||
FactoryGirl.create(:ci_project,
|
||||
email_add_pusher: true,
|
||||
email_only_broken_builds: false,
|
||||
email_recipients: "jeroen@example.com")
|
||||
end
|
||||
let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) }
|
||||
let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) }
|
||||
let(:build) { FactoryGirl.create(:ci_build, status: 'success', commit: commit, user: user) }
|
||||
|
||||
before do
|
||||
allow(mail).to receive_messages(
|
||||
project: project
|
||||
)
|
||||
end
|
||||
|
||||
it do
|
||||
perform_enqueued_jobs do
|
||||
expect{ mail.execute(build) }.to change{ ActionMailer::Base.deliveries.size }.by(2)
|
||||
expect(
|
||||
ActionMailer::Base.deliveries.map(&:to).flatten
|
||||
).to include("git@example.com", "jeroen@example.com")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'successful build and notify only broken builds' do
|
||||
let(:project) do
|
||||
FactoryGirl.create(:ci_project,
|
||||
email_add_pusher: true,
|
||||
email_only_broken_builds: true,
|
||||
email_recipients: "jeroen@example.com")
|
||||
end
|
||||
let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) }
|
||||
let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) }
|
||||
let(:build) { FactoryGirl.create(:ci_build, status: 'success', commit: commit, user: user) }
|
||||
|
||||
before do
|
||||
allow(mail).to receive_messages(
|
||||
project: project
|
||||
)
|
||||
end
|
||||
|
||||
it do
|
||||
perform_enqueued_jobs do
|
||||
expect do
|
||||
mail.execute(build) if mail.can_execute?(build)
|
||||
end.to_not change{ ActionMailer::Base.deliveries.size }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'successful build and can test service' do
|
||||
let(:project) do
|
||||
FactoryGirl.create(:ci_project,
|
||||
email_add_pusher: true,
|
||||
email_only_broken_builds: false,
|
||||
email_recipients: "jeroen@example.com")
|
||||
end
|
||||
let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) }
|
||||
let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) }
|
||||
let(:build) { FactoryGirl.create(:ci_build, status: 'success', commit: commit, user: user) }
|
||||
|
||||
before do
|
||||
allow(mail).to receive_messages(
|
||||
project: project
|
||||
)
|
||||
build
|
||||
end
|
||||
|
||||
it do
|
||||
expect(mail.can_test?).to eq(true)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'retried build should not receive email' do
|
||||
let(:project) do
|
||||
FactoryGirl.create(:ci_project,
|
||||
email_add_pusher: true,
|
||||
email_only_broken_builds: true,
|
||||
email_recipients: "jeroen@example.com")
|
||||
end
|
||||
let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) }
|
||||
let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) }
|
||||
let(:build) { FactoryGirl.create(:ci_build, status: 'failed', commit: commit, user: user) }
|
||||
|
||||
before do
|
||||
allow(mail).to receive_messages(
|
||||
project: project
|
||||
)
|
||||
end
|
||||
|
||||
it do
|
||||
Ci::Build.retry(build)
|
||||
perform_enqueued_jobs do
|
||||
expect do
|
||||
mail.execute(build) if mail.can_execute?(build)
|
||||
end.to_not change{ ActionMailer::Base.deliveries.size }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,43 +0,0 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Ci::SlackMessage, models: true do
|
||||
subject { Ci::SlackMessage.new(commit) }
|
||||
|
||||
let(:commit) { FactoryGirl.create(:ci_commit_with_two_jobs) }
|
||||
|
||||
context 'when all matrix builds succeeded' do
|
||||
let(:color) { 'good' }
|
||||
|
||||
it 'returns a message with success' do
|
||||
commit.create_builds('master', false, nil)
|
||||
commit.builds.update_all(status: "success")
|
||||
commit.reload
|
||||
|
||||
expect(subject.color).to eq(color)
|
||||
expect(subject.fallback).to include('Commit')
|
||||
expect(subject.fallback).to include("\##{commit.id}")
|
||||
expect(subject.fallback).to include('succeeded')
|
||||
expect(subject.attachments.first[:fields]).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
context 'when one of matrix builds failed' do
|
||||
let(:color) { 'danger' }
|
||||
|
||||
it 'returns a message with information about failed build' do
|
||||
commit.create_builds('master', false, nil)
|
||||
first_build = commit.builds.first
|
||||
second_build = commit.builds.last
|
||||
first_build.update(status: "success")
|
||||
second_build.update(status: "failed")
|
||||
|
||||
expect(subject.color).to eq(color)
|
||||
expect(subject.fallback).to include('Commit')
|
||||
expect(subject.fallback).to include("\##{commit.id}")
|
||||
expect(subject.fallback).to include('failed')
|
||||
expect(subject.attachments.first[:fields].size).to eq(1)
|
||||
expect(subject.attachments.first[:fields].first[:title]).to eq(second_build.name)
|
||||
expect(subject.attachments.first[:fields].first[:value]).to include("\##{second_build.id}")
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,57 +0,0 @@
|
|||
# == Schema Information
|
||||
#
|
||||
# Table name: services
|
||||
#
|
||||
# id :integer not null, primary key
|
||||
# type :string(255)
|
||||
# title :string(255)
|
||||
# project_id :integer not null
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# active :boolean default(FALSE), not null
|
||||
# properties :text
|
||||
#
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Ci::SlackService, models: true do
|
||||
describe "Associations" do
|
||||
it { is_expected.to belong_to :project }
|
||||
end
|
||||
|
||||
describe "Validations" do
|
||||
context "active" do
|
||||
before do
|
||||
subject.active = true
|
||||
end
|
||||
|
||||
it { is_expected.to validate_presence_of :webhook }
|
||||
end
|
||||
end
|
||||
|
||||
describe "Execute" do
|
||||
let(:slack) { Ci::SlackService.new }
|
||||
let(:commit) { FactoryGirl.create :ci_commit }
|
||||
let(:build) { FactoryGirl.create :ci_build, commit: commit, status: 'failed' }
|
||||
let(:webhook_url) { 'https://hooks.slack.com/services/SVRWFV0VVAR97N/B02R25XN3/ZBqu7xMupaEEICInN685' }
|
||||
let(:notify_only_broken_builds) { false }
|
||||
|
||||
before do
|
||||
allow(slack).to receive_messages(
|
||||
project: commit.project,
|
||||
project_id: commit.project_id,
|
||||
webhook: webhook_url,
|
||||
notify_only_broken_builds: notify_only_broken_builds
|
||||
)
|
||||
|
||||
WebMock.stub_request(:post, webhook_url)
|
||||
end
|
||||
|
||||
it "should call Slack API" do
|
||||
slack.execute(build)
|
||||
Ci::SlackNotifierWorker.drain
|
||||
|
||||
expect(WebMock).to have_requested(:post, webhook_url).once
|
||||
end
|
||||
end
|
||||
end
|
|
@ -34,11 +34,9 @@ describe Ci::Project, models: true do
|
|||
|
||||
it { is_expected.to have_many(:runner_projects) }
|
||||
it { is_expected.to have_many(:runners) }
|
||||
it { is_expected.to have_many(:web_hooks) }
|
||||
it { is_expected.to have_many(:events) }
|
||||
it { is_expected.to have_many(:variables) }
|
||||
it { is_expected.to have_many(:triggers) }
|
||||
it { is_expected.to have_many(:services) }
|
||||
|
||||
it { is_expected.to validate_presence_of :timeout }
|
||||
it { is_expected.to validate_presence_of :gitlab_id }
|
||||
|
|
|
@ -1,48 +0,0 @@
|
|||
# == Schema Information
|
||||
#
|
||||
# Table name: ci_services
|
||||
#
|
||||
# id :integer not null, primary key
|
||||
# type :string(255)
|
||||
# title :string(255)
|
||||
# project_id :integer not null
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# active :boolean default(FALSE), not null
|
||||
# properties :text
|
||||
#
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Ci::Service, models: true do
|
||||
|
||||
describe "Associations" do
|
||||
it { is_expected.to belong_to :project }
|
||||
end
|
||||
|
||||
describe "Mass assignment" do
|
||||
end
|
||||
|
||||
describe "Test Button" do
|
||||
before do
|
||||
@service = Ci::Service.new
|
||||
end
|
||||
|
||||
describe "Testable" do
|
||||
let(:commit) { FactoryGirl.create :ci_commit }
|
||||
let(:build) { FactoryGirl.create :ci_build, commit: commit }
|
||||
|
||||
before do
|
||||
allow(@service).to receive_messages(
|
||||
project: commit.project
|
||||
)
|
||||
build
|
||||
@testable = @service.can_test?
|
||||
end
|
||||
|
||||
describe :can_test do
|
||||
it { expect(@testable).to eq(true) }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,63 +0,0 @@
|
|||
# == Schema Information
|
||||
#
|
||||
# Table name: ci_web_hooks
|
||||
#
|
||||
# id :integer not null, primary key
|
||||
# url :string(255) not null
|
||||
# project_id :integer not null
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
#
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Ci::WebHook, models: true do
|
||||
describe "Associations" do
|
||||
it { is_expected.to belong_to :project }
|
||||
end
|
||||
|
||||
describe "Validations" do
|
||||
it { is_expected.to validate_presence_of(:url) }
|
||||
|
||||
context "url format" do
|
||||
it { is_expected.to allow_value("http://example.com").for(:url) }
|
||||
it { is_expected.to allow_value("https://excample.com").for(:url) }
|
||||
it { is_expected.to allow_value("http://test.com/api").for(:url) }
|
||||
it { is_expected.to allow_value("http://test.com/api?key=abc").for(:url) }
|
||||
it { is_expected.to allow_value("http://test.com/api?key=abc&type=def").for(:url) }
|
||||
|
||||
it { is_expected.not_to allow_value("example.com").for(:url) }
|
||||
it { is_expected.not_to allow_value("ftp://example.com").for(:url) }
|
||||
it { is_expected.not_to allow_value("herp-and-derp").for(:url) }
|
||||
end
|
||||
end
|
||||
|
||||
describe "execute" do
|
||||
before(:each) do
|
||||
@web_hook = FactoryGirl.create(:ci_web_hook)
|
||||
@project = @web_hook.project
|
||||
@data = { before: 'oldrev', after: 'newrev', ref: 'ref' }
|
||||
|
||||
WebMock.stub_request(:post, @web_hook.url)
|
||||
end
|
||||
|
||||
it "POSTs to the web hook URL" do
|
||||
@web_hook.execute(@data)
|
||||
expect(WebMock).to have_requested(:post, @web_hook.url).once
|
||||
end
|
||||
|
||||
it "POSTs the data as JSON" do
|
||||
json = @data.to_json
|
||||
|
||||
@web_hook.execute(@data)
|
||||
expect(WebMock).to have_requested(:post, @web_hook.url).with(body: json).once
|
||||
end
|
||||
|
||||
it "catches exceptions" do
|
||||
expect(Ci::WebHook).to receive(:post).and_raise("Some HTTP Post error")
|
||||
|
||||
expect{ @web_hook.execute(@data) }.
|
||||
to raise_error(RuntimeError, 'Some HTTP Post error')
|
||||
end
|
||||
end
|
||||
end
|
|
@ -247,6 +247,55 @@ describe HipchatService, models: true do
|
|||
end
|
||||
end
|
||||
|
||||
context 'build events' do
|
||||
let(:build) { create(:ci_build) }
|
||||
let(:data) { Gitlab::BuildDataBuilder.build(build) }
|
||||
|
||||
context 'for failed' do
|
||||
before { build.drop }
|
||||
|
||||
it "should call Hipchat API" do
|
||||
hipchat.execute(data)
|
||||
|
||||
expect(WebMock).to have_requested(:post, api_url).once
|
||||
end
|
||||
|
||||
it "should create a build message" do
|
||||
message = hipchat.send(:create_build_message, data)
|
||||
|
||||
project_url = project.web_url
|
||||
project_name = project.name_with_namespace.gsub(/\s/, '')
|
||||
sha = data[:sha]
|
||||
ref = data[:ref]
|
||||
ref_type = data[:tag] ? 'tag' : 'branch'
|
||||
duration = data[:commit][:duration]
|
||||
|
||||
expect(message).to eq("<a href=\"#{project_url}\">#{project_name}</a>: " \
|
||||
"Commit <a href=\"#{project_url}/commit/#{sha}/builds\">#{Commit.truncate_sha(sha)}</a> " \
|
||||
"of <a href=\"#{project_url}/commits/#{ref}\">#{ref}</a> #{ref_type} " \
|
||||
"by #{data[:commit][:author_name]} failed in #{duration} second(s)")
|
||||
end
|
||||
end
|
||||
|
||||
context 'for succeeded' do
|
||||
before do
|
||||
build.success
|
||||
end
|
||||
|
||||
it "should call Hipchat API" do
|
||||
hipchat.notify_only_broken_builds = false
|
||||
hipchat.execute(data)
|
||||
expect(WebMock).to have_requested(:post, api_url).once
|
||||
end
|
||||
|
||||
it "should notify only broken" do
|
||||
hipchat.notify_only_broken_builds = true
|
||||
hipchat.execute(data)
|
||||
expect(WebMock).to_not have_requested(:post, api_url).once
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "#message_options" do
|
||||
it "should be set to the defaults" do
|
||||
expect(hipchat.send(:message_options)).to eq({ notify: false, color: 'yellow' })
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe SlackService::BuildMessage do
|
||||
subject { SlackService::BuildMessage.new(args) }
|
||||
|
||||
let(:args) do
|
||||
{
|
||||
sha: '97de212e80737a608d939f648d959671fb0a0142',
|
||||
ref: 'develop',
|
||||
tag: false,
|
||||
|
||||
project_name: 'project_name',
|
||||
project_url: 'somewhere.com',
|
||||
|
||||
commit: {
|
||||
status: status,
|
||||
author_name: 'hacker',
|
||||
duration: 10,
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
context 'succeeded' do
|
||||
let(:status) { 'success' }
|
||||
let(:color) { 'good' }
|
||||
|
||||
it 'returns a message with information about succeeded build' do
|
||||
message = '<somewhere.com|project_name>: Commit <somewhere.com/commit/97de212e80737a608d939f648d959671fb0a0142/builds|97de212e> of <somewhere.com/commits/develop|develop> branch by hacker succeeded in 10 second(s)'
|
||||
expect(subject.pretext).to be_empty
|
||||
expect(subject.fallback).to eq(message)
|
||||
expect(subject.attachments).to eq([text: message, color: color])
|
||||
end
|
||||
end
|
||||
|
||||
context 'failed' do
|
||||
let(:status) { 'failed' }
|
||||
let(:color) { 'danger' }
|
||||
|
||||
it 'returns a message with information about failed build' do
|
||||
message = '<somewhere.com|project_name>: Commit <somewhere.com/commit/97de212e80737a608d939f648d959671fb0a0142/builds|97de212e> of <somewhere.com/commits/develop|develop> branch by hacker failed in 10 second(s)'
|
||||
expect(subject.pretext).to be_empty
|
||||
expect(subject.fallback).to eq(message)
|
||||
expect(subject.attachments).to eq([text: message, color: color])
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,11 +1,17 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe API::API, 'ProjectHooks', api: true do
|
||||
describe API::API, 'ProjectHooks', api: true do
|
||||
include ApiHelpers
|
||||
let(:user) { create(:user) }
|
||||
let(:user3) { create(:user) }
|
||||
let!(:project) { create(:project, creator_id: user.id, namespace: user.namespace) }
|
||||
let!(:hook) { create(:project_hook, project: project, url: "http://example.com", push_events: true, merge_requests_events: true, tag_push_events: true, issues_events: true, note_events: true, enable_ssl_verification: true) }
|
||||
let!(:hook) do
|
||||
create(:project_hook,
|
||||
project: project, url: "http://example.com",
|
||||
push_events: true, merge_requests_events: true, tag_push_events: true,
|
||||
issues_events: true, note_events: true, build_events: true,
|
||||
enable_ssl_verification: true)
|
||||
end
|
||||
|
||||
before do
|
||||
project.team << [user, :master]
|
||||
|
@ -26,6 +32,7 @@ describe API::API, 'ProjectHooks', api: true do
|
|||
expect(json_response.first['merge_requests_events']).to eq(true)
|
||||
expect(json_response.first['tag_push_events']).to eq(true)
|
||||
expect(json_response.first['note_events']).to eq(true)
|
||||
expect(json_response.first['build_events']).to eq(true)
|
||||
expect(json_response.first['enable_ssl_verification']).to eq(true)
|
||||
end
|
||||
end
|
||||
|
@ -83,6 +90,7 @@ describe API::API, 'ProjectHooks', api: true do
|
|||
expect(json_response['merge_requests_events']).to eq(false)
|
||||
expect(json_response['tag_push_events']).to eq(false)
|
||||
expect(json_response['note_events']).to eq(false)
|
||||
expect(json_response['build_events']).to eq(false)
|
||||
expect(json_response['enable_ssl_verification']).to eq(true)
|
||||
end
|
||||
|
||||
|
|
|
@ -58,57 +58,6 @@ describe Ci::API::API do
|
|||
end
|
||||
end
|
||||
|
||||
describe "POST /projects/:project_id/webhooks" do
|
||||
let!(:project) { FactoryGirl.create(:ci_project) }
|
||||
|
||||
context "Valid Webhook URL" do
|
||||
let!(:webhook) { { web_hook: "http://example.com/sth/1/ala_ma_kota" } }
|
||||
|
||||
before do
|
||||
options.merge!(webhook)
|
||||
end
|
||||
|
||||
it "should create webhook for specified project" do
|
||||
project.gl_project.team << [user, :master]
|
||||
post ci_api("/projects/#{project.id}/webhooks"), options
|
||||
expect(response.status).to eq(201)
|
||||
expect(json_response["url"]).to eq(webhook[:web_hook])
|
||||
end
|
||||
|
||||
it "fails to create webhook for non existsing project" do
|
||||
post ci_api("/projects/non-existant-id/webhooks"), options
|
||||
expect(response.status).to eq(404)
|
||||
end
|
||||
|
||||
it "non-manager is not authorized" do
|
||||
post ci_api("/projects/#{project.id}/webhooks"), options
|
||||
expect(response.status).to eq(401)
|
||||
end
|
||||
end
|
||||
|
||||
context "Invalid Webhook URL" do
|
||||
let!(:webhook) { { web_hook: "ala_ma_kota" } }
|
||||
|
||||
before do
|
||||
options.merge!(webhook)
|
||||
end
|
||||
|
||||
it "fails to create webhook for not valid url" do
|
||||
project.gl_project.team << [user, :master]
|
||||
post ci_api("/projects/#{project.id}/webhooks"), options
|
||||
expect(response.status).to eq(400)
|
||||
end
|
||||
end
|
||||
|
||||
context "Missed web_hook parameter" do
|
||||
it "fails to create webhook for not provided url" do
|
||||
project.gl_project.team << [user, :master]
|
||||
post ci_api("/projects/#{project.id}/webhooks"), options
|
||||
expect(response.status).to eq(400)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "GET /projects/:id" do
|
||||
let!(:project) { FactoryGirl.create(:ci_project) }
|
||||
|
||||
|
@ -158,28 +107,6 @@ describe Ci::API::API do
|
|||
end
|
||||
end
|
||||
|
||||
describe "DELETE /projects/:id" do
|
||||
let!(:project) { FactoryGirl.create(:ci_project) }
|
||||
|
||||
it "should delete a specific project" do
|
||||
project.gl_project.team << [user, :master]
|
||||
delete ci_api("/projects/#{project.id}"), options
|
||||
expect(response.status).to eq(200)
|
||||
expect { project.reload }.
|
||||
to raise_error(ActiveRecord::RecordNotFound)
|
||||
end
|
||||
|
||||
it "non-manager is not authorized" do
|
||||
delete ci_api("/projects/#{project.id}"), options
|
||||
expect(response.status).to eq(401)
|
||||
end
|
||||
|
||||
it "is getting not found error" do
|
||||
delete ci_api("/projects/not-existing_id"), options
|
||||
expect(response.status).to eq(404)
|
||||
end
|
||||
end
|
||||
|
||||
describe "POST /projects/:id/runners/:id" do
|
||||
let(:project) { FactoryGirl.create(:ci_project) }
|
||||
let(:runner) { FactoryGirl.create(:ci_runner) }
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Ci::WebHookService, services: true do
|
||||
let(:project) { FactoryGirl.create :ci_project }
|
||||
let(:gl_project) { FactoryGirl.create :empty_project, gitlab_ci_project: project }
|
||||
let(:commit) { FactoryGirl.create :ci_commit, gl_project: gl_project }
|
||||
let(:build) { FactoryGirl.create :ci_build, commit: commit }
|
||||
let(:hook) { FactoryGirl.create :ci_web_hook, project: project }
|
||||
|
||||
describe :execute do
|
||||
it "should execute successfully" do
|
||||
stub_request(:post, hook.url).to_return(status: 200)
|
||||
expect(Ci::WebHookService.new.build_end(build)).to be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
context 'build_data' do
|
||||
it "contains all needed fields" do
|
||||
expect(build_data(build)).to include(
|
||||
:build_id,
|
||||
:project_id,
|
||||
:ref,
|
||||
:build_status,
|
||||
:build_started_at,
|
||||
:build_finished_at,
|
||||
:before_sha,
|
||||
:project_name,
|
||||
:gitlab_url,
|
||||
:build_name
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def build_data(build)
|
||||
Ci::WebHookService.new.send :build_data, build
|
||||
end
|
||||
end
|
|
@ -0,0 +1,35 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe BuildEmailWorker do
|
||||
include RepoHelpers
|
||||
|
||||
let(:build) { create(:ci_build) }
|
||||
let(:user) { create(:user) }
|
||||
let(:data) { Gitlab::BuildDataBuilder.build(build) }
|
||||
|
||||
subject { BuildEmailWorker.new }
|
||||
|
||||
before do
|
||||
allow(build).to receive(:execute_hooks).and_return(false)
|
||||
build.success
|
||||
end
|
||||
|
||||
describe "#perform" do
|
||||
it "sends mail" do
|
||||
subject.perform(build.id, user.email, data.stringify_keys)
|
||||
|
||||
email = ActionMailer::Base.deliveries.last
|
||||
expect(email.subject).to include('Build success for')
|
||||
expect(email.to).to eq([user.email])
|
||||
end
|
||||
|
||||
it "gracefully handles an input SMTP error" do
|
||||
ActionMailer::Base.deliveries.clear
|
||||
allow(Notify).to receive(:build_success_email).and_raise(Net::SMTPFatalError)
|
||||
|
||||
subject.perform(build.id, user.email, data.stringify_keys)
|
||||
|
||||
expect(ActionMailer::Base.deliveries.count).to eq(0)
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue