Skip to content
Snippets Groups Projects
Commit 12eb5e20 authored by Jason Goodman's avatar Jason Goodman Committed by Douglas Barbosa Alexandre
Browse files

Add deployment events to chat notification services

This enables sending a chat message to Slack or Mattermost
  upon a successful, failed, or canceled deployment
Port of merge request gitlab-org/gitlab-ce!27338
parent 41172099
No related branches found
No related tags found
No related merge requests found
Showing
with 220 additions and 5 deletions
Loading
Loading
@@ -47,6 +47,12 @@ class Deployment < ApplicationRecord
Deployments::SuccessWorker.perform_async(id)
end
end
after_transition any => [:success, :failed, :canceled] do |deployment|
deployment.run_after_commit do
Deployments::FinishedWorker.perform_async(id)
end
end
end
 
enum status: {
Loading
Loading
@@ -82,6 +88,11 @@ def cluster
project.deployment_platform(environment: environment.name)&.cluster
end
 
def execute_hooks
deployment_data = Gitlab::DataBuilder::Deployment.build(self)
project.execute_services(deployment_data, :deployment_hooks)
end
def last?
self == environment.last_deployment
end
Loading
Loading
# frozen_string_literal: true
module ChatMessage
class DeploymentMessage < BaseMessage
attr_reader :commit_url
attr_reader :deployable_id
attr_reader :deployable_url
attr_reader :environment
attr_reader :short_sha
attr_reader :status
def initialize(data)
super
@commit_url = data[:commit_url]
@deployable_id = data[:deployable_id]
@deployable_url = data[:deployable_url]
@environment = data[:environment]
@short_sha = data[:short_sha]
@status = data[:status]
end
def attachments
[{
text: "#{project_link}\n#{deployment_link}, SHA #{commit_link}, by #{user_combined_name}",
color: color
}]
end
def activity
{}
end
private
def message
"Deploy to #{environment} #{humanized_status}"
end
def color
case status
when 'success'
'good'
when 'canceled'
'warning'
when 'failed'
'danger'
else
'#334455'
end
end
def project_link
link(project_name, project_url)
end
def deployment_link
link("Job ##{deployable_id}", deployable_url)
end
def commit_link
link(short_sha, commit_url)
end
def humanized_status
status == 'success' ? 'succeeded' : status
end
end
end
Loading
Loading
@@ -33,7 +33,7 @@ def confidential_note_channel
 
def self.supported_events
%w[push issue confidential_issue merge_request note confidential_note tag_push
pipeline wiki_page]
pipeline wiki_page deployment]
end
 
def fields
Loading
Loading
@@ -122,6 +122,8 @@ def get_message(object_kind, data)
ChatMessage::PipelineMessage.new(data) if should_pipeline_be_notified?(data)
when "wiki_page"
ChatMessage::WikiPageMessage.new(data)
when "deployment"
ChatMessage::DeploymentMessage.new(data)
end
end
 
Loading
Loading
Loading
Loading
@@ -33,6 +33,11 @@ def default_channel_placeholder
# No-op.
end
 
def self.supported_events
%w[push issue confidential_issue merge_request note confidential_note tag_push
pipeline wiki_page]
end
def default_fields
[
{ type: "text", name: "webhook", placeholder: "e.g. https://discordapp.com/api/webhooks/…" },
Loading
Loading
Loading
Loading
@@ -35,6 +35,11 @@ def webhook_placeholder
'https://chat.googleapis.com/v1/spaces…'
end
 
def self.supported_events
%w[push issue confidential_issue merge_request note confidential_note tag_push
pipeline wiki_page]
end
def default_fields
[
{ type: 'text', name: 'webhook', placeholder: "e.g. #{webhook_placeholder}" },
Loading
Loading
Loading
Loading
@@ -33,6 +33,11 @@ def event_field(event)
def default_channel_placeholder
end
 
def self.supported_events
%w[push issue confidential_issue merge_request note confidential_note tag_push
pipeline wiki_page]
end
def default_fields
[
{ type: 'text', name: 'webhook', placeholder: "e.g. #{webhook_placeholder}" },
Loading
Loading
Loading
Loading
@@ -50,6 +50,7 @@ class Service < ApplicationRecord
scope :job_hooks, -> { where(job_events: true, active: true) }
scope :pipeline_hooks, -> { where(pipeline_events: true, active: true) }
scope :wiki_page_hooks, -> { where(wiki_page_events: true, active: true) }
scope :deployment_hooks, -> { where(deployment_events: true, active: true) }
scope :external_issue_trackers, -> { issue_trackers.active.without_defaults }
scope :deployment, -> { where(category: 'deployment') }
 
Loading
Loading
@@ -335,6 +336,8 @@ def self.event_description(event)
"Event will be triggered when a wiki page is created/updated"
when "commit", "commit_events"
"Event will be triggered when a commit is created/updated"
when "deployment"
"Event will be triggered when a deployment finishes"
end
end
 
Loading
Loading
Loading
Loading
@@ -83,6 +83,7 @@
- pipeline_processing:ci_build_schedule
 
- deployment:deployments_success
- deployment:deployments_finished
 
- repository_check:repository_check_clear
- repository_check:repository_check_batch
Loading
Loading
# frozen_string_literal: true
module Deployments
class FinishedWorker
include ApplicationWorker
queue_namespace :deployment
def perform(deployment_id)
Deployment.find_by_id(deployment_id).try(:execute_hooks)
end
end
end
---
title: Add deployment events to chat notification services
merge_request: 27338
author:
type: added
# frozen_string_literal: true
class AddDeploymentEventsToServices < ActiveRecord::Migration[5.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_column_with_default(:services, :deployment_events, :boolean, default: false, allow_null: false)
end
def down
remove_column(:services, :deployment_events)
end
end
Loading
Loading
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
 
ActiveRecord::Schema.define(version: 20190423124640) do
ActiveRecord::Schema.define(version: 20190426180107) do
 
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
Loading
Loading
@@ -2893,6 +2893,7 @@
t.boolean "commit_events", default: true, null: false
t.boolean "job_events", default: false, null: false
t.boolean "confidential_note_events", default: true
t.boolean "deployment_events", default: false, null: false
t.index ["project_id"], name: "index_services_on_project_id", using: :btree
t.index ["template"], name: "index_services_on_template", using: :btree
t.index ["type"], name: "index_services_on_type", using: :btree
Loading
Loading
Loading
Loading
@@ -35,6 +35,7 @@
 
context 'when user does not have access to the environment' do
it 'fails the build' do
allow(Deployments::FinishedWorker).to receive(:perform_async)
subject
 
expect(ci_build.failed?).to be_truthy
Loading
Loading
# frozen_string_literal: true
module Gitlab
module DataBuilder
module Deployment
extend self
def build(deployment)
{
object_kind: 'deployment',
status: deployment.status,
deployable_id: deployment.deployable_id,
deployable_url: Gitlab::UrlBuilder.build(deployment.deployable),
environment: deployment.environment.name,
project: deployment.project.hook_attrs,
short_sha: deployment.short_sha,
user: deployment.user.hook_attrs,
commit_url: Gitlab::UrlBuilder.build(deployment.commit)
}
end
end
end
end
Loading
Loading
@@ -30,6 +30,8 @@ def url
snippet_url(object)
when Milestone
milestone_url(object)
when ::Ci::Build
project_job_url(object.project, object)
else
raise NotImplementedError.new("No URL builder defined for #{object.class}")
end
Loading
Loading
FactoryBot.define do
factory :deployment, class: Deployment do
sha '97de212e80737a608d939f648d959671fb0a0142'
sha 'b83d6e391c22777fca1ed3012fce84f633d7fed0'
ref 'master'
tag false
user nil
Loading
Loading
Loading
Loading
@@ -255,6 +255,13 @@
expect(find_field('Username').value).to eq 'test_user'
expect(find('#service_push_channel').value).to eq '#test_channel'
end
it 'defaults Deployment events to false for chat notification template settings' do
first(:link, 'Service Templates').click
click_link 'Slack notifications'
expect(find_field('Deployment')).not_to be_checked
end
end
 
context 'CI/CD page' do
Loading
Loading
@@ -398,10 +405,14 @@
def check_all_events
page.check('Active')
page.check('Push')
page.check('Tag push')
page.check('Note')
page.check('Issue')
page.check('Confidential issue')
page.check('Merge request')
page.check('Note')
page.check('Confidential note')
page.check('Tag push')
page.check('Pipeline')
page.check('Wiki page')
page.check('Deployment')
end
end
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::DataBuilder::Deployment do
describe '.build' do
it 'returns the object kind for a deployment' do
deployment = build(:deployment)
data = described_class.build(deployment)
expect(data[:object_kind]).to eq('deployment')
end
it 'returns data for the given build' do
environment = create(:environment, name: "somewhere")
project = create(:project, :repository, name: 'myproj')
commit = project.commit('HEAD')
deployment = create(:deployment, status: :failed, environment: environment, sha: commit.sha, project: project)
deployable = deployment.deployable
expected_deployable_url = Gitlab::Routing.url_helpers.project_job_url(deployable.project, deployable)
expected_commit_url = Gitlab::UrlBuilder.build(commit)
data = described_class.build(deployment)
expect(data[:status]).to eq('failed')
expect(data[:deployable_id]).to eq(deployable.id)
expect(data[:deployable_url]).to eq(expected_deployable_url)
expect(data[:environment]).to eq("somewhere")
expect(data[:project]).to eq(project.hook_attrs)
expect(data[:short_sha]).to eq(deployment.short_sha)
expect(data[:user]).to eq(deployment.user.hook_attrs)
expect(data[:commit_url]).to eq(expected_commit_url)
end
end
end
Loading
Loading
@@ -426,6 +426,7 @@ Service:
- wiki_page_events
- confidential_issues_events
- confidential_note_events
- deployment_events
ProjectHook:
- id
- url
Loading
Loading
Loading
Loading
@@ -856,6 +856,10 @@
let(:deployment) { build.deployment }
let(:environment) { deployment.environment }
 
before do
allow(Deployments::FinishedWorker).to receive(:perform_async)
end
it 'has deployments record with created status' do
expect(deployment).to be_created
expect(environment.name).to eq('review/master')
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment