summaryrefslogtreecommitdiff
path: root/app/models
diff options
context:
space:
mode:
authorGrzegorz Bizon <grzegorz.bizon@ntsn.pl>2015-12-14 11:46:02 +0100
committerGrzegorz Bizon <grzegorz.bizon@ntsn.pl>2015-12-14 12:26:40 +0100
commit2ec93abed7a1a3aa49b3267342824e0743de0f54 (patch)
tree7d3381dd4bd1f4d73ba234a56742edf491baa515 /app/models
parentbc1cfd384f4f972ad887b0501280b9f94d4c94ac (diff)
parentc81023435795766411c5954a4676ebb215af40a6 (diff)
downloadgitlab-ce-2ec93abed7a1a3aa49b3267342824e0743de0f54.tar.gz
Merge branch 'master' into ci/persist-registration-token
* master: (66 commits) Fix runners admin view Fix migrations Rename mention of gitlab-git-http-server to gitlab-workhorse Bump Redis requirement to 2.8 for Sidekiq 4 requirements Fix wording on runner setup page add details on how to change saml button label Fix tests Move awards back to gray panel and few improvements to sidebar Few UI improvements to new sidebar implementation Fix tests for new issuable sidebar Update changelog Implement new sidebar for merge request page Make edit link on issuable sidebar works Redesign issue page for new sidebar Move awards css to separate file Implement issuable sidebar partial Update CHANGELOG Clarify cache behavior Run builds from projects with enabled CI Use Gitlab::Git instead of Ci::Git ... Conflicts: db/schema.rb
Diffstat (limited to 'app/models')
-rw-r--r--app/models/ci/application_setting.rb38
-rw-r--r--app/models/ci/build.rb67
-rw-r--r--app/models/ci/commit.rb28
-rw-r--r--app/models/ci/event.rb27
-rw-r--r--app/models/ci/project.rb192
-rw-r--r--app/models/ci/project_status.rb31
-rw-r--r--app/models/ci/runner.rb6
-rw-r--r--app/models/ci/runner_project.rb4
-rw-r--r--app/models/ci/service.rb105
-rw-r--r--app/models/ci/trigger.rb2
-rw-r--r--app/models/ci/variable.rb4
-rw-r--r--app/models/ci/web_hook.rb43
-rw-r--r--app/models/commit_status.rb6
-rw-r--r--app/models/hooks/project_hook.rb1
-rw-r--r--app/models/hooks/web_hook.rb1
-rw-r--r--app/models/issue.rb8
-rw-r--r--app/models/note.rb3
-rw-r--r--app/models/project.rb77
-rw-r--r--app/models/project_services/builds_email_service.rb90
-rw-r--r--app/models/project_services/ci/hip_chat_message.rb73
-rw-r--r--app/models/project_services/ci/hip_chat_service.rb93
-rw-r--r--app/models/project_services/ci/mail_service.rb84
-rw-r--r--app/models/project_services/ci/slack_message.rb92
-rw-r--r--app/models/project_services/ci/slack_service.rb81
-rw-r--r--app/models/project_services/gitlab_ci_service.rb73
-rw-r--r--app/models/project_services/hipchat_service.rb49
-rw-r--r--app/models/project_services/slack_service.rb29
-rw-r--r--app/models/project_services/slack_service/base_message.rb3
-rw-r--r--app/models/project_services/slack_service/build_message.rb82
-rw-r--r--app/models/service.rb21
-rw-r--r--app/models/user.rb7
31 files changed, 389 insertions, 1031 deletions
diff --git a/app/models/ci/application_setting.rb b/app/models/ci/application_setting.rb
deleted file mode 100644
index 7f5df8ce6c4..00000000000
--- a/app/models/ci/application_setting.rb
+++ /dev/null
@@ -1,38 +0,0 @@
-# == Schema Information
-#
-# Table name: ci_application_settings
-#
-# id :integer not null, primary key
-# all_broken_builds :boolean
-# add_pusher :boolean
-# created_at :datetime
-# updated_at :datetime
-#
-
-module Ci
- class ApplicationSetting < ActiveRecord::Base
- extend Ci::Model
- CACHE_KEY = 'ci_application_setting.last'
-
- after_commit do
- Rails.cache.write(CACHE_KEY, self)
- end
-
- def self.expire
- Rails.cache.delete(CACHE_KEY)
- end
-
- def self.current
- Rails.cache.fetch(CACHE_KEY) do
- Ci::ApplicationSetting.last
- end
- end
-
- def self.create_from_defaults
- create(
- all_broken_builds: Settings.gitlab_ci['all_broken_builds'],
- add_pusher: Settings.gitlab_ci['add_pusher'],
- )
- end
- end
-end
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 52ce1b920fa..6d9cdb95295 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -84,6 +84,7 @@ module Ci
new_build.options = build.options
new_build.commands = build.commands
new_build.tag_list = build.tag_list
+ new_build.gl_project_id = build.gl_project_id
new_build.commit_id = build.commit_id
new_build.name = build.name
new_build.allow_failure = build.allow_failure
@@ -96,21 +97,16 @@ module Ci
end
state_machine :status, initial: :pending do
- after_transition any => [:success, :failed, :canceled] do |build, transition|
- return unless build.gl_project
-
- project = build.project
+ after_transition pending: :running do |build, transition|
+ build.execute_hooks
+ end
- if project.web_hooks?
- Ci::WebHookService.new.build_end(build)
- end
+ after_transition any => [:success, :failed, :canceled] do |build, transition|
+ return unless build.project
+ build.update_coverage
build.commit.create_next_builds(build)
- project.execute_services(build)
-
- if project.coverage_enabled?
- build.update_coverage
- end
+ build.execute_hooks
end
end
@@ -119,7 +115,7 @@ module Ci
end
def retryable?
- commands.present?
+ project.builds_enabled? && commands.present?
end
def retried?
@@ -132,7 +128,7 @@ module Ci
end
def timeout
- project.timeout
+ project.build_timeout
end
def variables
@@ -151,26 +147,21 @@ module Ci
project.name
end
- def project_recipients
- recipients = project.email_recipients.split(' ')
-
- if project.email_add_pusher? && user.present? && user.notification_email.present?
- recipients << user.notification_email
- end
-
- recipients.uniq
- end
-
def repo_url
- project.repo_url_with_auth
+ auth = "gitlab-ci-token:#{token}@"
+ project.http_url_to_repo.sub(/^https?:\/\//) do |prefix|
+ prefix + auth
+ end
end
def allow_git_fetch
- project.allow_git_fetch
+ project.build_allow_git_fetch
end
def update_coverage
- coverage = extract_coverage(trace, project.coverage_regex)
+ coverage_regex = project.build_coverage_regex
+ return unless coverage_regex
+ coverage = extract_coverage(trace, coverage_regex)
if coverage.is_a? Numeric
update_attributes(coverage: coverage)
@@ -203,7 +194,7 @@ module Ci
def trace
trace = raw_trace
if project && trace.present?
- trace.gsub(project.token, 'xxxxxx')
+ trace.gsub(project.runners_token, 'xxxxxx')
else
trace
end
@@ -230,29 +221,29 @@ module Ci
end
def token
- project.token
+ project.runners_token
end
def valid_token? token
- project.valid_token? token
+ project.valid_runners_token? token
end
def target_url
Gitlab::Application.routes.url_helpers.
- namespace_project_build_url(gl_project.namespace, gl_project, self)
+ namespace_project_build_url(project.namespace, project, self)
end
def cancel_url
if active?
Gitlab::Application.routes.url_helpers.
- cancel_namespace_project_build_path(gl_project.namespace, gl_project, self)
+ cancel_namespace_project_build_path(project.namespace, project, self)
end
end
def retry_url
if retryable?
Gitlab::Application.routes.url_helpers.
- retry_namespace_project_build_path(gl_project.namespace, gl_project, self)
+ retry_namespace_project_build_path(project.namespace, project, self)
end
end
@@ -271,10 +262,18 @@ module Ci
def download_url
if artifacts_file.exists?
Gitlab::Application.routes.url_helpers.
- download_namespace_project_build_path(gl_project.namespace, gl_project, self)
+ download_namespace_project_build_path(project.namespace, project, self)
end
end
+ def execute_hooks
+ build_data = Gitlab::BuildDataBuilder.build(self)
+ project.execute_hooks(build_data.dup, :build_hooks)
+ project.execute_services(build_data.dup, :build_hooks)
+ end
+
+
+
private
def yaml_variables
diff --git a/app/models/ci/commit.rb b/app/models/ci/commit.rb
index 75465685e98..d2a29236942 100644
--- a/app/models/ci/commit.rb
+++ b/app/models/ci/commit.rb
@@ -20,8 +20,8 @@ module Ci
class Commit < ActiveRecord::Base
extend Ci::Model
- belongs_to :gl_project, class_name: '::Project', foreign_key: :gl_project_id
- has_many :statuses, dependent: :destroy, class_name: 'CommitStatus'
+ belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id
+ has_many :statuses, class_name: 'CommitStatus'
has_many :builds, class_name: 'Ci::Build'
has_many :trigger_requests, dependent: :destroy, class_name: 'Ci::TriggerRequest'
@@ -38,10 +38,6 @@ module Ci
sha
end
- def project
- @project ||= gl_project.ensure_gitlab_ci_project
- end
-
def project_id
project.id
end
@@ -57,7 +53,7 @@ module Ci
end
def valid_commit_sha
- if self.sha == Ci::Git::BLANK_SHA
+ if self.sha == Gitlab::Git::BLANK_SHA
self.errors.add(:sha, " cant be 00000000 (branch removal)")
end
end
@@ -79,7 +75,7 @@ module Ci
end
def commit_data
- @commit ||= gl_project.commit(sha)
+ @commit ||= project.commit(sha)
rescue
nil
end
@@ -178,16 +174,18 @@ 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
def coverage
- if project.coverage_enabled?
- coverage_array = latest_builds.map(&:coverage).compact
- if coverage_array.size >= 1
- '%.2f' % (coverage_array.reduce(:+) / coverage_array.size)
- end
+ coverage_array = latest_builds.map(&:coverage).compact
+ if coverage_array.size >= 1
+ '%.2f' % (coverage_array.reduce(:+) / coverage_array.size)
end
end
@@ -197,7 +195,7 @@ module Ci
def config_processor
return nil unless ci_yaml_file
- @config_processor ||= Ci::GitlabCiYamlProcessor.new(ci_yaml_file, gl_project.path_with_namespace)
+ @config_processor ||= Ci::GitlabCiYamlProcessor.new(ci_yaml_file, project.path_with_namespace)
rescue Ci::GitlabCiYamlProcessor::ValidationError, Psych::SyntaxError => e
save_yaml_error(e.message)
nil
@@ -207,7 +205,7 @@ module Ci
end
def ci_yaml_file
- @ci_yaml_file ||= gl_project.repository.blob_at(sha, '.gitlab-ci.yml').data
+ @ci_yaml_file ||= project.repository.blob_at(sha, '.gitlab-ci.yml').data
rescue
nil
end
diff --git a/app/models/ci/event.rb b/app/models/ci/event.rb
deleted file mode 100644
index 8c39be42677..00000000000
--- a/app/models/ci/event.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-# == Schema Information
-#
-# Table name: ci_events
-#
-# id :integer not null, primary key
-# project_id :integer
-# user_id :integer
-# is_admin :integer
-# description :text
-# created_at :datetime
-# updated_at :datetime
-#
-
-module Ci
- class Event < ActiveRecord::Base
- extend Ci::Model
-
- belongs_to :project, class_name: 'Ci::Project'
-
- validates :description,
- presence: true,
- length: { in: 5..200 }
-
- scope :admin, ->(){ where(is_admin: true) }
- scope :project_wide, ->(){ where(is_admin: false) }
- end
-end
diff --git a/app/models/ci/project.rb b/app/models/ci/project.rb
deleted file mode 100644
index 669ee1cc0d2..00000000000
--- a/app/models/ci/project.rb
+++ /dev/null
@@ -1,192 +0,0 @@
-# == Schema Information
-#
-# Table name: ci_projects
-#
-# id :integer not null, primary key
-# name :string(255)
-# timeout :integer default(3600), not null
-# created_at :datetime
-# updated_at :datetime
-# token :string(255)
-# default_ref :string(255)
-# path :string(255)
-# always_build :boolean default(FALSE), not null
-# polling_interval :integer
-# public :boolean default(FALSE), not null
-# ssh_url_to_repo :string(255)
-# gitlab_id :integer
-# allow_git_fetch :boolean default(TRUE), not null
-# email_recipients :string(255) default(""), not null
-# email_add_pusher :boolean default(TRUE), not null
-# email_only_broken_builds :boolean default(TRUE), not null
-# skip_refs :string(255)
-# coverage_regex :string(255)
-# shared_runners_enabled :boolean default(FALSE)
-# generated_yaml_config :text
-#
-
-module Ci
- class Project < ActiveRecord::Base
- extend Ci::Model
-
- include Ci::ProjectStatus
-
- belongs_to :gl_project, class_name: '::Project', foreign_key: :gitlab_id
-
- 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
-
- #
- # Validations
- #
- validates_presence_of :timeout, :token, :default_ref, :gitlab_id
-
- validates_uniqueness_of :gitlab_id
-
- validates :polling_interval,
- presence: true,
- if: ->(project) { project.always_build.present? }
-
- before_validation :set_default_values
-
- class << self
- include Ci::CurrentSettings
-
- def unassigned(runner)
- joins("LEFT JOIN #{Ci::RunnerProject.table_name} ON #{Ci::RunnerProject.table_name}.project_id = #{Ci::Project.table_name}.id " \
- "AND #{Ci::RunnerProject.table_name}.runner_id = #{runner.id}").
- where("#{Ci::RunnerProject.table_name}.project_id" => nil)
- end
-
- def ordered_by_last_commit_date
- last_commit_subquery = "(SELECT gl_project_id, MAX(committed_at) committed_at FROM #{Ci::Commit.table_name} GROUP BY gl_project_id)"
- joins("LEFT JOIN #{last_commit_subquery} AS last_commit ON #{Ci::Project.table_name}.gitlab_id = last_commit.gl_project_id").
- joins(:gl_project).
- order("CASE WHEN last_commit.committed_at IS NULL THEN 1 ELSE 0 END, last_commit.committed_at DESC")
- end
- end
-
- def name
- name_with_namespace
- end
-
- def path
- path_with_namespace
- end
-
- def gitlab_url
- web_url
- end
-
- def any_runners?(&block)
- if runners.active.any?(&block)
- return true
- end
-
- shared_runners_enabled && Ci::Runner.shared.active.any?(&block)
- end
-
- def set_default_values
- self.token = SecureRandom.hex(15) if self.token.blank?
- self.default_ref ||= 'master'
- end
-
- def tracked_refs
- @tracked_refs ||= default_ref.split(",").map { |ref| ref.strip }
- end
-
- def valid_token? token
- self.token && self.token == token
- end
-
- def no_running_builds?
- # Get running builds not later than 3 days ago to ignore hangs
- builds.running.where("updated_at > ?", 3.days.ago).empty?
- end
-
- def email_notification?
- 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
-
- def timeout_in_minutes=(value)
- self.timeout = value.to_i * 60
- end
-
- def coverage_enabled?
- coverage_regex.present?
- end
-
- # Build a clone-able repo url
- # using http and basic auth
- def repo_url_with_auth
- auth = "gitlab-ci-token:#{token}@"
- http_url_to_repo.sub(/^https?:\/\//) do |prefix|
- prefix + auth
- 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
-
- def commits
- gl_project.ci_commits.ordered
- end
-
- def builds
- gl_project.ci_builds
- end
- end
-end
diff --git a/app/models/ci/project_status.rb b/app/models/ci/project_status.rb
deleted file mode 100644
index 2d35aeac225..00000000000
--- a/app/models/ci/project_status.rb
+++ /dev/null
@@ -1,31 +0,0 @@
-module Ci
- module ProjectStatus
- def status
- last_commit.status if last_commit
- end
-
- def broken?
- last_commit.failed? if last_commit
- end
-
- def success?
- last_commit.success? if last_commit
- end
-
- def broken_or_success?
- broken? || success?
- end
-
- def last_commit
- @last_commit ||= commits.last if commits.any?
- end
-
- def last_commit_date
- last_commit.try(:created_at)
- end
-
- def human_status
- status
- end
- end
-end
diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb
index 89710485811..38b20cd7faa 100644
--- a/app/models/ci/runner.rb
+++ b/app/models/ci/runner.rb
@@ -25,7 +25,7 @@ module Ci
has_many :builds, class_name: 'Ci::Build'
has_many :runner_projects, dependent: :destroy, class_name: 'Ci::RunnerProject'
- has_many :projects, through: :runner_projects, class_name: 'Ci::Project'
+ has_many :projects, through: :runner_projects, class_name: '::Project', foreign_key: :gl_project_id
has_one :last_build, ->() { order('id DESC') }, class_name: 'Ci::Build'
@@ -45,10 +45,6 @@ module Ci
query: "%#{query.try(:downcase)}%")
end
- def gl_projects_ids
- projects.select(:gitlab_id)
- end
-
def set_default_values
self.token = SecureRandom.hex(15) if self.token.blank?
end
diff --git a/app/models/ci/runner_project.rb b/app/models/ci/runner_project.rb
index 3f4fc43873e..93d9be144e8 100644
--- a/app/models/ci/runner_project.rb
+++ b/app/models/ci/runner_project.rb
@@ -14,8 +14,8 @@ module Ci
extend Ci::Model
belongs_to :runner, class_name: 'Ci::Runner'
- belongs_to :project, class_name: 'Ci::Project'
+ belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id
- validates_uniqueness_of :runner_id, scope: :project_id
+ validates_uniqueness_of :runner_id, scope: :gl_project_id
end
end
diff --git a/app/models/ci/service.rb b/app/models/ci/service.rb
deleted file mode 100644
index 8063c51e82b..00000000000
--- a/app/models/ci/service.rb
+++ /dev/null
@@ -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
diff --git a/app/models/ci/trigger.rb b/app/models/ci/trigger.rb
index b73c35d5ae5..23516709a41 100644
--- a/app/models/ci/trigger.rb
+++ b/app/models/ci/trigger.rb
@@ -16,7 +16,7 @@ module Ci
acts_as_paranoid
- belongs_to :project, class_name: 'Ci::Project'
+ belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id
has_many :trigger_requests, dependent: :destroy, class_name: 'Ci::TriggerRequest'
validates_presence_of :token
diff --git a/app/models/ci/variable.rb b/app/models/ci/variable.rb
index b3d2b809e03..56759d3e50f 100644
--- a/app/models/ci/variable.rb
+++ b/app/models/ci/variable.rb
@@ -15,10 +15,10 @@ module Ci
class Variable < ActiveRecord::Base
extend Ci::Model
- belongs_to :project, class_name: 'Ci::Project'
+ belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id
validates_presence_of :key
- validates_uniqueness_of :key, scope: :project_id
+ validates_uniqueness_of :key, scope: :gl_project_id
attr_encrypted :value, mode: :per_attribute_iv_and_salt, key: Gitlab::Application.secrets.db_key_base
end
diff --git a/app/models/ci/web_hook.rb b/app/models/ci/web_hook.rb
deleted file mode 100644
index 0dc15eb6683..00000000000
--- a/app/models/ci/web_hook.rb
+++ /dev/null
@@ -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
diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb
index ff619965a57..21c5c87bc3d 100644
--- a/app/models/commit_status.rb
+++ b/app/models/commit_status.rb
@@ -30,6 +30,7 @@
class CommitStatus < ActiveRecord::Base
self.table_name = 'ci_builds'
+ belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id
belongs_to :commit, class_name: 'Ci::Commit'
belongs_to :user
@@ -76,7 +77,7 @@ class CommitStatus < ActiveRecord::Base
end
after_transition [:pending, :running] => :success do |build, transition|
- MergeRequests::MergeWhenBuildSucceedsService.new(build.commit.gl_project, nil).trigger(build)
+ MergeRequests::MergeWhenBuildSucceedsService.new(build.commit.project, nil).trigger(build)
end
state :pending, value: 'pending'
@@ -86,8 +87,7 @@ class CommitStatus < ActiveRecord::Base
state :canceled, value: 'canceled'
end
- delegate :sha, :short_sha, :gl_project,
- to: :commit, prefix: false
+ delegate :sha, :short_sha, to: :commit, prefix: false
# TODO: this should be removed with all references
def before_sha
diff --git a/app/models/hooks/project_hook.rb b/app/models/hooks/project_hook.rb
index 337b3097126..22638057773 100644
--- a/app/models/hooks/project_hook.rb
+++ b/app/models/hooks/project_hook.rb
@@ -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
diff --git a/app/models/hooks/web_hook.rb b/app/models/hooks/web_hook.rb
index 715ec5908b7..40eb0e20b4b 100644
--- a/app/models/hooks/web_hook.rb
+++ b/app/models/hooks/web_hook.rb
@@ -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
diff --git a/app/models/issue.rb b/app/models/issue.rb
index 187b6482b6c..e04035b3af8 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -83,6 +83,14 @@ class Issue < ActiveRecord::Base
reference
end
+ def referenced_merge_requests
+ references = [self, *notes].flat_map do |note|
+ note.all_references(load_lazy_references: false).merge_requests
+ end.uniq
+
+ Gitlab::Markdown::ReferenceFilter::LazyReference.load(references).uniq.sort_by(&:iid)
+ end
+
# Reset issue events cache
#
# Since we do cache @event we need to reset cache in special cases:
diff --git a/app/models/note.rb b/app/models/note.rb
index 8f0efa8d4b7..04053ccc61e 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -377,6 +377,7 @@ class Note < ActiveRecord::Base
end
def award_emoji_name
- note.match(Gitlab::Markdown::EmojiFilter.emoji_pattern)[1]
+ original_name = note.match(Gitlab::Markdown::EmojiFilter.emoji_pattern)[1]
+ AwardEmoji.normilize_emoji_name(original_name)
end
end
diff --git a/app/models/project.rb b/app/models/project.rb
index e78868af1cc..e1f7bf971e3 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -56,6 +56,7 @@ class Project < ActiveRecord::Base
default_value_for :wiki_enabled, gitlab_config_features.wiki
default_value_for :wall_enabled, false
default_value_for :snippets_enabled, gitlab_config_features.snippets
+ default_value_for(:shared_runners_enabled) { current_application_settings.shared_runners_enabled }
# set last_activity_at to the same as created_at
after_create :set_last_activity_at
@@ -77,10 +78,10 @@ class Project < ActiveRecord::Base
# Project services
has_many :services
- has_one :gitlab_ci_service, dependent: :destroy
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
@@ -121,14 +122,21 @@ class Project < ActiveRecord::Base
has_many :deploy_keys, through: :deploy_keys_projects
has_many :users_star_projects, dependent: :destroy
has_many :starrers, through: :users_star_projects, source: :user
- has_many :ci_commits, dependent: :destroy, class_name: 'Ci::Commit', foreign_key: :gl_project_id
- has_many :ci_builds, through: :ci_commits, source: :builds, dependent: :destroy, class_name: 'Ci::Build'
has_many :releases, dependent: :destroy
has_many :lfs_objects_projects, dependent: :destroy
has_many :lfs_objects, through: :lfs_objects_projects
has_one :import_data, dependent: :destroy, class_name: "ProjectImportData"
- has_one :gitlab_ci_project, dependent: :destroy, class_name: "Ci::Project", foreign_key: :gitlab_id
+
+ has_many :commit_statuses, dependent: :destroy, class_name: 'CommitStatus', foreign_key: :gl_project_id
+ has_many :ci_commits, dependent: :destroy, class_name: 'Ci::Commit', foreign_key: :gl_project_id
+ has_many :builds, class_name: 'Ci::Build', foreign_key: :gl_project_id # the builds are created from the commit_statuses
+ has_many :runner_projects, dependent: :destroy, class_name: 'Ci::RunnerProject', foreign_key: :gl_project_id
+ has_many :runners, through: :runner_projects, source: :runner, class_name: 'Ci::Runner'
+ has_many :variables, dependent: :destroy, class_name: 'Ci::Variable', foreign_key: :gl_project_id
+ has_many :triggers, dependent: :destroy, class_name: 'Ci::Trigger', foreign_key: :gl_project_id
+
+ accepts_nested_attributes_for :variables, allow_destroy: true
delegate :name, to: :owner, allow_nil: true, prefix: true
delegate :members, to: :team, prefix: true
@@ -161,6 +169,11 @@ class Project < ActiveRecord::Base
if: ->(project) { project.avatar.present? && project.avatar_changed? }
validates :avatar, file_size: { maximum: 200.kilobytes.to_i }
+ before_validation :set_runners_token_token
+ def set_runners_token_token
+ self.runners_token = SecureRandom.hex(15) if self.runners_token.blank?
+ end
+
mount_uploader :avatar, AvatarUploader
# Scopes
@@ -256,6 +269,10 @@ class Project < ActiveRecord::Base
projects.iwhere('projects.path' => project_path).take
end
+ def find_by_ci_id(id)
+ find_by(ci_id: id.to_i)
+ end
+
def visibility_levels
Gitlab::VisibilityLevel.options
end
@@ -790,28 +807,6 @@ class Project < ActiveRecord::Base
ci_commit(sha) || ci_commits.create(sha: sha)
end
- def ensure_gitlab_ci_project
- gitlab_ci_project || create_gitlab_ci_project(
- shared_runners_enabled: current_application_settings.shared_runners_enabled
- )
- end
-
- # TODO: this should be migrated to Project table,
- # the same as issues_enabled
- def builds_enabled
- gitlab_ci_service && gitlab_ci_service.active
- end
-
- def builds_enabled?
- builds_enabled
- end
-
- def builds_enabled=(value)
- service = gitlab_ci_service || create_gitlab_ci_service
- service.active = value
- service.save
- end
-
def enable_ci
self.builds_enabled = true
end
@@ -825,4 +820,34 @@ class Project < ActiveRecord::Base
forked_project_link.destroy
end
end
+
+ def any_runners?(&block)
+ if runners.active.any?(&block)
+ return true
+ end
+
+ shared_runners_enabled? && Ci::Runner.shared.active.any?(&block)
+ end
+
+ def valid_runners_token? token
+ self.runners_token && self.runners_token == token
+ end
+
+ # TODO (ayufan): For now we use runners_token (backward compatibility)
+ # In 8.4 every build will have its own individual token valid for time of build
+ def valid_build_token? token
+ self.builds_enabled? && self.runners_token && self.runners_token == token
+ end
+
+ def build_coverage_enabled?
+ build_coverage_regex.present?
+ end
+
+ def build_timeout_in_minutes
+ build_timeout / 60
+ end
+
+ def build_timeout_in_minutes=(value)
+ self.build_timeout = value.to_i * 60
+ end
end
diff --git a/app/models/project_services/builds_email_service.rb b/app/models/project_services/builds_email_service.rb
new file mode 100644
index 00000000000..8247c79fc33
--- /dev/null
+++ b/app/models/project_services/builds_email_service.rb
@@ -0,0 +1,90 @@
+# == 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 initialize_properties
+ if properties.nil?
+ self.properties = {}
+ self.notify_only_broken_builds = true
+ end
+ end
+
+ 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 comma' },
+ { 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)
+ all_recipients = recipients.split(',')
+
+ if add_pusher? && data[:user][:email]
+ all_recipients << "#{data[:user][:email]}"
+ end
+
+ all_recipients
+ end
+end
diff --git a/app/models/project_services/ci/hip_chat_message.rb b/app/models/project_services/ci/hip_chat_message.rb
deleted file mode 100644
index d89466b689f..00000000000
--- a/app/models/project_services/ci/hip_chat_message.rb
+++ /dev/null
@@ -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
diff --git a/app/models/project_services/ci/hip_chat_service.rb b/app/models/project_services/ci/hip_chat_service.rb
deleted file mode 100644
index 0df03890efb..00000000000
--- a/app/models/project_services/ci/hip_chat_service.rb
+++ /dev/null
@@ -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
diff --git a/app/models/project_services/ci/mail_service.rb b/app/models/project_services/ci/mail_service.rb
deleted file mode 100644
index bb961d06972..00000000000
--- a/app/models/project_services/ci/mail_service.rb
+++ /dev/null
@@ -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
diff --git a/app/models/project_services/ci/slack_message.rb b/app/models/project_services/ci/slack_message.rb
deleted file mode 100644
index 1a6ff8e34c9..00000000000
--- a/app/models/project_services/ci/slack_message.rb
+++ /dev/null
@@ -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
diff --git a/app/models/project_services/ci/slack_service.rb b/app/models/project_services/ci/slack_service.rb
deleted file mode 100644
index 7064bfe78db..00000000000
--- a/app/models/project_services/ci/slack_service.rb
+++ /dev/null
@@ -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
diff --git a/app/models/project_services/gitlab_ci_service.rb b/app/models/project_services/gitlab_ci_service.rb
index 234e8e8b580..d73182d40ac 100644
--- a/app/models/project_services/gitlab_ci_service.rb
+++ b/app/models/project_services/gitlab_ci_service.rb
@@ -19,76 +19,5 @@
#
class GitlabCiService < CiService
- include Gitlab::Application.routes.url_helpers
-
- after_save :compose_service_hook, if: :activated?
- after_save :ensure_gitlab_ci_project, if: :activated?
-
- def compose_service_hook
- hook = service_hook || build_service_hook
- hook.save
- end
-
- def ensure_gitlab_ci_project
- return unless project
- project.ensure_gitlab_ci_project
- end
-
- def supported_events
- %w(push tag_push)
- end
-
- def execute(data)
- return unless supported_events.include?(data[:object_kind])
-
- ci_project = project.gitlab_ci_project
- if ci_project
- current_user = User.find_by(id: data[:user_id])
- Ci::CreateCommitService.new.execute(ci_project, current_user, data)
- end
- end
-
- def token
- if project.gitlab_ci_project.present?
- project.gitlab_ci_project.token
- end
- end
-
- def get_ci_commit(sha, ref)
- Ci::Project.find(project.gitlab_ci_project.id).commits.find_by_sha!(sha)
- end
-
- def commit_status(sha, ref)
- get_ci_commit(sha, ref).status
- rescue ActiveRecord::RecordNotFound
- :error
- end
-
- def commit_coverage(sha, ref)
- get_ci_commit(sha, ref).coverage
- rescue ActiveRecord::RecordNotFound
- :error
- end
-
- def build_page(sha, ref)
- if project.gitlab_ci_project.present?
- builds_namespace_project_commit_url(project.namespace, project, sha)
- end
- end
-
- def title
- 'GitLab CI'
- end
-
- def description
- 'Continuous integration server from GitLab'
- end
-
- def to_param
- 'gitlab_ci'
- end
-
- def fields
- []
- end
+ # this is no longer used
end
diff --git a/app/models/project_services/hipchat_service.rb b/app/models/project_services/hipchat_service.rb
index af2840a57f0..1e1686a11c6 100644
--- a/app/models/project_services/hipchat_service.rb
+++ b/app/models/project_services/hipchat_service.rb
@@ -22,8 +22,16 @@ 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 initialize_properties
+ if properties.nil?
+ self.properties = {}
+ self.notify_only_broken_builds = true
+ end
+ end
+
def title
'HipChat'
end
@@ -45,12 +53,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 +103,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 +246,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 +275,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
diff --git a/app/models/project_services/slack_service.rb b/app/models/project_services/slack_service.rb
index 7cd5e892507..375b4534d07 100644
--- a/app/models/project_services/slack_service.rb
+++ b/app/models/project_services/slack_service.rb
@@ -20,8 +20,16 @@
class SlackService < Service
prop_accessor :webhook, :username, :channel
+ boolean_accessor :notify_only_broken_builds
validates :webhook, presence: true, if: :activated?
+ def initialize_properties
+ if properties.nil?
+ self.properties = {}
+ self.notify_only_broken_builds = true
+ end
+ end
+
def title
'Slack'
end
@@ -45,12 +53,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 +87,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 +97,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 +114,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"
diff --git a/app/models/project_services/slack_service/base_message.rb b/app/models/project_services/slack_service/base_message.rb
index aa00d6061a1..f1182824687 100644
--- a/app/models/project_services/slack_service/base_message.rb
+++ b/app/models/project_services/slack_service/base_message.rb
@@ -10,6 +10,9 @@ class SlackService
format(message)
end
+ def fallback
+ end
+
def attachments
raise NotImplementedError
end
diff --git a/app/models/project_services/slack_service/build_message.rb b/app/models/project_services/slack_service/build_message.rb
new file mode 100644
index 00000000000..c124cad4afd
--- /dev/null
+++ b/app/models/project_services/slack_service/build_message.rb
@@ -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
diff --git a/app/models/service.rb b/app/models/service.rb
index d610abd1683..d3bf7f0ebd1 100644
--- a/app/models/service.rb
+++ b/app/models/service.rb
@@ -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
@@ -40,13 +41,14 @@ class Service < ActiveRecord::Base
validates :project_id, presence: true, unless: Proc.new { |service| service.template? }
- scope :visible, -> { where.not(type: 'GitlabIssueTrackerService') }
+ scope :visible, -> { where.not(type: ['GitlabIssueTrackerService', 'GitlabCiService']) }
scope :push_hooks, -> { where(push_events: true, active: true) }
scope :tag_push_hooks, -> { where(tag_push_events: true, active: true) }
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
@@ -170,7 +188,6 @@ class Service < ActiveRecord::Base
external_wiki
flowdock
gemnasium
- gitlab_ci
hipchat
irker
jira
diff --git a/app/models/user.rb b/app/models/user.rb
index 1a8d8f1e249..fdd14f4571d 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -136,7 +136,7 @@ class User < ActiveRecord::Base
has_many :assigned_merge_requests, dependent: :destroy, foreign_key: :assignee_id, class_name: "MergeRequest"
has_many :oauth_applications, class_name: 'Doorkeeper::Application', as: :owner, dependent: :destroy
has_one :abuse_report, dependent: :destroy
- has_many :ci_builds, dependent: :nullify, class_name: 'Ci::Build'
+ has_many :builds, dependent: :nullify, class_name: 'Ci::Build'
#
@@ -771,10 +771,9 @@ class User < ActiveRecord::Base
def ci_authorized_runners
@ci_authorized_runners ||= begin
- runner_ids = Ci::RunnerProject.joins(:project).
- where("ci_projects.gitlab_id IN (#{ci_projects_union.to_sql})").
+ runner_ids = Ci::RunnerProject.
+ where("ci_runner_projects.gl_project_id IN (#{ci_projects_union.to_sql})").
select(:runner_id)
-
Ci::Runner.specific.where(id: runner_ids)
end
end