Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab/ci/gitlab-gems.gitlab-ci.yml3
-rw-r--r--Gemfile3
-rw-r--r--Gemfile.checksum5
-rw-r--r--Gemfile.lock17
-rw-r--r--app/controllers/projects_controller.rb8
-rw-r--r--app/models/members/project_member.rb6
-rw-r--r--app/services/resource_access_tokens/create_service.rb10
-rw-r--r--app/views/projects/edit.html.haml215
-rw-r--r--app/views/projects/settings/access_tokens/_form.html.haml2
-rwxr-xr-xbin/gitlab-backup-cli14
-rw-r--r--db/migrate/20231024123444_add_archive_project_to_member_roles.rb9
-rw-r--r--db/schema_migrations/202310241234441
-rw-r--r--db/structure.sql1
-rw-r--r--doc/api/graphql/reference/index.md3
-rw-r--r--doc/api/member_roles.md13
-rw-r--r--doc/user/custom_roles.md2
-rw-r--r--gems/gitlab-backup-cli/.gitignore11
-rw-r--r--gems/gitlab-backup-cli/.gitlab-ci.yml4
-rw-r--r--gems/gitlab-backup-cli/.rspec3
-rw-r--r--gems/gitlab-backup-cli/.rubocop.yml19
-rw-r--r--gems/gitlab-backup-cli/Gemfile6
-rw-r--r--gems/gitlab-backup-cli/Gemfile.lock114
-rw-r--r--gems/gitlab-backup-cli/LICENSE.txt21
-rw-r--r--gems/gitlab-backup-cli/README.md7
-rw-r--r--gems/gitlab-backup-cli/Rakefile12
-rwxr-xr-xgems/gitlab-backup-cli/bin/console11
-rwxr-xr-xgems/gitlab-backup-cli/bin/setup8
-rw-r--r--gems/gitlab-backup-cli/gitlab-backup-cli.gemspec43
-rw-r--r--gems/gitlab-backup-cli/lib/gitlab/backup/cli.rb14
-rw-r--r--gems/gitlab-backup-cli/lib/gitlab/backup/cli/runner.rb34
-rw-r--r--gems/gitlab-backup-cli/lib/gitlab/backup/cli/version.rb9
-rw-r--r--gems/gitlab-backup-cli/sig/gitlab/backup/cli.rbs7
-rw-r--r--gems/gitlab-backup-cli/sig/gitlab/backup/cli/runner.rbs15
-rw-r--r--gems/gitlab-backup-cli/spec/gitlab/backup/cli_spec.rb7
-rw-r--r--gems/gitlab-backup-cli/spec/spec_helper.rb15
-rw-r--r--lib/sidebars/projects/menus/settings_menu.rb4
-rw-r--r--spec/lib/sidebars/projects/menus/settings_menu_spec.rb12
-rw-r--r--spec/models/members/project_member_spec.rb44
38 files changed, 597 insertions, 135 deletions
diff --git a/.gitlab/ci/gitlab-gems.gitlab-ci.yml b/.gitlab/ci/gitlab-gems.gitlab-ci.yml
index a773e9c7f90..cc8a058d354 100644
--- a/.gitlab/ci/gitlab-gems.gitlab-ci.yml
+++ b/.gitlab/ci/gitlab-gems.gitlab-ci.yml
@@ -29,3 +29,6 @@ include:
- local: .gitlab/ci/templates/gem.gitlab-ci.yml
inputs:
gem_name: "gitlab-http"
+ - local: .gitlab/ci/templates/gem.gitlab-ci.yml
+ inputs:
+ gem_name: "gitlab-backup-cli"
diff --git a/Gemfile b/Gemfile
index f25635fde1b..c1e9e34c3a5 100644
--- a/Gemfile
+++ b/Gemfile
@@ -40,6 +40,7 @@ gem 'gitlab-safe_request_store', path: 'gems/gitlab-safe_request_store' # ruboco
# GitLab Monorepo Gems
group :monorepo do
gem 'gitlab-utils', path: 'gems/gitlab-utils' # rubocop:todo Gemfile/MissingFeatureCategory
+ gem 'gitlab-backup-cli', path: 'gems/gitlab-backup-cli', feature_category: :backup_restore
end
# Responders respond_to and respond_with
@@ -265,7 +266,7 @@ gem 're2', '2.3.0' # rubocop:todo Gemfile/MissingFeatureCategory
# Misc
-gem 'semver_dialects', '~> 1.2.1' # rubocop:todo Gemfile/MissingFeatureCategory
+gem 'semver_dialects', '~> 1.5', feature_category: :static_application_security_testing
gem 'version_sorter', '~> 2.3' # rubocop:todo Gemfile/MissingFeatureCategory
gem 'csv_builder', path: 'gems/csv_builder' # rubocop:todo Gemfile/MissingFeatureCategory
diff --git a/Gemfile.checksum b/Gemfile.checksum
index 5c4be5c85ce..553e0131dda 100644
--- a/Gemfile.checksum
+++ b/Gemfile.checksum
@@ -104,6 +104,7 @@
{"name":"date","version":"3.3.3","platform":"java","checksum":"584e0a582d1eb2207b4eaac089d8a43f2ca10bea02682f286099642f15c56cce"},
{"name":"date","version":"3.3.3","platform":"ruby","checksum":"819792019d5712b748fb15f6dfaaedef14b0328723ef23583ea35f186774530f"},
{"name":"dead_end","version":"3.1.1","platform":"ruby","checksum":"1011df7f7c0149be004e11cbbc37747760227c55305cd902fd3c06e1394b2f5b"},
+{"name":"deb_version","version":"1.0.2","platform":"ruby","checksum":"c21f911d7f2fd1d61219caae254fc078e6598e477fdff8a05a18bec6c72ee713"},
{"name":"debug_inspector","version":"1.1.0","platform":"ruby","checksum":"eaa5a2d0195e1d65fb4164e8e7e466cca2e7eb53bc5e608cf12b8bf02c3a8606"},
{"name":"deckar01-task_list","version":"2.3.3","platform":"ruby","checksum":"918abaf3f81e6c0d224c2b7bef593d7f84ee5847a0692726d24e3fb272c2c758"},
{"name":"declarative","version":"0.0.20","platform":"ruby","checksum":"8021dd6cb17ab2b61233c56903d3f5a259c5cf43c80ff332d447d395b17d9ff9"},
@@ -580,7 +581,7 @@
{"name":"sd_notify","version":"0.1.1","platform":"ruby","checksum":"cbc7ac6caa7cedd26b30a72b5eeb6f36050dc0752df263452ea24fb5a4ad3131"},
{"name":"seed-fu","version":"2.3.7","platform":"ruby","checksum":"f19673443e9af799b730e3d4eca6a89b39e5a36825015dffd00d02ea3365cf74"},
{"name":"selenium-webdriver","version":"4.15.0","platform":"ruby","checksum":"36134e883c4df98f1b7e8519a3753c77427b74621147f8245aa6cac306d52297"},
-{"name":"semver_dialects","version":"1.2.1","platform":"ruby","checksum":"60a1f67659f79c51a667e8858ec9b089c1e4ce4f6d2a0f0b4ac101916946eb23"},
+{"name":"semver_dialects","version":"1.5.0","platform":"ruby","checksum":"0080f1abafc9c1af82d34e890d7c317b9eacb56b9e03040107ef5d1a51ca49ae"},
{"name":"sentry-rails","version":"5.8.0","platform":"ruby","checksum":"c11b2d909de2c2bfda793c45f64180fd784d54c46886338b683ee3f8efa7731b"},
{"name":"sentry-raven","version":"3.1.2","platform":"ruby","checksum":"103d3b122958810d34898ce2e705bcf549ddb9d855a70ce9a3970ee2484f364a"},
{"name":"sentry-ruby","version":"5.8.0","platform":"ruby","checksum":"caeb121433be379fb94e991a45265a287b13a9a9083e7264f539752369d37110"},
@@ -646,7 +647,7 @@
{"name":"test-prof","version":"1.2.3","platform":"ruby","checksum":"c52a40194cb30f399ed3eb6beb4c45b5daad8b8eb418e8ef69089e4dc7e01fd6"},
{"name":"test_file_finder","version":"0.2.1","platform":"ruby","checksum":"a5e9b369d80c76aefbb609acf5e11d89a048f35e565de3cc261c20112f0fcdb3"},
{"name":"text","version":"1.3.1","platform":"ruby","checksum":"2fbbbc82c1ce79c4195b13018a87cbb00d762bda39241bb3cdc32792759dd3f4"},
-{"name":"thor","version":"1.2.2","platform":"ruby","checksum":"2f93c652828cba9fcf4f65f5dc8c306f1a7317e05aad5835a13740122c17f24c"},
+{"name":"thor","version":"1.3.0","platform":"ruby","checksum":"1adc7f9e5b3655a68c71393fee8bd0ad088d14ee8e83a0b73726f23cbb3ca7c3"},
{"name":"thread_safe","version":"0.3.6","platform":"java","checksum":"bb28394cd0924c068981adee71f36a81c85c92e7d74d3f62372bd51489a0e0c2"},
{"name":"thread_safe","version":"0.3.6","platform":"ruby","checksum":"9ed7072821b51c57e8d6b7011a8e282e25aeea3a4065eab326e43f66f063b05a"},
{"name":"thrift","version":"0.16.0","platform":"ruby","checksum":"d023286ea89e30444c9f1c28dd76107f87d8aaf85fe1742da1d8cd3b5417dcce"},
diff --git a/Gemfile.lock b/Gemfile.lock
index 6fbd0fb6c01..e2ebb913813 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -24,6 +24,12 @@ PATH
typhoeus (~> 1.0, >= 1.0.1)
PATH
+ remote: gems/gitlab-backup-cli
+ specs:
+ gitlab-backup-cli (0.0.1)
+ thor (~> 1.3)
+
+PATH
remote: gems/gitlab-http
specs:
gitlab-http (0.1.0)
@@ -422,6 +428,7 @@ GEM
database_cleaner-core (2.0.1)
date (3.3.3)
dead_end (3.1.1)
+ deb_version (1.0.2)
debug_inspector (1.1.0)
deckar01-task_list (2.3.3)
html-pipeline
@@ -1493,9 +1500,10 @@ GEM
rexml (~> 3.2, >= 3.2.5)
rubyzip (>= 1.2.2, < 3.0)
websocket (~> 1.0)
- semver_dialects (1.2.1)
+ semver_dialects (1.5.0)
+ deb_version (~> 1.0.1)
pastel (~> 0.8.0)
- thor (~> 1.2.0)
+ thor (~> 1.3)
tty-command (~> 0.10.1)
sentry-rails (5.8.0)
railties (>= 5.0)
@@ -1617,7 +1625,7 @@ GEM
test_file_finder (0.2.1)
faraday (>= 1.0, < 3.0, != 2.0.0)
text (1.3.1)
- thor (1.2.2)
+ thor (1.3.0)
thread_safe (0.3.6)
thrift (0.16.0)
tilt (2.0.11)
@@ -1849,6 +1857,7 @@ DEPENDENCIES
gettext_i18n_rails (~> 1.11.0)
gettext_i18n_rails_js (~> 2.0.0)
gitaly (~> 16.5.0.pre.rc1)
+ gitlab-backup-cli!
gitlab-chronic (~> 0.10.5)
gitlab-dangerfiles (~> 4.6.0)
gitlab-experiment (~> 0.8.0)
@@ -2028,7 +2037,7 @@ DEPENDENCIES
sd_notify (~> 0.1.0)
seed-fu (~> 2.3.7)
selenium-webdriver (~> 4.15)
- semver_dialects (~> 1.2.1)
+ semver_dialects (~> 1.5)
sentry-rails (~> 5.8.0)
sentry-raven (~> 3.1)
sentry-ruby (~> 5.8.0)
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index 6f15bc553bf..cee56dca538 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -29,7 +29,8 @@ class ProjectsController < Projects::ApplicationController
before_action :authorize_read_code!, only: [:refs]
# Authorize
- before_action :authorize_admin_project!, only: [:edit, :update, :housekeeping, :download_export, :export, :remove_export, :generate_new_export]
+ before_action :authorize_admin_project_or_custom_permissions!, only: :edit
+ before_action :authorize_admin_project!, only: [:update, :housekeeping, :download_export, :export, :remove_export, :generate_new_export]
before_action :authorize_archive_project!, only: [:archive, :unarchive]
before_action :event_filter, only: [:show, :activity]
@@ -598,6 +599,11 @@ class ProjectsController < Projects::ApplicationController
def render_edit
render 'edit'
end
+
+ # Overridden in EE
+ def authorize_admin_project_or_custom_permissions!
+ authorize_admin_project!
+ end
end
ProjectsController.prepend_mod_with('ProjectsController')
diff --git a/app/models/members/project_member.rb b/app/models/members/project_member.rb
index d07e4f9e298..5e5f9ab7385 100644
--- a/app/models/members/project_member.rb
+++ b/app/models/members/project_member.rb
@@ -48,6 +48,12 @@ class ProjectMember < Member
end
end
+ def permissible_access_level_roles_for_project_access_token(current_user, project)
+ permissible_access_level_roles(current_user, project).filter do |_, value|
+ value <= project.project_authorizations.find_by(user: current_user).access_level
+ end
+ end
+
def access_level_roles
Gitlab::Access.options
end
diff --git a/app/services/resource_access_tokens/create_service.rb b/app/services/resource_access_tokens/create_service.rb
index 1c496aa5e77..824b1a8c377 100644
--- a/app/services/resource_access_tokens/create_service.rb
+++ b/app/services/resource_access_tokens/create_service.rb
@@ -17,6 +17,8 @@ module ResourceAccessTokens
access_level = params[:access_level] || Gitlab::Access::MAINTAINER
return error("Could not provision owner access to project access token") if do_not_allow_owner_access_level_for_project_bot?(access_level)
+ return error("Access level of the token can't be greater the access level of the user who created the token") unless validate_access_level(access_level)
+
return error(s_('AccessTokens|Access token limit reached')) if reached_access_token_limit?
user = create_user
@@ -125,6 +127,14 @@ module ResourceAccessTokens
ServiceResponse.success(payload: { access_token: access_token })
end
+ def validate_access_level(access_level)
+ return true unless resource.is_a?(Project)
+ return true if current_user.bot?
+ return true if current_user.can?(:manage_owners, resource)
+
+ current_user.authorized_project?(resource, access_level.to_i)
+ end
+
def do_not_allow_owner_access_level_for_project_bot?(access_level)
resource.is_a?(Project) &&
access_level.to_i == Gitlab::Access::OWNER &&
diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml
index 4e84a6ef7e7..fd0dc1178f7 100644
--- a/app/views/projects/edit.html.haml
+++ b/app/views/projects/edit.html.haml
@@ -5,116 +5,119 @@
- reduce_visibility_form_id = 'reduce-visibility-form'
- @force_desktop_expanded_sidebar = true
-= render Pajamas::AlertComponent.new(title: _('GitLab Pages has moved'),
+- if can?(current_user, :admin_project, @project)
+ = render Pajamas::AlertComponent.new(title: _('GitLab Pages has moved'),
alert_options: { class: 'gl-my-5', data: { feature_id: Users::CalloutsHelper::PAGES_MOVED_CALLOUT, dismiss_endpoint: callouts_path, defer_links: 'true' } }) do |c|
- - c.with_body do
- = _('To go to GitLab Pages, on the left sidebar, select %{pages_link}.').html_safe % {pages_link: link_to('Deploy > Pages', project_pages_path(@project)).html_safe}
-
-%section.settings.general-settings.no-animate.expanded#js-general-settings
- .settings-header
- %h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only= _('Naming, topics, avatar')
- = render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
- = _('Collapse')
- %p.gl-text-secondary= _('Update your project name, topics, description, and avatar.')
- .settings-content= render 'projects/settings/general'
-
-%section.settings.sharing-permissions.no-animate#js-shared-permissions{ class: ('expanded' if expanded), data: { testid: 'visibility-features-permissions-content' } }
- .settings-header
- %h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only= _('Visibility, project features, permissions')
- = render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
- = expanded ? _('Collapse') : _('Expand')
- %p.gl-text-secondary= _('Choose visibility level, enable/disable project features and their permissions, disable email notifications, and show default emoji reactions.')
-
- .settings-content
- = form_for @project, html: { multipart: true, class: "sharing-permissions-form", id: reduce_visibility_form_id }, authenticity_token: true do |f|
- %input{ name: 'update_section', type: 'hidden', value: 'js-shared-permissions' }
- %template.js-project-permissions-form-data{ type: "application/json" }= project_permissions_panel_data(@project).to_json.html_safe
- .js-project-permissions-form{ data: visibility_confirm_modal_data(@project, reduce_visibility_form_id) }
-- if show_merge_request_settings_callout?(@project)
- %section.settings.expanded
- = render Pajamas::AlertComponent.new(variant: :info,
+ - c.with_body do
+ = _('To go to GitLab Pages, on the left sidebar, select %{pages_link}.').html_safe % {pages_link: link_to('Deploy > Pages', project_pages_path(@project)).html_safe}
+
+ %section.settings.general-settings.no-animate.expanded#js-general-settings
+ .settings-header
+ %h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only= _('Naming, topics, avatar')
+ = render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
+ = _('Collapse')
+ %p.gl-text-secondary= _('Update your project name, topics, description, and avatar.')
+ .settings-content= render 'projects/settings/general'
+
+ %section.settings.sharing-permissions.no-animate#js-shared-permissions{ class: ('expanded' if expanded), data: { testid: 'visibility-features-permissions-content' } }
+ .settings-header
+ %h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only= _('Visibility, project features, permissions')
+ = render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
+ = expanded ? _('Collapse') : _('Expand')
+ %p.gl-text-secondary= _('Choose visibility level, enable/disable project features and their permissions, disable email notifications, and show default emoji reactions.')
+
+ .settings-content
+ = form_for @project, html: { multipart: true, class: "sharing-permissions-form", id: reduce_visibility_form_id }, authenticity_token: true do |f|
+ %input{ name: 'update_section', type: 'hidden', value: 'js-shared-permissions' }
+ %template.js-project-permissions-form-data{ type: "application/json" }= project_permissions_panel_data(@project).to_json.html_safe
+ .js-project-permissions-form{ data: visibility_confirm_modal_data(@project, reduce_visibility_form_id) }
+ - if show_merge_request_settings_callout?(@project)
+ %section.settings.expanded
+ = render Pajamas::AlertComponent.new(variant: :info,
title: _('Merge requests and approvals settings have moved.'),
alert_options: { class: 'js-merge-request-settings-callout gl-my-5', data: { feature_id: Users::CalloutsHelper::MERGE_REQUEST_SETTINGS_MOVED_CALLOUT, dismiss_endpoint: callouts_path, defer_links: 'true' } }) do |c|
- - c.with_body do
- = _('On the left sidebar, select %{merge_requests_link} to view them.').html_safe % { merge_requests_link: link_to('Settings > Merge requests', project_settings_merge_requests_path(@project)).html_safe }
-
-%section.settings.no-animate{ class: ('expanded' if expanded), data: { testid: 'badges-settings-content' } }
- .settings-header
- %h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only
- = s_('ProjectSettings|Badges')
- = render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
- = expanded ? _('Collapse') : _('Expand')
- %p.gl-text-secondary
- = s_('ProjectSettings|Customize this project\'s badges.')
- = link_to s_('ProjectSettings|What are badges?'), help_page_path('user/project/badges')
- .settings-content
- = render 'shared/badges/badge_settings'
-
-= render_if_exists 'compliance_management/compliance_framework/project_settings', expanded: expanded
-
-= render_if_exists 'projects/settings/default_issue_template'
-
-= render 'projects/service_desk_settings'
-
-%section.settings.advanced-settings.no-animate#js-project-advanced-settings{ class: ('expanded' if expanded), data: { testid: 'advanced-settings-content' } }
- .settings-header
- %h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only= _('Advanced')
- = render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
- = expanded ? _('Collapse') : _('Expand')
- %p.gl-text-secondary= s_('ProjectSettings|Housekeeping, export, archive, change path, transfer, and delete.')
-
- .settings-content
- = render_if_exists 'projects/settings/restore', project: @project
-
- = render Pajamas::CardComponent.new(card_options: { class: 'gl-new-card gl-mt-0' }, header_options: { class: 'gl-new-card-header gl-flex-direction-column' }, body_options: { class: 'gl-new-card-body gl-px-5 gl-py-4' }) do |c|
- - c.with_header do
- .gl-new-card-title-wrapper
- %h4.gl-new-card-title= _('Housekeeping')
- %p.gl-new-card-description
- = _('Runs a number of housekeeping tasks within the current repository, such as compressing file revisions and removing unreachable objects.')
- = link_to _('Learn more.'), help_page_path('administration/housekeeping'), target: '_blank', rel: 'noopener noreferrer'
-
- - c.with_body do
- .gl-display-flex.gl-flex-wrap.gl-gap-3
- = render Pajamas::ButtonComponent.new(method: :post, href: housekeeping_project_path(@project)) do
- = _('Run housekeeping')
- #js-project-prune-unreachable-objects-button{ data: { prune_objects_path: housekeeping_project_path(@project, prune: true), prune_objects_doc_path: help_page_path('administration/housekeeping', anchor: 'prune-unreachable-objects') } }
-
- = render 'export', project: @project
-
- = render_if_exists 'projects/settings/archive'
-
- = render Pajamas::CardComponent.new(card_options: { class: 'gl-new-card rename-repository' }, header_options: { class: 'gl-new-card-header gl-flex-direction-column' }, body_options: { class: 'gl-new-card-body gl-px-5 gl-py-4' }) do |c|
- - c.with_header do
- .gl-new-card-title-wrapper
- %h4.gl-new-card-title.warning-title= _('Change path')
- %p.gl-new-card-description
- - link = link_to('', help_page_path('user/project/settings/index', anchor: 'rename-a-repository'), target: '_blank', rel: 'noopener noreferrer')
- = safe_format(_("A project’s repository name defines its URL (the one you use to access the project via a browser) and its place on the file disk where GitLab is installed. %{link_start}Learn more.%{link_end}"), tag_pair(link, :link_start, :link_end))
-
- - c.with_body do
- = render 'projects/errors'
- = gitlab_ui_form_for @project do |f|
- .form-group
- %p
- %span.gl-font-weight-bold= _("Be careful. Renaming a project's repository can have unintended side effects.")
- = _('You will need to update your local repositories to point to the new location.')
- - if @project.deployment_platform.present?
- %p= _('Your deployment services will be broken, you will need to manually fix the services after renaming.')
- = f.label :path, _('Path'), class: 'label-bold'
+ - c.with_body do
+ = _('On the left sidebar, select %{merge_requests_link} to view them.').html_safe % { merge_requests_link: link_to('Settings > Merge requests', project_settings_merge_requests_path(@project)).html_safe }
+
+ %section.settings.no-animate{ class: ('expanded' if expanded), data: { testid: 'badges-settings-content' } }
+ .settings-header
+ %h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only
+ = s_('ProjectSettings|Badges')
+ = render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
+ = expanded ? _('Collapse') : _('Expand')
+ %p.gl-text-secondary
+ = s_('ProjectSettings|Customize this project\'s badges.')
+ = link_to s_('ProjectSettings|What are badges?'), help_page_path('user/project/badges')
+ .settings-content
+ = render 'shared/badges/badge_settings'
+
+ = render_if_exists 'compliance_management/compliance_framework/project_settings', expanded: expanded
+
+ = render_if_exists 'projects/settings/default_issue_template'
+
+ = render 'projects/service_desk_settings'
+
+ %section.settings.advanced-settings.no-animate#js-project-advanced-settings{ class: ('expanded' if expanded), data: { testid: 'advanced-settings-content' } }
+ .settings-header
+ %h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only= _('Advanced')
+ = render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
+ = expanded ? _('Collapse') : _('Expand')
+ %p.gl-text-secondary= s_('ProjectSettings|Housekeeping, export, archive, change path, transfer, and delete.')
+
+ .settings-content
+ = render_if_exists 'projects/settings/restore', project: @project
+
+ = render Pajamas::CardComponent.new(card_options: { class: 'gl-new-card gl-mt-0' }, header_options: { class: 'gl-new-card-header gl-flex-direction-column' }, body_options: { class: 'gl-new-card-body gl-px-5 gl-py-4' }) do |c|
+ - c.with_header do
+ .gl-new-card-title-wrapper
+ %h4.gl-new-card-title= _('Housekeeping')
+ %p.gl-new-card-description
+ = _('Runs a number of housekeeping tasks within the current repository, such as compressing file revisions and removing unreachable objects.')
+ = link_to _('Learn more.'), help_page_path('administration/housekeeping'), target: '_blank', rel: 'noopener noreferrer'
+
+ - c.with_body do
+ .gl-display-flex.gl-flex-wrap.gl-gap-3
+ = render Pajamas::ButtonComponent.new(method: :post, href: housekeeping_project_path(@project)) do
+ = _('Run housekeeping')
+ #js-project-prune-unreachable-objects-button{ data: { prune_objects_path: housekeeping_project_path(@project, prune: true), prune_objects_doc_path: help_page_path('administration/housekeeping', anchor: 'prune-unreachable-objects') } }
+
+ = render 'export', project: @project
+
+ = render_if_exists 'projects/settings/archive'
+
+ = render Pajamas::CardComponent.new(card_options: { class: 'gl-new-card rename-repository' }, header_options: { class: 'gl-new-card-header gl-flex-direction-column' }, body_options: { class: 'gl-new-card-body gl-px-5 gl-py-4' }) do |c|
+ - c.with_header do
+ .gl-new-card-title-wrapper
+ %h4.gl-new-card-title.warning-title= _('Change path')
+ %p.gl-new-card-description
+ - link = link_to('', help_page_path('user/project/settings/index', anchor: 'rename-a-repository'), target: '_blank', rel: 'noopener noreferrer')
+ = safe_format(_("A project’s repository name defines its URL (the one you use to access the project via a browser) and its place on the file disk where GitLab is installed. %{link_start}Learn more.%{link_end}"), tag_pair(link, :link_start, :link_end))
+
+ - c.with_body do
+ = render 'projects/errors'
+ = gitlab_ui_form_for @project do |f|
.form-group
- .input-group
- .input-group-prepend
- .input-group-text
- #{Gitlab::Utils.append_path(root_url, @project.namespace.full_path)}/
- = f.text_field :path, class: 'form-control gl-form-input-xl', data: { testid: 'project-path-field' }
- = f.submit _('Change path'), class: "btn-danger", data: { testid: 'change-path-button' }, pajamas_button: true
-
- = render 'transfer', project: @project
-
- = render 'remove_fork', project: @project
-
- = render 'remove', project: @project
+ %p
+ %span.gl-font-weight-bold= _("Be careful. Renaming a project's repository can have unintended side effects.")
+ = _('You will need to update your local repositories to point to the new location.')
+ - if @project.deployment_platform.present?
+ %p= _('Your deployment services will be broken, you will need to manually fix the services after renaming.')
+ = f.label :path, _('Path'), class: 'label-bold'
+ .form-group
+ .input-group
+ .input-group-prepend
+ .input-group-text
+ #{Gitlab::Utils.append_path(root_url, @project.namespace.full_path)}/
+ = f.text_field :path, class: 'form-control gl-form-input-xl', data: { testid: 'project-path-field' }
+ = f.submit _('Change path'), class: "btn-danger", data: { testid: 'change-path-button' }, pajamas_button: true
+
+ = render 'transfer', project: @project
+
+ = render 'remove_fork', project: @project
+
+ = render 'remove', project: @project
+- elsif can?(current_user, :archive_project, @project)
+ = render_if_exists 'projects/settings/archive'
.save-project-loader.hide
.center
diff --git a/app/views/projects/settings/access_tokens/_form.html.haml b/app/views/projects/settings/access_tokens/_form.html.haml
index 919462a0f62..ee993962c7a 100644
--- a/app/views/projects/settings/access_tokens/_form.html.haml
+++ b/app/views/projects/settings/access_tokens/_form.html.haml
@@ -7,7 +7,7 @@
resource: @project,
token: @resource_access_token,
scopes: @scopes,
- access_levels: ProjectMember.permissible_access_level_roles(current_user, @project),
+ access_levels: ProjectMember.permissible_access_level_roles_for_project_access_token(current_user, @project),
default_access_level: Gitlab::Access::GUEST,
prefix: :resource_access_token,
description_prefix: :project_access_token,
diff --git a/bin/gitlab-backup-cli b/bin/gitlab-backup-cli
new file mode 100755
index 00000000000..4037684be07
--- /dev/null
+++ b/bin/gitlab-backup-cli
@@ -0,0 +1,14 @@
+#!/usr/bin/env ruby
+# frozen_string_literal: true
+
+$:.unshift File.expand_path("../../lib", __FILE__)
+
+# We require APP_PATH when the rails environment is required only,
+# this allows for faster CLI execution when rails is not needed
+APP_PATH = File.expand_path('../config/application', __dir__)
+
+require_relative '../config/boot'
+
+require 'gitlab/backup/cli'
+
+Gitlab::Backup::Cli::Runner.start(ARGV)
diff --git a/db/migrate/20231024123444_add_archive_project_to_member_roles.rb b/db/migrate/20231024123444_add_archive_project_to_member_roles.rb
new file mode 100644
index 00000000000..27ff86450e8
--- /dev/null
+++ b/db/migrate/20231024123444_add_archive_project_to_member_roles.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddArchiveProjectToMemberRoles < Gitlab::Database::Migration[2.1]
+ enable_lock_retries!
+
+ def change
+ add_column :member_roles, :archive_project, :boolean, default: false, null: false
+ end
+end
diff --git a/db/schema_migrations/20231024123444 b/db/schema_migrations/20231024123444
new file mode 100644
index 00000000000..578f1cef1bd
--- /dev/null
+++ b/db/schema_migrations/20231024123444
@@ -0,0 +1 @@
+db84d40c9afd9121aa24617167fa82b86cabc98bf274e61057eef02e1fafd7c3 \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 333cf6c65a4..1055e902056 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -18525,6 +18525,7 @@ CREATE TABLE member_roles (
admin_merge_request boolean DEFAULT false NOT NULL,
admin_group_member boolean DEFAULT false NOT NULL,
manage_project_access_tokens boolean DEFAULT false NOT NULL,
+ archive_project boolean DEFAULT false NOT NULL,
CONSTRAINT check_4364846f58 CHECK ((char_length(description) <= 255)),
CONSTRAINT check_9907916995 CHECK ((char_length(name) <= 255))
);
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 2edb3b4e351..c7921115e1a 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -5134,6 +5134,7 @@ Input type: `MemberRoleCreateInput`
| <a id="mutationmemberrolecreateadmingroupmember"></a>`adminGroupMember` | [`Boolean`](#boolean) | Permission to admin group members. |
| <a id="mutationmemberrolecreateadminmergerequest"></a>`adminMergeRequest` | [`Boolean`](#boolean) | Permission to admin merge requests. |
| <a id="mutationmemberrolecreateadminvulnerability"></a>`adminVulnerability` | [`Boolean`](#boolean) | Permission to admin vulnerability. |
+| <a id="mutationmemberrolecreatearchiveproject"></a>`archiveProject` | [`Boolean`](#boolean) | Permission to archive projects. |
| <a id="mutationmemberrolecreatebaseaccesslevel"></a>`baseAccessLevel` | [`MemberAccessLevel!`](#memberaccesslevel) | Base access level for the custom role. |
| <a id="mutationmemberrolecreateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationmemberrolecreatedescription"></a>`description` | [`String`](#string) | Description of the member role. |
@@ -20322,6 +20323,7 @@ Represents a member role.
| <a id="memberroleadmingroupmember"></a>`adminGroupMember` **{warning-solid}** | [`Boolean`](#boolean) | **Introduced** in 16.5. This feature is an Experiment. It can be changed or removed at any time. Permission to admin group members. |
| <a id="memberroleadminmergerequest"></a>`adminMergeRequest` **{warning-solid}** | [`Boolean`](#boolean) | **Introduced** in 16.5. This feature is an Experiment. It can be changed or removed at any time. Permission to admin merge requests. |
| <a id="memberroleadminvulnerability"></a>`adminVulnerability` **{warning-solid}** | [`Boolean`](#boolean) | **Introduced** in 16.5. This feature is an Experiment. It can be changed or removed at any time. Permission to admin vulnerability. |
+| <a id="memberrolearchiveproject"></a>`archiveProject` **{warning-solid}** | [`Boolean`](#boolean) | **Introduced** in 16.6. This feature is an Experiment. It can be changed or removed at any time. Permission to archive projects. |
| <a id="memberrolebaseaccesslevel"></a>`baseAccessLevel` **{warning-solid}** | [`AccessLevel!`](#accesslevel) | **Introduced** in 16.5. This feature is an Experiment. It can be changed or removed at any time. Base access level for the custom role. |
| <a id="memberroledescription"></a>`description` | [`String`](#string) | Description of the member role. |
| <a id="memberroleenabledpermissions"></a>`enabledPermissions` **{warning-solid}** | [`[MemberRolePermission!]`](#memberrolepermission) | **Introduced** in 16.5. This feature is an Experiment. It can be changed or removed at any time. Array of all permissions enabled for the custom role. |
@@ -29406,6 +29408,7 @@ Member role permission.
| <a id="memberrolepermissionadmin_group_member"></a>`ADMIN_GROUP_MEMBER` | Allows admin access to group members. |
| <a id="memberrolepermissionadmin_merge_request"></a>`ADMIN_MERGE_REQUEST` | Allows admin access to the merge requests. |
| <a id="memberrolepermissionadmin_vulnerability"></a>`ADMIN_VULNERABILITY` | Allows admin access to the vulnerability reports. |
+| <a id="memberrolepermissionarchive_project"></a>`ARCHIVE_PROJECT` | Allows to archive projects. |
| <a id="memberrolepermissionmanage_project_access_tokens"></a>`MANAGE_PROJECT_ACCESS_TOKENS` | Allows manage access to the project access tokens. |
| <a id="memberrolepermissionread_code"></a>`READ_CODE` | Allows read-only access to the source code. |
| <a id="memberrolepermissionread_dependency"></a>`READ_DEPENDENCY` | Allows read-only access to the dependencies. |
diff --git a/doc/api/member_roles.md b/doc/api/member_roles.md
index cc50a8e225a..63de583de25 100644
--- a/doc/api/member_roles.md
+++ b/doc/api/member_roles.md
@@ -13,12 +13,14 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> - [Read dependency added](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/126247) in GitLab 16.3.
> - [Name and description fields added](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/126423) in GitLab 16.3.
> - [Admin merge request introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/128302) in GitLab 16.4 [with a flag](../administration/feature_flags.md) named `admin_merge_request`. Disabled by default.
+> - [Feature flag `admin_merge_request` removed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/132578) in GitLab 16.5.
> - [Admin group members introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/131914) in GitLab 16.5 [with a flag](../administration/feature_flags.md) named `admin_group_member`. Disabled by default. The feature flag has been removed in GitLab 16.6.
> - [Manage project access tokens introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/132342) in GitLab 16.5 in [with a flag](../administration/feature_flags.md) named `manage_project_access_tokens`. Disabled by default.
+> - [Archive project introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/134998) in GitLab 16.6 in [with a flag](../administration/feature_flags.md) named `archive_project`. Disabled by default.
FLAG:
-On self-managed GitLab, by default these two features are not available. To make them available, an administrator can [enable the feature flags](../administration/feature_flags.md) named `admin_merge_request` and `admin_member_custom_role`.
-On GitLab.com, this feature is not available.
+On self-managed GitLab, by default these features are not available. To make them available, an administrator can [enable the feature flags](../administration/feature_flags.md) named `admin_group_member`, `manage_project_access_tokens` and `archive_project`.
+On GitLab.com, these features are not available.
## List all member roles of a group
@@ -48,6 +50,7 @@ If successful, returns [`200`](rest/index.md#status-codes) and the following res
| `[].read_vulnerability` | boolean | Permission to read project vulnerabilities. |
| `[].admin_group_member` | boolean | Permission to admin members of a group. |
| `[].manage_project_access_tokens` | boolean | Permission to manage project access tokens. |
+| `[].archive_project` | boolean | Permission to archive projects. |
Example request:
@@ -70,7 +73,8 @@ Example response:
"read_code": true,
"read_dependency": false,
"read_vulnerability": false,
- "manage_project_access_tokens": false
+ "manage_project_access_tokens": false,
+ "archive_project": false
},
{
"id": 3,
@@ -83,7 +87,8 @@ Example response:
"read_code": false,
"read_dependency": true,
"read_vulnerability": true,
- "manage_project_access_tokens": false
+ "manage_project_access_tokens": false,
+ "archive_project": false
}
]
```
diff --git a/doc/user/custom_roles.md b/doc/user/custom_roles.md
index 1b827d82792..bbb48724078 100644
--- a/doc/user/custom_roles.md
+++ b/doc/user/custom_roles.md
@@ -15,6 +15,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> - Ability to create and remove a custom role with the UI [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/393235) in GitLab 16.4.
> - Ability to manage group members [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/17364) in GitLab 16.5.
> - Ability to manage project access tokens [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/421778) in GitLab 16.5 [with a flag](../administration/feature_flags.md) named `manage_project_access_tokens`.
+> - Ability to archive projects [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/425957) in GitLab 16.6 in [with a flag](../administration/feature_flags.md) named `archive_project`. Disabled by default.
Custom roles allow group Owners or instance administrators to create roles
specific to the needs of their organization.
@@ -97,6 +98,7 @@ These requirements are documented in the `Required permission` column in the fol
| `admin_merge_request` | GitLab 16.4 and later | Not applicable | View and approve [merge requests](project/merge_requests/index.md), and view the associated merge request code. <br> Does not allow users to view or change merge request approval rules. |
| `manage_project_access_tokens` | GitLab 16.5 and later | Not applicable | Create, delete, and list [project access tokens](project/settings/project_access_tokens.md). |
| `admin_group_member` | GitLab 16.5 and later | Not applicable | Add or remove [group members](group/manage.md). |
+| `archive_project` | GitLab 16.6 and later | Not applicable | Archive and unarchive [projects](project/settings/index.md#archive-a-project). |
## Billing and seat usage
diff --git a/gems/gitlab-backup-cli/.gitignore b/gems/gitlab-backup-cli/.gitignore
new file mode 100644
index 00000000000..b04a8c840df
--- /dev/null
+++ b/gems/gitlab-backup-cli/.gitignore
@@ -0,0 +1,11 @@
+/.bundle/
+/.yardoc
+/_yardoc/
+/coverage/
+/doc/
+/pkg/
+/spec/reports/
+/tmp/
+
+# rspec failure tracking
+.rspec_status
diff --git a/gems/gitlab-backup-cli/.gitlab-ci.yml b/gems/gitlab-backup-cli/.gitlab-ci.yml
new file mode 100644
index 00000000000..884ca22a853
--- /dev/null
+++ b/gems/gitlab-backup-cli/.gitlab-ci.yml
@@ -0,0 +1,4 @@
+include:
+ - local: gems/gem.gitlab-ci.yml
+ inputs:
+ gem_name: "gitlab-backup-cli"
diff --git a/gems/gitlab-backup-cli/.rspec b/gems/gitlab-backup-cli/.rspec
new file mode 100644
index 00000000000..34c5164d9b5
--- /dev/null
+++ b/gems/gitlab-backup-cli/.rspec
@@ -0,0 +1,3 @@
+--format documentation
+--color
+--require spec_helper
diff --git a/gems/gitlab-backup-cli/.rubocop.yml b/gems/gitlab-backup-cli/.rubocop.yml
new file mode 100644
index 00000000000..cc0632f32dd
--- /dev/null
+++ b/gems/gitlab-backup-cli/.rubocop.yml
@@ -0,0 +1,19 @@
+---
+inherit_gem:
+ gitlab-styles:
+ - rubocop-bundler.yml
+ - rubocop-fips.yml
+ - rubocop-gemspec.yml
+ - rubocop-layout.yml
+ - rubocop-lint.yml
+ - rubocop-metrics.yml
+ - rubocop-naming.yml
+ - rubocop-performance.yml
+ - rubocop-rspec.yml
+ - rubocop-security.yml
+ - rubocop-style.yml
+
+AllCops:
+ TargetRubyVersion: 3.0
+ SuggestExtensions: false
+ NewCops: disable
diff --git a/gems/gitlab-backup-cli/Gemfile b/gems/gitlab-backup-cli/Gemfile
new file mode 100644
index 00000000000..e5c133f93f2
--- /dev/null
+++ b/gems/gitlab-backup-cli/Gemfile
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+source "https://rubygems.org"
+
+# Specify your gem's dependencies in gitlab-backup-cli.gemspec
+gemspec
diff --git a/gems/gitlab-backup-cli/Gemfile.lock b/gems/gitlab-backup-cli/Gemfile.lock
new file mode 100644
index 00000000000..73c2b942865
--- /dev/null
+++ b/gems/gitlab-backup-cli/Gemfile.lock
@@ -0,0 +1,114 @@
+PATH
+ remote: .
+ specs:
+ gitlab-backup-cli (0.0.1)
+ thor (~> 1.3)
+
+GEM
+ remote: https://rubygems.org/
+ specs:
+ activesupport (7.1.1)
+ base64
+ bigdecimal
+ concurrent-ruby (~> 1.0, >= 1.0.2)
+ connection_pool (>= 2.2.5)
+ drb
+ i18n (>= 1.6, < 2)
+ minitest (>= 5.1)
+ mutex_m
+ tzinfo (~> 2.0)
+ ast (2.4.2)
+ base64 (0.2.0)
+ bigdecimal (3.1.4)
+ concurrent-ruby (1.2.2)
+ connection_pool (2.4.1)
+ diff-lcs (1.5.0)
+ drb (2.2.0)
+ ruby2_keywords
+ gitlab-styles (11.0.0)
+ rubocop (~> 1.57.1)
+ rubocop-graphql (~> 0.18)
+ rubocop-performance (~> 1.15)
+ rubocop-rails (~> 2.17)
+ rubocop-rspec (~> 2.22)
+ i18n (1.14.1)
+ concurrent-ruby (~> 1.0)
+ json (2.6.3)
+ language_server-protocol (3.17.0.3)
+ minitest (5.20.0)
+ mutex_m (0.2.0)
+ parallel (1.23.0)
+ parser (3.2.2.4)
+ ast (~> 2.4.1)
+ racc
+ racc (1.7.3)
+ rack (3.0.8)
+ rainbow (3.1.1)
+ rake (13.1.0)
+ regexp_parser (2.8.2)
+ rexml (3.2.6)
+ rspec (3.12.0)
+ rspec-core (~> 3.12.0)
+ rspec-expectations (~> 3.12.0)
+ rspec-mocks (~> 3.12.0)
+ rspec-core (3.12.2)
+ rspec-support (~> 3.12.0)
+ rspec-expectations (3.12.3)
+ diff-lcs (>= 1.2.0, < 2.0)
+ rspec-support (~> 3.12.0)
+ rspec-mocks (3.12.6)
+ diff-lcs (>= 1.2.0, < 2.0)
+ rspec-support (~> 3.12.0)
+ rspec-support (3.12.1)
+ rubocop (1.57.2)
+ json (~> 2.3)
+ language_server-protocol (>= 3.17.0)
+ parallel (~> 1.10)
+ parser (>= 3.2.2.4)
+ rainbow (>= 2.2.2, < 4.0)
+ regexp_parser (>= 1.8, < 3.0)
+ rexml (>= 3.2.5, < 4.0)
+ rubocop-ast (>= 1.28.1, < 2.0)
+ ruby-progressbar (~> 1.7)
+ unicode-display_width (>= 2.4.0, < 3.0)
+ rubocop-ast (1.30.0)
+ parser (>= 3.2.1.0)
+ rubocop-capybara (2.19.0)
+ rubocop (~> 1.41)
+ rubocop-factory_bot (2.24.0)
+ rubocop (~> 1.33)
+ rubocop-graphql (0.19.0)
+ rubocop (>= 0.87, < 2)
+ rubocop-performance (1.19.1)
+ rubocop (>= 1.7.0, < 2.0)
+ rubocop-ast (>= 0.4.0)
+ rubocop-rails (2.20.0)
+ activesupport (>= 4.2.0)
+ rack (>= 1.1)
+ rubocop (>= 1.33.0, < 2.0)
+ rubocop-rspec (2.25.0)
+ rubocop (~> 1.40)
+ rubocop-capybara (~> 2.17)
+ rubocop-factory_bot (~> 2.22)
+ ruby-progressbar (1.13.0)
+ ruby2_keywords (0.0.5)
+ thor (1.3.0)
+ tzinfo (2.0.6)
+ concurrent-ruby (~> 1.0)
+ unicode-display_width (2.5.0)
+
+PLATFORMS
+ arm64-darwin-21
+ arm64-darwin-22
+ ruby
+ x86_64-linux
+
+DEPENDENCIES
+ gitlab-backup-cli!
+ gitlab-styles (~> 11.0)
+ rake (~> 13.0)
+ rspec (~> 3.0)
+ rubocop-rails (<= 2.20)
+
+BUNDLED WITH
+ 2.4.21
diff --git a/gems/gitlab-backup-cli/LICENSE.txt b/gems/gitlab-backup-cli/LICENSE.txt
new file mode 100644
index 00000000000..3def1245ee6
--- /dev/null
+++ b/gems/gitlab-backup-cli/LICENSE.txt
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2023-present GitLab B.V.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/gems/gitlab-backup-cli/README.md b/gems/gitlab-backup-cli/README.md
new file mode 100644
index 00000000000..0d6b12b65aa
--- /dev/null
+++ b/gems/gitlab-backup-cli/README.md
@@ -0,0 +1,7 @@
+# Gitlab::Backup::Cli
+
+This gem will contain the Backup CLI logic.
+
+## License
+
+The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
diff --git a/gems/gitlab-backup-cli/Rakefile b/gems/gitlab-backup-cli/Rakefile
new file mode 100644
index 00000000000..cca71754493
--- /dev/null
+++ b/gems/gitlab-backup-cli/Rakefile
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+require "bundler/gem_tasks"
+require "rspec/core/rake_task"
+
+RSpec::Core::RakeTask.new(:spec)
+
+require "rubocop/rake_task"
+
+RuboCop::RakeTask.new
+
+task default: %i[spec rubocop]
diff --git a/gems/gitlab-backup-cli/bin/console b/gems/gitlab-backup-cli/bin/console
new file mode 100755
index 00000000000..3aa2eb40f21
--- /dev/null
+++ b/gems/gitlab-backup-cli/bin/console
@@ -0,0 +1,11 @@
+#!/usr/bin/env ruby
+# frozen_string_literal: true
+
+require "bundler/setup"
+require "gitlab/backup/cli"
+
+# You can add fixtures and/or initialization code here to make experimenting
+# with your gem easier. You can also use a different console, if you like.
+
+require "irb"
+IRB.start(__FILE__)
diff --git a/gems/gitlab-backup-cli/bin/setup b/gems/gitlab-backup-cli/bin/setup
new file mode 100755
index 00000000000..dce67d860af
--- /dev/null
+++ b/gems/gitlab-backup-cli/bin/setup
@@ -0,0 +1,8 @@
+#!/usr/bin/env bash
+set -euo pipefail
+IFS=$'\n\t'
+set -vx
+
+bundle install
+
+# Do any other automated setup that you need to do here
diff --git a/gems/gitlab-backup-cli/gitlab-backup-cli.gemspec b/gems/gitlab-backup-cli/gitlab-backup-cli.gemspec
new file mode 100644
index 00000000000..40ed07d68e4
--- /dev/null
+++ b/gems/gitlab-backup-cli/gitlab-backup-cli.gemspec
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+require_relative "lib/gitlab/backup/cli/version"
+
+Gem::Specification.new do |spec|
+ spec.name = "gitlab-backup-cli"
+ spec.version = Gitlab::Backup::Cli::VERSION
+ spec.authors = ["Gabriel Mazetto"]
+ spec.email = ["brodock@gmail.com"]
+
+ spec.summary = "GitLab Backup CLI"
+ spec.description = "GitLab Backup CLI"
+ spec.homepage = "https://gitlab.com/gitlab-org/gitlab/-/tree/master/gems/gitlab-backup-cli"
+ spec.license = "MIT"
+ spec.required_ruby_version = ">= 3.0"
+
+ spec.metadata["rubygems_mfa_required"] = "true"
+ spec.metadata["homepage_uri"] = spec.homepage
+ spec.metadata["source_code_uri"] = spec.homepage
+
+ spec.files = Dir['lib/**/*.rb']
+ spec.test_files = Dir['spec/**/*']
+
+ # Specify which files should be added to the gem when it is released.
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
+ spec.files = Dir.chdir(__dir__) do
+ `git ls-files -z`.split("\x0").reject do |f|
+ (File.expand_path(f) == __FILE__) ||
+ f.start_with?(*%w[bin/ test/ spec/ features/ .git .gitlab-ci .rspec .rubocop.yml Gemfile])
+ end
+ end
+
+ spec.bindir = "exe"
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
+ spec.require_paths = ["lib"]
+
+ spec.add_dependency "thor", "~> 1.3"
+
+ spec.add_development_dependency "gitlab-styles", "~> 11.0"
+ spec.add_development_dependency "rake", "~> 13.0"
+ spec.add_development_dependency "rspec", "~> 3.0"
+ spec.add_development_dependency "rubocop-rails", "<= 2.20" # https://github.com/rubocop/rubocop-rails/issues/1173
+end
diff --git a/gems/gitlab-backup-cli/lib/gitlab/backup/cli.rb b/gems/gitlab-backup-cli/lib/gitlab/backup/cli.rb
new file mode 100644
index 00000000000..5f0654c9d03
--- /dev/null
+++ b/gems/gitlab-backup-cli/lib/gitlab/backup/cli.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Backup
+ # GitLab Backup CLI
+ module Cli
+ autoload :VERSION, 'gitlab/backup/cli/version'
+ autoload :Runner, 'gitlab/backup/cli/runner'
+
+ class Error < StandardError; end
+ # Your code goes here...
+ end
+ end
+end
diff --git a/gems/gitlab-backup-cli/lib/gitlab/backup/cli/runner.rb b/gems/gitlab-backup-cli/lib/gitlab/backup/cli/runner.rb
new file mode 100644
index 00000000000..ceb77a62499
--- /dev/null
+++ b/gems/gitlab-backup-cli/lib/gitlab/backup/cli/runner.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+require 'thor'
+
+module Gitlab
+ module Backup
+ module Cli
+ # GitLab Backup CLI
+ #
+ # This supersedes the previous backup rake files and will be
+ # the default interface to handle backups
+ class Runner < Thor
+ def self.exit_on_failure?
+ true
+ end
+
+ map %w[--version -v] => :version
+ desc 'version', 'Display the version information'
+
+ def version
+ puts "GitLab Backup CLI (#{VERSION})"
+ end
+
+ private
+
+ def rails_environment!
+ require APP_PATH
+
+ Rails.application.load_tasks
+ end
+ end
+ end
+ end
+end
diff --git a/gems/gitlab-backup-cli/lib/gitlab/backup/cli/version.rb b/gems/gitlab-backup-cli/lib/gitlab/backup/cli/version.rb
new file mode 100644
index 00000000000..32bf3de31da
--- /dev/null
+++ b/gems/gitlab-backup-cli/lib/gitlab/backup/cli/version.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Backup
+ module Cli
+ VERSION = "0.0.1"
+ end
+ end
+end
diff --git a/gems/gitlab-backup-cli/sig/gitlab/backup/cli.rbs b/gems/gitlab-backup-cli/sig/gitlab/backup/cli.rbs
new file mode 100644
index 00000000000..25540c06400
--- /dev/null
+++ b/gems/gitlab-backup-cli/sig/gitlab/backup/cli.rbs
@@ -0,0 +1,7 @@
+module Gitlab
+ module Backup
+ module Cli
+ VERSION: String
+ end
+ end
+end
diff --git a/gems/gitlab-backup-cli/sig/gitlab/backup/cli/runner.rbs b/gems/gitlab-backup-cli/sig/gitlab/backup/cli/runner.rbs
new file mode 100644
index 00000000000..56b031b82bc
--- /dev/null
+++ b/gems/gitlab-backup-cli/sig/gitlab/backup/cli/runner.rbs
@@ -0,0 +1,15 @@
+module Gitlab
+ module Backup
+ module Cli
+ class Runner
+ def self.exit_on_failure?: -> bool
+
+ def version: -> void
+
+ private
+
+ def rails_environment!: -> void
+ end
+ end
+ end
+end
diff --git a/gems/gitlab-backup-cli/spec/gitlab/backup/cli_spec.rb b/gems/gitlab-backup-cli/spec/gitlab/backup/cli_spec.rb
new file mode 100644
index 00000000000..db3aaeccb05
--- /dev/null
+++ b/gems/gitlab-backup-cli/spec/gitlab/backup/cli_spec.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+RSpec.describe Gitlab::Backup::Cli do
+ it "has a version number" do
+ expect(Gitlab::Backup::Cli::VERSION).not_to be nil
+ end
+end
diff --git a/gems/gitlab-backup-cli/spec/spec_helper.rb b/gems/gitlab-backup-cli/spec/spec_helper.rb
new file mode 100644
index 00000000000..6c4ae2df96c
--- /dev/null
+++ b/gems/gitlab-backup-cli/spec/spec_helper.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require "gitlab/backup/cli"
+
+RSpec.configure do |config|
+ # Enable flags like --only-failures and --next-failure
+ config.example_status_persistence_file_path = ".rspec_status"
+
+ # Disable RSpec exposing methods globally on `Module` and `main`
+ config.disable_monkey_patching!
+
+ config.expect_with :rspec do |c|
+ c.syntax = :expect
+ end
+end
diff --git a/lib/sidebars/projects/menus/settings_menu.rb b/lib/sidebars/projects/menus/settings_menu.rb
index 8fed1c46425..077eebf58b9 100644
--- a/lib/sidebars/projects/menus/settings_menu.rb
+++ b/lib/sidebars/projects/menus/settings_menu.rb
@@ -57,10 +57,6 @@ module Sidebars
monitor_menu_item,
usage_quotas_menu_item
]
- elsif context.current_user && can?(context.current_user, :manage_resource_access_tokens, context.project)
- [
- access_tokens_menu_item
- ]
else
[]
end
diff --git a/spec/lib/sidebars/projects/menus/settings_menu_spec.rb b/spec/lib/sidebars/projects/menus/settings_menu_spec.rb
index 81ca9670ac6..605cec8be5e 100644
--- a/spec/lib/sidebars/projects/menus/settings_menu_spec.rb
+++ b/spec/lib/sidebars/projects/menus/settings_menu_spec.rb
@@ -59,18 +59,6 @@ RSpec.describe Sidebars::Projects::Menus::SettingsMenu, feature_category: :navig
let(:item_id) { :access_tokens }
it_behaves_like 'access rights checks'
-
- describe 'when the user is not an admin but has manage_resource_access_tokens' do
- before do
- allow(Ability).to receive(:allowed?).and_call_original
- allow(Ability).to receive(:allowed?).with(user, :admin_project, project).and_return(false)
- allow(Ability).to receive(:allowed?).with(user, :manage_resource_access_tokens, project).and_return(true)
- end
-
- it 'includes access token menu item' do
- expect(subject.title).to eql('Access Tokens')
- end
- end
end
describe 'Repository' do
diff --git a/spec/models/members/project_member_spec.rb b/spec/models/members/project_member_spec.rb
index a2b5bde8890..a9725a796bf 100644
--- a/spec/models/members/project_member_spec.rb
+++ b/spec/models/members/project_member_spec.rb
@@ -51,6 +51,50 @@ RSpec.describe ProjectMember, feature_category: :groups_and_projects do
end
end
+ describe '.permissible_access_level_roles_for_project_access_token' do
+ let_it_be(:owner) { create(:user) }
+ let_it_be(:maintainer) { create(:user) }
+ let_it_be(:developer) { create(:user) }
+ let_it_be(:group) { create(:group) }
+ let_it_be(:project) { create(:project, group: group) }
+
+ before do
+ project.add_owner(owner)
+ project.add_maintainer(maintainer)
+ project.add_developer(developer)
+ end
+
+ subject(:access_levels) { described_class.permissible_access_level_roles_for_project_access_token(user, project) }
+
+ context 'when member can manage owners' do
+ let(:user) { owner }
+
+ it 'returns Gitlab::Access.options_with_owner' do
+ expect(access_levels).to eq(Gitlab::Access.options_with_owner)
+ end
+ end
+
+ context 'when member cannot manage owners' do
+ let(:user) { maintainer }
+
+ it 'returns Gitlab::Access.options' do
+ expect(access_levels).to eq(Gitlab::Access.options)
+ end
+ end
+
+ context 'when the user is a developer' do
+ let(:user) { developer }
+
+ it 'returns Gitlab::Access.options' do
+ expect(access_levels).to eq({
+ "Guest" => 10,
+ "Reporter" => 20,
+ "Developer" => 30
+ })
+ end
+ end
+ end
+
describe '#real_source_type' do
subject { create(:project_member).real_source_type }