diff --git a/.haml-lint.yml b/.haml-lint.yml
index 32c7de0fb78b47bc01bcfa23c1d8fc925e494d51..fcdc47af60f9fc7e77e42ee2d10cf0cdd452034d 100644
--- a/.haml-lint.yml
+++ b/.haml-lint.yml
@@ -70,14 +70,15 @@ linters:
enabled: false
RuboCop:
- enabled: false
+ enabled: true
# These cops are incredibly noisy when it comes to HAML templates, so we
# ignore them.
ignored_cops:
- - Lint/BlockAlignment
- - Lint/EndAlignment
+ - Layout/BlockAlignment
+ - Layout/EndAlignment
- Lint/Void
- Metrics/LineLength
+ - Naming/FileName
- Style/AlignParameters
- Style/BlockNesting
- Style/ElseAlignment
@@ -91,6 +92,52 @@ linters:
- Style/TrailingWhitespace
- Style/WhileUntilModifier
+ # These cops should eventually get enabled
+ - Cop/LineBreakAfterGuardClauses
+ - Cop/LineBreakAroundConditionalBlock
+ - Cop/ProjectPathHelper
+ - GitlabSecurity/PublicSend
+ - Layout/LeadingCommentSpace
+ - Layout/SpaceAfterColon
+ - Layout/SpaceAfterComma
+ - Layout/SpaceAroundOperators
+ - Layout/SpaceBeforeBlockBraces
+ - Layout/SpaceBeforeComma
+ - Layout/SpaceBeforeFirstArg
+ - Layout/SpaceInsideArrayLiteralBrackets
+ - Layout/SpaceInsideHashLiteralBraces
+ - Layout/SpaceInsideStringInterpolation
+ - Layout/TrailingBlankLines
+ - Lint/BooleanSymbol
+ - Lint/LiteralInInterpolation
+ - Lint/ParenthesesAsGroupedExpression
+ - Lint/RedundantWithIndex
+ - Lint/Syntax
+ - Lint/UselessAssignment
+ - Metrics/BlockNesting
+ - Naming/VariableName
+ - Performance/RedundantMatch
+ - Performance/StringReplacement
+ - Rails/Presence
+ - Rails/RequestReferer
+ - Style/AndOr
+ - Style/ColonMethodCall
+ - Style/ConditionalAssignment
+ - Style/HashSyntax
+ - Style/IdenticalConditionalBranches
+ - Style/NegatedIf
+ - Style/NestedTernaryOperator
+ - Style/Not
+ - Style/ParenthesesAroundCondition
+ - Style/RedundantParentheses
+ - Style/SelfAssignment
+ - Style/Semicolon
+ - Style/TernaryParentheses
+ - Style/TrailingCommaInHashLiteral
+ - Style/UnlessElse
+ - Style/WordArray
+ - Style/ZeroLengthPredicate
+
RubyComments:
enabled: true
diff --git a/app/views/ci/runner/_how_to_setup_runner.html.haml b/app/views/ci/runner/_how_to_setup_runner.html.haml
index 13f96b9747c590c21f74c42f44dd1d120b62a6b8..c26eb873718c81f3366b99e92898fb136540c1f2 100644
--- a/app/views/ci/runner/_how_to_setup_runner.html.haml
+++ b/app/views/ci/runner/_how_to_setup_runner.html.haml
@@ -1,6 +1,6 @@
- link = link_to _("Install GitLab Runner"), 'https://docs.gitlab.com/runner/install/', target: '_blank'
.append-bottom-10
- %h4= _("Setup a #{type} Runner manually")
+ %h4= _("Setup a %{type} Runner manually") % { type: type }
%ol
%li
diff --git a/app/views/projects/_issuable_by_email.html.haml b/app/views/projects/_issuable_by_email.html.haml
index 22adf5b40085b0747ea9d3b2b5e9ba3104aba4f9..d59191a6f8754e8ef65b23c9e2de853a7ad58605 100644
--- a/app/views/projects/_issuable_by_email.html.haml
+++ b/app/views/projects/_issuable_by_email.html.haml
@@ -19,9 +19,16 @@
= text_field_tag :issuable_email, email, class: "monospace js-select-on-focus form-control", readonly: true
.input-group-append
= clipboard_button(target: '#issuable_email', class: 'btn btn-clipboard input-group-text btn-transparent d-none d-sm-block')
+
+ - if issuable_type == 'issue'
+ - enter_title_text = _('Enter the issue title')
+ - enter_description_text = _('Enter the issue description')
+ - else
+ - enter_title_text = _('Enter the merge request title')
+ - enter_description_text = _('Enter the merge request description')
= mail_to email, class: 'btn btn-clipboard btn-transparent',
- subject: _("Enter the #{name} title"),
- body: _("Enter the #{name} description"),
+ subject: enter_title_text,
+ body: enter_description_text,
title: _('Send email'),
data: { toggle: 'tooltip', placement: 'bottom' } do
= sprite_icon('mail')
diff --git a/app/views/projects/mirrors/_instructions.html.haml b/app/views/projects/mirrors/_instructions.html.haml
index e051f9e6331de343e7b990d2ec138d870160a15b..35a6885318a138721abd28945d1f73a9549886b1 100644
--- a/app/views/projects/mirrors/_instructions.html.haml
+++ b/app/views/projects/mirrors/_instructions.html.haml
@@ -4,7 +4,9 @@
= _('The repository must be accessible over http://
,
https://
, ssh://
and git://
.').html_safe
%li= _('Include the username in the URL if required: https://username@gitlab.company.com/group/project.git
.').html_safe
- %li= _("The update action will time out after #{import_will_timeout_message(Gitlab.config.gitlab_shell.git_timeout)} minutes. For big repositories, use a clone/push combination.")
+ %li
+ - minutes = Gitlab.config.gitlab_shell.git_timeout / 60
+ = _("The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination.") % { number_of_minutes: minutes }
%li= _('The Git LFS objects will not be synced.').html_safe
%li
= _('This user will be the author of all events in the activity feed that are the result of an update,
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 0bf552d60848d18256106e8eed5c98031f881ba1..cbefb81dfa21758334683366a2f00f18c3e7a6db 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -2391,6 +2391,18 @@ msgstr ""
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr ""
+msgid "Enter the issue description"
+msgstr ""
+
+msgid "Enter the issue title"
+msgstr ""
+
+msgid "Enter the merge request description"
+msgstr ""
+
+msgid "Enter the merge request title"
+msgstr ""
+
msgid "Environments"
msgstr ""
@@ -5050,6 +5062,9 @@ msgstr ""
msgid "Settings"
msgstr ""
+msgid "Setup a %{type} Runner manually"
+msgstr ""
+
msgid "Setup a specific Runner automatically"
msgstr ""
@@ -5552,6 +5567,9 @@ msgstr ""
msgid "The time taken by each data entry gathered by that stage."
msgstr ""
+msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
+msgstr ""
+
msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of :
. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
msgstr ""
diff --git a/rubocop/cop/line_break_around_conditional_block.rb b/rubocop/cop/line_break_around_conditional_block.rb
index 011f2bcf8bf5935a3632259a521ea7e57c071904..59fe6e5d98cf2a1f01bebfb974b1b14d702d6362 100644
--- a/rubocop/cop/line_break_around_conditional_block.rb
+++ b/rubocop/cop/line_break_around_conditional_block.rb
@@ -48,6 +48,8 @@ module RuboCop
MSG = 'Add a line break around conditional blocks'
def on_if(node)
+ # This cop causes errors in haml files, so let's skip those
+ return if in_haml?(node)
return if node.single_line?
return unless node.if? || node.unless?
@@ -116,6 +118,10 @@ module RuboCop
def end_line?(line)
line =~ /^\s*(end|})/
end
+
+ def in_haml?(node)
+ node.location.expression.source_buffer.name.end_with?('.haml.rb')
+ end
end
end
end
diff --git a/rubocop/cop/ruby_interpolation_in_translation.rb b/rubocop/cop/ruby_interpolation_in_translation.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b9411fcfd6ca97d641e15f12264b0e0fa7910f3c
--- /dev/null
+++ b/rubocop/cop/ruby_interpolation_in_translation.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+module RuboCop
+ module Cop
+ class RubyInterpolationInTranslation < RuboCop::Cop::Cop
+ MSG = "Don't use ruby interpolation \#{} inside translated strings, instead use \%{}"
+
+ TRANSLATION_METHODS = ':_ :s_ :N_ :n_'
+ RUBY_INTERPOLATION_REGEX = /.*\#\{.*\}/
+
+ def_node_matcher :translation_method?, <<~PATTERN
+ (send nil? {#{TRANSLATION_METHODS}} $dstr ...)
+ PATTERN
+
+ def_node_matcher :plural_translation_method?, <<~PATTERN
+ (send nil? :n_ str $dstr ...)
+ PATTERN
+
+ def on_send(node)
+ interpolation = translation_method?(node) || plural_translation_method?(node)
+ return unless interpolation
+
+ interpolation.descendants.each do |possible_violation|
+ add_offense(possible_violation, message: MSG) if possible_violation.type != :str
+ end
+ end
+ end
+ end
+end
diff --git a/rubocop/rubocop.rb b/rubocop/rubocop.rb
index 88c9bbf24f498aff2d621d9078ba1b3104ad057b..eaf421a7235b9fbd0b58cf27bf30db4fff3610e8 100644
--- a/rubocop/rubocop.rb
+++ b/rubocop/rubocop.rb
@@ -28,3 +28,4 @@ require_relative 'cop/rspec/env_assignment'
require_relative 'cop/rspec/factories_in_migration_specs'
require_relative 'cop/sidekiq_options_queue'
require_relative 'cop/destroy_all'
+require_relative 'cop/ruby_interpolation_in_translation'
diff --git a/spec/rubocop/cop/ruby_interpolation_in_translation_spec.rb b/spec/rubocop/cop/ruby_interpolation_in_translation_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..7bd50866577bc9f16d10663826c33489a78771c4
--- /dev/null
+++ b/spec/rubocop/cop/ruby_interpolation_in_translation_spec.rb
@@ -0,0 +1,68 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+require 'rubocop'
+require 'rubocop/rspec/support'
+
+require_relative '../../../rubocop/cop/ruby_interpolation_in_translation'
+
+# Disabling interpolation check as we deliberately want to have #{} in strings.
+# rubocop:disable Lint/InterpolationCheck
+describe RuboCop::Cop::RubyInterpolationInTranslation do
+ subject(:cop) { described_class.new }
+
+ it 'does not add an offence for a regular messages' do
+ inspect_source('_("Hello world")')
+
+ expect(cop.offenses).to be_empty
+ end
+
+ it 'adds the correct offence when using interpolation in a string' do
+ inspect_source('_("Hello #{world}")')
+
+ offense = cop.offenses.first
+
+ expect(offense.location.source).to eq('#{world}')
+ expect(offense.message).to eq('Don\'t use ruby interpolation #{} inside translated strings, instead use %{}')
+ end
+
+ it 'detects when using a ruby interpolation in the first argument of a pluralized string' do
+ inspect_source('n_("Hello #{world}", "Hello world")')
+
+ expect(cop.offenses).not_to be_empty
+ end
+
+ it 'detects when using a ruby interpolation in the second argument of a pluralized string' do
+ inspect_source('n_("Hello world", "Hello #{world}")')
+
+ expect(cop.offenses).not_to be_empty
+ end
+
+ it 'detects when using interpolation in a namespaced translation' do
+ inspect_source('s_("Hello|#{world}")')
+
+ expect(cop.offenses).not_to be_empty
+ end
+
+ it 'does not add an offence for messages defined over multiple lines' do
+ source = <<~SRC
+ _("Hello "\
+ "world ")
+ SRC
+
+ inspect_source(source)
+ expect(cop.offenses).to be_empty
+ end
+
+ it 'adds an offence for violations in a message defined over multiple lines' do
+ source = <<~SRC
+ _("Hello "\
+ "\#{world} ")
+ SRC
+
+ inspect_source(source)
+ expect(cop.offenses).not_to be_empty
+ end
+end
+# rubocop:enable Lint/InterpolationCheck