Commit 72177a39 authored by Fabio Huser's avatar Fabio Huser Committed by Mayra Cabrera

Add disable option for the auto-close referenced issues feature

parent db0c0b74
......@@ -386,6 +386,7 @@ class ProjectsController < Projects::ApplicationController
:template_project_id,
:merge_method,
:initialize_with_readme,
:autoclose_referenced_issues,
project_feature_attributes: %i[
builds_access_level
......
......@@ -102,6 +102,8 @@ module Types
description: 'Indicates if a link to create or view a merge request should display after a push to Git repositories of the project from the command line'
field :remove_source_branch_after_merge, GraphQL::BOOLEAN_TYPE, null: true,
description: 'Indicates if `Delete source branch` option should be enabled by default for all new merge requests of the project'
field :autoclose_referenced_issues, GraphQL::BOOLEAN_TYPE, null: true,
description: 'Indicates if issues referenced by merge requests and commits within the default branch are closed automatically'
field :namespace, Types::NamespaceType, null: true,
description: 'Namespace of the project'
......
......@@ -75,6 +75,7 @@ class Project < ApplicationRecord
default_value_for :snippets_enabled, gitlab_config_features.snippets
default_value_for :only_allow_merge_if_all_discussions_are_resolved, false
default_value_for :remove_source_branch_after_merge, true
default_value_for :autoclose_referenced_issues, true
default_value_for(:ci_config_path) { Gitlab::CurrentSettings.default_ci_config_path }
add_authentication_token_field :runners_token, encrypted: -> { Feature.enabled?(:projects_tokens_optional_encryption, default_enabled: true) ? :optional : :required }
......@@ -679,6 +680,12 @@ class Project < ApplicationRecord
end
end
def autoclose_referenced_issues
return true if super.nil?
super
end
def preload_protected_branches
preloader = ActiveRecord::Associations::Preloader.new
preloader.preload(self, protected_branches: [:push_access_levels, :merge_access_levels])
......
......@@ -9,13 +9,23 @@
= _('Select the branch you want to set as the default for this project. All merge requests and commits will automatically be made against this branch unless you specify a different one.')
.settings-content
= form_for [@project.namespace.becomes(Namespace), @project], remote: true, html: { multipart: true, anchor: 'default-branch-settings' }, authenticity_token: true do |f|
%fieldset
- if @project.empty_repo?
.text-secondary
= _('A default branch cannot be chosen for an empty project.')
- else
= form_for [@project.namespace.becomes(Namespace), @project], remote: true, html: { multipart: true, anchor: 'default-branch-settings' }, authenticity_token: true do |f|
%fieldset
.form-group
= f.label :default_branch, "Default Branch", class: 'label-bold'
= f.select(:default_branch, @project.repository.branch_names, {}, {class: 'select2 select-wide'})
.form-group
.form-check
= f.check_box :autoclose_referenced_issues, class: 'form-check-input'
= f.label :autoclose_referenced_issues, class: 'form-check-label' do
%strong= _("Auto-close referenced issues on default branch")
.form-text.text-muted
= _("Issues referenced by merge requests and commits within the default branch will be closed automatically")
= link_to icon('question-circle'), help_page_path('user/project/issues/managing_issues.html', anchor: 'disabling-automatic-issue-closing'), target: '_blank'
= f.submit 'Save changes', class: "btn btn-success"
---
title: Add capability to disable issue auto-close feature per project
merge_request: 21704
author: Fabio Huser
type: added
# frozen_string_literal: true
class AddAutocloseReferencedIssuesToProjects < ActiveRecord::Migration[5.2]
DOWNTIME = false
def change
add_column :projects, :autoclose_referenced_issues, :boolean
end
end
......@@ -3348,6 +3348,7 @@ ActiveRecord::Schema.define(version: 2020_01_06_085831) do
t.boolean "remove_source_branch_after_merge"
t.date "marked_for_deletion_at"
t.integer "marked_for_deletion_by_user_id"
t.boolean "autoclose_referenced_issues"
t.index "lower((name)::text)", name: "index_projects_on_lower_name"
t.index ["created_at", "id"], name: "index_projects_on_created_at_and_id"
t.index ["creator_id"], name: "index_projects_on_creator_id"
......
......@@ -4609,6 +4609,11 @@ type Project {
"""
archived: Boolean
"""
Indicates if issues referenced by merge requests and commits within the default branch are closed automatically
"""
autocloseReferencedIssues: Boolean
"""
URL to avatar image file of the project
"""
......
......@@ -322,6 +322,20 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "autocloseReferencedIssues",
"description": "Indicates if issues referenced by merge requests and commits within the default branch are closed automatically",
"args": [
],
"type": {
"kind": "SCALAR",
"name": "Boolean",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "avatarUrl",
"description": "URL to avatar image file of the project",
......
......@@ -704,6 +704,7 @@ The API can be explored interactively using the [GraphiQL IDE](../index.md#graph
| `onlyAllowMergeIfAllDiscussionsAreResolved` | Boolean | Indicates if merge requests of the project can only be merged when all the discussions are resolved |
| `printingMergeRequestLinkEnabled` | Boolean | Indicates if a link to create or view a merge request should display after a push to Git repositories of the project from the command line |
| `removeSourceBranchAfterMerge` | Boolean | Indicates if `Delete source branch` option should be enabled by default for all new merge requests of the project |
| `autocloseReferencedIssues` | Boolean | Indicates if issues referenced by merge requests and commits within the default branch are closed automatically |
| `namespace` | Namespace | Namespace of the project |
| `group` | Group | Group of the project |
| `statistics` | ProjectStatistics | Statistics of the project |
......
......@@ -156,6 +156,7 @@ When the user is authenticated and `simple` is not set this returns something li
"remove_source_branch_after_merge": false,
"request_access_enabled": false,
"merge_method": "merge",
"autoclose_referenced_issues": true,
"statistics": {
"commit_count": 37,
"storage_size": 1038090,
......@@ -254,6 +255,7 @@ When the user is authenticated and `simple` is not set this returns something li
"packages_enabled": true,
"service_desk_enabled": false,
"service_desk_address": null,
"autoclose_referenced_issues": true,
"statistics": {
"commit_count": 12,
"storage_size": 2066080,
......@@ -385,6 +387,7 @@ This endpoint supports [keyset pagination](README.md#keyset-based-pagination) fo
"remove_source_branch_after_merge": false,
"request_access_enabled": false,
"merge_method": "merge",
"autoclose_referenced_issues": true,
"statistics": {
"commit_count": 37,
"storage_size": 1038090,
......@@ -483,6 +486,7 @@ This endpoint supports [keyset pagination](README.md#keyset-based-pagination) fo
"packages_enabled": true,
"service_desk_enabled": false,
"service_desk_address": null,
"autoclose_referenced_issues": true,
"statistics": {
"commit_count": 12,
"storage_size": 2066080,
......@@ -593,6 +597,7 @@ Example response:
"remove_source_branch_after_merge": false,
"request_access_enabled": false,
"merge_method": "merge",
"autoclose_referenced_issues": true,
"statistics": {
"commit_count": 37,
"storage_size": 1038090,
......@@ -688,6 +693,7 @@ Example response:
"packages_enabled": true,
"service_desk_enabled": false,
"service_desk_address": null,
"autoclose_referenced_issues": true,
"statistics": {
"commit_count": 12,
"storage_size": 2066080,
......@@ -829,6 +835,7 @@ GET /projects/:id
"packages_enabled": true,
"service_desk_enabled": false,
"service_desk_address": null,
"autoclose_referenced_issues": true,
"statistics": {
"commit_count": 37,
"storage_size": 1038090,
......@@ -986,6 +993,7 @@ POST /projects
| `only_allow_merge_if_pipeline_succeeds` | boolean | no | Set whether merge requests can only be merged with successful jobs |
| `only_allow_merge_if_all_discussions_are_resolved` | boolean | no | Set whether merge requests can only be merged when all the discussions are resolved |
| `merge_method` | string | no | Set the [merge method](#project-merge-method) used |
| `autoclose_referenced_issues` | boolean | no | Set whether auto-closing referenced issues on default branch |
| `remove_source_branch_after_merge` | boolean | no | Enable `Delete source branch` option by default for all new merge requests |
| `lfs_enabled` | boolean | no | Enable LFS |
| `request_access_enabled` | boolean | no | Allow users to request member access |
......@@ -1050,6 +1058,7 @@ POST /projects/user/:user_id
| `only_allow_merge_if_pipeline_succeeds` | boolean | no | Set whether merge requests can only be merged with successful jobs |
| `only_allow_merge_if_all_discussions_are_resolved` | boolean | no | Set whether merge requests can only be merged when all the discussions are resolved |
| `merge_method` | string | no | Set the [merge method](#project-merge-method) used |
| `autoclose_referenced_issues` | boolean | no | Set whether auto-closing referenced issues on default branch |
| `remove_source_branch_after_merge` | boolean | no | Enable `Delete source branch` option by default for all new merge requests |
| `lfs_enabled` | boolean | no | Enable LFS |
| `request_access_enabled` | boolean | no | Allow users to request member access |
......@@ -1113,6 +1122,7 @@ PUT /projects/:id
| `only_allow_merge_if_pipeline_succeeds` | boolean | no | Set whether merge requests can only be merged with successful jobs |
| `only_allow_merge_if_all_discussions_are_resolved` | boolean | no | Set whether merge requests can only be merged when all the discussions are resolved |
| `merge_method` | string | no | Set the [merge method](#project-merge-method) used |
| `autoclose_referenced_issues` | boolean | no | Set whether auto-closing referenced issues on default branch |
| `remove_source_branch_after_merge` | boolean | no | Enable `Delete source branch` option by default for all new merge requests |
| `lfs_enabled` | boolean | no | Enable LFS |
| `request_access_enabled` | boolean | no | Allow users to request member access |
......@@ -1244,6 +1254,7 @@ Example responses:
"remove_source_branch_after_merge": false,
"request_access_enabled": false,
"merge_method": "merge",
"autoclose_referenced_issues": true,
"_links": {
"self": "http://example.com/api/v4/projects",
"issues": "http://example.com/api/v4/projects/1/issues",
......@@ -1332,6 +1343,7 @@ Example response:
"remove_source_branch_after_merge": false,
"request_access_enabled": false,
"merge_method": "merge",
"autoclose_referenced_issues": true,
"_links": {
"self": "http://example.com/api/v4/projects",
"issues": "http://example.com/api/v4/projects/1/issues",
......@@ -1419,6 +1431,7 @@ Example response:
"remove_source_branch_after_merge": false,
"request_access_enabled": false,
"merge_method": "merge",
"autoclose_referenced_issues": true,
"_links": {
"self": "http://example.com/api/v4/projects",
"issues": "http://example.com/api/v4/projects/1/issues",
......@@ -1593,6 +1606,7 @@ Example response:
"remove_source_branch_after_merge": false,
"request_access_enabled": false,
"merge_method": "merge",
"autoclose_referenced_issues": true,
"_links": {
"self": "http://example.com/api/v4/projects",
"issues": "http://example.com/api/v4/projects/1/issues",
......@@ -1699,6 +1713,7 @@ Example response:
"remove_source_branch_after_merge": false,
"request_access_enabled": false,
"merge_method": "merge",
"autoclose_referenced_issues": true,
"_links": {
"self": "http://example.com/api/v4/projects",
"issues": "http://example.com/api/v4/projects/1/issues",
......
......@@ -211,6 +211,19 @@ as well as `#22` and `#23` in group/otherproject. `#17` won't be closed as it do
not match the pattern. It works with multi-line commit messages as well as one-liners
when used from the command line with `git commit -m`.
#### Disabling automatic issue closing
The automatic issue closing feature can be disabled on a per-project basis
within the [project's repository settings](../settings/index.md). Referenced
issues will still be displayed as such but won't be closed automatically.
![disable issue auto close - settings](img/disable_issue_auto_close.png)
This only applies to issues affected by new merge requests or commits. Already
closed issues remain as-is. Disabling automatic issue closing only affects merge
requests *within* the project and won't prevent other projects from closing it
via cross-project issues.
#### Customizing the issue closing pattern **(CORE ONLY)**
In order to change the default issue closing pattern, GitLab administrators must edit the
......
......@@ -331,6 +331,7 @@ module API
expose :auto_devops_deploy_strategy do |project, options|
project.auto_devops.nil? ? 'continuous' : project.auto_devops.deploy_strategy
end
expose :autoclose_referenced_issues
# rubocop: disable CodeReuse/ActiveRecord
def self.preload_relation(projects_relation, options = {})
......
......@@ -47,6 +47,7 @@ module API
optional :ci_default_git_depth, type: Integer, desc: 'Default number of revisions for shallow cloning'
optional :auto_devops_enabled, type: Boolean, desc: 'Flag indication if Auto DevOps is enabled'
optional :auto_devops_deploy_strategy, type: String, values: %w(continuous manual timed_incremental), desc: 'Auto Deploy strategy'
optional :autoclose_referenced_issues, type: Boolean, desc: 'Flag indication if referenced issues auto-closing is enabled'
end
params :optional_project_params_ee do
......@@ -85,6 +86,7 @@ module API
:container_registry_enabled,
:default_branch,
:description,
:autoclose_referenced_issues,
:issues_access_level,
:lfs_enabled,
:merge_requests_access_level,
......
......@@ -11,11 +11,13 @@ module Gitlab
end
def initialize(project, current_user = nil)
@project = project
@extractor = Gitlab::ReferenceExtractor.new(project, current_user)
end
def closed_by_message(message)
return [] if message.nil?
return [] unless @project.autoclose_referenced_issues
closing_statements = []
message.scan(ISSUE_CLOSING_REGEX) do
......
......@@ -2381,6 +2381,9 @@ msgstr ""
msgid "Auto-cancel redundant, pending pipelines"
msgstr ""
msgid "Auto-close referenced issues on default branch"
msgstr ""
msgid "AutoDevOps|Auto DevOps"
msgstr ""
......@@ -10077,6 +10080,9 @@ msgstr ""
msgid "Issues closed"
msgstr ""
msgid "Issues referenced by merge requests and commits within the default branch will be closed automatically"
msgstr ""
msgid "Issues with comments, merge requests with diffs and comments, labels, milestones, snippets, and other project entities"
msgstr ""
......
......@@ -23,7 +23,7 @@ describe GitlabSchema.types['Project'] do
only_allow_merge_if_all_discussions_are_resolved printing_merge_request_link_enabled
namespace group statistics repository merge_requests merge_request issues
issue pipelines removeSourceBranchAfterMerge sentryDetailedError snippets
grafanaIntegration
grafanaIntegration autocloseReferencedIssues
]
is_expected.to include_graphql_fields(*expected_fields)
......
......@@ -438,6 +438,17 @@ describe Gitlab::ClosingIssueExtractor do
.to match_array([issue])
end
end
context "with autoclose referenced issues disabled" do
before do
project.update!(autoclose_referenced_issues: false)
end
it do
message = "Awesome commit (Closes #{reference})"
expect(subject.closed_by_message(message)).to eq([])
end
end
end
def urls
......
......@@ -463,6 +463,7 @@ project:
- import_failures
- container_expiration_policy
- resource_groups
- autoclose_referenced_issues
award_emoji:
- awardable
- user
......
......@@ -534,6 +534,7 @@ Project:
- pages_https_only
- merge_requests_disable_committers_approval
- require_password_to_approve
- autoclose_referenced_issues
ProjectTracingSetting:
- external_url
Author:
......
......@@ -390,6 +390,17 @@ eos
expect(commit.closes_issues).to include(issue)
expect(commit.closes_issues).to include(other_issue)
end
it 'ignores referenced issues when auto-close is disabled' do
project.update!(autoclose_referenced_issues: false)
allow(commit).to receive_messages(
safe_message: "Fixes ##{issue.iid}",
committer_email: committer.email
)
expect(commit.closes_issues).to be_empty
end
end
it_behaves_like 'a mentionable' do
......
......@@ -963,6 +963,15 @@ describe MergeRequest do
expect(subject.closes_issues).to be_empty
end
it 'ignores referenced issues when auto-close is disabled' do
subject.project.update!(autoclose_referenced_issues: false)
allow(subject.project).to receive(:default_branch)
.and_return(subject.target_branch)
expect(subject.closes_issues).to be_empty
end
end
describe '#issues_mentioned_but_not_closing' do
......
......@@ -474,6 +474,32 @@ describe Project do
end
end
describe '#autoclose_referenced_issues' do
context 'when DB entry is nil' do
let(:project) { create(:project, autoclose_referenced_issues: nil) }
it 'returns true' do
expect(project.autoclose_referenced_issues).to be_truthy
end
end
context 'when DB entry is true' do
let(:project) { create(:project, autoclose_referenced_issues: true) }
it 'returns true' do
expect(project.autoclose_referenced_issues).to be_truthy
end
end
context 'when DB entry is false' do
let(:project) { create(:project, autoclose_referenced_issues: false) }
it 'returns false' do
expect(project.autoclose_referenced_issues).to be_falsey
end
end
end
describe 'project token' do
it 'sets an random token if none provided' do
project = FactoryBot.create(:project, runners_token: '')
......
......@@ -635,6 +635,7 @@ describe API::Projects do
wiki_enabled: false,
resolve_outdated_diff_discussions: false,
remove_source_branch_after_merge: true,
autoclose_referenced_issues: true,
only_allow_merge_if_pipeline_succeeds: false,
request_access_enabled: true,
only_allow_merge_if_all_discussions_are_resolved: false,
......@@ -807,6 +808,22 @@ describe API::Projects do
expect(json_response['only_allow_merge_if_all_discussions_are_resolved']).to be_truthy
end
it 'sets a project as enabling auto close referenced issues' do
project = attributes_for(:project, autoclose_referenced_issues: true)
post api('/projects', user), params: project
expect(json_response['autoclose_referenced_issues']).to be_truthy
end
it 'sets a project as disabling auto close referenced issues' do
project = attributes_for(:project, autoclose_referenced_issues: false)
post api('/projects', user), params: project
expect(json_response['autoclose_referenced_issues']).to be_falsey
end
it 'sets the merge method of a project to rebase merge' do
project = attributes_for(:project, merge_method: 'rebase_merge')
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment