diff options
-rw-r--r-- | CHANGELOG | 2 | ||||
-rw-r--r-- | config/gitlab.yml.example | 2 | ||||
-rw-r--r-- | config/initializers/1_settings.rb | 2 | ||||
-rw-r--r-- | lib/gitlab/closing_issue_extractor.rb | 19 | ||||
-rw-r--r-- | spec/lib/gitlab/closing_issue_extractor_spec.rb | 84 | ||||
-rw-r--r-- | spec/models/commit_spec.rb | 4 |
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 |