Commit 12dd5ac2 authored by Kamil Trzciński's avatar Kamil Trzciński Committed by Robert Speicher

All CI offline migrations

parent 94246c7a
......@@ -37,7 +37,6 @@ module ServiceParams
:namespace,
:new_issue_url,
:notify,
:notify_only_broken_builds,
:notify_only_broken_pipelines,
:password,
:priority,
......
module Emails
module Builds
def build_fail_email(build_id, to)
@build = Ci::Build.find(build_id)
@project = @build.project
add_project_headers
add_build_headers('failed')
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
add_project_headers
add_build_headers('success')
mail(to: to, subject: subject("Build success for #{@project.name}", @build.short_sha))
end
private
def add_build_headers(status)
headers['X-GitLab-Build-Id'] = @build.id
headers['X-GitLab-Build-Ref'] = @build.ref
headers['X-GitLab-Build-Status'] = status.to_s
end
end
end
......@@ -6,7 +6,6 @@ class Notify < BaseMailer
include Emails::Notes
include Emails::Projects
include Emails::Profile
include Emails::Builds
include Emails::Pipelines
include Emails::Members
......
......@@ -15,7 +15,7 @@ module Ci
def persisted_environment
@persisted_environment ||= Environment.find_by(
name: expanded_environment_name,
project_id: gl_project_id
project: project
)
end
......@@ -223,7 +223,8 @@ module Ci
def merge_request
merge_requests = MergeRequest.includes(:merge_request_diff)
.where(source_branch: ref, source_project_id: pipeline.gl_project_id)
.where(source_branch: ref,
source_project: pipeline.project)
.reorder(iid: :asc)
merge_requests.find do |merge_request|
......@@ -231,10 +232,6 @@ module Ci
end
end
def project_id
gl_project_id
end
def repo_url
auth = "gitlab-ci-token:#{ensure_token!}@"
project.http_url_to_repo.sub(/^https?:\/\//) do |prefix|
......@@ -561,7 +558,7 @@ module Ci
end
def unscoped_project
@unscoped_project ||= Project.unscoped.find_by(id: gl_project_id)
@unscoped_project ||= Project.unscoped.find_by(id: project_id)
end
CI_REGISTRY_USER = 'gitlab-ci-token'.freeze
......
......@@ -5,9 +5,7 @@ module Ci
include Importable
include AfterCommitQueue
self.table_name = 'ci_commits'
belongs_to :project, foreign_key: :gl_project_id
belongs_to :project
belongs_to :user
has_many :statuses, class_name: 'CommitStatus', foreign_key: :commit_id
......
......@@ -9,7 +9,7 @@ module Ci
has_many :builds
has_many :runner_projects, dependent: :destroy
has_many :projects, through: :runner_projects, foreign_key: :gl_project_id
has_many :projects, through: :runner_projects
has_one :last_build, ->() { order('id DESC') }, class_name: 'Ci::Build'
......@@ -24,7 +24,7 @@ module Ci
scope :owned_or_shared, ->(project_id) do
joins('LEFT JOIN ci_runner_projects ON ci_runner_projects.runner_id = ci_runners.id')
.where("ci_runner_projects.gl_project_id = :project_id OR ci_runners.is_shared = true", project_id: project_id)
.where("ci_runner_projects.project_id = :project_id OR ci_runners.is_shared = true", project_id: project_id)
end
scope :assignable_for, ->(project) do
......
......@@ -3,8 +3,8 @@ module Ci
extend Ci::Model
belongs_to :runner
belongs_to :project, foreign_key: :gl_project_id
belongs_to :project
validates :runner_id, uniqueness: { scope: :gl_project_id }
validates :runner_id, uniqueness: { scope: :project_id }
end
end
......@@ -4,7 +4,7 @@ module Ci
acts_as_paranoid
belongs_to :project, foreign_key: :gl_project_id
belongs_to :project
belongs_to :owner, class_name: "User"
has_many :trigger_requests, dependent: :destroy
......
......@@ -2,11 +2,11 @@ module Ci
class Variable < ActiveRecord::Base
extend Ci::Model
belongs_to :project, foreign_key: :gl_project_id
belongs_to :project
validates :key,
presence: true,
uniqueness: { scope: :gl_project_id },
uniqueness: { scope: :project_id },
length: { maximum: 255 },
format: { with: /\A[a-zA-Z0-9_]+\z/,
message: "can contain only letters, digits and '_'." }
......
......@@ -5,7 +5,7 @@ class CommitStatus < ActiveRecord::Base
self.table_name = 'ci_builds'
belongs_to :project, foreign_key: :gl_project_id
belongs_to :project
belongs_to :pipeline, class_name: 'Ci::Pipeline', foreign_key: :commit_id
belongs_to :user
......@@ -133,6 +133,12 @@ class CommitStatus < ActiveRecord::Base
false
end
# Added in 9.0 to keep backward compatibility for projects exported in 8.17
# and prior.
def gl_project_id
'dummy'
end
def detailed_status(current_user)
Gitlab::Ci::Status::Factory
.new(self, current_user)
......
......@@ -89,7 +89,6 @@ 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 :pipelines_email_service, dependent: :destroy
has_one :irker_service, dependent: :destroy
has_one :pivotaltracker_service, dependent: :destroy
......@@ -159,13 +158,13 @@ class Project < ActiveRecord::Base
has_one :project_feature, dependent: :destroy
has_one :statistics, class_name: 'ProjectStatistics', dependent: :delete
has_many :commit_statuses, dependent: :destroy, foreign_key: :gl_project_id
has_many :pipelines, dependent: :destroy, class_name: 'Ci::Pipeline', 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 :commit_statuses, dependent: :destroy
has_many :pipelines, dependent: :destroy, class_name: 'Ci::Pipeline'
has_many :builds, class_name: 'Ci::Build' # the builds are created from the commit_statuses
has_many :runner_projects, dependent: :destroy, class_name: 'Ci::RunnerProject'
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
has_many :variables, dependent: :destroy, class_name: 'Ci::Variable'
has_many :triggers, dependent: :destroy, class_name: 'Ci::Trigger'
has_many :environments, dependent: :destroy
has_many :deployments, dependent: :destroy
......
# This class is to be removed with 9.1
# We should also by then remove BuildsEmailService from database
class BuildsEmailService < Service
prop_accessor :recipients
boolean_accessor :add_pusher
boolean_accessor :notify_only_broken_builds
validates :recipients, presence: true, if: ->(s) { s.activated? && !s.add_pusher? }
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 self.to_param
'builds_email'
end
def self.supported_events
%w(build)
end
def execute(push_data)
return unless supported_events.include?(push_data[:object_kind])
return unless should_build_be_notified?(push_data)
recipients = all_recipients(push_data)
if recipients.any?
BuildEmailWorker.perform_async(
push_data[:build_id],
recipients,
push_data
)
end
end
def can_test?
project.builds.any?
end
def disabled_title
"Please setup a build on your repository."
end
def test_data(project = nil, user = nil)
Gitlab::DataBuilder::Build.build(project.builds.last)
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 test(data)
begin
# bypass build status verification when testing
data[:build_status] = "failed"
data[:build_allow_failure] = false
result = execute(data)
rescue StandardError => error
return { success: false, result: error }
end
{ success: true, result: result }
end
def should_build_be_notified?(data)
case data[:build_status]
when 'success'
!notify_only_broken_builds?
when 'failed'
!allow_failure?(data)
else
false
end
end
def allow_failure?(data)
data[:build_allow_failure] == true
end
def all_recipients(data)
all_recipients = []
unless recipients.blank?
all_recipients += recipients.split(',').compact.reject(&:blank?)
end
if add_pusher? && data[:user][:email]
all_recipients << data[:user][:email]
end
all_recipients
%w[]
end
end
module ChatMessage
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 :user_url
attr_reader :duration
attr_reader :stage
attr_reader :build_id
attr_reader :build_name
def initialize(params)
@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]
@user_url = params[:commit][:author_url]
@duration = params[:commit][:duration]
@stage = params[:build_stage]
@build_name = params[:build_name]
@build_id = params[:build_id]
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_link} #{humanized_status} on build #{build_link} of stage #{stage} in #{duration} #{'second'.pluralize(duration)}"
end
def build_url
"#{project_url}/builds/#{build_id}"
end
def build_link
link(build_name, build_url)
end
def user_link
link(user_name, user_url)
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
link(ref, branch_url)
end
def project_link
link(project_name, project_url)
end
def commit_url
"#{project_url}/commit/#{sha}/builds"
end
def commit_link
link(Commit.truncate_sha(sha), commit_url)
end
end
end
......@@ -6,7 +6,7 @@ class ChatNotificationService < Service
default_value_for :category, 'chat'
prop_accessor :webhook, :username, :channel
boolean_accessor :notify_only_broken_builds, :notify_only_broken_pipelines
boolean_accessor :notify_only_broken_pipelines
validates :webhook, presence: true, url: true, if: :activated?
......@@ -16,7 +16,6 @@ class ChatNotificationService < Service
if properties.nil?
self.properties = {}
self.notify_only_broken_builds = true
self.notify_only_broken_pipelines = true
end
end
......@@ -27,7 +26,7 @@ class ChatNotificationService < Service
def self.supported_events
%w[push issue confidential_issue merge_request note tag_push
build pipeline wiki_page]
pipeline wiki_page]
end
def execute(data)
......@@ -89,8 +88,6 @@ class ChatNotificationService < Service
ChatMessage::MergeMessage.new(data) unless is_update?(data)
when "note"
ChatMessage::NoteMessage.new(data)
when "build"
ChatMessage::BuildMessage.new(data) if should_build_be_notified?(data)
when "pipeline"
ChatMessage::PipelineMessage.new(data) if should_pipeline_be_notified?(data)
when "wiki_page"
......@@ -125,17 +122,6 @@ class ChatNotificationService < Service
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
def should_pipeline_be_notified?(data)
case data[:object_attributes][:status]
when 'success'
......
......@@ -9,13 +9,13 @@ class HipchatService < Service
].freeze
prop_accessor :token, :room, :server, :color, :api_version
boolean_accessor :notify_only_broken_builds, :notify
boolean_accessor :notify_only_broken_pipelines, :notify
validates :token, presence: true, if: :activated?
def initialize_properties
if properties.nil?
self.properties = {}
self.notify_only_broken_builds = true
self.notify_only_broken_pipelines = true
end
end
......@@ -41,12 +41,12 @@ class HipchatService < Service
placeholder: 'Leave blank for default (v2)' },
{ type: 'text', name: 'server',
placeholder: 'Leave blank for default. https://hipchat.example.com' },
{ type: 'checkbox', name: 'notify_only_broken_builds' },
{ type: 'checkbox', name: 'notify_only_broken_pipelines' },
]
end
def self.supported_events
%w(push issue confidential_issue merge_request note tag_push build)
%w(push issue confidential_issue merge_request note tag_push pipeline)
end
def execute(data)
......@@ -90,8 +90,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)
when "pipeline"
create_pipeline_message(data) if should_pipeline_be_notified?(data)
end
end
......@@ -240,28 +240,29 @@ 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]
def create_pipeline_message(data)
pipeline_attributes = data[:object_attributes]
pipeline_id = pipeline_attributes[:id]
ref_type = pipeline_attributes[:tag] ? 'tag' : 'branch'
ref = pipeline_attributes[:ref]
user_name = (data[:user] && data[:user][:name]) || 'API'
status = pipeline_attributes[:status]
duration = pipeline_attributes[:duration]
branch_link = "<a href=\"#{project_url}/commits/#{CGI.escape(ref)}\">#{ref}</a>"
commit_link = "<a href=\"#{project_url}/commit/#{CGI.escape(sha)}/builds\">#{Commit.truncate_sha(sha)}</a>"
pipeline_url = "<a href=\"#{project_url}/pipelines/#{pipeline_id}\">##{pipeline_id}</a>"
"#{project_link}: Commit #{commit_link} of #{branch_link} #{ref_type} by #{user_name} #{humanized_status(status)} in #{duration} second(s)"
"#{project_link}: Pipeline #{pipeline_url} of #{branch_link} #{ref_type} by #{user_name} #{humanized_status(status)} in #{duration} second(s)"
end
def message_color(data)
build_status_color(data) || color || 'yellow'
pipeline_status_color(data) || color || 'yellow'
end
def build_status_color(data)
return unless data && data[:object_kind] == 'build'
def pipeline_status_color(data)
return unless data && data[:object_kind] == 'pipeline'
case data[:commit][:status]
case data[:object_attributes][:status]
when 'success'
'green'
else
......@@ -294,10 +295,10 @@ class HipchatService < Service
end
end
def should_build_be_notified?(data)
case data[:commit][:status]
def should_pipeline_be_notified?(data)
case data[:object_attributes][:status]
when 'success'
!notify_only_broken_builds?
!notify_only_broken_pipelines?
when 'failed'
true
else
......
......@@ -30,7 +30,6 @@ class MattermostService < ChatNotificationService
[
{ type: 'text', name: 'webhook', placeholder: 'e.g. http://mattermost_host/hooks/…' },
{ type: 'text', name: 'username', placeholder: 'e.g. GitLab' },
{ type: 'checkbox', name: 'notify_only_broken_builds' },
{ type: 'checkbox', name: 'notify_only_broken_pipelines' },
]
end
......
......@@ -29,7 +29,6 @@ class SlackService < ChatNotificationService
[
{ type: 'text', name: 'webhook', placeholder: 'e.g. https://hooks.slack.com/services/…' },
{ type: 'text', name: 'username', placeholder: 'e.g. GitLab' },
{ type: 'checkbox', name: 'notify_only_broken_builds' },
{ type: 'checkbox', name: 'notify_only_broken_pipelines' },
]
end
......
......@@ -215,7 +215,6 @@ class Service < ActiveRecord::Base
assembla
bamboo
buildkite
builds_email
bugzilla
campfire
custom_issue_tracker
......
......@@ -877,7 +877,7 @@ class User < ActiveRecord::Base
def ci_authorized_runners
@ci_authorized_runners ||= begin
runner_ids = Ci::RunnerProject.
where("ci_runner_projects.gl_project_id IN (#{ci_projects_union.to_sql})").
where("ci_runner_projects.project_id IN (#{ci_projects_union.to_sql})").
select(:runner_id)
Ci::Runner.specific.where(id: runner_ids)
end
......
......@@ -55,13 +55,13 @@ module Ci
new_builds.
# don't run projects which have not enabled shared runners and builds
joins(:project).where(projects: { shared_runners_enabled: true }).
joins('LEFT JOIN project_features ON ci_builds.gl_project_id = project_features.project_id').
joins('LEFT JOIN project_features ON ci_builds.project_id = project_features.project_id').
where('project_features.builds_access_level IS NULL or project_features.builds_access_level > 0').
# Implement fair scheduling
# this returns builds that are ordered by number of running builds
# we prefer projects that don't use shared runners at all
joins("LEFT JOIN (#{running_builds_for_shared_runners.to_sql}) AS project_builds ON ci_builds.gl_project_id=project_builds.gl_project_id").
joins("LEFT JOIN (#{running_builds_for_shared_runners.to_sql}) AS project_builds ON ci_builds.project_id=project_builds.project_id").
order('COALESCE(project_builds.running_builds, 0) ASC', 'ci_builds.id ASC')
end
......@@ -71,7 +71,7 @@ module Ci
def running_builds_for_shared_runners
Ci::Build.running.where(runner: Ci::Runner.shared).
group(:gl_project_id).select(:gl_project_id, 'count(*) AS running_builds')
group(:project_id).select(:project_id, 'count(*) AS running_builds')
end
def new_builds
......
- 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 (job failed)
%h3
Project:
= link_to namespace_project_url(@project.namespace, @project) do
= @project.name
%p
Commit: #{link_to @build.short_sha, namespace_project_commit_url(@build.project.namespace, @build.project, @build.sha)}
%p
Author: #{@build.pipeline.git_author_name}
%p
Branch: #{@build.ref}
%p
Stage: #{@build.stage}
%p
Job: #{@build.name}
%p
Message: #{@build.pipeline.git_commit_message}
%p
Job details: #{link_to "Job #{@build.id}", namespace_project_build_url(@build.project.namespace, @build.project, @build)}
Job failed for <%= @project.name %>
Status: <%= @build.status %>
Commit: <%= @build.pipeline.short_sha %>
Author: <%= @build.pipeline.git_author_name %>
Branch: <%= @build.ref %>
Stage: <%= @build.stage %>
Job: <%= @build.name %>
Message: <%= @build.pipeline.git_commit_message %>
Url: <%= namespace_project_build_url(@build.project.namespace, @build.project, @build) %>
- 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 (job successful)
%h3
Project:
= link_to namespace_project_url(@project.namespace, @project) do
= @project.name
%p
Commit: #{link_to @build.short_sha, namespace_project_commit_url(@build.project.namespace, @build.project, @build.sha)}
%p
Author: #{@build.pipeline.git_author_name}
%p
Branch: #{@build.ref}
%p
Stage: #{@build.stage}
%p
Job: #{@build.name}
%p
Message: #{@build.pipeline.git_commit_message}
%p
Job details: #{link_to "Job #{@build.id}", namespace_project_build_url(@build.project.namespace, @build.project, @build)}
Job successful for <%= @project.name %>
Status: <%= @build.status %>
Commit: <%= @build.pipeline.short_sha %>
Author: <%= @build.pipeline.git_author_name %>
Branch: <%= @build.ref %>
Stage: <%= @build.stage %>
Job: <%= @build.name %>
Message: <%= @build.pipeline.git_commit_message %>
Url: <%= namespace_project_build_url(@build.project.namespace, @build.project, @build) %>
class BuildEmailWorker
include Sidekiq::Worker
include BuildQueue
def perform(build_id, recipients, push_data)
recipients.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
---
title: Drop unused ci_projects table and some unused project_id columns,
then rename gl_project_id to project_id. Stop exporting job trace when
exporting projects.
merge_request: 9378
author: David Wagner
---
title: Migrate SlackService and MattermostService from build_events to pipeline_events, and migrate BuildsEmailService to PipelinesEmailService. Update Hipchat to use pipeline events rather than build events.
merge_request: 8196
author:
---
title: Remove various unused CI tables and columns
merge_request: 9639
author:
---
title: Rename table ci_commits to ci_pipelines
merge_request: 9638
author:
......@@ -5,7 +5,7 @@ class AddIndexForLatestSuccessfulPipeline < ActiveRecord::Migration
disable_ddl_transaction!
def up
add_concurrent_index :ci_commits, [:gl_project_id, :ref, :status]
add_concurrent_index(:ci_commits, [:gl_project_id, :ref, :status])
end
def down
......
class DropCiProjects < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def up
drop_table :ci_projects
end
def down
create_table "ci_projects", force: :cascade do |t|
t.string "name"
t.integer "timeout", default: 3600, null: false
t.datetime "created_at"
t.datetime "updated_at"
t.string "token"
t.string "default_ref"
t.string "path"
t.boolean "always_build", default: false, null: false
t.integer "polling_interval"
t.boolean "public", default: false, null: false
t.string "ssh_url_to_repo"
t.integer "gitlab_id"
t.boolean "allow_git_fetch", default: true, null: false
t.string "email_recipients", default: "", null: false
t.boolean "email_add_pusher", default: true, null: false
t.boolean "email_only_broken_builds", default: true, null: false
t.string "skip_refs"
t.string "coverage_regex"
t.boolean "shared_runners_enabled", default: false
t.text "generated_yaml_config"
end
end
end
class RemoveOldProjectIdColumns < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
disable_ddl_transaction!
DOWNTIME = true
DOWNTIME_REASON = 'Unused columns are being removed.'
def up
remove_index :ci_builds, :project_id if
index_exists?(:ci_builds, :project_id)
remove_column :ci_builds, :project_id
remove_column :ci_commits, :project_id
remove_column :ci_runner_projects, :project_id
remove_column :ci_triggers, :project_id
remove_column :ci_variables, :project_id
end
def down
add_column :ci_builds, :project_id, :integer
add_column :ci_commits, :project_id, :integer
add_column :ci_runner_projects, :project_id, :integer
add_column :ci_triggers, :project_id, :integer
add_column :ci_variables, :project_id, :integer
add_concurrent_index :ci_builds, :project_id
end
end
class RenameGlProjectIdToProjectId < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = true
DOWNTIME_REASON = 'Renaming an actively used column.'
def change
rename_column :ci_builds, :gl_project_id, :project_id
rename_column :ci_commits, :gl_project_id, :project_id
rename_column :ci_runner_projects, :gl_project_id, :project_id
rename_column :ci_triggers, :gl_project_id, :project_id
rename_column :ci_variables, :gl_project_id, :project_id
end
end
class RenameCiCommitsToCiPipelines < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = true
DOWNTIME_REASON = 'Rename table ci_commits to ci_pipelines'
def change
rename_table 'ci_commits', 'ci_pipelines'
end
end
class RemoveUnusedCiTablesAndColumns < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = true
DOWNTIME_REASON =
'Remove unused columns in used tables.' \
' Downtime required in case Rails caches them'
def up
%w[ci_application_settings
ci_events
ci_jobs
ci_sessions
ci_taggings
ci_tags].each do |table|
drop_table(table)
end
remove_column :ci_pipelines, :push_data, :text
remove_column :ci_builds, :job_id, :integer
remove_column :ci_builds, :deploy, :boolean
end
def down
add_column :ci_builds, :deploy, :boolean
add_column :ci_builds, :job_id, :integer
add_column :ci_pipelines, :push_data, :text
create_table "ci_tags", force: :cascade do |t|
t.string "name"
t.integer "taggings_count", default: 0
end
create_table "ci_taggings", force: :cascade do |t|
t.integer "tag_id"
t.integer "taggable_id"
t.string "taggable_type"
t.integer "tagger_id"
t.string "tagger_type"
t.string "context", limit: 128
t.datetime "created_at"
end
add_index "ci_taggings", %w[taggable_id taggable_type context]
create_table "ci_sessions", force: :cascade do |t|
t.string "session_id", null: false
t.text "data"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "ci_jobs", force: :cascade do |t|
t.integer "project_id", null: false
t.text "commands"
t.boolean "active", default: true, null: false
t.datetime "created_at"
t.datetime "updated_at"
t.string "name"
t.boolean "build_branches", default: true, null: false
t.boolean "build_tags", default: false, null: false
t.string "job_type", default: "parallel"
t.string "refs"
t.datetime "deleted_at"
end
create_table "ci_events", force: :cascade do |t|
t.integer "project_id"
t.integer "user_id"
t.integer "is_admin"
t.text "description"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "ci_application_settings", force: :cascade do |t|
t.boolean "all_broken_builds"
t.boolean "add_pusher"
t.datetime "created_at"
t.datetime "updated_at"
end
end
end
class MigrateBuildEventsToPipelineEvents < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
include Gitlab::Database
DOWNTIME = false
def up
Gitlab::Database.with_connection_pool(2) do |pool|
threads = []
threads << Thread.new do
pool.with_connection do |connection|
Thread.current[:foreign_key_connection] = connection
execute(<<-SQL.strip_heredoc)
UPDATE services
SET properties = replace(properties,
'notify_only_broken_builds',
'notify_only_broken_pipelines')
, pipeline_events = #{true_value}
, build_events = #{false_value}
WHERE type IN
('SlackService', 'MattermostService', 'HipchatService')
AND build_events = #{true_value};
SQL
end
end
threads << Thread.new do
pool.with_connection do |connection|
Thread.current[:foreign_key_connection] = connection
execute(update_pipeline_services_sql)
end
end
threads.each(&:join)
end
end
def down
# Don't bother to migrate the data back
end
def connection
# Rails memoizes connection objects, but this causes them to be shared
# amongst threads; we don't want that.
Thread.current[:foreign_key_connection] || ActiveRecord::Base.connection
end
private
def update_pipeline_services_sql
if Gitlab::Database.postgresql?
<<-SQL
UPDATE services
SET type = 'PipelinesEmailService'
, properties = replace(properties,
'notify_only_broken_builds',
'notify_only_broken_pipelines')
, pipeline_events = #{true_value}
, build_events = #{false_value}
WHERE type = 'BuildsEmailService'
AND
(SELECT 1 FROM services pipeline_services
WHERE pipeline_services.project_id = services.project_id
AND pipeline_services.type = 'PipelinesEmailService' LIMIT 1)
IS NULL;
SQL
else
<<-SQL
UPDATE services build_services
LEFT OUTER JOIN services pipeline_services
ON build_services.project_id = pipeline_services.project_id
AND pipeline_services.type = 'PipelinesEmailService'
SET build_services.type = 'PipelinesEmailService'
, build_services.properties = replace(build_services.properties,
'notify_only_broken_builds',
'notify_only_broken_pipelines')
, build_services.pipeline_events = #{true_value}
, build_services.build_events = #{false_value}
WHERE build_services.type = 'BuildsEmailService'
AND pipeline_services.id IS NULL;
SQL
end.strip_heredoc
end
end
......@@ -185,15 +185,7 @@ ActiveRecord::Schema.define(version: 20170315174634) do
add_index "chat_teams", ["namespace_id"], name: "index_chat_teams_on_namespace_id", unique: true, using: :btree
create_table "ci_application_settings", force: :cascade do |t|
t.boolean "all_broken_builds"
t.boolean "add_pusher"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "ci_builds", force: :cascade do |t|
t.integer "project_id"
t.string "status"
t.datetime "finished_at"
t.text "trace"
......@@ -204,9 +196,7 @@ ActiveRecord::Schema.define(version: 20170315174634) do
t.float "coverage"
t.integer "commit_id"
t.text "commands"
t.integer "job_id"
t.string "name"
t.boolean "deploy", default: false
t.text "options"
t.boolean "allow_failure", default: false, null: false
t.string "stage"
......@@ -219,7 +209,7 @@ ActiveRecord::Schema.define(version: 20170315174634) do
t.string "target_url"
t.string "description"
t.text "artifacts_file"
t.integer "gl_project_id"
t.integer "project_id"
t.text "artifacts_metadata"
t.integer "erased_by_id"
t.datetime "erased_at"
......@@ -238,25 +228,22 @@ ActiveRecord::Schema.define(version: 20170315174634) do
add_index "ci_builds", ["commit_id", "status", "type"], name: "index_ci_builds_on_commit_id_and_status_and_type", using: :btree
add_index "ci_builds", ["commit_id", "type", "name", "ref"], name: "index_ci_builds_on_commit_id_and_type_and_name_and_ref", using: :btree
add_index "ci_builds", ["commit_id", "type", "ref"], name: "index_ci_builds_on_commit_id_and_type_and_ref", using: :btree
add_index "ci_builds", ["gl_project_id"], name: "index_ci_builds_on_gl_project_id", using: :btree
add_index "ci_builds", ["project_id"], name: "index_ci_builds_on_project_id", using: :btree
add_index "ci_builds", ["runner_id"], name: "index_ci_builds_on_runner_id", using: :btree
add_index "ci_builds", ["status", "type", "runner_id"], name: "index_ci_builds_on_status_and_type_and_runner_id", using: :btree
add_index "ci_builds", ["status"], name: "index_ci_builds_on_status", using: :btree
add_index "ci_builds", ["token"], name: "index_ci_builds_on_token", unique: true, using: :btree
create_table "ci_commits", force: :cascade do |t|
t.integer "project_id"
create_table "ci_pipelines", force: :cascade do |t|
t.string "ref"
t.string "sha"
t.string "before_sha"
t.text "push_data"
t.datetime "created_at"
t.datetime "updated_at"
t.boolean "tag", default: false
t.text "yaml_errors"
t.datetime "committed_at"
t.integer "gl_project_id"
t.integer "project_id"
t.string "status"
t.datetime "started_at"
t.datetime "finished_at"
......@@ -265,67 +252,20 @@ ActiveRecord::Schema.define(version: 20170315174634) do
t.integer "lock_version"
end
add_index "ci_commits", ["gl_project_id", "ref", "status"], name: "index_ci_commits_on_gl_project_id_and_ref_and_status", using: :btree
add_index "ci_commits", ["gl_project_id", "sha"], name: "index_ci_commits_on_gl_project_id_and_sha", using: :btree
add_index "ci_commits", ["gl_project_id"], name: "index_ci_commits_on_gl_project_id", using: :btree
add_index "ci_commits", ["status"], name: "index_ci_commits_on_status", using: :btree
add_index "ci_commits", ["user_id"], name: "index_ci_commits_on_user_id", using: :btree
create_table "ci_events", force: :cascade do |t|
t.integer "project_id"
t.integer "user_id"
t.integer "is_admin"
t.text "description"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "ci_jobs", force: :cascade do |t|
t.integer "project_id", null: false
t.text "commands"
t.boolean "active", default: true, null: false
t.datetime "created_at"
t.datetime "updated_at"
t.string "name"
t.boolean "build_branches", default: true, null: false
t.boolean "build_tags", default: false, null: false
t.string "job_type", default: "parallel"
t.string "refs"
t.datetime "deleted_at"
end
create_table "ci_projects", force: :cascade do |t|
t.string "name"
t.integer "timeout", default: 3600, null: false
t.datetime "created_at"
t.datetime "updated_at"
t.string "token"
t.string "default_ref"
t.string "path"
t.boolean "always_build", default: false, null: false
t.integer "polling_interval"
t.boolean "public", default: false, null: false
t.string "ssh_url_to_repo"
t.integer "gitlab_id"
t.boolean "allow_git_fetch", default: true, null: false
t.string "email_recipients", default: "", null: false
t.boolean "email_add_pusher", default: true, null: false
t.boolean "email_only_broken_builds", default: true, null: false
t.string "skip_refs"
t.string "coverage_regex"
t.boolean "shared_runners_enabled", default: false
t.text "generated_yaml_config"
end
add_index "ci_pipelines", ["project_id", "ref", "status"], name: "index_ci_pipelines_on_project_id_and_ref_and_status", using: :btree
add_index "ci_pipelines", ["project_id", "sha"], name: "index_ci_pipelines_on_project_id_and_sha", using: :btree
add_index "ci_pipelines", ["project_id"], name: "index_ci_pipelines_on_project_id", using: :btree
add_index "ci_pipelines", ["status"], name: "index_ci_pipelines_on_status", using: :btree
add_index "ci_pipelines", ["user_id"], name: "index_ci_pipelines_on_user_id", using: :btree
create_table "ci_runner_projects", force: :cascade do |t|
t.integer "runner_id", null: false
t.integer "project_id"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "gl_project_id"
t.integer "project_id"
end
add_index "ci_runner_projects", ["gl_project_id"], name: "index_ci_runner_projects_on_gl_project_id", using: :btree
add_index "ci_runner_projects", ["project_id"], name: "index_ci_runner_projects_on_project_id", using: :btree
add_index "ci_runner_projects", ["runner_id"], name: "index_ci_runner_projects_on_runner_id", using: :btree
create_table "ci_runners", force: :cascade do |t|
......@@ -349,30 +289,6 @@ ActiveRecord::Schema.define(version: 20170315174634) do
add_index "ci_runners", ["locked"], name: "index_ci_runners_on_locked", using: :btree
add_index "ci_runners", ["token"], name: "index_ci_runners_on_token", using: :btree
create_table "ci_sessions", force: :cascade do |t|
t.string "session_id", null: false
t.text "data"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "ci_taggings", force: :cascade do |t|
t.integer "tag_id"
t.integer "taggable_id"
t.string "taggable_type"
t.integer "tagger_id"
t.string "tagger_type"
t.string "context", limit: 128
t.datetime "created_at"
end
add_index "ci_taggings", ["taggable_id", "taggable_type", "context"], name: "index_ci_taggings_on_taggable_id_and_taggable_type_and_context", using: :btree
create_table "ci_tags", force: :cascade do |t|
t.string "name"
t.integer "taggings_count", default: 0
end
create_table "ci_trigger_requests", force: :cascade do |t|
t.integer "trigger_id", null: false
t.text "variables"
......@@ -385,28 +301,26 @@ ActiveRecord::Schema.define(version: 20170315174634) do
create_table "ci_triggers", force: :cascade do |t|
t.string "token"
t.integer "project_id"
t.datetime "deleted_at"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "gl_project_id"
t.integer "project_id"
t.integer "owner_id"
t.string "description"
end
add_index "ci_triggers", ["gl_project_id"], name: "index_ci_triggers_on_gl_project_id", using: :btree
add_index "ci_triggers", ["project_id"], name: "index_ci_triggers_on_project_id", using: :btree
create_table "ci_variables", force: :cascade do |t|
t.integer "project_id"
t.string "key"
t.text "value"
t.text "encrypted_value"
t.string "encrypted_value_salt"
t.string "encrypted_value_iv"
t.integer "gl_project_id"
t.integer "project_id"
end
add_index "ci_variables", ["gl_project_id"], name: "index_ci_variables_on_gl_project_id", using: :btree
add_index "ci_variables", ["project_id"], name: "index_ci_variables_on_project_id", using: :btree
create_table "deploy_keys_projects", force: :cascade do |t|
t.integer "deploy_key_id", null: false
......@@ -1378,7 +1292,7 @@ ActiveRecord::Schema.define(version: 20170315174634) do
add_foreign_key "labels", "namespaces", column: "group_id", on_delete: :cascade
add_foreign_key "lists", "boards"
add_foreign_key "lists", "labels"
add_foreign_key "merge_request_metrics", "ci_commits", column: "pipeline_id", on_delete: :cascade
add_foreign_key "merge_request_metrics", "ci_pipelines", column: "pipeline_id", on_delete: :cascade
add_foreign_key "merge_request_metrics", "merge_requests", on_delete: :cascade
add_foreign_key "merge_requests_closing_issues", "issues", on_delete: :cascade
add_foreign_key "merge_requests_closing_issues", "merge_requests", on_delete: :cascade
......
......@@ -139,43 +139,6 @@ Get Buildkite service settings for a project.
GET /projects/:id/services/buildkite
```
## Build-Emails
Get emails for GitLab CI builds.
### Create/Edit Build-Emails service
Set Build-Emails service for a project.
```
PUT /projects/:id/services/jobs-email
```
Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `recipients` | string | yes | Comma-separated list of recipient email addresses |
| `add_pusher` | boolean | no | Add pusher to recipients list |
| `notify_only_broken_jobs` | boolean | no | Notify only broken jobs |
### Delete Job-Emails service
Delete Build-Emails service for a project.
```
DELETE /projects/:id/services/jobs-email
```
### Get Job-Emails service settings
Get Build-Emails service settings for a project.
```
GET /projects/:id/services/jobs-email
```
## Campfire
Simple web-based real-time group chat
......@@ -580,8 +543,7 @@ Parameters:
| --------- | ---- | -------- | ----------- |
| `recipients` | string | yes | Comma-separated list of recipient email addresses |
| `add_pusher` | boolean | no | Add pusher to recipients list |
| `notify_only_broken_jobs` | boolean | no | Notify only broken pipelines |
| `notify_only_broken_pipelines` | boolean | no | Notify only broken pipelines |
### Delete Pipeline-Emails service
......
......@@ -207,15 +207,6 @@ you expected.
You are also able to view the status of any commit in the various pages in
GitLab, such as **Commits** and **Merge requests**.
## Enabling build emails
If you want to receive e-mail notifications about the result status of the
jobs, you should explicitly enable the **Builds Emails** service under your
project's settings.
For more information read the
[Builds emails service documentation](../../user/project/integrations/builds_emails.md).
## Examples
Visit the [examples README][examples] to see a list of examples using GitLab
......
This document was moved to [user/project/integrations/builds_emails.md](../user/project/integrations/builds_emails.md).
# Enabling build emails
By enabling this service, you will be able to receive e-mail notifications about
the result status of your builds.
Navigate to the [Integrations page](project_services.md#accessing-the-project-services)
and select the **Builds emails** service to configure it.
In the _Recipients_ area, provide a list of e-mails separated by comma.
Check the _Add pusher_ checkbox if you want the committer to also receive
e-mail notifications about each build's status.
If you enable the _Notify only broken builds_ option, e-mail notifications will
be sent only for failed builds.
......@@ -28,7 +28,6 @@ There, you will see a checkbox with the following events that can be triggered:
- Merge request
- Note
- Tag push
- Build
- Pipeline
- Wiki page
......@@ -41,7 +40,6 @@ At the end, fill in your Mattermost details:
| ----- | ----------- |
| **Webhook** | The incoming webhook URL which you have to setup on Mattermost, it will be something like: http://mattermost.example/hooks/5xo… |
| **Username** | Optional username which can be on messages sent to Mattermost. Fill this in if you want to change the username of the bot. |
| **Notify only broken builds** | If you choose to enable the **Build** event and you want to be only notified about failed builds. |
| **Notify only broken pipelines** | If you choose to enable the **Pipeline** event and you want to be only notified about failed pipelines. |
![Mattermost configuration](img/mattermost_configuration.png)
......@@ -32,7 +32,6 @@ Click on the service links to see further configuration instructions and details
| Assembla | Project Management Software (Source Commits Endpoint) |
| [Atlassian Bamboo CI](bamboo.md) | A continuous integration and build server |
| Buildkite | Continuous integration and deployments |
| [Builds emails](builds_emails.md) | Email the builds status to a list of recipients |
| [Bugzilla](bugzilla.md) | Bugzilla issue tracker |
| Campfire | Simple web-based real-time group chat |
| Custom Issue Tracker | Custom issue tracker |
......@@ -48,6 +47,7 @@ Click on the service links to see further configuration instructions and details
| [Kubernetes](kubernetes.md) | A containerized deployment service |
| [Mattermost slash commands](mattermost_slash_commands.md) | Mattermost chat and ChatOps slash commands |
| [Mattermost Notifications](mattermost.md) | Receive event notifications in Mattermost |
| Pipelines emails | Email the pipeline status to a list of recipients |
| [Slack Notifications](slack.md) | Receive event notifications in Slack |
| [Slack slash commands](slack_slash_commands.md) | Slack chat and ChatOps slash commands |
| PivotalTracker | Project Management Software (Source Commits Endpoint) |
......
......@@ -25,7 +25,6 @@ There, you will see a checkbox with the following events that can be triggered:
- Merge request
- Note
- Tag push
- Build
- Pipeline
- Wiki page
......@@ -38,7 +37,6 @@ At the end, fill in your Slack details:
| ----- | ----------- |
| **Webhook** | The [incoming webhook URL][slackhook] which you have to setup on Slack. |
| **Username** | Optional username which can be on messages sent to Slack. Fill this in if you want to change the username of the bot. |
| **Notify only broken builds** | If you choose to enable the **Build** event and you want to be only notified about failed builds. |
| **Notify only broken pipelines** | If you choose to enable the **Pipeline** event and you want to be only notified about failed pipelines. |
After you are all done, click **Save changes** for the changes to take effect.
......
......@@ -107,26 +107,6 @@ module API
desc: 'Enable SSL verification for communication'
}
],
'builds-email' => [
{
required: true,
name: :recipients,
type: String,
desc: 'Comma-separated list of recipient email addresses'
},
{
required: false,
name: :add_pusher,
type: Boolean,
desc: 'Add pusher to recipients list'
},
{
required: false,
name: :notify_only_broken_jobs,
type: Boolean,
desc: 'Notify only broken jobs'
}
],
'campfire' => [
{
required: true,
......@@ -403,9 +383,9 @@ module API
},
{
required: false,
name: :notify_only_broken_jobs,
name: :notify_only_broken_pipelines,
type: Boolean,
desc: 'Notify only broken jobs'
desc: 'Notify only broken pipelines'
}
],
'pivotaltracker' => [
......@@ -550,7 +530,6 @@ module API
BambooService,
BugzillaService,
BuildkiteService,
BuildsEmailService,
CampfireService,
CustomIssueTrackerService,
DroneCiService,
......
......@@ -73,6 +73,9 @@ excluded_attributes:
- :milestone_id
award_emoji:
- :awardable_id
statuses:
- :trace
- :token
methods:
labels:
......@@ -81,6 +84,7 @@ methods:
- :type
statuses:
- :type
- :gl_project_id
services:
- :type
merge_request_diff:
......
......@@ -15,7 +15,7 @@ module Gitlab
USER_REFERENCES = %w[author_id assignee_id updated_by_id user_id created_by_id merge_user_id resolved_by_id].freeze
PROJECT_REFERENCES = %w[project_id source_project_id gl_project_id target_project_id].freeze
PROJECT_REFERENCES = %w[project_id source_project_id target_project_id].freeze
BUILD_MODELS = %w[Ci::Build commit_status].freeze
......@@ -98,12 +98,11 @@ module Gitlab
end
def generate_imported_object
if BUILD_MODELS.include?(@relation_name) # call #trace= method after assigning the other attributes
trace = @relation_hash.delete('trace')
if BUILD_MODELS.include?(@relation_name)
@relation_hash.delete('trace') # old export files have trace
@relation_hash.delete('token')
imported_object do |object|
object.trace = trace
object.commit_id = nil
end
else
......@@ -121,7 +120,6 @@ module Gitlab
# project_id may not be part of the export, but we always need to populate it if required.
@relation_hash['project_id'] = project_id
@relation_hash['gl_project_id'] = project_id if @relation_hash['gl_project_id']
@relation_hash['target_project_id'] = project_id if @relation_hash['target_project_id']
end
......
......@@ -35,7 +35,7 @@ describe Projects::VariablesController do
context 'updating a variable with valid characters' do
before do
variable.gl_project_id = project.id
variable.project_id = project.id
project.variables << variable
end
......
FactoryGirl.define do
factory :ci_runner_project, class: Ci::RunnerProject do
runner_id 1
gl_project_id 1
project_id 1
end
end
......@@ -26,7 +26,7 @@ feature 'Admin updates settings', feature: true do
fill_in 'Webhook', with: 'http://localhost'
fill_in 'Username', with: 'test_user'
fill_in 'service_push_channel', with: '#test_channel'
page.check('Notify only broken builds')
page.check('Notify only broken pipelines')
check_all_events
click_on 'Save'
......@@ -50,7 +50,6 @@ feature 'Admin updates settings', feature: true do
page.check('Note')
page.check('Issue')
page.check('Merge request')
page.check('Build')
page.check('Pipeline')
end
end
......@@ -7,7 +7,7 @@ feature 'Projects > Slack service > Setup events', feature: true do
background do
service.fields
service.update_attributes(push_channel: 1, issue_channel: 2, merge_request_channel: 3, note_channel: 4, tag_push_channel: 5, build_channel: 6, wiki_page_channel: 7)
service.update_attributes(push_channel: 1, issue_channel: 2, merge_request_channel: 3, note_channel: 4, tag_push_channel: 5, pipeline_channel: 6, wiki_page_channel: 7)
project.team << [user, :master]
login_as(user)
end
......@@ -20,7 +20,7 @@ feature 'Projects > Slack service > Setup events', feature: true do
expect(page.find_field("service_merge_request_channel").value).to have_content '3'
expect(page.find_field("service_note_channel").value).to have_content '4'
expect(page.find_field("service_tag_push_channel").value).to have_content '5'
expect(page.find_field("service_build_channel").value).to have_content '6'
expect(page.find_field("service_pipeline_channel").value).to have_content '6'
expect(page.find_field("service_wiki_page_channel").value).to have_content '7'
end
end
......@@ -130,7 +130,6 @@ project:
- campfire_service
- drone_ci_service
- emails_on_push_service
- builds_email_service
- pipelines_email_service
- mattermost_slash_commands_service
- slack_slash_commands_service
......
......@@ -6507,7 +6507,6 @@
"tag": null,
"yaml_errors": null,
"committed_at": null,
"gl_project_id": 5,
"status": "failed",
"started_at": null,
"finished_at": null,
......@@ -6565,7 +6564,6 @@
"artifacts_file": {
"url": null
},
"gl_project_id": 5,
"artifacts_metadata": {
"url": null
},
......@@ -6603,7 +6601,6 @@
"artifacts_file": {
"url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/72/p5_build_artifacts.zip"
},
"gl_project_id": 5,
"artifacts_metadata": {
"url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/72/p5_build_artifacts_metadata.gz"
},
......@@ -6624,7 +6621,6 @@
"tag": null,
"yaml_errors": null,
"committed_at": null,
"gl_project_id": 5,
"status": "failed",
"started_at": null,
"finished_at": null,
......@@ -6659,7 +6655,6 @@
"artifacts_file": {
"url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/74/p5_build_artifacts.zip"
},
"gl_project_id": 5,
"artifacts_metadata": {
"url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/74/p5_build_artifacts_metadata.gz"
},
......@@ -6695,7 +6690,6 @@
"artifacts_file": {
"url": null
},
"gl_project_id": 5,
"artifacts_metadata": {
"url": null
},
......@@ -6716,7 +6710,6 @@
"tag": null,
"yaml_errors": null,
"committed_at": null,
"gl_project_id": 5,
"status": "failed",
"started_at": null,
"finished_at": null,
......@@ -6751,7 +6744,6 @@
"artifacts_file": {
"url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/76/p5_build_artifacts.zip"
},
"gl_project_id": 5,
"artifacts_metadata": {
"url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/76/p5_build_artifacts_metadata.gz"
},
......@@ -6787,7 +6779,6 @@
"artifacts_file": {
"url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/75/p5_build_artifacts.zip"
},
"gl_project_id": 5,
"artifacts_metadata": {
"url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/75/p5_build_artifacts_metadata.gz"
},
......@@ -6808,7 +6799,6 @@
"tag": null,
"yaml_errors": null,
"committed_at": null,
"gl_project_id": 5,
"status": "failed",
"started_at": null,
"finished_at": null,
......@@ -6843,7 +6833,6 @@
"artifacts_file": {
"url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/78/p5_build_artifacts.zip"
},
"gl_project_id": 5,
"artifacts_metadata": {
"url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/78/p5_build_artifacts_metadata.gz"
},
......@@ -6879,7 +6868,6 @@
"artifacts_file": {
"url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/77/p5_build_artifacts.zip"
},
"gl_project_id": 5,
"artifacts_metadata": {
"url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/77/p5_build_artifacts_metadata.gz"
},
......@@ -6900,7 +6888,6 @@
"tag": null,
"yaml_errors": null,
"committed_at": null,
"gl_project_id": 5,
"status": "failed",
"started_at": null,
"finished_at": null,
......@@ -6935,7 +6922,6 @@
"artifacts_file": {
"url": null
},
"gl_project_id": 5,
"artifacts_metadata": {
"url": null
},
......@@ -6971,7 +6957,6 @@
"artifacts_file": {
"url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/80/p5_build_artifacts.zip"
},
"gl_project_id": 5,
"artifacts_metadata": {
"url": "/Users/Test/Test/gitlab-development-kit/gitlab/shared/artifacts/2016_03/5/80/p5_build_artifacts_metadata.gz"
},
......@@ -6985,11 +6970,10 @@
{
"id": 123,
"token": "cdbfasdf44a5958c83654733449e585",
"project_id": null,
"project_id": 5,
"deleted_at": null,
"created_at": "2017-01-16T15:25:28.637Z",
"updated_at": "2017-01-16T15:25:28.637Z",
"gl_project_id": 123
"updated_at": "2017-01-16T15:25:28.637Z"
}
],
"deploy_keys": [
......@@ -7047,7 +7031,7 @@
"updated_at": "2016-06-14T15:01:51.303Z",
"active": false,
"properties": {
"notify_only_broken_builds": true
"notify_only_broken_pipelines": true
},
"template": false,
"push_events": true,
......@@ -7055,7 +7039,7 @@
"merge_requests_events": true,
"tag_push_events": true,
"note_events": true,
"build_events": true,
"pipeline_events": true,
"category": "common",
"default": false,
"wiki_page_events": true
......@@ -7174,7 +7158,7 @@
"updated_at": "2016-06-14T15:01:51.219Z",
"active": false,
"properties": {
"notify_only_broken_builds": true
"notify_only_broken_pipelines": true
},
"template": false,
"push_events": true,
......@@ -7182,7 +7166,7 @@
"merge_requests_events": true,
"tag_push_events": true,
"note_events": true,
"build_events": true,
"pipeline_events": true,
"category": "common",
"default": false,
"wiki_page_events": true
......@@ -7334,27 +7318,6 @@
"default": false,
"wiki_page_events": true
},
{
"id": 85,
"title": "Builds emails",
"project_id": 5,
"created_at": "2016-06-14T15:01:51.090Z",
"updated_at": "2016-06-14T15:01:51.090Z",
"active": false,
"properties": {
"notify_only_broken_builds": true
},
"template": false,
"push_events": true,
"issues_events": true,
"merge_requests_events": true,
"tag_push_events": true,
"note_events": true,
"build_events": true,
"category": "common",
"default": false,
"wiki_page_events": true
},
{
"id": 84,
"title": "Buildkite",
......
......@@ -129,6 +129,25 @@ describe Gitlab::ImportExport::ProjectTreeRestorer, services: true do
expect(Ci::Build.where(token: 'abcd')).to be_empty
end
end
context 'has restored the correct number of records' do
it 'has the correct number of merge requests' do
expect(@project.merge_requests.size).to eq(9)
end
it 'has the correct number of triggers' do
expect(@project.triggers.size).to eq(1)
end
it 'has the correct number of pipelines and statuses' do
expect(@project.pipelines.size).to eq(5)
@project.pipelines.zip([2, 2, 2, 2, 2])
.each do |(pipeline, expected_status_size)|
expect(pipeline.statuses.size).to eq(expected_status_size)
end
end
end
end
end
......
......@@ -176,7 +176,6 @@ Ci::Pipeline:
- tag
- yaml_errors
- committed_at
- gl_project_id
- status
- started_at
- finished_at
......@@ -211,7 +210,6 @@ CommitStatus:
- target_url
- description
- artifacts_file
- gl_project_id
- artifacts_metadata
- erased_by_id
- erased_at
......@@ -232,7 +230,6 @@ Ci::Variable:
- encrypted_value
- encrypted_value_salt
- encrypted_value_iv
- gl_project_id
Ci::Trigger:
- id
- token
......@@ -240,7 +237,6 @@ Ci::Trigger:
- deleted_at
- created_at
- updated_at
- gl_project_id
- owner_id
- description
DeployKey:
......
require 'spec_helper'
require 'email_spec'
describe Notify do
include EmailSpec::Matchers
include_context 'gitlab email notification'
describe 'build notification email' do
let(:build) { create(:ci_build) }
let(:project) { build.project }
shared_examples 'build email' do
it 'contains name of project' do
is_expected.to have_body_text build.project_name
end
it 'contains link to project' do
is_expected.to have_body_text namespace_project_path(project.namespace, project)
end
end
shared_examples 'an email with X-GitLab headers containing build details' do
it 'has X-GitLab-Build* headers' do
is_expected.to have_header 'X-GitLab-Build-Id', /#{build.id}/
is_expected.to have_header 'X-GitLab-Build-Ref', /#{build.ref}/
end
end
describe 'build success' do
subject { Notify.build_success_email(build.id, 'wow@example.com') }
before { build.success }
it_behaves_like 'build email'
it_behaves_like 'an email with X-GitLab headers containing build details'
it_behaves_like 'an email with X-GitLab headers containing project details'
it 'has header indicating build status' do
is_expected.to have_header 'X-GitLab-Build-Status', 'success'
end
it 'has the correct subject' do
is_expected.to have_subject /Build success for/
end
end
describe 'build fail' do
subject { Notify.build_fail_email(build.id, 'wow@example.com') }
before { build.drop }
it_behaves_like 'build email'
it_behaves_like 'an email with X-GitLab headers containing build details'
it_behaves_like 'an email with X-GitLab headers containing project details'
it 'has header indicating build status' do
is_expected.to have_header 'X-GitLab-Build-Status', 'failed'
end
it 'has the correct subject' do
is_expected.to have_subject /Build failed for/
end
end
end
end
require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20170301205640_migrate_build_events_to_pipeline_events.rb')
# This migration uses multiple threads, and thus different transactions. This
# means data created in this spec may not be visible to some threads. To work
# around this we use the TRUNCATE cleaning strategy.
describe MigrateBuildEventsToPipelineEvents, truncate: true do
let(:migration) { described_class.new }
let(:project_with_pipeline_service) { create(:empty_project) }
let(:project_with_build_service) { create(:empty_project) }
before do
ActiveRecord::Base.connection.execute <<-SQL
INSERT INTO services (properties, build_events, pipeline_events, type)
VALUES
('{"notify_only_broken_builds":true}', true, false, 'SlackService')
, ('{"notify_only_broken_builds":true}', true, false, 'MattermostService')
, ('{"notify_only_broken_builds":true}', true, false, 'HipchatService')
;
SQL
ActiveRecord::Base.connection.execute <<-SQL
INSERT INTO services
(properties, build_events, pipeline_events, type, project_id)
VALUES
('{"notify_only_broken_builds":true}', true, false,
'BuildsEmailService', #{project_with_pipeline_service.id})
, ('{"notify_only_broken_pipelines":true}', false, true,
'PipelinesEmailService', #{project_with_pipeline_service.id})
, ('{"notify_only_broken_builds":true}', true, false,
'BuildsEmailService', #{project_with_build_service.id})
;
SQL
end
describe '#up' do
before do
silence_migration = Module.new do
# rubocop:disable Rails/Delegate
def execute(query)
connection.execute(query)
end
end
migration.extend(silence_migration)
migration.up
end
it 'migrates chat service properly' do
[SlackService, MattermostService, HipchatService].each do |service|
expect(service.count).to eq(1)
verify_service_record(service.first)
end
end
it 'migrates pipelines email service only if it has none before' do
Project.find_each do |project|
pipeline_service_count =
project.services.where(type: 'PipelinesEmailService').count
expect(pipeline_service_count).to eq(1)
verify_service_record(project.pipelines_email_service)
end
end
def verify_service_record(service)
expect(service.notify_only_broken_pipelines).to be(true)
expect(service.build_events).to be(false)
expect(service.pipeline_events).to be(true)
end
end
end
......@@ -795,8 +795,8 @@ describe Ci::Build, :models do
describe '#merge_request' do
def create_mr(build, pipeline, factory: :merge_request, created_at: Time.now)
create(factory, source_project_id: pipeline.gl_project_id,
target_project_id: pipeline.gl_project_id,
create(factory, source_project: pipeline.project,
target_project: pipeline.project,
source_branch: build.ref,
created_at: created_at)
end
......
......@@ -6,7 +6,7 @@ describe Ci::Variable, models: true do
let(:secret_value) { 'secret' }
it { is_expected.to validate_presence_of(:key) }
it { is_expected.to validate_uniqueness_of(:key).scoped_to(:gl_project_id) }
it { is_expected.to validate_uniqueness_of(:key).scoped_to(:project_id) }
it { is_expected.to validate_length_of(:key).is_at_most(255) }
it { is_expected.to allow_value('foo').for(:key) }
it { is_expected.not_to allow_value('foo bar').for(:key) }
......
require 'spec_helper'
describe BuildsEmailService do
let(:data) do
Gitlab::DataBuilder::Build.build(create(:ci_build))
end
describe 'Validations' do
context 'when service is active' do
before { subject.active = true }
it { is_expected.to validate_presence_of(:recipients) }
context 'when pusher is added' do
before { subject.add_pusher = true }
it { is_expected.not_to validate_presence_of(:recipients) }
end
end
context 'when service is inactive' do
before { subject.active = false }
it { is_expected.not_to validate_presence_of(:recipients) }
end
end
describe '#test_data' do
let(:build) { create(:ci_build) }
let(:project) { build.project }
let(:user) { create(:user) }
before { project.team << [user, :developer] }
it 'builds test data' do
data = subject.test_data(project)
expect(data[:object_kind]).to eq("build")
end
end
describe '#test' do
it 'sends email' do
data = Gitlab::DataBuilder::Build.build(create(:ci_build))
subject.recipients = 'test@gitlab.com'
expect(BuildEmailWorker).to receive(:perform_async)
subject.test(data)
end
context 'notify only failed builds is true' do
it 'sends email' do
data = Gitlab::DataBuilder::Build.build(create(:ci_build))
data[:build_status] = "success"
subject.recipients = 'test@gitlab.com'
expect(subject).not_to receive(:notify_only_broken_builds)
expect(BuildEmailWorker).to receive(:perform_async)
subject.test(data)
end
end
end
describe '#execute' do
it 'sends email' do
subject.recipients = 'test@gitlab.com'
data[:build_status] = 'failed'
expect(BuildEmailWorker).to receive(:perform_async)
subject.execute(data)
end
it 'does not send email with succeeded build and notify_only_broken_builds on' do
expect(subject).to receive(:notify_only_broken_builds).and_return(true)
data[:build_status] = 'success'
expect(BuildEmailWorker).not_to receive(:perform_async)
subject.execute(data)
end
it 'does not send email with failed build and build_allow_failure is true' do
data[:build_status] = 'failed'
data[:build_allow_failure] = true
expect(BuildEmailWorker).not_to receive(:perform_async)
subject.execute(data)
end
it 'does not send email with unknown build status' do
data[:build_status] = 'foo'
expect(BuildEmailWorker).not_to receive(:perform_async)
subject.execute(data)
end
it 'does not send email when recipients list is empty' do
subject.recipients = ' ,, '
data[:build_status] = 'failed'
expect(BuildEmailWorker).not_to receive(:perform_async)
subject.execute(data)
end
end
end
require 'spec_helper'
describe ChatMessage::BuildMessage do
subject { described_class.new(args) }
let(:args) do
{
sha: '97de212e80737a608d939f648d959671fb0a0142',
ref: 'develop',
tag: false,
project_name: 'project_name',
project_url: 'http://example.gitlab.com',
build_id: 1,
build_name: build_name,
build_stage: stage,
commit: {
status: status,
author_name: 'hacker',
author_url: 'http://example.gitlab.com/hacker',
duration: duration,
},
}
end
let(:message) { build_message }
let(:stage) { 'test' }
let(:status) { 'success' }
let(:build_name) { 'rspec' }
let(:duration) { 10 }
context 'build succeeded' do
let(:status) { 'success' }
let(:color) { 'good' }
let(:message) { build_message('passed') }
it 'returns a message with information about succeeded build' do
expect(subject.pretext).to be_empty
expect(subject.fallback).to eq(message)
expect(subject.attachments).to eq([text: message, color: color])
end
end
context 'build failed' do
let(:status) { 'failed' }
let(:color) { 'danger' }
it 'returns a message with information about failed build' do
expect(subject.pretext).to be_empty
expect(subject.fallback).to eq(message)
expect(subject.attachments).to eq([text: message, color: color])
end
end
it 'returns a message with information on build' do
expect(subject.fallback).to include("on build <http://example.gitlab.com/builds/1|#{build_name}>")
end
it 'returns a message with stage name' do
expect(subject.fallback).to include("of stage #{stage}")
end
it 'returns a message with link to author' do
expect(subject.fallback).to include("by <http://example.gitlab.com/hacker|hacker>")
end
def build_message(status_text = status, stage_text = stage, build_text = build_name)
"<http://example.gitlab.com|project_name>:" \
" Commit <http://example.gitlab.com/commit/" \
"97de212e80737a608d939f648d959671fb0a0142/builds|97de212e>" \
" of <http://example.gitlab.com/commits/develop|develop> branch" \
" by <http://example.gitlab.com/hacker|hacker> #{status_text}" \
" on build <http://example.gitlab.com/builds/1|#{build_text}>" \
" of stage #{stage_text} in #{duration} #{'second'.pluralize(duration)}"
end
end
......@@ -280,13 +280,14 @@ describe HipchatService, models: true do
end
end
context 'build events' do
let(:pipeline) { create(:ci_empty_pipeline) }
let(:build) { create(:ci_build, pipeline: pipeline) }
let(:data) { Gitlab::DataBuilder::Build.build(build.reload) }
context 'pipeline events' do
let(:pipeline) { create(:ci_empty_pipeline, user: create(:user)) }
let(:data) { Gitlab::DataBuilder::Pipeline.build(pipeline) }
context 'for failed' do
before { build.drop }
before do
pipeline.drop
end
it "calls Hipchat API" do
hipchat.execute(data)
......@@ -295,35 +296,36 @@ describe HipchatService, models: true do
end
it "creates a build message" do
message = hipchat.send(:create_build_message, data)
message = hipchat.__send__(:create_pipeline_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]
pipeline_attributes = data[:object_attributes]
ref = pipeline_attributes[:ref]
ref_type = pipeline_attributes[:tag] ? 'tag' : 'branch'
duration = pipeline_attributes[:duration]
user_name = data[:user][:name]
expect(message).to eq("<a href=\"#{project_url}\">#{project_name}</a>: " \
"Commit <a href=\"#{project_url}/commit/#{sha}/builds\">#{Commit.truncate_sha(sha)}</a> " \
"Pipeline <a href=\"#{project_url}/pipelines/#{pipeline.id}\">##{pipeline.id}</a> " \
"of <a href=\"#{project_url}/commits/#{ref}\">#{ref}</a> #{ref_type} " \
"by #{data[:commit][:author_name]} failed in #{duration} second(s)")
"by #{user_name} failed in #{duration} second(s)")
end
end
context 'for succeeded' do
before do
build.success
pipeline.succeed
end
it "calls Hipchat API" do
hipchat.notify_only_broken_builds = false
hipchat.notify_only_broken_pipelines = false
hipchat.execute(data)
expect(WebMock).to have_requested(:post, api_url).once
end
it "notifies only broken" do
hipchat.notify_only_broken_builds = true
hipchat.notify_only_broken_pipelines = true
hipchat.execute(data)
expect(WebMock).not_to have_requested(:post, api_url).once
end
......@@ -349,17 +351,19 @@ describe HipchatService, models: true do
context 'with a successful build' do
it 'uses the green color' do
build_data = { object_kind: 'build', commit: { status: 'success' } }
data = { object_kind: 'pipeline',
object_attributes: { status: 'success' } }
expect(hipchat.__send__(:message_options, build_data)).to eq({ notify: false, color: 'green' })
expect(hipchat.__send__(:message_options, data)).to eq({ notify: false, color: 'green' })
end
end
context 'with a failed build' do
it 'uses the red color' do
build_data = { object_kind: 'build', commit: { status: 'failed' } }
data = { object_kind: 'pipeline',
object_attributes: { status: 'failed' } }
expect(hipchat.__send__(:message_options, build_data)).to eq({ notify: false, color: 'red' })
expect(hipchat.__send__(:message_options, data)).to eq({ notify: false, color: 'red' })
end
end
end
......
......@@ -29,8 +29,7 @@ describe Project, models: true do
it { is_expected.to have_one(:campfire_service).dependent(:destroy) }
it { is_expected.to have_one(:drone_ci_service).dependent(:destroy) }
it { is_expected.to have_one(:emails_on_push_service).dependent(:destroy) }
it { is_expected.to have_one(:builds_email_service).dependent(:destroy) }
it { is_expected.to have_one(:emails_on_push_service).dependent(:destroy) }
it { is_expected.to have_one(:pipelines_email_service).dependent(:destroy) }
it { is_expected.to have_one(:irker_service).dependent(:destroy) }
it { is_expected.to have_one(:pivotaltracker_service).dependent(:destroy) }
it { is_expected.to have_one(:hipchat_service).dependent(:destroy) }
......
......@@ -19,7 +19,7 @@ describe Ci::RetryBuildService, :services do
erased_at].freeze
IGNORE_ACCESSORS =
%i[type lock_version target_url gl_project_id deploy job_id base_tags
%i[type lock_version target_url base_tags
commit_id deployments erased_by_id last_deployment project_id
runner_id tag_taggings taggings tags trigger_request_id
user_id].freeze
......
require 'spec_helper'
describe BuildEmailWorker do
include EmailHelpers
include RepoHelpers
let(:build) { create(:ci_build) }
let(:user) { create(:user) }
let(:data) { Gitlab::DataBuilder::Build.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
reset_delivered_emails!
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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment