Commit 7942b0c3 authored by Heinrich Lee Yu's avatar Heinrich Lee Yu

Merge branch '191367-allow-deleting-projects-immediately' into 'master'

Allow immediate deletion of projects

See merge request gitlab-org/gitlab!65522
parents 51062952 42d670fa
...@@ -46,7 +46,7 @@ export default { ...@@ -46,7 +46,7 @@ export default {
return sprintf( return sprintf(
s__(`AdminProjects| s__(`AdminProjects|
You’re about to permanently delete the project %{projectName}, its repository, You’re about to permanently delete the project %{projectName}, its repository,
and all related resources including issues, merge requests, etc.. Once you confirm and press and all related resources, including issues and merge requests. Once you confirm and press
%{strong_start}Delete project%{strong_end}, it cannot be undone or recovered.`), %{strong_start}Delete project%{strong_end}, it cannot be undone or recovered.`),
{ {
projectName: `<strong>${escape(this.projectName)}</strong>`, projectName: `<strong>${escape(this.projectName)}</strong>`,
......
...@@ -24,9 +24,6 @@ export default { ...@@ -24,9 +24,6 @@ export default {
alertBody: __( alertBody: __(
'Once a project is permanently deleted, it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd}, including issues, merge requests etc.', 'Once a project is permanently deleted, it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd}, including issues, merge requests etc.',
), ),
modalBody: __(
"This action cannot be undone. You will lose this project's repository and all related resources, including issues, merge requests, etc.",
),
}, },
}; };
</script> </script>
...@@ -46,7 +43,6 @@ export default { ...@@ -46,7 +43,6 @@ export default {
</template> </template>
</gl-sprintf> </gl-sprintf>
</gl-alert> </gl-alert>
<p>{{ $options.strings.modalBody }}</p>
</template> </template>
</shared-delete-button> </shared-delete-button>
</template> </template>
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
.sub-section .sub-section
%h4.danger-title= _('Delete project') %h4.danger-title= _('Delete project')
%p %p
%strong= _('Deleting the project will delete its repository and all related resources including issues, merge requests, etc.') %strong= _('Deleting the project will delete its repository and all related resources, including issues and merge requests.')
= link_to _('Learn more.'), help_page_path('user/project/settings/index', anchor: 'removing-a-fork-relationship'), target: '_blank', rel: 'noopener noreferrer' = link_to _('Learn more.'), help_page_path('user/project/settings/index', anchor: 'removing-a-fork-relationship'), target: '_blank', rel: 'noopener noreferrer'
%p %p
%strong= _('Deleted projects cannot be restored!') %strong= _('Deleted projects cannot be restored!')
......
...@@ -364,13 +364,18 @@ namespace if needed. ...@@ -364,13 +364,18 @@ namespace if needed.
#### Delete a project #### Delete a project
NOTE: You can mark a project to be deleted.
Only project Owners and administrators have [permissions](../../permissions.md#project-members-permissions) to delete a project.
Prerequisite:
- You must have at least the Owner role for a project.
To delete a project: To delete a project:
1. Navigate to your project, and select **Settings > General > Advanced**. 1. On the top bar, select **Menu > Projects** and find your project.
1. In the "Delete project" section, click the **Delete project** button. 1. On the left sidebar, select **Settings > General**.
1. Expand **Advanced**.
1. In the "Delete project" section, select **Delete project**.
1. Confirm the action when asked to. 1. Confirm the action when asked to.
This action deletes a project including all associated resources (issues, merge requests, and so on). This action deletes a project including all associated resources (issues, merge requests, and so on).
...@@ -385,6 +390,28 @@ WARNING: ...@@ -385,6 +390,28 @@ WARNING:
The default behavior of [delayed project deletion](https://gitlab.com/gitlab-org/gitlab/-/issues/32935) in GitLab 12.6 was changed to The default behavior of [delayed project deletion](https://gitlab.com/gitlab-org/gitlab/-/issues/32935) in GitLab 12.6 was changed to
[Immediate deletion](https://gitlab.com/gitlab-org/gitlab/-/issues/220382) in GitLab 13.2. [Immediate deletion](https://gitlab.com/gitlab-org/gitlab/-/issues/220382) in GitLab 13.2.
#### Delete a project immediately **(PREMIUM)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/191367) in GitLab 14.1.
If you don't want to wait, you can delete a project immediately.
Prerequisites:
- You must have at least the Owner role for a project.
- You have [marked the project for deletion](#delete-a-project).
To immediately delete a project marked for deletion:
1. On the top bar, select **Menu > Projects** and find your project.
1. On the left sidebar, select **Settings > General**.
1. Expand **Advanced**.
1. In the "Permanently delete project" section, select **Delete project**.
1. Confirm the action when asked to.
Your project, its repository, and all related resources, including issues and merge requests,
are deleted.
#### Restore a project **(PREMIUM)** #### Restore a project **(PREMIUM)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/32935) in GitLab 12.6. > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/32935) in GitLab 12.6.
......
...@@ -30,7 +30,7 @@ export default { ...@@ -30,7 +30,7 @@ export default {
}, },
strings: { strings: {
modalBody: __( modalBody: __(
"Once a project is permanently deleted, it cannot be recovered. You will lose this project's repository and all related resources, including issues, merge requests etc.", "Once a project is permanently deleted, it cannot be recovered. You will lose this project's repository and all related resources, including issues and merge requests.",
), ),
helpLabel: __('Recovering projects'), helpLabel: __('Recovering projects'),
recoveryMessage: __('You can recover this project until %{date}'), recoveryMessage: __('You can recover this project until %{date}'),
......
...@@ -36,6 +36,8 @@ module EE ...@@ -36,6 +36,8 @@ module EE
override :destroy override :destroy
def destroy def destroy
return super unless project.adjourned_deletion? return super unless project.adjourned_deletion?
return super if project.marked_for_deletion? && params[:permanently_delete].present?
return access_denied! unless can?(current_user, :remove_project, project) return access_denied! unless can?(current_user, :remove_project, project)
result = ::Projects::MarkForDeletionService.new(project, current_user, {}).execute result = ::Projects::MarkForDeletionService.new(project, current_user, {}).execute
......
...@@ -100,13 +100,13 @@ module EE ...@@ -100,13 +100,13 @@ module EE
end end
def permanent_delete_message(project) def permanent_delete_message(project)
message = _('This action will %{strongOpen}permanently delete%{strongClose} %{codeOpen}%{project}%{codeClose} %{strongOpen}immediately%{strongClose}, including its repositories and all related resources, including issues, merge requests, etc.') message = _('This action will %{strongOpen}permanently delete%{strongClose} %{codeOpen}%{project}%{codeClose} %{strongOpen}immediately%{strongClose}, including its repositories and all related resources, including issues and merge requests.')
html_escape(message) % remove_message_data(project) html_escape(message) % remove_message_data(project)
end end
def marked_for_removal_message(project) def marked_for_removal_message(project)
date = permanent_deletion_date(Time.now.utc) date = permanent_deletion_date(Time.now.utc)
message = _('This action will %{strongOpen}permanently delete%{strongClose} %{codeOpen}%{project}%{codeClose} %{strongOpen}on %{date}%{strongClose}, including its repositories and all related resources, including issues, merge requests, etc.') message = _('This action will %{strongOpen}permanently delete%{strongClose} %{codeOpen}%{project}%{codeClose} %{strongOpen}on %{date}%{strongClose}, including its repositories and all related resources, including issues and merge requests.')
html_escape(message) % remove_message_data(project).merge(date: date) html_escape(message) % remove_message_data(project).merge(date: date)
end end
......
...@@ -22,4 +22,5 @@ ...@@ -22,4 +22,5 @@
#js-project-delete-button{ data: { form_path: project_path(project), confirm_phrase: project.path } } #js-project-delete-button{ data: { form_path: project_path(project), confirm_phrase: project.path } }
- else - else
= render 'projects/settings/restore', project: project = render 'projects/settings/restore', project: project
= render 'projects/settings/permanently_delete', project: project
.sub-section
%h4.danger-title= _('Permanently delete project')
%p
%strong= _('Deleting the project will delete its repository and all related resources, including issues and merge requests.')
%p= permanent_delete_message(project)
%p
%strong= _('Are you ABSOLUTELY SURE you wish to delete this project?')
#js-project-delete-button{ data: { form_path: project_path(project, permanently_delete: true), confirm_phrase: project.path } }
...@@ -2,12 +2,12 @@ ...@@ -2,12 +2,12 @@
- date = permanent_deletion_date(project.marked_for_deletion_at) - date = permanent_deletion_date(project.marked_for_deletion_at)
.sub-section .sub-section
%h4.danger-title= _('Restore project') %h4= _('Restore project')
%p %p
%strong= _('This project will be deleted on %{date}') %{ date: date } %strong= _('This project will be deleted on %{date}') %{ date: date }
%p %p
= _("Restoring the project will prevent the project from being removed on this date and restore people's ability to make changes to it.") = _("Restoring the project will prevent the project from being removed on this date and restore people's ability to make changes to it.")
= _("The repository can be committed to, and issues, comments and other entities can be created.") = _("The repository can be committed to, and issues, comments and other entities can be created.")
%strong= _('Only active this projects shows up in the search and on the dashboard.') %strong= _('Only active projects show up in the search and on the dashboard.')
= link_to _('Restore project'), namespace_project_restore_path(project.namespace, project), = link_to _('Restore project'), namespace_project_restore_path(project.namespace, project),
method: :post, class: "gl-button btn btn-danger" method: :post, class: "gl-button btn"
...@@ -749,6 +749,34 @@ RSpec.describe ProjectsController do ...@@ -749,6 +749,34 @@ RSpec.describe ProjectsController do
expect(response).to redirect_to(dashboard_projects_path) expect(response).to redirect_to(dashboard_projects_path)
end end
end end
context 'when project is already marked for deletion' do
let(:project) { create(:project, group: group, marked_for_deletion_at: Date.current) }
context 'when permanently_delete param is set' do
it 'deletes project right away' do
expect(ProjectDestroyWorker).to receive(:perform_async)
delete :destroy, params: { namespace_id: project.namespace, id: project, permanently_delete: true }
expect(project.reload.pending_delete).to eq(true)
expect(response).to have_gitlab_http_status(:found)
expect(response).to redirect_to(dashboard_projects_path)
end
end
context 'when permanently_delete param is not set' do
it 'does nothing' do
expect(ProjectDestroyWorker).not_to receive(:perform_async)
delete :destroy, params: { namespace_id: project.namespace, id: project }
expect(project.reload.pending_delete).to eq(false)
expect(response).to have_gitlab_http_status(:found)
expect(response).to redirect_to(project_path(project))
end
end
end
end end
context 'when feature is disabled for group' do context 'when feature is disabled for group' do
......
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe 'Project', :js, quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/222234' do RSpec.describe 'Project', :js do
describe 'when creating from group template' do describe 'when creating from group template', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/222234' do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:group) { create(:group, name: 'parent-group') } let(:group) { create(:group, name: 'parent-group') }
let(:template_subgroup) { create(:group, parent: group, name: 'template-subgroup') } let(:template_subgroup) { create(:group, parent: group, name: 'template-subgroup') }
...@@ -35,4 +35,29 @@ RSpec.describe 'Project', :js, quarantine: 'https://gitlab.com/gitlab-org/gitlab ...@@ -35,4 +35,29 @@ RSpec.describe 'Project', :js, quarantine: 'https://gitlab.com/gitlab-org/gitlab
expect(find('.js-select-namespace')).to have_content other_subgroup.name expect(find('.js-select-namespace')).to have_content other_subgroup.name
end end
end end
describe 'immediately deleting a project marked for deletion' do
let(:project) { create(:project, marked_for_deletion_at: Date.current) }
let(:user) { project.owner }
before do
stub_licensed_features(adjourned_deletion_for_projects_and_groups: true)
sign_in user
visit edit_project_path(project)
end
it 'deletes the project immediately', :sidekiq_inline do
expect { remove_with_confirm('Delete project', project.path, 'Yes, delete project') }.to change { Project.count }.by(-1)
expect(page).to have_content "Project '#{project.full_name}' is in the process of being deleted."
expect(Project.all.count).to be_zero
end
def remove_with_confirm(button_text, confirm_with, confirm_button_text = 'Confirm')
click_button button_text
fill_in 'confirm_name_input', with: confirm_with
click_button confirm_button_text
end
end
end end
...@@ -43,7 +43,7 @@ exports[`Project remove modal initialized matches the snapshot 1`] = ` ...@@ -43,7 +43,7 @@ exports[`Project remove modal initialized matches the snapshot 1`] = `
<div> <div>
<p> <p>
Once a project is permanently deleted, it cannot be recovered. You will lose this project's repository and all related resources, including issues, merge requests etc. Once a project is permanently deleted, it cannot be recovered. You will lose this project's repository and all related resources, including issues and merge requests.
</p> </p>
<p <p
......
...@@ -2368,7 +2368,7 @@ msgstr "" ...@@ -2368,7 +2368,7 @@ msgstr ""
msgid "AdminDashboard|Error loading the statistics. Please try again" msgid "AdminDashboard|Error loading the statistics. Please try again"
msgstr "" msgstr ""
msgid "AdminProjects| You’re about to permanently delete the project %{projectName}, its repository, and all related resources including issues, merge requests, etc.. Once you confirm and press %{strong_start}Delete project%{strong_end}, it cannot be undone or recovered." msgid "AdminProjects| You’re about to permanently delete the project %{projectName}, its repository, and all related resources, including issues and merge requests. Once you confirm and press %{strong_start}Delete project%{strong_end}, it cannot be undone or recovered."
msgstr "" msgstr ""
msgid "AdminProjects|Delete" msgid "AdminProjects|Delete"
...@@ -10713,7 +10713,7 @@ msgstr "" ...@@ -10713,7 +10713,7 @@ msgstr ""
msgid "Deleting a user has the following effects:" msgid "Deleting a user has the following effects:"
msgstr "" msgstr ""
msgid "Deleting the project will delete its repository and all related resources including issues, merge requests, etc." msgid "Deleting the project will delete its repository and all related resources, including issues and merge requests."
msgstr "" msgstr ""
msgid "Deletion pending. This project will be deleted on %{date}. Repository and other project resources are read-only." msgid "Deletion pending. This project will be deleted on %{date}. Repository and other project resources are read-only."
...@@ -23070,7 +23070,7 @@ msgstr "" ...@@ -23070,7 +23070,7 @@ msgstr ""
msgid "Once a project is permanently deleted, it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd}, including issues, merge requests etc." msgid "Once a project is permanently deleted, it %{strongStart}cannot be recovered%{strongEnd}. Permanently deleting this project will %{strongStart}immediately delete%{strongEnd} its repositories and %{strongStart}all related resources%{strongEnd}, including issues, merge requests etc."
msgstr "" msgstr ""
msgid "Once a project is permanently deleted, it cannot be recovered. You will lose this project's repository and all related resources, including issues, merge requests etc." msgid "Once a project is permanently deleted, it cannot be recovered. You will lose this project's repository and all related resources, including issues and merge requests."
msgstr "" msgstr ""
msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}." msgid "Once imported, repositories can be mirrored over SSH. Read more %{link_start}here%{link_end}."
...@@ -23120,7 +23120,7 @@ msgstr "" ...@@ -23120,7 +23120,7 @@ msgstr ""
msgid "Only Project Members" msgid "Only Project Members"
msgstr "" msgstr ""
msgid "Only active this projects shows up in the search and on the dashboard." msgid "Only active projects show up in the search and on the dashboard."
msgstr "" msgstr ""
msgid "Only admins can delete project" msgid "Only admins can delete project"
...@@ -23969,6 +23969,9 @@ msgstr "" ...@@ -23969,6 +23969,9 @@ msgstr ""
msgid "PerformanceBar|Trace" msgid "PerformanceBar|Trace"
msgstr "" msgstr ""
msgid "Permanently delete project"
msgstr ""
msgid "Permissions" msgid "Permissions"
msgstr "" msgstr ""
...@@ -33450,16 +33453,13 @@ msgstr "" ...@@ -33450,16 +33453,13 @@ msgstr ""
msgid "This action cannot be undone, and will permanently delete the %{key} SSH key" msgid "This action cannot be undone, and will permanently delete the %{key} SSH key"
msgstr "" msgstr ""
msgid "This action cannot be undone. You will lose this project's repository and all related resources, including issues, merge requests, etc."
msgstr ""
msgid "This action has been performed too many times. Try again later." msgid "This action has been performed too many times. Try again later."
msgstr "" msgstr ""
msgid "This action will %{strongOpen}permanently delete%{strongClose} %{codeOpen}%{project}%{codeClose} %{strongOpen}immediately%{strongClose}, including its repositories and all related resources, including issues, merge requests, etc." msgid "This action will %{strongOpen}permanently delete%{strongClose} %{codeOpen}%{project}%{codeClose} %{strongOpen}immediately%{strongClose}, including its repositories and all related resources, including issues and merge requests."
msgstr "" msgstr ""
msgid "This action will %{strongOpen}permanently delete%{strongClose} %{codeOpen}%{project}%{codeClose} %{strongOpen}on %{date}%{strongClose}, including its repositories and all related resources, including issues, merge requests, etc." msgid "This action will %{strongOpen}permanently delete%{strongClose} %{codeOpen}%{project}%{codeClose} %{strongOpen}on %{date}%{strongClose}, including its repositories and all related resources, including issues and merge requests."
msgstr "" msgstr ""
msgid "This also resolves all related threads" msgid "This also resolves all related threads"
......
...@@ -256,7 +256,7 @@ RSpec.describe 'Project' do ...@@ -256,7 +256,7 @@ RSpec.describe 'Project' do
expect(page).to have_selector '#confirm_name_input:focus' expect(page).to have_selector '#confirm_name_input:focus'
end end
it 'deletes a project', :sidekiq_might_not_need_inline do it 'deletes a project', :sidekiq_inline do
expect { remove_with_confirm('Delete project', project.path, 'Yes, delete project') }.to change { Project.count }.by(-1) expect { remove_with_confirm('Delete project', project.path, 'Yes, delete project') }.to change { Project.count }.by(-1)
expect(page).to have_content "Project '#{project.full_name}' is in the process of being deleted." expect(page).to have_content "Project '#{project.full_name}' is in the process of being deleted."
expect(Project.all.count).to be_zero expect(Project.all.count).to be_zero
......
...@@ -57,10 +57,6 @@ exports[`Project remove modal initialized matches the snapshot 1`] = ` ...@@ -57,10 +57,6 @@ exports[`Project remove modal initialized matches the snapshot 1`] = `
/> />
</gl-alert-stub> </gl-alert-stub>
<p>
This action cannot be undone. You will lose this project's repository and all related resources, including issues, merge requests, etc.
</p>
<p <p
class="gl-mb-1" class="gl-mb-1"
> >
......
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