diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index a65dd6703f28a4886b239dadcc92bd21e5348dbd..049a19095bedd2a79369cbc7c16c0019186b84c9 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -439,7 +439,6 @@ def import_from_gitlab_message def show_inactive_project_deletion_banner?(project) return false unless project.present? && project.saved? return false unless delete_inactive_projects? - return false unless Feature.enabled?(:inactive_projects_deletion, project.root_namespace) project.inactive? end diff --git a/app/workers/projects/inactive_projects_deletion_cron_worker.rb b/app/workers/projects/inactive_projects_deletion_cron_worker.rb index a280c9203d6e104a095e450bd6e7d2cb20beb2c0..ba6d44ec4a55086d7f6b024449df99f58c393c47 100644 --- a/app/workers/projects/inactive_projects_deletion_cron_worker.rb +++ b/app/workers/projects/inactive_projects_deletion_cron_worker.rb @@ -39,8 +39,6 @@ def perform raise TimeoutError end - next unless Feature.enabled?(:inactive_projects_deletion, project.root_namespace) - with_context(project: project, user: admin_user) do deletion_warning_email_sent_on = notified_inactive_projects["project:#{project.id}"] diff --git a/config/feature_flags/development/inactive_projects_deletion.yml b/config/feature_flags/development/inactive_projects_deletion.yml deleted file mode 100644 index e9bb91f62cc1742bbb585491ff6d709173e19fe0..0000000000000000000000000000000000000000 --- a/config/feature_flags/development/inactive_projects_deletion.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: inactive_projects_deletion -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85689 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/357968 -milestone: '15.0' -type: development -group: group::compliance -default_enabled: false diff --git a/doc/administration/inactive_project_deletion.md b/doc/administration/inactive_project_deletion.md index 224b52d420ed4c1a7c9b6ccacd7514df8887b594..ed46996143e225ae5a62531fbc8ad42715a12745 100644 --- a/doc/administration/inactive_project_deletion.md +++ b/doc/administration/inactive_project_deletion.md @@ -6,18 +6,16 @@ info: To determine the technical writer assigned to the Stage/Group associated w # Inactive project deletion **(FREE SELF)** -> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85689) in GitLab 15.0 [with a flag](../administration/feature_flags.md) named `inactive_projects_deletion`. Disabled by default. - -FLAG: -On self-managed GitLab, by default this feature is not available. To make it available, ask an administrator to -[enable the feature flag](../administration/feature_flags.md) named `inactive_projects_deletion`. -On GitLab.com, this feature is not available. This feature is not ready for production use. +> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85689) in GitLab 15.0 [with a flag](../administration/feature_flags.md) named `inactive_projects_deletion`. Disabled by default. +> - [Feature flag `inactive_projects_deletion`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96803) removed in GitLab 15.4. Administrators of large GitLab instances can find that over time, projects become inactive and are no longer used. These projects take up unnecessary disk space. With inactive project deletion, you can identify these projects, warn the maintainers ahead of time, and then delete the projects if they remain inactive. When an inactive project is deleted, the action generates an audit event that it was performed by the first active administrator. +For the default setting on GitLab.com, see the [GitLab.com settings page](../user/gitlab_com/index.md#inactive-project-deletion). + ## Configure inactive project deletion You can configure inactive projects deletion or turn it off using either: diff --git a/doc/api/settings.md b/doc/api/settings.md index 5e1be5bce51283854ec1f9de7b3b035fc6a2abb0..c3578f53c32e9c2cbe85e0e6909f1957f236bb28 100644 --- a/doc/api/settings.md +++ b/doc/api/settings.md @@ -286,7 +286,7 @@ listed in the descriptions of the relevant settings. | `default_snippet_visibility` | string | no | What visibility level new snippets receive. Can take `private`, `internal` and `public` as a parameter. Default is `private`. | | `delayed_project_deletion` **(PREMIUM SELF)** | boolean | no | Enable delayed project deletion by default in new groups. Default is `false`. [From GitLab 15.1](https://gitlab.com/gitlab-org/gitlab/-/issues/352960), can only be enabled when `delayed_group_deletion` is true. | | `delayed_group_deletion` **(PREMIUM SELF)** | boolean | no | Enable delayed group deletion. Default is `true`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/352959) in GitLab 15.0. [From GitLab 15.1](https://gitlab.com/gitlab-org/gitlab/-/issues/352960), disables and locks the group-level setting for delayed protect deletion when set to `false`. | -| `delete_inactive_projects` | boolean | no | Enable inactive project deletion feature. Default is `false`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/84519) in GitLab 14.10. [Became operational](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85689) in GitLab 15.0 (with feature flag `inactive_projects_deletion`, disabled by default). | +| `delete_inactive_projects` | boolean | no | Enable inactive project deletion feature. Default is `false`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/84519) in GitLab 14.10. [Became operational without feature flag](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96803) in GitLab 15.4. | | `deletion_adjourned_period` **(PREMIUM SELF)** | integer | no | The number of days to wait before deleting a project or group that is marked for deletion. Value must be between `1` and `90`. Defaults to `7`. [From GitLab 15.1](https://gitlab.com/gitlab-org/gitlab/-/issues/352960), a hook on `deletion_adjourned_period` sets the period to `1` on every update, and sets both `delayed_project_deletion` and `delayed_group_deletion` to `false` if the period is `0`. | | `diff_max_patch_bytes` | integer | no | Maximum [diff patch size](../user/admin_area/diff_limits.md), in bytes. | | `diff_max_files` | integer | no | Maximum [files in a diff](../user/admin_area/diff_limits.md). | diff --git a/ee/spec/workers/ee/projects/inactive_projects_deletion_cron_worker_spec.rb b/ee/spec/workers/ee/projects/inactive_projects_deletion_cron_worker_spec.rb index e479ffd30d0cfc082951742fa7af387d286acd45..4f639b7444b114924f21f26ced274612482636cd 100644 --- a/ee/spec/workers/ee/projects/inactive_projects_deletion_cron_worker_spec.rb +++ b/ee/spec/workers/ee/projects/inactive_projects_deletion_cron_worker_spec.rb @@ -41,7 +41,6 @@ stub_application_setting(inactive_projects_delete_after_months: 14) stub_application_setting(deletion_adjourned_period: 7) stub_application_setting(delete_inactive_projects: true) - stub_feature_flags(inactive_projects_deletion: true) end it 'does not send deletion warning email for inactive projects that are already marked for deletion' do diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb index 04c066986b72a61e02284c299357709ef079d14c..7365a3c3276077aaaf32123000ec421f00744f86 100644 --- a/spec/helpers/projects_helper_spec.rb +++ b/spec/helpers/projects_helper_spec.rb @@ -1147,37 +1147,23 @@ def license_name context 'with the setting enabled' do before do stub_application_setting(delete_inactive_projects: true) + stub_application_setting(inactive_projects_min_size_mb: 0) + stub_application_setting(inactive_projects_send_warning_email_after_months: 1) end - context 'with the feature flag disabled' do - before do - stub_feature_flags(inactive_projects_deletion: false) - end - + context 'with an active project' do it_behaves_like 'does not show the banner' end - context 'with the feature flag enabled' do + context 'with an inactive project' do before do - stub_feature_flags(inactive_projects_deletion: true) - stub_application_setting(inactive_projects_min_size_mb: 0) - stub_application_setting(inactive_projects_send_warning_email_after_months: 1) + project.statistics.storage_size = 1.megabyte + project.last_activity_at = 1.year.ago + project.save! end - context 'with an active project' do - it_behaves_like 'does not show the banner' - end - - context 'with an inactive project' do - before do - project.statistics.storage_size = 1.megabyte - project.last_activity_at = 1.year.ago - project.save! - end - - it 'shows the banner' do - expect(helper.show_inactive_project_deletion_banner?(project)).to be(true) - end + it 'shows the banner' do + expect(helper.show_inactive_project_deletion_banner?(project)).to be(true) end end end diff --git a/spec/workers/projects/inactive_projects_deletion_cron_worker_spec.rb b/spec/workers/projects/inactive_projects_deletion_cron_worker_spec.rb index ec10c66968d2b946fd16d4ed92ad4cb0fa904081..50b5b0a6e7bad4c91c07500c0de56b0bc1530b2d 100644 --- a/spec/workers/projects/inactive_projects_deletion_cron_worker_spec.rb +++ b/spec/workers/projects/inactive_projects_deletion_cron_worker_spec.rb @@ -85,86 +85,58 @@ end end - context 'when delete inactive projects feature is enabled' do + context 'when delete inactive projects feature is enabled', :clean_gitlab_redis_shared_state, :sidekiq_inline do before do stub_application_setting(delete_inactive_projects: true) end - context 'when feature flag is disabled' do - before do - stub_feature_flags(inactive_projects_deletion: false) - end - - it 'does not invoke Projects::InactiveProjectsDeletionNotificationWorker' do - expect(::Projects::InactiveProjectsDeletionNotificationWorker).not_to receive(:perform_async) - expect(::Projects::DestroyService).not_to receive(:new) - - worker.perform - end - - it 'does not delete the inactive projects' do - worker.perform - - expect(inactive_large_project.reload.pending_delete).to eq(false) + it 'invokes Projects::InactiveProjectsDeletionNotificationWorker for inactive projects' do + Gitlab::Redis::SharedState.with do |redis| + expect(redis).to receive(:hset).with('inactive_projects_deletion_warning_email_notified', + "project:#{inactive_large_project.id}", Date.current) end + expect(::Projects::InactiveProjectsDeletionNotificationWorker).to receive(:perform_async).with( + inactive_large_project.id, deletion_date).and_call_original + expect(::Projects::DestroyService).not_to receive(:new) - it_behaves_like 'worker is running for more than 4 minutes' - it_behaves_like 'worker finishes processing in less than 4 minutes' + worker.perform end - context 'when feature flag is enabled', :clean_gitlab_redis_shared_state, :sidekiq_inline do - before do - stub_feature_flags(inactive_projects_deletion: true) - end - - it 'invokes Projects::InactiveProjectsDeletionNotificationWorker for inactive projects' do - Gitlab::Redis::SharedState.with do |redis| - expect(redis).to receive(:hset).with('inactive_projects_deletion_warning_email_notified', - "project:#{inactive_large_project.id}", Date.current) - end - expect(::Projects::InactiveProjectsDeletionNotificationWorker).to receive(:perform_async).with( - inactive_large_project.id, deletion_date).and_call_original - expect(::Projects::DestroyService).not_to receive(:new) - - worker.perform + it 'does not invoke InactiveProjectsDeletionNotificationWorker for already notified inactive projects' do + Gitlab::Redis::SharedState.with do |redis| + redis.hset('inactive_projects_deletion_warning_email_notified', "project:#{inactive_large_project.id}", + Date.current.to_s) end - it 'does not invoke InactiveProjectsDeletionNotificationWorker for already notified inactive projects' do - Gitlab::Redis::SharedState.with do |redis| - redis.hset('inactive_projects_deletion_warning_email_notified', "project:#{inactive_large_project.id}", - Date.current.to_s) - end + expect(::Projects::InactiveProjectsDeletionNotificationWorker).not_to receive(:perform_async) + expect(::Projects::DestroyService).not_to receive(:new) - expect(::Projects::InactiveProjectsDeletionNotificationWorker).not_to receive(:perform_async) - expect(::Projects::DestroyService).not_to receive(:new) + worker.perform + end - worker.perform + it 'invokes Projects::DestroyService for projects that are inactive even after being notified' do + Gitlab::Redis::SharedState.with do |redis| + redis.hset('inactive_projects_deletion_warning_email_notified', "project:#{inactive_large_project.id}", + 15.months.ago.to_date.to_s) end - it 'invokes Projects::DestroyService for projects that are inactive even after being notified' do - Gitlab::Redis::SharedState.with do |redis| - redis.hset('inactive_projects_deletion_warning_email_notified', "project:#{inactive_large_project.id}", - 15.months.ago.to_date.to_s) - end - - expect(::Projects::InactiveProjectsDeletionNotificationWorker).not_to receive(:perform_async) - expect(::Projects::DestroyService).to receive(:new).with(inactive_large_project, admin_user, {}) - .at_least(:once).and_call_original + expect(::Projects::InactiveProjectsDeletionNotificationWorker).not_to receive(:perform_async) + expect(::Projects::DestroyService).to receive(:new).with(inactive_large_project, admin_user, {}) + .at_least(:once).and_call_original - worker.perform + worker.perform - expect(inactive_large_project.reload.pending_delete).to eq(true) + expect(inactive_large_project.reload.pending_delete).to eq(true) - Gitlab::Redis::SharedState.with do |redis| - expect(redis.hget('inactive_projects_deletion_warning_email_notified', - "project:#{inactive_large_project.id}")).to be_nil - end + Gitlab::Redis::SharedState.with do |redis| + expect(redis.hget('inactive_projects_deletion_warning_email_notified', + "project:#{inactive_large_project.id}")).to be_nil end - - it_behaves_like 'worker is running for more than 4 minutes' - it_behaves_like 'worker finishes processing in less than 4 minutes' end + it_behaves_like 'worker is running for more than 4 minutes' + it_behaves_like 'worker finishes processing in less than 4 minutes' + it_behaves_like 'an idempotent worker' end end