summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG2
-rw-r--r--config/gitlab.yml.example2
-rw-r--r--config/initializers/1_settings.rb2
-rw-r--r--lib/gitlab/closing_issue_extractor.rb19
-rw-r--r--spec/lib/gitlab/closing_issue_extractor_spec.rb84
-rw-r--r--spec/models/commit_spec.rb4
6 files changed, 99 insertions, 14 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 6784c1f258f..aad4887b226 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -11,7 +11,7 @@ v 7.8.0
-
-
-
- -
+ - Allow more variations for commit messages closing issues (Julien Bianchi and Hannes Rosenögger)
-
-
-
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index 92f601282e0..8d97965935e 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -77,7 +77,7 @@ production: &base
# This happens when the commit is pushed or merged into the default branch of a project.
# When not specified the default issue_closing_pattern as specified below will be used.
# Tip: you can test your closing pattern at http://rubular.com
- # issue_closing_pattern: '([Cc]lose[sd]|[Ff]ixe[sd]) #(\d+)'
+ # issue_closing_pattern: '((?:[Cc]los(?:e[sd]|ing)|[Ff]ix(?:e[sd]|ing)?) +(?:(?:issues? +)?#\d+(?:(?:, *| +and +)?))+)'
## Default project features settings
default_projects_features:
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index cdb958aa6a6..1ec842761ff 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -109,7 +109,7 @@ Settings.gitlab['signup_enabled'] ||= true if Settings.gitlab['signup_enabled'].
Settings.gitlab['signin_enabled'] ||= true if Settings.gitlab['signin_enabled'].nil?
Settings.gitlab['restricted_visibility_levels'] = Settings.send(:verify_constant_array, Gitlab::VisibilityLevel, Settings.gitlab['restricted_visibility_levels'], [])
Settings.gitlab['username_changing_enabled'] = true if Settings.gitlab['username_changing_enabled'].nil?
-Settings.gitlab['issue_closing_pattern'] = '([Cc]lose[sd]|[Ff]ixe[sd]) #(\d+)' if Settings.gitlab['issue_closing_pattern'].nil?
+Settings.gitlab['issue_closing_pattern'] = '((?:[Cc]los(?:e[sd]|ing)|[Ff]ix(?:e[sd]|ing)?) +(?:(?:issues? +)?#\d+(?:(?:, *| +and +)?))+)' if Settings.gitlab['issue_closing_pattern'].nil?
Settings.gitlab['default_projects_features'] ||= {}
Settings.gitlab['webhook_timeout'] ||= 10
Settings.gitlab.default_projects_features['issues'] = true if Settings.gitlab.default_projects_features['issues'].nil?
diff --git a/lib/gitlab/closing_issue_extractor.rb b/lib/gitlab/closing_issue_extractor.rb
index 401e6e047b1..a9fd59f03d9 100644
--- a/lib/gitlab/closing_issue_extractor.rb
+++ b/lib/gitlab/closing_issue_extractor.rb
@@ -3,14 +3,19 @@ module Gitlab
ISSUE_CLOSING_REGEX = Regexp.new(Gitlab.config.gitlab.issue_closing_pattern)
def self.closed_by_message_in_project(message, project)
- md = ISSUE_CLOSING_REGEX.match(message)
- if md
- extractor = Gitlab::ReferenceExtractor.new
- extractor.analyze(md[0], project)
- extractor.issues_for(project)
- else
- []
+ issues = []
+
+ unless message.nil?
+ md = message.scan(ISSUE_CLOSING_REGEX)
+
+ md.each do |ref|
+ extractor = Gitlab::ReferenceExtractor.new
+ extractor.analyze(ref[0], project)
+ issues += extractor.issues_for(project)
+ end
end
+
+ issues.uniq
end
end
end
diff --git a/spec/lib/gitlab/closing_issue_extractor_spec.rb b/spec/lib/gitlab/closing_issue_extractor_spec.rb
new file mode 100644
index 00000000000..867455daf23
--- /dev/null
+++ b/spec/lib/gitlab/closing_issue_extractor_spec.rb
@@ -0,0 +1,84 @@
+require 'spec_helper'
+
+describe Gitlab::ClosingIssueExtractor do
+ let(:project) { create(:project) }
+ let(:issue) { create(:issue, project: project) }
+ let(:iid1) { issue.iid }
+
+ describe :closed_by_message_in_project do
+ context 'with a single reference' do
+ it do
+ message = "Awesome commit (Closes ##{iid1})"
+ subject.closed_by_message_in_project(message, project).should == [issue]
+ end
+
+ it do
+ message = "Awesome commit (closes ##{iid1})"
+ subject.closed_by_message_in_project(message, project).should == [issue]
+ end
+
+ it do
+ message = "Closed ##{iid1}"
+ subject.closed_by_message_in_project(message, project).should == [issue]
+ end
+
+ it do
+ message = "closed ##{iid1}"
+ subject.closed_by_message_in_project(message, project).should == [issue]
+ end
+
+ it do
+ message = "Awesome commit (fixes ##{iid1})"
+ subject.closed_by_message_in_project(message, project).should == [issue]
+ end
+
+ it do
+ message = "Awesome commit (fix ##{iid1})"
+ subject.closed_by_message_in_project(message, project).should == [issue]
+ end
+ end
+
+ context 'with multiple references' do
+ let(:other_issue) { create(:issue, project: project) }
+ let(:third_issue) { create(:issue, project: project) }
+ let(:iid2) { other_issue.iid }
+ let(:iid3) { third_issue.iid }
+
+ it 'fetches issues in single line message' do
+ message = "Closes ##{iid1} and fix ##{iid2}"
+
+ subject.closed_by_message_in_project(message, project).
+ should == [issue, other_issue]
+ end
+
+ it 'fetches comma-separated issues references in single line message' do
+ message = "Closes ##{iid1}, closes ##{iid2}"
+
+ subject.closed_by_message_in_project(message, project).
+ should == [issue, other_issue]
+ end
+
+ it 'fetches comma-separated issues numbers in single line message' do
+ message = "Closes ##{iid1}, ##{iid2} and ##{iid3}"
+
+ subject.closed_by_message_in_project(message, project).
+ should == [issue, other_issue, third_issue]
+ end
+
+ it 'fetches issues in multi-line message' do
+ message = "Awesome commit (closes ##{iid1})\nAlso fixes ##{iid2}"
+
+ subject.closed_by_message_in_project(message, project).
+ should == [issue, other_issue]
+ end
+
+ it 'fetches issues in hybrid message' do
+ message = "Awesome commit (closes ##{iid1})\n"\
+ "Also fixing issues ##{iid2}, ##{iid3} and #4"
+
+ subject.closed_by_message_in_project(message, project).
+ should == [issue, other_issue, third_issue]
+ end
+ end
+ end
+end
diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb
index a6ec44da4be..7a2a7a4ce9b 100644
--- a/spec/models/commit_spec.rb
+++ b/spec/models/commit_spec.rb
@@ -57,16 +57,12 @@ eos
let(:other_issue) { create :issue, project: other_project }
it 'detects issues that this commit is marked as closing' do
- stub_const('Gitlab::ClosingIssueExtractor::ISSUE_CLOSING_REGEX',
- /Fixes #\d+/)
commit.stub(safe_message: "Fixes ##{issue.iid}")
commit.closes_issues(project).should == [issue]
end
it 'does not detect issues from other projects' do
ext_ref = "#{other_project.path_with_namespace}##{other_issue.iid}"
- stub_const('Gitlab::ClosingIssueExtractor::ISSUE_CLOSING_REGEX',
- /^([Cc]loses|[Ff]ixes)/)
commit.stub(safe_message: "Fixes #{ext_ref}")
commit.closes_issues(project).should be_empty
end