Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-05-24 12:10:31 +00:00
parent 0a35aa9705
commit 1f5ca81aa6
39 changed files with 526 additions and 602 deletions

View File

@ -1654,11 +1654,8 @@ Gitlab/NamespacedClass:
- 'app/models/project_services/ci_service.rb'
- 'app/models/project_services/discord_service.rb'
- 'app/models/project_services/drone_ci_service.rb'
- 'app/models/project_services/external_wiki_service.rb'
- 'app/models/project_services/flowdock_service.rb'
- 'app/models/project_services/hangouts_chat_service.rb'
- 'app/models/project_services/hipchat_service.rb'
- 'app/models/project_services/irker_service.rb'
- 'app/models/project_services/issue_tracker_data.rb'
- 'app/models/project_services/jenkins_service.rb'
- 'app/models/project_services/jira_tracker_data.rb'
@ -1669,9 +1666,6 @@ Gitlab/NamespacedClass:
- 'app/models/project_services/mock_monitoring_service.rb'
- 'app/models/project_services/monitoring_service.rb'
- 'app/models/project_services/open_project_tracker_data.rb'
- 'app/models/project_services/packagist_service.rb'
- 'app/models/project_services/pipelines_email_service.rb'
- 'app/models/project_services/pivotaltracker_service.rb'
- 'app/models/project_services/prometheus_service.rb'
- 'app/models/project_services/pushover_service.rb'
- 'app/models/project_services/slack_service.rb'

View File

@ -0,0 +1,52 @@
# frozen_string_literal: true
module Integrations
class ExternalWiki < Integration
include ActionView::Helpers::UrlHelper
prop_accessor :external_wiki_url
validates :external_wiki_url, presence: true, public_url: true, if: :activated?
def title
s_('ExternalWikiService|External wiki')
end
def description
s_('ExternalWikiService|Link to an external wiki from the sidebar.')
end
def self.to_param
'external_wiki'
end
def fields
[
{
type: 'text',
name: 'external_wiki_url',
title: s_('ExternalWikiService|External wiki URL'),
placeholder: s_('ExternalWikiService|https://example.com/xxx/wiki/...'),
help: 'Enter the URL to the external wiki.',
required: true
}
]
end
def help
docs_link = link_to _('Learn more.'), Rails.application.routes.url_helpers.help_page_url('user/project/wiki/index', anchor: 'link-an-external-wiki'), target: '_blank', rel: 'noopener noreferrer'
s_('Link an external wiki from the project\'s sidebar. %{docs_link}').html_safe % { docs_link: docs_link.html_safe }
end
def execute(_data)
response = Gitlab::HTTP.get(properties['external_wiki_url'], verify: true)
response.body if response.code == 200
rescue StandardError
nil
end
def self.supported_events
%w()
end
end
end

View File

@ -0,0 +1,52 @@
# frozen_string_literal: true
module Integrations
class Flowdock < Integration
include ActionView::Helpers::UrlHelper
prop_accessor :token
validates :token, presence: true, if: :activated?
def title
'Flowdock'
end
def description
s_('FlowdockService|Send event notifications from GitLab to Flowdock flows.')
end
def help
docs_link = link_to _('Learn more.'), Rails.application.routes.url_helpers.help_page_url('api/services', anchor: 'flowdock'), target: '_blank', rel: 'noopener noreferrer'
s_('FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}').html_safe % { docs_link: docs_link.html_safe }
end
def self.to_param
'flowdock'
end
def fields
[
{ type: 'text', name: 'token', placeholder: s_('FlowdockService|1b609b52537...'), required: true, help: 'Enter your Flowdock token.' }
]
end
def self.supported_events
%w(push)
end
def execute(data)
return unless supported_events.include?(data[:object_kind])
::Flowdock::Git.post(
data[:ref],
data[:before],
data[:after],
token: token,
repo: project.repository,
repo_url: "#{Gitlab.config.gitlab.url}/#{project.full_path}",
commit_url: "#{Gitlab.config.gitlab.url}/#{project.full_path}/-/commit/%s",
diff_url: "#{Gitlab.config.gitlab.url}/#{project.full_path}/compare/%s...%s"
)
end
end
end

View File

@ -0,0 +1,123 @@
# frozen_string_literal: true
require 'uri'
module Integrations
class Irker < Integration
prop_accessor :server_host, :server_port, :default_irc_uri
prop_accessor :recipients, :channels
boolean_accessor :colorize_messages
validates :recipients, presence: true, if: :validate_recipients?
before_validation :get_channels
def title
'Irker (IRC gateway)'
end
def description
'Send IRC messages.'
end
def self.to_param
'irker'
end
def self.supported_events
%w(push)
end
def execute(data)
return unless supported_events.include?(data[:object_kind])
IrkerWorker.perform_async(project_id, channels,
colorize_messages, data, settings)
end
def settings
{
server_host: server_host.presence || 'localhost',
server_port: server_port.presence || 6659
}
end
def fields
[
{ type: 'text', name: 'server_host', placeholder: 'localhost',
help: 'Irker daemon hostname (defaults to localhost)' },
{ type: 'text', name: 'server_port', placeholder: 6659,
help: 'Irker daemon port (defaults to 6659)' },
{ type: 'text', name: 'default_irc_uri', title: 'Default IRC URI',
help: 'A default IRC URI to prepend before each recipient (optional)',
placeholder: 'irc://irc.network.net:6697/' },
{ type: 'textarea', name: 'recipients',
placeholder: 'Recipients/channels separated by whitespaces', required: true,
help: 'Recipients have to be specified with a full URI: '\
'irc[s]://irc.network.net[:port]/#channel. Special cases: if '\
'you want the channel to be a nickname instead, append ",isnick" to ' \
'the channel name; if the channel is protected by a secret password, ' \
' append "?key=secretpassword" to the URI (Note that due to a bug, if you ' \
' want to use a password, you have to omit the "#" on the channel). If you ' \
' specify a default IRC URI to prepend before each recipient, you can just ' \
' give a channel name.' },
{ type: 'checkbox', name: 'colorize_messages' }
]
end
def help
' NOTE: Irker does NOT have built-in authentication, which makes it' \
' vulnerable to spamming IRC channels if it is hosted outside of a ' \
' firewall. Please make sure you run the daemon within a secured network ' \
' to prevent abuse. For more details, read: http://www.catb.org/~esr/irker/security.html.'
end
private
def get_channels
return true unless activated?
return true if recipients.nil? || recipients.empty?
map_recipients
errors.add(:recipients, 'are all invalid') if channels.empty?
true
end
def map_recipients
self.channels = recipients.split(/\s+/).map do |recipient|
format_channel(recipient)
end
channels.reject!(&:nil?)
end
def format_channel(recipient)
uri = nil
# Try to parse the chan as a full URI
begin
uri = consider_uri(URI.parse(recipient))
rescue URI::InvalidURIError
end
unless uri.present? && default_irc_uri.nil?
begin
new_recipient = URI.join(default_irc_uri, '/', recipient).to_s
uri = consider_uri(URI.parse(new_recipient))
rescue StandardError
log_error("Unable to create a valid URL", default_irc_uri: default_irc_uri, recipient: recipient)
end
end
uri
end
def consider_uri(uri)
return if uri.scheme.nil?
# Authorize both irc://domain.com/#chan and irc://domain.com/chan
if uri.is_a?(URI) && uri.scheme[/^ircs?\z/] && !uri.path.nil?
uri.to_s
end
end
end
end

View File

@ -0,0 +1,67 @@
# frozen_string_literal: true
module Integrations
class Packagist < Integration
prop_accessor :username, :token, :server
validates :username, presence: true, if: :activated?
validates :token, presence: true, if: :activated?
default_value_for :push_events, true
default_value_for :tag_push_events, true
after_save :compose_service_hook, if: :activated?
def title
'Packagist'
end
def description
s_('Integrations|Update your Packagist projects.')
end
def self.to_param
'packagist'
end
def fields
[
{ type: 'text', name: 'username', placeholder: '', required: true },
{ type: 'text', name: 'token', placeholder: '', required: true },
{ type: 'text', name: 'server', placeholder: 'https://packagist.org', required: false }
]
end
def self.supported_events
%w(push merge_request tag_push)
end
def execute(data)
return unless supported_events.include?(data[:object_kind])
service_hook.execute(data)
end
def test(data)
begin
result = execute(data)
return { success: false, result: result[:message] } if result[:http_status] != 202
rescue StandardError => error
return { success: false, result: error }
end
{ success: true, result: result[:message] }
end
def compose_service_hook
hook = service_hook || build_service_hook
hook.url = hook_url
hook.save
end
def hook_url
base_url = server.presence || 'https://packagist.org'
"#{base_url}/api/update-package?username=#{username}&apiToken=#{token}"
end
end
end

View File

@ -0,0 +1,105 @@
# frozen_string_literal: true
module Integrations
class PipelinesEmail < Integration
include NotificationBranchSelection
prop_accessor :recipients, :branches_to_be_notified
boolean_accessor :notify_only_broken_pipelines, :notify_only_default_branch
validates :recipients, presence: true, if: :validate_recipients?
def initialize_properties
if properties.nil?
self.properties = {}
self.notify_only_broken_pipelines = true
self.branches_to_be_notified = "default"
elsif !self.notify_only_default_branch.nil?
# In older versions, there was only a boolean property named
# `notify_only_default_branch`. Now we have a string property named
# `branches_to_be_notified`. Instead of doing a background migration, we
# opted to set a value for the new property based on the old one, if
# users hasn't specified one already. When users edit the service and
# selects a value for this new property, it will override everything.
self.branches_to_be_notified ||= notify_only_default_branch? ? "default" : "all"
end
end
def title
_('Pipeline status emails')
end
def description
_('Email the pipeline status to a list of recipients.')
end
def self.to_param
'pipelines_email'
end
def self.supported_events
%w[pipeline]
end
def self.default_test_event
'pipeline'
end
def execute(data, force: false)
return unless supported_events.include?(data[:object_kind])
return unless force || should_pipeline_be_notified?(data)
all_recipients = retrieve_recipients(data)
return unless all_recipients.any?
pipeline_id = data[:object_attributes][:id]
PipelineNotificationWorker.new.perform(pipeline_id, recipients: all_recipients)
end
def can_test?
project&.ci_pipelines&.any?
end
def fields
[
{ type: 'textarea',
name: 'recipients',
help: _('Comma-separated list of email addresses.'),
required: true },
{ type: 'checkbox',
name: 'notify_only_broken_pipelines' },
{ type: 'select',
name: 'branches_to_be_notified',
choices: branch_choices }
]
end
def test(data)
result = execute(data, force: true)
{ success: true, result: result }
rescue StandardError => error
{ success: false, result: error }
end
def should_pipeline_be_notified?(data)
notify_for_branch?(data) && notify_for_pipeline?(data)
end
def notify_for_pipeline?(data)
case data[:object_attributes][:status]
when 'success'
!notify_only_broken_pipelines?
when 'failed'
true
else
false
end
end
def retrieve_recipients(data)
recipients.to_s.split(/[,\r\n ]+/).reject(&:empty?)
end
end
end

View File

@ -0,0 +1,78 @@
# frozen_string_literal: true
module Integrations
class Pivotaltracker < Integration
API_ENDPOINT = 'https://www.pivotaltracker.com/services/v5/source_commits'
prop_accessor :token, :restrict_to_branch
validates :token, presence: true, if: :activated?
def title
'PivotalTracker'
end
def description
s_('PivotalTrackerService|Add commit messages as comments to PivotalTracker stories.')
end
def self.to_param
'pivotaltracker'
end
def fields
[
{
type: 'text',
name: 'token',
placeholder: s_('PivotalTrackerService|Pivotal Tracker API token.'),
required: true
},
{
type: 'text',
name: 'restrict_to_branch',
placeholder: s_('PivotalTrackerService|Comma-separated list of branches which will be ' \
'automatically inspected. Leave blank to include all branches.')
}
]
end
def self.supported_events
%w(push)
end
def execute(data)
return unless supported_events.include?(data[:object_kind])
return unless allowed_branch?(data[:ref])
data[:commits].each do |commit|
message = {
'source_commit' => {
'commit_id' => commit[:id],
'author' => commit[:author][:name],
'url' => commit[:url],
'message' => commit[:message]
}
}
Gitlab::HTTP.post(
API_ENDPOINT,
body: message.to_json,
headers: {
'Content-Type' => 'application/json',
'X-TrackerToken' => token
}
)
end
end
private
def allowed_branch?(ref)
return true unless ref.present? && restrict_to_branch.present?
branch = Gitlab::Git.ref_name(ref)
allowed_branches = restrict_to_branch.split(',').map(&:strip)
branch.present? && allowed_branches.include?(branch)
end
end
end

View File

@ -193,15 +193,17 @@ class Project < ApplicationRecord
has_one :datadog_service, class_name: 'Integrations::Datadog'
has_one :emails_on_push_service, class_name: 'Integrations::EmailsOnPush'
has_one :ewm_service, class_name: 'Integrations::Ewm'
has_one :external_wiki_service, class_name: 'Integrations::ExternalWiki'
has_one :flowdock_service, class_name: 'Integrations::Flowdock'
has_one :irker_service, class_name: 'Integrations::Irker'
has_one :jira_service, class_name: 'Integrations::Jira'
has_one :packagist_service, class_name: 'Integrations::Packagist'
has_one :pipelines_email_service, class_name: 'Integrations::PipelinesEmail'
has_one :pivotaltracker_service, class_name: 'Integrations::Pivotaltracker'
has_one :redmine_service, class_name: 'Integrations::Redmine'
has_one :youtrack_service, class_name: 'Integrations::Youtrack'
has_one :discord_service
has_one :drone_ci_service
has_one :pipelines_email_service
has_one :irker_service
has_one :pivotaltracker_service
has_one :flowdock_service
has_one :mattermost_slash_commands_service
has_one :mattermost_service
has_one :slack_slash_commands_service
@ -210,12 +212,10 @@ class Project < ApplicationRecord
has_one :teamcity_service
has_one :pushover_service
has_one :jenkins_service
has_one :external_wiki_service
has_one :prometheus_service, inverse_of: :project
has_one :mock_ci_service
has_one :mock_monitoring_service
has_one :microsoft_teams_service
has_one :packagist_service
has_one :hangouts_chat_service
has_one :unify_circuit_service
has_one :webex_teams_service

View File

@ -1,50 +0,0 @@
# frozen_string_literal: true
class ExternalWikiService < Integration
include ActionView::Helpers::UrlHelper
prop_accessor :external_wiki_url
validates :external_wiki_url, presence: true, public_url: true, if: :activated?
def title
s_('ExternalWikiService|External wiki')
end
def description
s_('ExternalWikiService|Link to an external wiki from the sidebar.')
end
def self.to_param
'external_wiki'
end
def fields
[
{
type: 'text',
name: 'external_wiki_url',
title: s_('ExternalWikiService|External wiki URL'),
placeholder: s_('ExternalWikiService|https://example.com/xxx/wiki/...'),
help: 'Enter the URL to the external wiki.',
required: true
}
]
end
def help
docs_link = link_to _('Learn more.'), Rails.application.routes.url_helpers.help_page_url('user/project/wiki/index', anchor: 'link-an-external-wiki'), target: '_blank', rel: 'noopener noreferrer'
s_('Link an external wiki from the project\'s sidebar. %{docs_link}').html_safe % { docs_link: docs_link.html_safe }
end
def execute(_data)
response = Gitlab::HTTP.get(properties['external_wiki_url'], verify: true)
response.body if response.code == 200
rescue StandardError
nil
end
def self.supported_events
%w()
end
end

View File

@ -1,50 +0,0 @@
# frozen_string_literal: true
class FlowdockService < Integration
include ActionView::Helpers::UrlHelper
prop_accessor :token
validates :token, presence: true, if: :activated?
def title
'Flowdock'
end
def description
s_('FlowdockService|Send event notifications from GitLab to Flowdock flows.')
end
def help
docs_link = link_to _('Learn more.'), Rails.application.routes.url_helpers.help_page_url('api/services', anchor: 'flowdock'), target: '_blank', rel: 'noopener noreferrer'
s_('FlowdockService|Send event notifications from GitLab to Flowdock flows. %{docs_link}').html_safe % { docs_link: docs_link.html_safe }
end
def self.to_param
'flowdock'
end
def fields
[
{ type: 'text', name: 'token', placeholder: s_('FlowdockService|1b609b52537...'), required: true, help: 'Enter your Flowdock token.' }
]
end
def self.supported_events
%w(push)
end
def execute(data)
return unless supported_events.include?(data[:object_kind])
Flowdock::Git.post(
data[:ref],
data[:before],
data[:after],
token: token,
repo: project.repository,
repo_url: "#{Gitlab.config.gitlab.url}/#{project.full_path}",
commit_url: "#{Gitlab.config.gitlab.url}/#{project.full_path}/-/commit/%s",
diff_url: "#{Gitlab.config.gitlab.url}/#{project.full_path}/compare/%s...%s"
)
end
end

View File

@ -1,121 +0,0 @@
# frozen_string_literal: true
require 'uri'
class IrkerService < Integration
prop_accessor :server_host, :server_port, :default_irc_uri
prop_accessor :recipients, :channels
boolean_accessor :colorize_messages
validates :recipients, presence: true, if: :validate_recipients?
before_validation :get_channels
def title
'Irker (IRC gateway)'
end
def description
'Send IRC messages.'
end
def self.to_param
'irker'
end
def self.supported_events
%w(push)
end
def execute(data)
return unless supported_events.include?(data[:object_kind])
IrkerWorker.perform_async(project_id, channels,
colorize_messages, data, settings)
end
def settings
{
server_host: server_host.presence || 'localhost',
server_port: server_port.presence || 6659
}
end
def fields
[
{ type: 'text', name: 'server_host', placeholder: 'localhost',
help: 'Irker daemon hostname (defaults to localhost)' },
{ type: 'text', name: 'server_port', placeholder: 6659,
help: 'Irker daemon port (defaults to 6659)' },
{ type: 'text', name: 'default_irc_uri', title: 'Default IRC URI',
help: 'A default IRC URI to prepend before each recipient (optional)',
placeholder: 'irc://irc.network.net:6697/' },
{ type: 'textarea', name: 'recipients',
placeholder: 'Recipients/channels separated by whitespaces', required: true,
help: 'Recipients have to be specified with a full URI: '\
'irc[s]://irc.network.net[:port]/#channel. Special cases: if '\
'you want the channel to be a nickname instead, append ",isnick" to ' \
'the channel name; if the channel is protected by a secret password, ' \
' append "?key=secretpassword" to the URI (Note that due to a bug, if you ' \
' want to use a password, you have to omit the "#" on the channel). If you ' \
' specify a default IRC URI to prepend before each recipient, you can just ' \
' give a channel name.' },
{ type: 'checkbox', name: 'colorize_messages' }
]
end
def help
' NOTE: Irker does NOT have built-in authentication, which makes it' \
' vulnerable to spamming IRC channels if it is hosted outside of a ' \
' firewall. Please make sure you run the daemon within a secured network ' \
' to prevent abuse. For more details, read: http://www.catb.org/~esr/irker/security.html.'
end
private
def get_channels
return true unless activated?
return true if recipients.nil? || recipients.empty?
map_recipients
errors.add(:recipients, 'are all invalid') if channels.empty?
true
end
def map_recipients
self.channels = recipients.split(/\s+/).map do |recipient|
format_channel(recipient)
end
channels.reject!(&:nil?)
end
def format_channel(recipient)
uri = nil
# Try to parse the chan as a full URI
begin
uri = consider_uri(URI.parse(recipient))
rescue URI::InvalidURIError
end
unless uri.present? && default_irc_uri.nil?
begin
new_recipient = URI.join(default_irc_uri, '/', recipient).to_s
uri = consider_uri(URI.parse(new_recipient))
rescue StandardError
log_error("Unable to create a valid URL", default_irc_uri: default_irc_uri, recipient: recipient)
end
end
uri
end
def consider_uri(uri)
return if uri.scheme.nil?
# Authorize both irc://domain.com/#chan and irc://domain.com/chan
if uri.is_a?(URI) && uri.scheme[/^ircs?\z/] && !uri.path.nil?
uri.to_s
end
end
end

View File

@ -2,18 +2,6 @@
class JiraTrackerData < ApplicationRecord
include Services::DataFields
include IgnorableColumns
ignore_columns %i[
encrypted_proxy_address
encrypted_proxy_address_iv
encrypted_proxy_port
encrypted_proxy_port_iv
encrypted_proxy_username
encrypted_proxy_username_iv
encrypted_proxy_password
encrypted_proxy_password_iv
], remove_with: '14.0', remove_after: '2021-05-22'
attr_encrypted :url, encryption_options
attr_encrypted :api_url, encryption_options

View File

@ -1,65 +0,0 @@
# frozen_string_literal: true
class PackagistService < Integration
prop_accessor :username, :token, :server
validates :username, presence: true, if: :activated?
validates :token, presence: true, if: :activated?
default_value_for :push_events, true
default_value_for :tag_push_events, true
after_save :compose_service_hook, if: :activated?
def title
'Packagist'
end
def description
s_('Integrations|Update your Packagist projects.')
end
def self.to_param
'packagist'
end
def fields
[
{ type: 'text', name: 'username', placeholder: '', required: true },
{ type: 'text', name: 'token', placeholder: '', required: true },
{ type: 'text', name: 'server', placeholder: 'https://packagist.org', required: false }
]
end
def self.supported_events
%w(push merge_request tag_push)
end
def execute(data)
return unless supported_events.include?(data[:object_kind])
service_hook.execute(data)
end
def test(data)
begin
result = execute(data)
return { success: false, result: result[:message] } if result[:http_status] != 202
rescue StandardError => error
return { success: false, result: error }
end
{ success: true, result: result[:message] }
end
def compose_service_hook
hook = service_hook || build_service_hook
hook.url = hook_url
hook.save
end
def hook_url
base_url = server.presence || 'https://packagist.org'
"#{base_url}/api/update-package?username=#{username}&apiToken=#{token}"
end
end

View File

@ -1,103 +0,0 @@
# frozen_string_literal: true
class PipelinesEmailService < Integration
include NotificationBranchSelection
prop_accessor :recipients, :branches_to_be_notified
boolean_accessor :notify_only_broken_pipelines, :notify_only_default_branch
validates :recipients, presence: true, if: :validate_recipients?
def initialize_properties
if properties.nil?
self.properties = {}
self.notify_only_broken_pipelines = true
self.branches_to_be_notified = "default"
elsif !self.notify_only_default_branch.nil?
# In older versions, there was only a boolean property named
# `notify_only_default_branch`. Now we have a string property named
# `branches_to_be_notified`. Instead of doing a background migration, we
# opted to set a value for the new property based on the old one, if
# users hasn't specified one already. When users edit the service and
# selects a value for this new property, it will override everything.
self.branches_to_be_notified ||= notify_only_default_branch? ? "default" : "all"
end
end
def title
_('Pipeline status emails')
end
def description
_('Email the pipeline status to a list of recipients.')
end
def self.to_param
'pipelines_email'
end
def self.supported_events
%w[pipeline]
end
def self.default_test_event
'pipeline'
end
def execute(data, force: false)
return unless supported_events.include?(data[:object_kind])
return unless force || should_pipeline_be_notified?(data)
all_recipients = retrieve_recipients(data)
return unless all_recipients.any?
pipeline_id = data[:object_attributes][:id]
PipelineNotificationWorker.new.perform(pipeline_id, recipients: all_recipients)
end
def can_test?
project&.ci_pipelines&.any?
end
def fields
[
{ type: 'textarea',
name: 'recipients',
help: _('Comma-separated list of email addresses.'),
required: true },
{ type: 'checkbox',
name: 'notify_only_broken_pipelines' },
{ type: 'select',
name: 'branches_to_be_notified',
choices: branch_choices }
]
end
def test(data)
result = execute(data, force: true)
{ success: true, result: result }
rescue StandardError => error
{ success: false, result: error }
end
def should_pipeline_be_notified?(data)
notify_for_branch?(data) && notify_for_pipeline?(data)
end
def notify_for_pipeline?(data)
case data[:object_attributes][:status]
when 'success'
!notify_only_broken_pipelines?
when 'failed'
true
else
false
end
end
def retrieve_recipients(data)
recipients.to_s.split(/[,\r\n ]+/).reject(&:empty?)
end
end

View File

@ -1,76 +0,0 @@
# frozen_string_literal: true
class PivotaltrackerService < Integration
API_ENDPOINT = 'https://www.pivotaltracker.com/services/v5/source_commits'
prop_accessor :token, :restrict_to_branch
validates :token, presence: true, if: :activated?
def title
'PivotalTracker'
end
def description
s_('PivotalTrackerService|Add commit messages as comments to PivotalTracker stories.')
end
def self.to_param
'pivotaltracker'
end
def fields
[
{
type: 'text',
name: 'token',
placeholder: s_('PivotalTrackerService|Pivotal Tracker API token.'),
required: true
},
{
type: 'text',
name: 'restrict_to_branch',
placeholder: s_('PivotalTrackerService|Comma-separated list of branches which will be ' \
'automatically inspected. Leave blank to include all branches.')
}
]
end
def self.supported_events
%w(push)
end
def execute(data)
return unless supported_events.include?(data[:object_kind])
return unless allowed_branch?(data[:ref])
data[:commits].each do |commit|
message = {
'source_commit' => {
'commit_id' => commit[:id],
'author' => commit[:author][:name],
'url' => commit[:url],
'message' => commit[:message]
}
}
Gitlab::HTTP.post(
API_ENDPOINT,
body: message.to_json,
headers: {
'Content-Type' => 'application/json',
'X-TrackerToken' => token
}
)
end
end
private
def allowed_branch?(ref)
return true unless ref.present? && restrict_to_branch.present?
branch = Gitlab::Git.ref_name(ref)
allowed_branches = restrict_to_branch.split(',').map(&:strip)
branch.present? && allowed_branches.include?(branch)
end
end

View File

@ -1,8 +0,0 @@
---
name: ci_needs_optional
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55468
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/323891
milestone: '13.10'
type: development
group: group::pipeline authoring
default_enabled: true

View File

@ -1,8 +0,0 @@
---
name: usage_data_unique_users_committing_ciconfigfile
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/52172
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/299403
milestone: '13.9'
type: development
group: group::pipeline authoring
default_enabled: true

View File

@ -14,4 +14,3 @@ distribution:
- ee
tier:
- free
skip_validation: true

View File

@ -2069,14 +2069,7 @@ To download artifacts from a job in the current pipeline, use the basic form of
#### Optional `needs`
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/30680) in GitLab 13.10.
> - [Deployed behind a feature flag](../../user/feature_flags.md), disabled by default.
> - [Enabled by default](https://gitlab.com/gitlab-org/gitlab/-/issues/323891) in GitLab 13.11.
> - Enabled on GitLab.com.
> - Recommended for production use.
> - For GitLab self-managed instances, GitLab administrators can opt to [disable it](#enable-or-disable-optional-needs). **(FREE SELF)**
WARNING:
This feature might not be available to you. Check the **version history** note above for details.
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/323891) in GitLab 14.0.
To need a job that sometimes does not exist in the pipeline, add `optional: true`
to the `needs` configuration. If not defined, `optional: false` is the default.
@ -2110,25 +2103,6 @@ rspec:
optional: true
```
#### Enable or disable optional needs **(FREE SELF)**
Optional needs is under development but ready for production use.
It is deployed behind a feature flag that is **enabled by default**.
[GitLab administrators with access to the GitLab Rails console](../../administration/feature_flags.md)
can opt to disable it.
To enable it:
```ruby
Feature.enable(:ci_needs_optional)
```
To disable it:
```ruby
Feature.disable(:ci_needs_optional)
```
### `tags`
Use `tags` to select a specific runner from the list of all runners that are

View File

@ -573,6 +573,20 @@ Snowplow Mini can be used for testing frontend and backend events on a productio
For GitLab.com, we're setting up a [QA and Testing environment](https://gitlab.com/gitlab-org/telemetry/-/issues/266) using Snowplow Mini.
### Troubleshooting
To control content security policy warnings when using an external host, you can allow or disallow them by modifying `config/gitlab.yml`. To allow them, add the relevant host for `connect_src`. For example, for `https://snowplow.trx.gitlab.net`:
```yaml
development:
<<: *base
gitlab:
content_security_policy:
enabled: true
directives:
connect_src: "'self' http://localhost:* http://127.0.0.1:* ws://localhost:* wss://localhost:* ws://127.0.0.1:* https://snowplow.trx.gitlab.net/"
```
## Snowplow Schemas
### `gitlab_standard`

View File

@ -784,22 +784,22 @@ module API
::Integrations::Datadog,
::Integrations::EmailsOnPush,
::Integrations::Ewm,
::Integrations::ExternalWiki,
::Integrations::Flowdock,
::Integrations::Irker,
::Integrations::Jira,
::Integrations::Packagist,
::Integrations::PipelinesEmail,
::Integrations::Pivotaltracker,
::Integrations::Redmine,
::Integrations::Youtrack,
::BuildkiteService,
::DiscordService,
::DroneCiService,
::ExternalWikiService,
::FlowdockService,
::HangoutsChatService,
::IrkerService,
::JenkinsService,
::MattermostSlashCommandsService,
::SlackSlashCommandsService,
::PackagistService,
::PipelinesEmailService,
::PivotaltrackerService,
::PrometheusService,
::PushoverService,
::SlackService,

View File

@ -34,7 +34,7 @@ module Flowdock
# Send git push notification to Flowdock
def post
messages.each do |message|
Flowdock::Client.new(flow_token: @token).post_to_thread(message)
::Flowdock::Client.new(flow_token: @token).post_to_thread(message)
end
end

View File

@ -35,14 +35,9 @@ module Gitlab
end
def value
if ::Feature.enabled?(:ci_needs_optional, default_enabled: :yaml)
{ name: @config,
artifacts: true,
optional: false }
else
{ name: @config,
artifacts: true }
end
{ name: @config,
artifacts: true,
optional: false }
end
end
@ -66,14 +61,9 @@ module Gitlab
end
def value
if ::Feature.enabled?(:ci_needs_optional, default_enabled: :yaml)
{ name: job,
artifacts: artifacts || artifacts.nil?,
optional: !!optional }
else
{ name: job,
artifacts: artifacts || artifacts.nil? }
end
{ name: job,
artifacts: artifacts || artifacts.nil?,
optional: !!optional }
end
end

View File

@ -146,7 +146,7 @@ module Gitlab
end
@needs_attributes.flat_map do |need|
next if ::Feature.enabled?(:ci_needs_optional, default_enabled: :yaml) && need[:optional]
next if need[:optional]
result = @previous_stages.any? do |stage|
stage.seeds_names.include?(need[:name])

View File

@ -5,7 +5,8 @@ module Gitlab
class StiType < ActiveRecord::Type::String
NAMESPACED_INTEGRATIONS = Set.new(%w(
Asana Assembla Bamboo Bugzilla Campfire Confluence CustomIssueTracker Datadog
EmailsOnPush Ewm IssueTracker Jira Redmine Youtrack
EmailsOnPush Ewm ExternalWiki Flowdock IssueTracker Irker Jira Packagist PipelinesEmail
Pivotaltracker Redmine Youtrack
)).freeze
def cast(value)

View File

@ -351,7 +351,6 @@
category: pipeline_authoring
redis_slot: pipeline_authoring
aggregation: weekly
feature_flag: usage_data_unique_users_committing_ciconfigfile
- name: o_pipeline_authoring_unique_users_pushing_mr_ciconfigfile
category: pipeline_authoring
redis_slot: pipeline_authoring

View File

@ -127,9 +127,9 @@ FactoryBot.define do
end
end
factory :external_wiki_service do
factory :external_wiki_service, class: 'Integrations::ExternalWiki' do
project
type { ExternalWikiService }
type { 'ExternalWikiService' }
active { true }
external_wiki_url { 'http://external-wiki-url.com' }
end
@ -167,7 +167,7 @@ FactoryBot.define do
type { 'SlackService' }
end
factory :pipelines_email_service do
factory :pipelines_email_service, class: 'Integrations::PipelinesEmail' do
project
active { true }
type { 'PipelinesEmailService' }

View File

@ -25,16 +25,6 @@ RSpec.describe ::Gitlab::Ci::Config::Entry::Need do
it 'returns job needs configuration' do
expect(need.value).to eq(name: 'job_name', artifacts: true, optional: false)
end
context 'when the FF ci_needs_optional is disabled' do
before do
stub_feature_flags(ci_needs_optional: false)
end
it 'returns job needs configuration without `optional`' do
expect(need.value).to eq(name: 'job_name', artifacts: true)
end
end
end
it_behaves_like 'job type'
@ -134,16 +124,6 @@ RSpec.describe ::Gitlab::Ci::Config::Entry::Need do
it 'returns job needs configuration' do
expect(need.value).to eq(name: 'job_name', artifacts: true, optional: true)
end
context 'when the FF ci_needs_optional is disabled' do
before do
stub_feature_flags(ci_needs_optional: false)
end
it 'returns job needs configuration without `optional`' do
expect(need.value).to eq(name: 'job_name', artifacts: true)
end
end
end
end

View File

@ -1101,17 +1101,6 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
it "does not return an error" do
expect(subject.errors).to be_empty
end
context 'when the FF ci_needs_optional is disabled' do
before do
stub_feature_flags(ci_needs_optional: false)
end
it "returns an error" do
expect(subject.errors).to contain_exactly(
"'rspec' job needs 'build' job, but it was not added to the pipeline")
end
end
end
end

View File

@ -905,7 +905,7 @@ RSpec.describe Integration do
with_them do
it 'returns the right result' do
expect(build(:service, type: type, active: active).external_wiki?).to eq(result)
expect(create(:service, type: type, active: active).external_wiki?).to eq(result)
end
end
end

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe ExternalWikiService do
RSpec.describe Integrations::ExternalWiki do
describe "Associations" do
it { is_expected.to belong_to :project }
it { is_expected.to have_one :service_hook }

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe FlowdockService do
RSpec.describe Integrations::Flowdock do
describe "Associations" do
it { is_expected.to belong_to :project }
it { is_expected.to have_one :service_hook }

View File

@ -4,7 +4,7 @@ require 'spec_helper'
require 'socket'
require 'json'
RSpec.describe IrkerService do
RSpec.describe Integrations::Irker do
describe 'Associations' do
it { is_expected.to belong_to :project }
it { is_expected.to have_one :service_hook }

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe PackagistService do
RSpec.describe Integrations::Packagist do
let(:packagist_params) do
{
active: true,

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe PipelinesEmailService, :mailer do
RSpec.describe Integrations::PipelinesEmail, :mailer do
let(:pipeline) do
create(:ci_pipeline, :failed,
project: project,

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe PivotaltrackerService do
RSpec.describe Integrations::Pivotaltracker do
include StubRequests
describe 'Associations' do
@ -35,7 +35,7 @@ RSpec.describe PivotaltrackerService do
end
end
let(:url) { PivotaltrackerService::API_ENDPOINT }
let(:url) { described_class::API_ENDPOINT }
def push_data(branch: 'master')
{

View File

@ -1158,7 +1158,7 @@ RSpec.describe Project, factory_default: :keep do
it 'returns an active external wiki' do
create(:service, project: project, type: 'ExternalWikiService', active: true)
is_expected.to be_kind_of(ExternalWikiService)
is_expected.to be_kind_of(Integrations::ExternalWiki)
end
it 'does not return an inactive external wiki' do