diff options
Diffstat (limited to 'app/models/project_services')
28 files changed, 298 insertions, 156 deletions
diff --git a/app/models/project_services/asana_service.rb b/app/models/project_services/asana_service.rb index c4fcdcc05c5..f31bf931a41 100644 --- a/app/models/project_services/asana_service.rb +++ b/app/models/project_services/asana_service.rb @@ -3,6 +3,8 @@ require 'asana' class AsanaService < Service + include ActionView::Helpers::UrlHelper + prop_accessor :api_key, :restrict_to_branch validates :api_key, presence: true, if: :activated? @@ -11,20 +13,12 @@ class AsanaService < Service end def description - s_('AsanaService|Asana - Teamwork without email') + s_('AsanaService|Add commit messages as comments to Asana tasks') end def help - 'This service adds commit messages as comments to Asana tasks. -Once enabled, commit messages are checked for Asana task URLs -(for example, `https://app.asana.com/0/123456/987654`) or task IDs -starting with # (for example, `#987654`). Every task ID found will -get the commit comment added to it. - -You can also close a task with a message containing: `fix #123456`. - -You can create a Personal Access Token here: -https://app.asana.com/0/developer-console' + docs_link = link_to _('Learn more.'), Rails.application.routes.url_helpers.help_page_url('user/project/integrations/asana'), target: '_blank', rel: 'noopener noreferrer' + s_('Add commit messages as comments to Asana tasks. %{docs_link}').html_safe % { docs_link: docs_link.html_safe } end def self.to_param @@ -36,13 +30,17 @@ https://app.asana.com/0/developer-console' { type: 'text', name: 'api_key', - placeholder: s_('AsanaService|User Personal Access Token. User must have access to task, all comments will be attributed to this user.'), + title: 'API key', + help: s_('AsanaService|User Personal Access Token. User must have access to the task. All comments are attributed to this user.'), + # Example Personal Access Token from Asana docs + placeholder: '0/68a9e79b868c6789e79a124c30b0', required: true }, { type: 'text', name: 'restrict_to_branch', - placeholder: s_('AsanaService|Comma-separated list of branches which will be automatically inspected. Leave blank to include all branches.') + title: 'Restrict to branch (optional)', + help: s_('AsanaService|Comma-separated list of branches to be automatically inspected. Leave blank to include all branches.') } ] end diff --git a/app/models/project_services/assembla_service.rb b/app/models/project_services/assembla_service.rb index 60575e45a90..8845fb99605 100644 --- a/app/models/project_services/assembla_service.rb +++ b/app/models/project_services/assembla_service.rb @@ -9,7 +9,7 @@ class AssemblaService < Service end def description - 'Project Management Software (Source Commits Endpoint)' + _('Manage projects.') end def self.to_param diff --git a/app/models/project_services/bamboo_service.rb b/app/models/project_services/bamboo_service.rb index 8c1f4fef09b..a892d1a4314 100644 --- a/app/models/project_services/bamboo_service.rb +++ b/app/models/project_services/bamboo_service.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true class BambooService < CiService + include ActionView::Helpers::UrlHelper include ReactiveService prop_accessor :bamboo_url, :build_key, :username, :password @@ -31,15 +32,16 @@ class BambooService < CiService end def title - s_('BambooService|Atlassian Bamboo CI') + s_('BambooService|Atlassian Bamboo') end def description - s_('BambooService|A continuous integration and build server') + s_('BambooService|Use the Atlassian Bamboo CI/CD server with GitLab.') end def help - s_('BambooService|You must set up automatic revision labeling and a repository trigger in Bamboo.') + docs_link = link_to _('Learn more.'), Rails.application.routes.url_helpers.help_page_url('user/project/integrations/bamboo'), target: '_blank', rel: 'noopener noreferrer' + s_('BambooService|Use Atlassian Bamboo to run CI/CD pipelines. You must set up automatic revision labeling and a repository trigger in Bamboo. %{docs_link}').html_safe % { docs_link: docs_link.html_safe } end def self.to_param @@ -48,13 +50,32 @@ class BambooService < CiService def fields [ - { type: 'text', name: 'bamboo_url', - placeholder: s_('BambooService|Bamboo root URL like https://bamboo.example.com'), required: true }, - { type: 'text', name: 'build_key', - placeholder: s_('BambooService|Bamboo build plan key like KEY'), required: true }, - { type: 'text', name: 'username', - placeholder: s_('BambooService|A user with API access, if applicable') }, - { type: 'password', name: 'password' } + { + type: 'text', + name: 'bamboo_url', + title: s_('BambooService|Bamboo URL'), + placeholder: s_('https://bamboo.example.com'), + help: s_('BambooService|Bamboo service root URL.'), + required: true + }, + { + type: 'text', + name: 'build_key', + placeholder: s_('KEY'), + help: s_('BambooService|Bamboo build plan key.'), + required: true + }, + { + type: 'text', + name: 'username', + help: s_('BambooService|The user with API access to the Bamboo server.') + }, + { + type: 'password', + name: 'password', + non_empty_password_title: s_('ProjectService|Enter new password'), + non_empty_password_help: s_('ProjectService|Leave blank to use your current password') + } ] end diff --git a/app/models/project_services/chat_message/merge_message.rb b/app/models/project_services/chat_message/merge_message.rb index b9916a54d75..e45bb9b8ce1 100644 --- a/app/models/project_services/chat_message/merge_message.rb +++ b/app/models/project_services/chat_message/merge_message.rb @@ -28,7 +28,7 @@ module ChatMessage def activity { - title: "Merge Request #{state_or_action_text} by #{user_combined_name}", + title: "Merge request #{state_or_action_text} by #{user_combined_name}", subtitle: "in #{project_link}", text: merge_request_link, image: user_avatar diff --git a/app/models/project_services/chat_notification_service.rb b/app/models/project_services/chat_notification_service.rb index cf7cad09676..4a99842b4d5 100644 --- a/app/models/project_services/chat_notification_service.rb +++ b/app/models/project_services/chat_notification_service.rb @@ -61,11 +61,11 @@ class ChatNotificationService < Service def default_fields [ - { type: 'text', name: 'webhook', placeholder: "e.g. #{webhook_placeholder}", required: true }.freeze, - { type: 'text', name: 'username', placeholder: 'e.g. GitLab' }.freeze, - { type: 'checkbox', name: 'notify_only_broken_pipelines' }.freeze, + { type: 'text', name: 'webhook', placeholder: "#{webhook_placeholder}", required: true }.freeze, + { type: 'text', name: 'username', placeholder: 'GitLab-integration' }.freeze, + { type: 'checkbox', name: 'notify_only_broken_pipelines', help: 'Do not send notifications for successful pipelines.' }.freeze, { type: 'select', name: 'branches_to_be_notified', choices: branch_choices }.freeze, - { type: 'text', name: 'labels_to_be_notified', placeholder: 'e.g. ~backend', help: 'Only supported for issue, merge request and note events.' }.freeze + { type: 'text', name: 'labels_to_be_notified', placeholder: '~backend,~frontend', help: 'Send notifications for issue, merge request, and comment events with the listed labels only. Leave blank to receive notifications for all events.' }.freeze ].freeze end diff --git a/app/models/project_services/ci_service.rb b/app/models/project_services/ci_service.rb index 47106d7bdbb..29edb9ec16f 100644 --- a/app/models/project_services/ci_service.rb +++ b/app/models/project_services/ci_service.rb @@ -2,7 +2,7 @@ # Base class for CI services # List methods you need to implement to get your CI service -# working with GitLab Merge Requests +# working with GitLab merge requests class CiService < Service default_value_for :category, 'ci' diff --git a/app/models/project_services/custom_issue_tracker_service.rb b/app/models/project_services/custom_issue_tracker_service.rb index fc58ba27c3d..aab8661ec55 100644 --- a/app/models/project_services/custom_issue_tracker_service.rb +++ b/app/models/project_services/custom_issue_tracker_service.rb @@ -17,9 +17,9 @@ class CustomIssueTrackerService < IssueTrackerService def fields [ - { type: 'text', name: 'project_url', placeholder: 'Project url', required: true }, - { type: 'text', name: 'issues_url', placeholder: 'Issue url', required: true }, - { type: 'text', name: 'new_issue_url', placeholder: 'New Issue url', required: true } + { type: 'text', name: 'project_url', title: _('Project URL'), required: true }, + { type: 'text', name: 'issues_url', title: s_('ProjectService|Issue URL'), required: true }, + { type: 'text', name: 'new_issue_url', title: s_('ProjectService|New issue URL'), required: true } ] end end diff --git a/app/models/project_services/datadog_service.rb b/app/models/project_services/datadog_service.rb index a48dea71645..9a2d99c46c9 100644 --- a/app/models/project_services/datadog_service.rb +++ b/app/models/project_services/datadog_service.rb @@ -78,7 +78,9 @@ class DatadogService < Service { type: 'password', name: 'api_key', - title: 'API key', + title: _('API key'), + non_empty_password_title: s_('ProjectService|Enter new API key'), + non_empty_password_help: s_('ProjectService|Leave blank to use your current API key'), help: "<a href=\"#{api_keys_url}\" target=\"_blank\">API key</a> used for authentication with Datadog", required: true }, diff --git a/app/models/project_services/discord_service.rb b/app/models/project_services/discord_service.rb index 37bbb9b8752..d7adf63fde4 100644 --- a/app/models/project_services/discord_service.rb +++ b/app/models/project_services/discord_service.rb @@ -3,6 +3,8 @@ require "discordrb/webhooks" class DiscordService < ChatNotificationService + include ActionView::Helpers::UrlHelper + ATTACHMENT_REGEX = /: (?<entry>.*?)\n - (?<name>.*)\n*/.freeze def title @@ -10,7 +12,7 @@ class DiscordService < ChatNotificationService end def description - s_("DiscordService|Receive event notifications in Discord") + s_("DiscordService|Send notifications about project events to a Discord channel.") end def self.to_param @@ -18,13 +20,8 @@ class DiscordService < ChatNotificationService end def help - "This service sends notifications about project events to Discord channels.<br /> - To set up this service: - <ol> - <li><a href='https://support.discordapp.com/hc/en-us/articles/228383668-Intro-to-Webhooks'>Setup a custom Incoming Webhook</a>.</li> - <li>Paste the <strong>Webhook URL</strong> into the field below.</li> - <li>Select events below to enable notifications.</li> - </ol>" + docs_link = link_to _('How do I set up this service?'), Rails.application.routes.url_helpers.help_page_url('user/project/integrations/discord_notifications'), target: '_blank', rel: 'noopener noreferrer' + s_('Send notifications about project events to a Discord channel. %{docs_link}').html_safe % { docs_link: docs_link.html_safe } end def event_field(event) @@ -36,13 +33,12 @@ class DiscordService < ChatNotificationService end def self.supported_events - %w[push issue confidential_issue merge_request note confidential_note tag_push - pipeline wiki_page] + %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/…" }, + { type: "text", name: "webhook", placeholder: "https://discordapp.com/api/webhooks/…", help: "URL to the webhook for the Discord channel." }, { type: "checkbox", name: "notify_only_broken_pipelines" }, { type: 'select', name: 'branches_to_be_notified', choices: branch_choices } ] diff --git a/app/models/project_services/drone_ci_service.rb b/app/models/project_services/drone_ci_service.rb index 5a49f780d46..ab1ba768a8f 100644 --- a/app/models/project_services/drone_ci_service.rb +++ b/app/models/project_services/drone_ci_service.rb @@ -79,21 +79,25 @@ class DroneCiService < CiService end def title - 'Drone CI' + 'Drone' end def description - 'Drone is a Continuous Integration platform built on Docker, written in Go' + s_('ProjectService|Run CI/CD pipelines with Drone.') end def self.to_param 'drone_ci' end + def help + s_('ProjectService|Run CI/CD pipelines with Drone.') + end + def fields [ - { type: 'text', name: 'token', placeholder: 'Drone CI project specific token', required: true }, - { type: 'text', name: 'drone_url', placeholder: 'http://drone.example.com', required: true }, + { type: 'text', name: 'token', help: s_('ProjectService|Token for the Drone project.'), required: true }, + { type: 'text', name: 'drone_url', title: s_('ProjectService|Drone server URL'), placeholder: 'http://drone.example.com', required: true }, { type: 'checkbox', name: 'enable_ssl_verification', title: "Enable SSL verification" } ] end diff --git a/app/models/project_services/emails_on_push_service.rb b/app/models/project_services/emails_on_push_service.rb index 01d8647d439..cdb69684d16 100644 --- a/app/models/project_services/emails_on_push_service.rb +++ b/app/models/project_services/emails_on_push_service.rb @@ -3,10 +3,19 @@ class EmailsOnPushService < Service include NotificationBranchSelection + RECIPIENTS_LIMIT = 750 + boolean_accessor :send_from_committer_email boolean_accessor :disable_diffs prop_accessor :recipients, :branches_to_be_notified - validates :recipients, presence: true, if: :valid_recipients? + validates :recipients, presence: true, if: :validate_recipients? + validate :number_of_recipients_within_limit, if: :validate_recipients? + + def self.valid_recipients(recipients) + recipients.split.select do |recipient| + recipient.include?('@') + end.uniq(&:downcase) + end def title s_('EmailsOnPushService|Emails on push') @@ -63,11 +72,26 @@ class EmailsOnPushService < Service domains = Notify.allowed_email_domains.map { |domain| "user@#{domain}" }.join(", ") [ { type: 'checkbox', name: 'send_from_committer_email', title: s_("EmailsOnPushService|Send from committer"), - help: s_("EmailsOnPushService|Send notifications from the committer's email address if the domain is part of the domain GitLab is running on (e.g. %{domains}).") % { domains: domains } }, + help: s_("EmailsOnPushService|Send notifications from the committer's email address if the domain matches the domain used by your GitLab instance (such as %{domains}).") % { domains: domains } }, { type: 'checkbox', name: 'disable_diffs', title: s_("EmailsOnPushService|Disable code diffs"), help: s_("EmailsOnPushService|Don't include possibly sensitive code diffs in notification body.") }, { type: 'select', name: 'branches_to_be_notified', choices: branch_choices }, - { type: 'textarea', name: 'recipients', placeholder: s_('EmailsOnPushService|Emails separated by whitespace') } + { + type: 'textarea', + name: 'recipients', + placeholder: s_('EmailsOnPushService|tanuki@example.com gitlab@example.com'), + help: s_('EmailsOnPushService|Emails separated by whitespace.') + } ] end + + private + + def number_of_recipients_within_limit + return if recipients.blank? + + if self.class.valid_recipients(recipients).size > RECIPIENTS_LIMIT + errors.add(:recipients, s_("EmailsOnPushService|can't exceed %{recipients_limit}") % { recipients_limit: RECIPIENTS_LIMIT }) + end + end end diff --git a/app/models/project_services/external_wiki_service.rb b/app/models/project_services/external_wiki_service.rb index 0a09000fff4..c41783d1af4 100644 --- a/app/models/project_services/external_wiki_service.rb +++ b/app/models/project_services/external_wiki_service.rb @@ -1,16 +1,16 @@ # frozen_string_literal: true class ExternalWikiService < Service + 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') + s_('ExternalWikiService|External wiki') end def description - s_('ExternalWikiService|Replaces the link to the internal wiki with a link to an external wiki.') + s_('ExternalWikiService|Link to an external wiki from the sidebar.') end def self.to_param @@ -22,12 +22,20 @@ class ExternalWikiService < Service { type: 'text', name: 'external_wiki_url', - placeholder: s_('ExternalWikiService|The URL of the external Wiki'), + 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 diff --git a/app/models/project_services/hipchat_service.rb b/app/models/project_services/hipchat_service.rb index 22c2aebaec3..cd49c6d253d 100644 --- a/app/models/project_services/hipchat_service.rb +++ b/app/models/project_services/hipchat_service.rb @@ -39,7 +39,7 @@ class HipchatService < Service { type: 'text', name: 'room', placeholder: 'Room name or ID' }, { type: 'checkbox', name: 'notify' }, { type: 'select', name: 'color', choices: %w(yellow red green purple gray random) }, - { type: 'text', name: 'api_version', + { type: 'text', name: 'api_version', title: _('API version'), placeholder: 'Leave blank for default (v2)' }, { type: 'text', name: 'server', placeholder: 'Leave blank for default. https://hipchat.example.com' }, diff --git a/app/models/project_services/irker_service.rb b/app/models/project_services/irker_service.rb index 4a6c8339625..4f1ce16ebb2 100644 --- a/app/models/project_services/irker_service.rb +++ b/app/models/project_services/irker_service.rb @@ -6,7 +6,7 @@ class IrkerService < Service prop_accessor :server_host, :server_port, :default_irc_uri prop_accessor :recipients, :channels boolean_accessor :colorize_messages - validates :recipients, presence: true, if: :valid_recipients? + validates :recipients, presence: true, if: :validate_recipients? before_validation :get_channels diff --git a/app/models/project_services/issue_tracker_service.rb b/app/models/project_services/issue_tracker_service.rb index 694374e9548..19a5b4a74bb 100644 --- a/app/models/project_services/issue_tracker_service.rb +++ b/app/models/project_services/issue_tracker_service.rb @@ -73,9 +73,9 @@ class IssueTrackerService < Service def fields [ - { type: 'text', name: 'project_url', placeholder: 'Project url', required: true }, - { type: 'text', name: 'issues_url', placeholder: 'Issue url', required: true }, - { type: 'text', name: 'new_issue_url', placeholder: 'New Issue url', required: true } + { type: 'text', name: 'project_url', title: _('Project URL'), required: true }, + { type: 'text', name: 'issues_url', title: s_('ProjectService|Issue URL'), required: true }, + { type: 'text', name: 'new_issue_url', title: s_('ProjectService|New issue URL'), required: true } ] end diff --git a/app/models/project_services/jenkins_service.rb b/app/models/project_services/jenkins_service.rb index 63ecfc66877..6a123517b84 100644 --- a/app/models/project_services/jenkins_service.rb +++ b/app/models/project_services/jenkins_service.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class JenkinsService < CiService + include ActionView::Helpers::UrlHelper + prop_accessor :jenkins_url, :project_name, :username, :password before_update :reset_password @@ -29,7 +31,6 @@ class JenkinsService < CiService end def execute(data) - return if project.disabled_services.include?(to_param) return unless supported_events.include?(data[:object_kind]) service_hook.execute(data, "#{data[:object_kind]}_hook") @@ -59,15 +60,16 @@ class JenkinsService < CiService end def title - 'Jenkins CI' + 'Jenkins' end def description - 'An extendable open source continuous integration server' + s_('An extendable open source CI/CD server.') end def help - "You must have installed the Git Plugin and GitLab Plugin in Jenkins. [More information](#{Gitlab::Routing.url_helpers.help_page_url('integration/jenkins')})" + docs_link = link_to _('Learn more.'), Rails.application.routes.url_helpers.help_page_url('integration/jenkins'), target: '_blank', rel: 'noopener noreferrer' + s_('Trigger Jenkins builds when you push to a repository, or when a merge request is created, updated, or merged. %{docs_link}').html_safe % { docs_link: docs_link.html_safe } end def self.to_param @@ -77,15 +79,33 @@ class JenkinsService < CiService def fields [ { - type: 'text', name: 'jenkins_url', - placeholder: 'Jenkins URL like http://jenkins.example.com' + type: 'text', + name: 'jenkins_url', + title: s_('ProjectService|Jenkins server URL'), + required: true, + placeholder: 'http://jenkins.example.com', + help: s_('The URL of the Jenkins server.') + }, + { + type: 'text', + name: 'project_name', + required: true, + placeholder: 'my_project_name', + help: s_('The name of the Jenkins project. Copy the name from the end of the URL to the project.') }, { - type: 'text', name: 'project_name', placeholder: 'Project Name', - help: 'The URL-friendly project name. Example: my_project_name' + type: 'text', + name: 'username', + required: true, + help: s_('The username for the Jenkins server.') }, - { type: 'text', name: 'username' }, - { type: 'password', name: 'password' } + { + type: 'password', + name: 'password', + help: s_('The password for the Jenkins server.'), + non_empty_password_title: s_('ProjectService|Enter new password.'), + non_empty_password_help: s_('ProjectService|Leave blank to use your current password.') + } ] end end diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb index 5857d86f921..3e14bf44c12 100644 --- a/app/models/project_services/jira_service.rb +++ b/app/models/project_services/jira_service.rb @@ -31,8 +31,8 @@ class JiraService < IssueTrackerService # TODO: we can probably just delegate as part of # https://gitlab.com/gitlab-org/gitlab/issues/29404 - data_field :username, :password, :url, :api_url, :jira_issue_transition_id, :project_key, :issues_enabled, - :vulnerabilities_enabled, :vulnerabilities_issuetype, :proxy_address, :proxy_port, :proxy_username, :proxy_password + data_field :username, :password, :url, :api_url, :jira_issue_transition_automatic, :jira_issue_transition_id, :project_key, :issues_enabled, + :vulnerabilities_enabled, :vulnerabilities_issuetype before_update :reset_password after_commit :update_deployment_type, on: [:create, :update], if: :update_deployment_type? @@ -116,7 +116,7 @@ class JiraService < IssueTrackerService end def description - s_('JiraService|Jira issue tracker') + s_('JiraService|Track issues in Jira') end def self.to_param @@ -124,15 +124,37 @@ class JiraService < IssueTrackerService end def fields - transition_id_help_path = help_page_path('user/project/integrations/jira', anchor: 'obtaining-a-transition-id') - transition_id_help_link_start = '<a href="%{transition_id_help_path}" target="_blank" rel="noopener noreferrer">'.html_safe % { transition_id_help_path: transition_id_help_path } - [ - { type: 'text', name: 'url', title: s_('JiraService|Web URL'), placeholder: 'https://jira.example.com', required: true }, - { type: 'text', name: 'api_url', title: s_('JiraService|Jira API URL'), placeholder: s_('JiraService|If different from Web URL') }, - { type: 'text', name: 'username', title: s_('JiraService|Username or Email'), placeholder: s_('JiraService|Use a username for server version and an email for cloud version'), required: true }, - { type: 'password', name: 'password', title: s_('JiraService|Password or API token'), placeholder: s_('JiraService|Use a password for server version and an API token for cloud version'), required: true }, - { type: 'text', name: 'jira_issue_transition_id', title: s_('JiraService|Jira workflow transition IDs'), placeholder: s_('JiraService|For example, 12, 24'), help: s_('JiraService|Set transition IDs for Jira workflow transitions. %{link_start}Learn more%{link_end}'.html_safe % { link_start: transition_id_help_link_start, link_end: '</a>'.html_safe }) } + { + type: 'text', + name: 'url', + title: s_('JiraService|Web URL'), + placeholder: 'https://jira.example.com', + help: s_('JiraService|Base URL of the Jira instance.'), + required: true + }, + { + type: 'text', + name: 'api_url', + title: s_('JiraService|Jira API URL'), + help: s_('JiraService|If different from Web URL.') + }, + { + type: 'text', + name: 'username', + title: s_('JiraService|Username or Email'), + help: s_('JiraService|Use a username for server version and an email for cloud version.'), + required: true + }, + { + type: 'password', + name: 'password', + title: s_('JiraService|Password or API token'), + non_empty_password_title: s_('JiraService|Enter new password or API token'), + non_empty_password_help: s_('JiraService|Leave blank to use your current password or API token.'), + help: s_('JiraService|Use a password for server version and an API token for cloud version.'), + required: true + } ] end @@ -159,17 +181,19 @@ class JiraService < IssueTrackerService # support any events. end - def find_issue(issue_key, rendered_fields: false) - options = {} - options = options.merge(expand: 'renderedFields') if rendered_fields + def find_issue(issue_key, rendered_fields: false, transitions: false) + expands = [] + expands << 'renderedFields' if rendered_fields + expands << 'transitions' if transitions + options = { expand: expands.join(',') } if expands.any? - jira_request { client.Issue.find(issue_key, options) } + jira_request { client.Issue.find(issue_key, options || {}) } end def close_issue(entity, external_issue, current_user) - issue = find_issue(external_issue.iid) + issue = find_issue(external_issue.iid, transitions: jira_issue_transition_automatic) - return if issue.nil? || has_resolution?(issue) || !jira_issue_transition_id.present? + return if issue.nil? || has_resolution?(issue) || !issue_transition_enabled? commit_id = case entity when Commit then entity.id @@ -244,6 +268,10 @@ class JiraService < IssueTrackerService true end + def issue_transition_enabled? + jira_issue_transition_automatic || jira_issue_transition_id.present? + end + private def server_info @@ -264,20 +292,44 @@ class JiraService < IssueTrackerService # the issue is transitioned at the order given by the user # if any transition fails it will log the error message and stop the transition sequence def transition_issue(issue) - jira_issue_transition_id.scan(Gitlab::Regex.jira_transition_id_regex).each do |transition_id| - issue.transitions.build.save!(transition: { id: transition_id }) - rescue => error - log_error( - "Issue transition failed", - error: { - exception_class: error.class.name, - exception_message: error.message, - exception_backtrace: Gitlab::BacktraceCleaner.clean_backtrace(error.backtrace) - }, - client_url: client_url - ) - return false + return transition_issue_to_done(issue) if jira_issue_transition_automatic + + jira_issue_transition_id.scan(Gitlab::Regex.jira_transition_id_regex).all? do |transition_id| + transition_issue_to_id(issue, transition_id) + end + end + + def transition_issue_to_id(issue, transition_id) + issue.transitions.build.save!( + transition: { id: transition_id } + ) + + true + rescue => error + log_error( + "Issue transition failed", + error: { + exception_class: error.class.name, + exception_message: error.message, + exception_backtrace: Gitlab::BacktraceCleaner.clean_backtrace(error.backtrace) + }, + client_url: client_url + ) + + false + end + + def transition_issue_to_done(issue) + transitions = issue.transitions rescue [] + + transition = transitions.find do |transition| + status = transition&.to&.statusCategory + status && status['key'] == 'done' end + + return false unless transition + + transition_issue_to_id(issue, transition.id) end def log_usage(action, user) diff --git a/app/models/project_services/jira_tracker_data.rb b/app/models/project_services/jira_tracker_data.rb index 6cbcb1550c1..2c145abf5c9 100644 --- a/app/models/project_services/jira_tracker_data.rb +++ b/app/models/project_services/jira_tracker_data.rb @@ -2,20 +2,23 @@ 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 attr_encrypted :username, encryption_options attr_encrypted :password, encryption_options - attr_encrypted :proxy_address, encryption_options - attr_encrypted :proxy_port, encryption_options - attr_encrypted :proxy_username, encryption_options - attr_encrypted :proxy_password, encryption_options - - validates :proxy_address, length: { maximum: 2048 } - validates :proxy_port, length: { maximum: 5 } - validates :proxy_username, length: { maximum: 255 } - validates :proxy_password, length: { maximum: 255 } enum deployment_type: { unknown: 0, server: 1, cloud: 2 }, _prefix: :deployment end diff --git a/app/models/project_services/mattermost_service.rb b/app/models/project_services/mattermost_service.rb index 9cff979fcf2..732a7c32a03 100644 --- a/app/models/project_services/mattermost_service.rb +++ b/app/models/project_services/mattermost_service.rb @@ -2,13 +2,14 @@ class MattermostService < ChatNotificationService include SlackMattermost::Notifier + include ActionView::Helpers::UrlHelper def title - 'Mattermost notifications' + s_('Mattermost notifications') end def description - 'Receive event notifications in Mattermost' + s_('Send notifications about project events to Mattermost channels.') end def self.to_param @@ -16,21 +17,15 @@ class MattermostService < ChatNotificationService end def help - 'This service sends notifications about projects events to Mattermost channels.<br /> - To set up this service: - <ol> - <li><a href="https://docs.mattermost.com/developer/webhooks-incoming.html#enabling-incoming-webhooks">Enable incoming webhooks</a> in your Mattermost installation.</li> - <li><a href="https://docs.mattermost.com/developer/webhooks-incoming.html#creating-integrations-using-incoming-webhooks">Add an incoming webhook</a> in your Mattermost team. The default channel can be overridden for each event.</li> - <li>Paste the webhook <strong>URL</strong> into the field below.</li> - <li>Select events below to enable notifications. The <strong>Channel handle</strong> and <strong>Username</strong> fields are optional.</li> - </ol>' + docs_link = link_to _('Learn more.'), Rails.application.routes.url_helpers.help_page_url('user/project/integrations/mattermost'), target: '_blank', rel: 'noopener noreferrer' + s_('Send notifications about project events to Mattermost channels. %{docs_link}').html_safe % { docs_link: docs_link.html_safe } end def default_channel_placeholder - "Channel handle (e.g. town-square)" + 'my-channel' end def webhook_placeholder - 'http://mattermost.example.com/hooks/…' + 'http://mattermost.example.com/hooks/' end end diff --git a/app/models/project_services/mattermost_slash_commands_service.rb b/app/models/project_services/mattermost_slash_commands_service.rb index f39d3947e5b..60235a09dcd 100644 --- a/app/models/project_services/mattermost_slash_commands_service.rb +++ b/app/models/project_services/mattermost_slash_commands_service.rb @@ -14,7 +14,7 @@ class MattermostSlashCommandsService < SlashCommandsService end def description - "Perform common operations in Mattermost" + "Perform common tasks with slash commands." end def self.to_param diff --git a/app/models/project_services/microsoft_teams_service.rb b/app/models/project_services/microsoft_teams_service.rb index e8e12a9a206..803c1255195 100644 --- a/app/models/project_services/microsoft_teams_service.rb +++ b/app/models/project_services/microsoft_teams_service.rb @@ -2,7 +2,7 @@ class MicrosoftTeamsService < ChatNotificationService def title - 'Microsoft Teams Notification' + 'Microsoft Teams notifications' end def description @@ -14,13 +14,7 @@ class MicrosoftTeamsService < ChatNotificationService end def help - 'This service sends notifications about projects events to Microsoft Teams channels.<br /> - To set up this service: - <ol> - <li><a href="https://docs.microsoft.com/en-us/microsoftteams/platform/concepts/connectors/connectors-using#setting-up-a-custom-incoming-webhook">Setup a custom Incoming Webhook using Office 365 Connectors For Microsoft Teams</a>.</li> - <li>Paste the <strong>Webhook URL</strong> into the field below.</li> - <li>Select events below to enable notifications.</li> - </ol>' + '<p>Use this service to send notifications about events in GitLab projects to your Microsoft Teams channels. <a href="https://docs.gitlab.com/ee/user/project/integrations/microsoft_teams.html">How do I configure this integration?</a></p>' end def webhook_placeholder @@ -40,8 +34,8 @@ class MicrosoftTeamsService < ChatNotificationService def default_fields [ - { type: 'text', name: 'webhook', placeholder: "e.g. #{webhook_placeholder}" }, - { type: 'checkbox', name: 'notify_only_broken_pipelines' }, + { type: 'text', name: 'webhook', placeholder: "#{webhook_placeholder}" }, + { type: 'checkbox', name: 'notify_only_broken_pipelines', help: 'If selected, successful pipelines do not trigger a notification event.' }, { type: 'select', name: 'branches_to_be_notified', choices: branch_choices } ] end diff --git a/app/models/project_services/mock_ci_service.rb b/app/models/project_services/mock_ci_service.rb index c5e5f4f6400..bd6344c6e1a 100644 --- a/app/models/project_services/mock_ci_service.rb +++ b/app/models/project_services/mock_ci_service.rb @@ -21,10 +21,13 @@ class MockCiService < CiService def fields [ - { type: 'text', + { + type: 'text', name: 'mock_service_url', + title: s_('ProjectService|Mock service URL'), placeholder: 'http://localhost:4004', - required: true } + required: true + } ] end diff --git a/app/models/project_services/pipelines_email_service.rb b/app/models/project_services/pipelines_email_service.rb index 8af4cd952c9..0a0a41c525c 100644 --- a/app/models/project_services/pipelines_email_service.rb +++ b/app/models/project_services/pipelines_email_service.rb @@ -5,7 +5,7 @@ class PipelinesEmailService < Service prop_accessor :recipients, :branches_to_be_notified boolean_accessor :notify_only_broken_pipelines, :notify_only_default_branch - validates :recipients, presence: true, if: :valid_recipients? + validates :recipients, presence: true, if: :validate_recipients? def initialize_properties if properties.nil? @@ -25,11 +25,11 @@ class PipelinesEmailService < Service end def title - _('Pipelines emails') + _('Pipeline status emails') end def description - _('Email the pipelines status to a list of recipients.') + _('Email the pipeline status to a list of recipients.') end def self.to_param @@ -64,7 +64,7 @@ class PipelinesEmailService < Service [ { type: 'textarea', name: 'recipients', - placeholder: _('Emails separated by comma'), + help: _('Comma-separated list of email addresses.'), required: true }, { type: 'checkbox', name: 'notify_only_broken_pipelines' }, diff --git a/app/models/project_services/pushover_service.rb b/app/models/project_services/pushover_service.rb index 7324890551c..1781ec7456d 100644 --- a/app/models/project_services/pushover_service.rb +++ b/app/models/project_services/pushover_service.rb @@ -20,7 +20,7 @@ class PushoverService < Service def fields [ - { type: 'text', name: 'api_key', placeholder: s_('PushoverService|Your application key'), required: true }, + { type: 'text', name: 'api_key', title: _('API key'), placeholder: s_('PushoverService|Your application key'), required: true }, { type: 'text', name: 'user_key', placeholder: s_('PushoverService|Your user key'), required: true }, { type: 'text', name: 'device', placeholder: s_('PushoverService|Leave blank for all active devices') }, { type: 'select', name: 'priority', required: true, choices: diff --git a/app/models/project_services/redmine_service.rb b/app/models/project_services/redmine_service.rb index df78520d65f..26a6cf86bf4 100644 --- a/app/models/project_services/redmine_service.rb +++ b/app/models/project_services/redmine_service.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true class RedmineService < IssueTrackerService + include ActionView::Helpers::UrlHelper validates :project_url, :issues_url, :new_issue_url, presence: true, public_url: true, if: :activated? def title @@ -8,7 +9,12 @@ class RedmineService < IssueTrackerService end def description - s_('IssueTracker|Redmine issue tracker') + s_('IssueTracker|Use Redmine as the issue tracker.') + end + + def help + docs_link = link_to _('Learn more.'), Rails.application.routes.url_helpers.help_page_url('user/project/integrations/redmine'), target: '_blank', rel: 'noopener noreferrer' + s_('IssueTracker|Use Redmine as the issue tracker. %{docs_link}').html_safe % { docs_link: docs_link.html_safe } end def self.to_param diff --git a/app/models/project_services/slack_service.rb b/app/models/project_services/slack_service.rb index f42b3de39d5..7badcc24870 100644 --- a/app/models/project_services/slack_service.rb +++ b/app/models/project_services/slack_service.rb @@ -16,7 +16,7 @@ class SlackService < ChatNotificationService end def description - 'Receive event notifications in Slack' + 'Send notifications about project events to Slack.' end def self.to_param @@ -24,7 +24,7 @@ class SlackService < ChatNotificationService end def default_channel_placeholder - _('Slack channels (e.g. general, development)') + _('general, development') end def webhook_placeholder diff --git a/app/models/project_services/teamcity_service.rb b/app/models/project_services/teamcity_service.rb index 209b691ef98..6fc24a4778c 100644 --- a/app/models/project_services/teamcity_service.rb +++ b/app/models/project_services/teamcity_service.rb @@ -51,27 +51,43 @@ class TeamcityService < CiService end def title - 'JetBrains TeamCity CI' + 'JetBrains TeamCity' end def description - 'A continuous integration and build server' + s_('ProjectService|Run CI/CD pipelines with JetBrains TeamCity.') end def help - 'You will want to configure monitoring of all branches so merge '\ - 'requests build, that setting is in the vsc root advanced settings.' + s_('To run CI/CD pipelines with JetBrains TeamCity, input the GitLab project details in the TeamCity project Version Control Settings.') end def fields [ - { type: 'text', name: 'teamcity_url', - placeholder: 'TeamCity root URL like https://teamcity.example.com', required: true }, - { type: 'text', name: 'build_type', - placeholder: 'Build configuration ID', required: true }, - { type: 'text', name: 'username', - placeholder: 'A user with permissions to trigger a manual build' }, - { type: 'password', name: 'password' } + { + type: 'text', + name: 'teamcity_url', + title: s_('ProjectService|TeamCity server URL'), + placeholder: 'https://teamcity.example.com', + required: true + }, + { + type: 'text', + name: 'build_type', + help: s_('ProjectService|The build configuration ID of the TeamCity project.'), + required: true + }, + { + type: 'text', + name: 'username', + help: s_('ProjectService|Must have permission to trigger a manual build in TeamCity.') + }, + { + type: 'password', + name: 'password', + non_empty_password_title: s_('ProjectService|Enter new password'), + non_empty_password_help: s_('ProjectService|Leave blank to use your current password') + } ] end diff --git a/app/models/project_services/youtrack_service.rb b/app/models/project_services/youtrack_service.rb index 7fb3bde44a5..30abd0159b3 100644 --- a/app/models/project_services/youtrack_service.rb +++ b/app/models/project_services/youtrack_service.rb @@ -26,8 +26,8 @@ class YoutrackService < IssueTrackerService def fields [ - { type: 'text', name: 'project_url', title: 'Project URL', placeholder: 'Project URL', required: true }, - { type: 'text', name: 'issues_url', title: 'Issue URL', placeholder: 'Issue URL', required: true } + { type: 'text', name: 'project_url', title: _('Project URL'), required: true }, + { type: 'text', name: 'issues_url', title: s_('ProjectService|Issue URL'), required: true } ] end end |