summaryrefslogtreecommitdiff
path: root/app/models/project_services
diff options
context:
space:
mode:
Diffstat (limited to 'app/models/project_services')
-rw-r--r--app/models/project_services/asana_service.rb24
-rw-r--r--app/models/project_services/assembla_service.rb2
-rw-r--r--app/models/project_services/bamboo_service.rb41
-rw-r--r--app/models/project_services/chat_message/merge_message.rb2
-rw-r--r--app/models/project_services/chat_notification_service.rb8
-rw-r--r--app/models/project_services/ci_service.rb2
-rw-r--r--app/models/project_services/custom_issue_tracker_service.rb6
-rw-r--r--app/models/project_services/datadog_service.rb4
-rw-r--r--app/models/project_services/discord_service.rb18
-rw-r--r--app/models/project_services/drone_ci_service.rb12
-rw-r--r--app/models/project_services/emails_on_push_service.rb30
-rw-r--r--app/models/project_services/external_wiki_service.rb16
-rw-r--r--app/models/project_services/hipchat_service.rb2
-rw-r--r--app/models/project_services/irker_service.rb2
-rw-r--r--app/models/project_services/issue_tracker_service.rb6
-rw-r--r--app/models/project_services/jenkins_service.rb40
-rw-r--r--app/models/project_services/jira_service.rb112
-rw-r--r--app/models/project_services/jira_tracker_data.rb21
-rw-r--r--app/models/project_services/mattermost_service.rb19
-rw-r--r--app/models/project_services/mattermost_slash_commands_service.rb2
-rw-r--r--app/models/project_services/microsoft_teams_service.rb14
-rw-r--r--app/models/project_services/mock_ci_service.rb7
-rw-r--r--app/models/project_services/pipelines_email_service.rb8
-rw-r--r--app/models/project_services/pushover_service.rb2
-rw-r--r--app/models/project_services/redmine_service.rb8
-rw-r--r--app/models/project_services/slack_service.rb4
-rw-r--r--app/models/project_services/teamcity_service.rb38
-rw-r--r--app/models/project_services/youtrack_service.rb4
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