diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-05-24 12:10:31 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-05-24 12:10:31 +0000 |
commit | 1f5ca81aa6e674089c9652484e5f3bb89f86703c (patch) | |
tree | acb5e564d2005766e93b8a5d02be04d7b6107925 | |
parent | 0a35aa97051a1255e0bd8f12f30afd25ead228ff (diff) | |
download | gitlab-ce-1f5ca81aa6e674089c9652484e5f3bb89f86703c.tar.gz |
Add latest changes from gitlab-org/gitlab@master
39 files changed, 526 insertions, 602 deletions
diff --git a/.rubocop_manual_todo.yml b/.rubocop_manual_todo.yml index 9bdf1aa0082..9a6e8451c97 100644 --- a/.rubocop_manual_todo.yml +++ b/.rubocop_manual_todo.yml @@ -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' diff --git a/app/models/integrations/external_wiki.rb b/app/models/integrations/external_wiki.rb new file mode 100644 index 00000000000..fec435443fa --- /dev/null +++ b/app/models/integrations/external_wiki.rb @@ -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 diff --git a/app/models/integrations/flowdock.rb b/app/models/integrations/flowdock.rb new file mode 100644 index 00000000000..443f61e65dd --- /dev/null +++ b/app/models/integrations/flowdock.rb @@ -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 diff --git a/app/models/integrations/irker.rb b/app/models/integrations/irker.rb new file mode 100644 index 00000000000..7048dd641ea --- /dev/null +++ b/app/models/integrations/irker.rb @@ -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 diff --git a/app/models/integrations/packagist.rb b/app/models/integrations/packagist.rb new file mode 100644 index 00000000000..b597bd11175 --- /dev/null +++ b/app/models/integrations/packagist.rb @@ -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 diff --git a/app/models/integrations/pipelines_email.rb b/app/models/integrations/pipelines_email.rb new file mode 100644 index 00000000000..585bc14242a --- /dev/null +++ b/app/models/integrations/pipelines_email.rb @@ -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 diff --git a/app/models/integrations/pivotaltracker.rb b/app/models/integrations/pivotaltracker.rb new file mode 100644 index 00000000000..46f97cc3c6b --- /dev/null +++ b/app/models/integrations/pivotaltracker.rb @@ -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 diff --git a/app/models/project.rb b/app/models/project.rb index 26205244efe..31b527f405d 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -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 diff --git a/app/models/project_services/external_wiki_service.rb b/app/models/project_services/external_wiki_service.rb deleted file mode 100644 index f49b008533d..00000000000 --- a/app/models/project_services/external_wiki_service.rb +++ /dev/null @@ -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 diff --git a/app/models/project_services/flowdock_service.rb b/app/models/project_services/flowdock_service.rb deleted file mode 100644 index 7aae5af7454..00000000000 --- a/app/models/project_services/flowdock_service.rb +++ /dev/null @@ -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 diff --git a/app/models/project_services/irker_service.rb b/app/models/project_services/irker_service.rb deleted file mode 100644 index 5cca620c659..00000000000 --- a/app/models/project_services/irker_service.rb +++ /dev/null @@ -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 diff --git a/app/models/project_services/jira_tracker_data.rb b/app/models/project_services/jira_tracker_data.rb index 2c145abf5c9..00b6ab6a70f 100644 --- a/app/models/project_services/jira_tracker_data.rb +++ b/app/models/project_services/jira_tracker_data.rb @@ -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 diff --git a/app/models/project_services/packagist_service.rb b/app/models/project_services/packagist_service.rb deleted file mode 100644 index f3ea8c64302..00000000000 --- a/app/models/project_services/packagist_service.rb +++ /dev/null @@ -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 diff --git a/app/models/project_services/pipelines_email_service.rb b/app/models/project_services/pipelines_email_service.rb deleted file mode 100644 index 4603193ac8e..00000000000 --- a/app/models/project_services/pipelines_email_service.rb +++ /dev/null @@ -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 diff --git a/app/models/project_services/pivotaltracker_service.rb b/app/models/project_services/pivotaltracker_service.rb deleted file mode 100644 index 6e67984591d..00000000000 --- a/app/models/project_services/pivotaltracker_service.rb +++ /dev/null @@ -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 diff --git a/config/feature_flags/development/ci_needs_optional.yml b/config/feature_flags/development/ci_needs_optional.yml deleted file mode 100644 index eacb0ab6d51..00000000000 --- a/config/feature_flags/development/ci_needs_optional.yml +++ /dev/null @@ -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 diff --git a/config/feature_flags/development/usage_data_unique_users_committing_ciconfigfile.yml b/config/feature_flags/development/usage_data_unique_users_committing_ciconfigfile.yml deleted file mode 100644 index 1d3092ed615..00000000000 --- a/config/feature_flags/development/usage_data_unique_users_committing_ciconfigfile.yml +++ /dev/null @@ -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 diff --git a/config/metrics/counts_all/20210216175026_service_desk_issues.yml b/config/metrics/counts_all/20210216175026_service_desk_issues.yml index b852ae8b62f..299fbfa3b7f 100644 --- a/config/metrics/counts_all/20210216175026_service_desk_issues.yml +++ b/config/metrics/counts_all/20210216175026_service_desk_issues.yml @@ -14,4 +14,3 @@ distribution: - ee tier: - free -skip_validation: true diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index e9b3a2213c8..1d166ed0ca2 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -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 diff --git a/doc/development/snowplow/index.md b/doc/development/snowplow/index.md index 6e2bf00f191..c4f5125ac0d 100644 --- a/doc/development/snowplow/index.md +++ b/doc/development/snowplow/index.md @@ -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` diff --git a/lib/api/helpers/services_helpers.rb b/lib/api/helpers/services_helpers.rb index f9abc069e4b..c796b50f5f3 100644 --- a/lib/api/helpers/services_helpers.rb +++ b/lib/api/helpers/services_helpers.rb @@ -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, diff --git a/lib/flowdock/git.rb b/lib/flowdock/git.rb index 539fd66a510..897ee647d87 100644 --- a/lib/flowdock/git.rb +++ b/lib/flowdock/git.rb @@ -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 diff --git a/lib/gitlab/ci/config/entry/need.rb b/lib/gitlab/ci/config/entry/need.rb index 29dc48c7b42..f1b67635c08 100644 --- a/lib/gitlab/ci/config/entry/need.rb +++ b/lib/gitlab/ci/config/entry/need.rb @@ -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 diff --git a/lib/gitlab/ci/pipeline/seed/build.rb b/lib/gitlab/ci/pipeline/seed/build.rb index 39dee7750d6..299b27a5f13 100644 --- a/lib/gitlab/ci/pipeline/seed/build.rb +++ b/lib/gitlab/ci/pipeline/seed/build.rb @@ -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]) diff --git a/lib/gitlab/integrations/sti_type.rb b/lib/gitlab/integrations/sti_type.rb index ccb8551fb3f..5a38c382b81 100644 --- a/lib/gitlab/integrations/sti_type.rb +++ b/lib/gitlab/integrations/sti_type.rb @@ -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) diff --git a/lib/gitlab/usage_data_counters/known_events/common.yml b/lib/gitlab/usage_data_counters/known_events/common.yml index f2504396cc4..95b7b30b236 100644 --- a/lib/gitlab/usage_data_counters/known_events/common.yml +++ b/lib/gitlab/usage_data_counters/known_events/common.yml @@ -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 diff --git a/spec/factories/integrations.rb b/spec/factories/integrations.rb index 12de759b10c..8b8a950d081 100644 --- a/spec/factories/integrations.rb +++ b/spec/factories/integrations.rb @@ -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' } diff --git a/spec/features/projects/services/user_activates_flowdock_spec.rb b/spec/features/projects/integrations/user_activates_flowdock_spec.rb index 4a4d7bbecfd..4a4d7bbecfd 100644 --- a/spec/features/projects/services/user_activates_flowdock_spec.rb +++ b/spec/features/projects/integrations/user_activates_flowdock_spec.rb diff --git a/spec/features/projects/services/user_activates_pivotaltracker_spec.rb b/spec/features/projects/integrations/user_activates_pivotaltracker_spec.rb index 83f66d4fa7b..83f66d4fa7b 100644 --- a/spec/features/projects/services/user_activates_pivotaltracker_spec.rb +++ b/spec/features/projects/integrations/user_activates_pivotaltracker_spec.rb diff --git a/spec/lib/gitlab/ci/config/entry/need_spec.rb b/spec/lib/gitlab/ci/config/entry/need_spec.rb index a0a5dd52ad4..ab2e8d4db78 100644 --- a/spec/lib/gitlab/ci/config/entry/need_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/need_spec.rb @@ -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 diff --git a/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb b/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb index 058fb25807d..020f957cf70 100644 --- a/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb @@ -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 diff --git a/spec/models/integration_spec.rb b/spec/models/integration_spec.rb index cb3fb7a011c..976d5750a12 100644 --- a/spec/models/integration_spec.rb +++ b/spec/models/integration_spec.rb @@ -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 diff --git a/spec/models/project_services/external_wiki_service_spec.rb b/spec/models/integrations/external_wiki_spec.rb index c6891401a0f..8c20b810301 100644 --- a/spec/models/project_services/external_wiki_service_spec.rb +++ b/spec/models/integrations/external_wiki_spec.rb @@ -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 } diff --git a/spec/models/project_services/flowdock_service_spec.rb b/spec/models/integrations/flowdock_spec.rb index 94a49fb3080..2de6f7dd2f1 100644 --- a/spec/models/project_services/flowdock_service_spec.rb +++ b/spec/models/integrations/flowdock_spec.rb @@ -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 } diff --git a/spec/models/project_services/irker_service_spec.rb b/spec/models/integrations/irker_spec.rb index 07963947de8..a69be1292e0 100644 --- a/spec/models/project_services/irker_service_spec.rb +++ b/spec/models/integrations/irker_spec.rb @@ -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 } diff --git a/spec/models/project_services/packagist_service_spec.rb b/spec/models/integrations/packagist_spec.rb index 33b5c9809c7..48f7e81adca 100644 --- a/spec/models/project_services/packagist_service_spec.rb +++ b/spec/models/integrations/packagist_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe PackagistService do +RSpec.describe Integrations::Packagist do let(:packagist_params) do { active: true, diff --git a/spec/models/project_services/pipelines_email_service_spec.rb b/spec/models/integrations/pipelines_email_spec.rb index 21cc5d44558..90055b04bb8 100644 --- a/spec/models/project_services/pipelines_email_service_spec.rb +++ b/spec/models/integrations/pipelines_email_spec.rb @@ -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, diff --git a/spec/models/project_services/pivotaltracker_service_spec.rb b/spec/models/integrations/pivotaltracker_spec.rb index 8de85cc7fa5..2ce90b6f739 100644 --- a/spec/models/project_services/pivotaltracker_service_spec.rb +++ b/spec/models/integrations/pivotaltracker_spec.rb @@ -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') { diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index e98821a04b6..8e606a00144 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -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 |