diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-06-08 15:08:54 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-06-08 15:08:54 +0300 |
commit | 473b876fe3d7e0b36eb6268cc44a4fe0d94f4422 (patch) | |
tree | 2f6d5f2ecad53015024b6b1509896f5933c5c3e6 | |
parent | dca8df0c90d8727d69b3501b15b481546897f3cd (diff) |
Add latest changes from gitlab-org/gitlab@master
73 files changed, 962 insertions, 141 deletions
diff --git a/.gitlab/ci/database.gitlab-ci.yml b/.gitlab/ci/database.gitlab-ci.yml index 45aa6a35d6c..37befd78bb5 100644 --- a/.gitlab/ci/database.gitlab-ci.yml +++ b/.gitlab/ci/database.gitlab-ci.yml @@ -72,6 +72,7 @@ db:check-schema-single-db: db:check-migrations: extends: - .db-job-base + - .use-pg13 # Should match the db same version used by GDK - .rails:rules:ee-and-foss-mr-with-migration script: - git fetch origin $CI_MERGE_REQUEST_TARGET_BRANCH_NAME:$CI_MERGE_REQUEST_TARGET_BRANCH_NAME --depth 20 diff --git a/.rubocop_todo/rspec/factory_bot/avoid_create.yml b/.rubocop_todo/rspec/factory_bot/avoid_create.yml index a6037c5093e..dc68e204685 100644 --- a/.rubocop_todo/rspec/factory_bot/avoid_create.yml +++ b/.rubocop_todo/rspec/factory_bot/avoid_create.yml @@ -336,13 +336,11 @@ RSpec/FactoryBot/AvoidCreate: - 'spec/helpers/wiki_helper_spec.rb' - 'spec/helpers/wiki_page_version_helper_spec.rb' - 'spec/lib/sidebars/admin/menus/abuse_reports_menu_spec.rb' - - 'spec/lib/sidebars/admin/menus/monitoring_menu_spec.rb' - 'spec/lib/sidebars/groups/menus/ci_cd_menu_spec.rb' - 'spec/lib/sidebars/groups/menus/group_information_menu_spec.rb' - 'spec/lib/sidebars/groups/menus/issues_menu_spec.rb' - 'spec/lib/sidebars/groups/menus/kubernetes_menu_spec.rb' - 'spec/lib/sidebars/groups/menus/merge_requests_menu_spec.rb' - - 'spec/lib/sidebars/groups/menus/observability_menu_spec.rb' - 'spec/lib/sidebars/groups/menus/packages_registries_menu_spec.rb' - 'spec/lib/sidebars/groups/menus/settings_menu_spec.rb' - 'spec/lib/sidebars/groups/super_sidebar_panel_spec.rb' @@ -358,17 +356,8 @@ RSpec/FactoryBot/AvoidCreate: - 'spec/lib/sidebars/projects/menus/project_information_menu_spec.rb' - 'spec/lib/sidebars/projects/menus/repository_menu_spec.rb' - 'spec/lib/sidebars/projects/menus/security_compliance_menu_spec.rb' - - 'spec/lib/sidebars/projects/menus/settings_menu_spec.rb' - 'spec/lib/sidebars/projects/menus/shimo_menu_spec.rb' - 'spec/lib/sidebars/projects/panel_spec.rb' - - 'spec/lib/sidebars/projects/super_sidebar_panel_spec.rb' - - 'spec/lib/sidebars/search/panel_spec.rb' - - 'spec/lib/sidebars/user_profile/panel_spec.rb' - - 'spec/lib/sidebars/user_settings/panel_spec.rb' - - 'spec/lib/sidebars/your_work/menus/issues_menu_spec.rb' - - 'spec/lib/sidebars/your_work/menus/merge_requests_menu_spec.rb' - - 'spec/lib/sidebars/your_work/menus/todos_menu_spec.rb' - - 'spec/lib/sidebars/your_work/panel_spec.rb' - 'spec/mailers/abuse_report_mailer_spec.rb' - 'spec/mailers/devise_mailer_spec.rb' - 'spec/mailers/emails/auto_devops_spec.rb' diff --git a/CHANGELOG.md b/CHANGELOG.md index 5312e970f39..f6a441270ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ documentation](doc/development/changelog.md) for instructions on adding your own entry. +## 16.0.4 (2023-06-08) + +### Fixed (1 change) + +- [Fix LDAP tls_options not working](gitlab-org/gitlab@e6038d0d4e8bb190ccfeca5fe7204d6a6af266e5) ([merge request](gitlab-org/gitlab!122797)) + ## 16.0.3 (2023-06-06) ### Fixed (3 changes) diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index 7b9a37398c1..8df471bc6d2 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -1934b9991139b48ed58d78dc89b35c262acd5150 +468ba7de9380a1eb6321dbdf9b7ddffd7eda90f7 diff --git a/app/assets/javascripts/super_sidebar/components/context_switcher_toggle.vue b/app/assets/javascripts/super_sidebar/components/context_switcher_toggle.vue index cfb7e7732e9..17227a2b123 100644 --- a/app/assets/javascripts/super_sidebar/components/context_switcher_toggle.vue +++ b/app/assets/javascripts/super_sidebar/components/context_switcher_toggle.vue @@ -34,7 +34,7 @@ export default { <template> <button type="button" - class="context-switcher-toggle gl-p-0 gl-bg-transparent gl-hover-bg-t-gray-a-08 gl-focus-bg-t-gray-a-08 gl-border-0 border-top border-bottom gl-border-gray-a-08 gl-box-shadow-none gl-display-flex gl-align-items-center gl-font-weight-bold gl-w-full gl-h-8 gl-flex-shrink-0" + class="context-switcher-toggle gl-p-0 gl-bg-transparent gl-hover-bg-t-gray-a-08 gl-focus-bg-t-gray-a-08 gl-border-0 border-top border-bottom gl-border-gray-a-08! gl-box-shadow-none gl-display-flex gl-align-items-center gl-font-weight-bold gl-w-full gl-h-8 gl-flex-shrink-0" data-qa-selector="context_switcher" > <span diff --git a/app/helpers/admin/application_settings/settings_helper.rb b/app/helpers/admin/application_settings/settings_helper.rb index 1741d6a953a..0a7f20caa02 100644 --- a/app/helpers/admin/application_settings/settings_helper.rb +++ b/app/helpers/admin/application_settings/settings_helper.rb @@ -15,6 +15,55 @@ module Admin def project_missing_pipeline_yaml?(project) project.repository&.gitlab_ci_yml.blank? end + + def code_suggestions_token_explanation + link_start = code_suggestions_link_start(code_suggestions_pat_docs_url) + + # rubocop:disable Layout/LineLength + # rubocop:disable Style/FormatString + s_('CodeSuggestionsSM|Your personal access token from GitLab.com. See the %{link_start}documentation%{link_end} for information on creating a personal access token.') + .html_safe % { link_start: link_start, link_end: '</a>'.html_safe } + # rubocop:enable Style/FormatString + # rubocop:enable Layout/LineLength + end + + def code_suggestions_agreement + terms_link_start = code_suggestions_link_start(code_suggestions_agreement_url) + ai_docs_link_start = code_suggestions_link_start(code_suggestions_ai_docs_url) + + # rubocop:disable Layout/LineLength + # rubocop:disable Style/FormatString + s_('CodeSuggestionsSM|• Agree to the %{terms_link_start}GitLab Testing Agreement%{link_end}.%{br} • Acknowledge that GitLab will send data from the instance, including personal data, to Google for cloud hosting.%{br} We may also send data to %{ai_docs_link_start}third-party AI providers%{link_end} to provide this feature.') + .html_safe % { terms_link_start: terms_link_start, ai_docs_link_start: ai_docs_link_start, link_end: '</a>'.html_safe, br: '</br>'.html_safe } + # rubocop:enable Style/FormatString + # rubocop:enable Layout/LineLength + end + + private + + # rubocop:disable Gitlab/DocUrl + # We want to link SaaS docs for flexibility for every URL related to Code Suggestions on Self Managed. + # We expect to update docs often during the Beta and we want to point user to the most up to date information. + def code_suggestions_docs_url + 'https://docs.gitlab.com/ee/user/project/repository/code_suggestions.html' + end + + def code_suggestions_agreement_url + 'https://about.gitlab.com/handbook/legal/testing-agreement/' + end + + def code_suggestions_ai_docs_url + 'https://docs.gitlab.com/ee/user/ai_features.html' + end + + def code_suggestions_pat_docs_url + 'https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html#create-a-personal-access-token' + end + # rubocop:enable Gitlab/DocUrl + + def code_suggestions_link_start(url) + "<a href=\"#{url}\" target=\"_blank\" rel=\"noopener noreferrer\">".html_safe + end end end end diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb index bf8dea15c58..adbf7ab7cf2 100644 --- a/app/helpers/application_settings_helper.rb +++ b/app/helpers/application_settings_helper.rb @@ -218,6 +218,7 @@ module ApplicationSettingsHelper :admin_mode, :after_sign_out_path, :after_sign_up_text, + :ai_access_token, :akismet_api_key, :akismet_enabled, :allow_local_requests_from_hooks_and_services, @@ -309,6 +310,7 @@ module ApplicationSettingsHelper :inactive_projects_delete_after_months, :inactive_projects_min_size_mb, :inactive_projects_send_warning_email_after_months, + :instance_level_code_suggestions_enabled, :invisible_captcha_enabled, :jira_connect_application_key, :jira_connect_public_key_storage_enabled, diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index db858fd6796..a823b8a93fb 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -720,6 +720,10 @@ class ApplicationSetting < MainClusterwide::ApplicationRecord allow_nil: false, inclusion: { in: [true, false], message: N_('must be a boolean value') } + validates :ai_access_token, + presence: { message: N_("is required to enable Code Suggestions") }, + if: :instance_level_code_suggestions_enabled + attr_encrypted :asset_proxy_secret_key, mode: :per_attribute_iv, key: Settings.attr_encrypted_db_key_base_truncated, @@ -948,4 +952,5 @@ class ApplicationSetting < MainClusterwide::ApplicationRecord end end +ApplicationSetting.prepend(ApplicationSettingMaskedAttrs) ApplicationSetting.prepend_mod_with('ApplicationSetting') diff --git a/app/models/application_setting_implementation.rb b/app/models/application_setting_implementation.rb index ea07fe99c99..c73bdf8793c 100644 --- a/app/models/application_setting_implementation.rb +++ b/app/models/application_setting_implementation.rb @@ -37,6 +37,7 @@ module ApplicationSettingImplementation { admin_mode: false, after_sign_up_text: nil, + ai_access_token: nil, akismet_enabled: false, akismet_api_key: nil, allow_local_requests_from_system_hooks: true, @@ -104,6 +105,7 @@ module ApplicationSettingImplementation housekeeping_gc_period: 200, housekeeping_incremental_repack_period: 10, import_sources: Settings.gitlab['import_sources'], + instance_level_code_suggestions_enabled: false, invisible_captcha_enabled: false, issues_create_limit: 300, jira_connect_application_key: nil, diff --git a/app/models/concerns/application_setting_masked_attrs.rb b/app/models/concerns/application_setting_masked_attrs.rb new file mode 100644 index 00000000000..14a7185e39e --- /dev/null +++ b/app/models/concerns/application_setting_masked_attrs.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +# Similar to MASK_PASSWORD mechanism we do for EE, see: +# https://gitlab.com/gitlab-org/gitlab/-/blob/463bb1f855d71fadef931bd50f1692ee04f211a8/ee/app/models/ee/application_setting.rb#L15 +# but for non-EE attributes. +module ApplicationSettingMaskedAttrs + MASK = '*****' + + def ai_access_token=(value) + return if value == MASK + + super + end +end diff --git a/app/services/auto_merge/merge_when_pipeline_succeeds_service.rb b/app/services/auto_merge/merge_when_pipeline_succeeds_service.rb index d18f2935d92..2bbb8f925a4 100644 --- a/app/services/auto_merge/merge_when_pipeline_succeeds_service.rb +++ b/app/services/auto_merge/merge_when_pipeline_succeeds_service.rb @@ -11,9 +11,14 @@ module AutoMerge end def process(merge_request) + logger.info("Processing Automerge") return unless merge_request.actual_head_pipeline_success? + + logger.info("Pipeline Success") return unless merge_request.mergeable? + logger.info("Merge request mergeable") + merge_request.merge_async(merge_request.merge_user_id, merge_request.merge_params) end @@ -40,5 +45,9 @@ module AutoMerge def notify(merge_request) notification_service.async.merge_when_pipeline_succeeds(merge_request, current_user) if merge_request.saved_change_to_auto_merge_enabled? end + + def logger + @logger ||= Gitlab::AppLogger + end end end diff --git a/app/services/merge_requests/mergeability/logger.rb b/app/services/merge_requests/mergeability/logger.rb index 88ef6d81eaa..612c79f0aae 100644 --- a/app/services/merge_requests/mergeability/logger.rb +++ b/app/services/merge_requests/mergeability/logger.rb @@ -22,8 +22,8 @@ module MergeRequests result = yield + observe_result(mergeability_name, result) observe("mergeability.#{mergeability_name}.duration_s", current_monotonic_time - op_started_at) - observe_sql_counters(mergeability_name, op_start_db_counters, current_db_counter_payload) result @@ -31,7 +31,13 @@ module MergeRequests private - attr_reader :destination, :merge_request + attr_reader :destination, :merge_request, :stored_result + + def observe_result(name, result) + return unless result.respond_to?(:success?) + + observe("mergeability.#{name}.successful", result.success?) + end def observe(name, value) observations[name.to_s].push(value) diff --git a/app/views/admin/application_settings/_ai_access.html.haml b/app/views/admin/application_settings/_ai_access.html.haml new file mode 100644 index 00000000000..41b0a08128e --- /dev/null +++ b/app/views/admin/application_settings/_ai_access.html.haml @@ -0,0 +1,32 @@ +- return if Gitlab.org_or_com? + +- expanded = integration_expanded?('ai_access') +- token_is_present = @application_setting.ai_access_token.present? +- token_label = token_is_present ? s_('CodeSuggestionsSM|Enter new personal access token') : s_('CodeSuggestionsSM|Personal access token') +- token_value = token_is_present ? ApplicationSettingMaskedAttrs::MASK : '' + +%section.settings.no-animate#js-ai-access-settings{ class: ('expanded' if expanded) } + .settings-header + %h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only + = s_('CodeSuggestionsSM|Code Suggestions') + = render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do + = expanded ? _('Collapse') : _('Expand') + %p + = s_('CodeSuggestionsSM|Enable Code Suggestion for users of this GitLab instance.') + = link_to sprite_icon('question-o'), code_suggestions_docs_url, target: '_blank', class: 'has-tooltip', title: _('More information') + + .settings-content + = gitlab_ui_form_for @application_setting, url: general_admin_application_settings_path(anchor: 'js-ai-access-settings'), html: { class: 'fieldset-form', id: 'ai-access-settings' } do |f| + = form_errors(@application_setting) + + %fieldset + .form-group + = f.gitlab_ui_checkbox_component :instance_level_code_suggestions_enabled, + s_('CodeSuggestionsSM|Turn on Code Suggestions for this instance. By turning on this feature, you:'), + help_text: code_suggestions_agreement + = f.label :ai_access_token, token_label, class: 'label-bold' + = f.password_field :ai_access_token, value: token_value, autocomplete: 'on', class: 'form-control gl-form-input', aria: { describedby: 'code_suggestions_token_explanation' } + %p.form-text.text-muted{ id: 'code_suggestions_token_explanation' } + = code_suggestions_token_explanation + + = f.submit _('Save changes'), pajamas_button: true diff --git a/app/views/admin/application_settings/general.html.haml b/app/views/admin/application_settings/general.html.haml index 3413774b361..022930bd6b4 100644 --- a/app/views/admin/application_settings/general.html.haml +++ b/app/views/admin/application_settings/general.html.haml @@ -109,3 +109,4 @@ = render 'admin/application_settings/floc' = render_if_exists 'admin/application_settings/add_license' = render 'admin/application_settings/jira_connect' += render_if_exists 'admin/application_settings/ai_access' diff --git a/config/locales/en.yml b/config/locales/en.yml index 87184d51a17..1de1c20259d 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -10,6 +10,8 @@ en: pwa_short_name: "PWA short name" pwa_description: "PWA description" pwa_icon: "Icon" + application_setting: + ai_access_token: "Personal access token" incident_management/timeline_event: note: 'Timeline text' issue_link: diff --git a/config/open_api.yml b/config/open_api.yml index cbf70c24ce1..257db9dd692 100644 --- a/config/open_api.yml +++ b/config/open_api.yml @@ -95,6 +95,8 @@ metadata: description: Operations related to metadata of the GitLab instance - name: metrics_user_starred_dashboards description: Operations related to User-starred metrics dashboards + - name: ml_model_registry + description: Operations related to Model registry - name: npm_packages description: Operations related to NPM packages - name: nuget_packages diff --git a/db/post_migrate/20230517005523_ensure_backfill_bigint_id_is_completed.rb b/db/post_migrate/20230517005523_ensure_backfill_bigint_id_is_completed.rb new file mode 100644 index 00000000000..850ac7bcf5d --- /dev/null +++ b/db/post_migrate/20230517005523_ensure_backfill_bigint_id_is_completed.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +class EnsureBackfillBigintIdIsCompleted < Gitlab::Database::Migration[2.1] + include Gitlab::Database::MigrationHelpers::ConvertToBigint + + restrict_gitlab_migration gitlab_schema: :gitlab_ci + disable_ddl_transaction! + + TABLE_NAME = :ci_pipeline_variables + + def up + ensure_batched_background_migration_is_finished( + job_class_name: 'CopyColumnUsingBackgroundMigrationJob', + table_name: TABLE_NAME, + column_name: 'id', + job_arguments: [['id'], ['id_convert_to_bigint']] + ) + end + + def down + # no-op + end +end diff --git a/db/post_migrate/20230518005523_add_concurrent_index_for_ci_pipeline_variables_bigint_id.rb b/db/post_migrate/20230518005523_add_concurrent_index_for_ci_pipeline_variables_bigint_id.rb new file mode 100644 index 00000000000..19c48f7bf11 --- /dev/null +++ b/db/post_migrate/20230518005523_add_concurrent_index_for_ci_pipeline_variables_bigint_id.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +class AddConcurrentIndexForCiPipelineVariablesBigintId < Gitlab::Database::Migration[2.1] + include Gitlab::Database::MigrationHelpers::ConvertToBigint + + disable_ddl_transaction! + + TABLE_NAME = :ci_pipeline_variables + INDEX_NAME = "index_#{TABLE_NAME}_on_id_convert_to_bigint" + + def up + add_concurrent_index TABLE_NAME, :id_convert_to_bigint, unique: true, name: INDEX_NAME + end + + def down + remove_concurrent_index_by_name TABLE_NAME, INDEX_NAME + end +end diff --git a/db/schema_migrations/20230517005523 b/db/schema_migrations/20230517005523 new file mode 100644 index 00000000000..7fdac2b9961 --- /dev/null +++ b/db/schema_migrations/20230517005523 @@ -0,0 +1 @@ +4d84a87532b45436e64d0c919b361548b4b69b200ec3a91f454af718a51fd22a
\ No newline at end of file diff --git a/db/schema_migrations/20230518005523 b/db/schema_migrations/20230518005523 new file mode 100644 index 00000000000..700da3dff74 --- /dev/null +++ b/db/schema_migrations/20230518005523 @@ -0,0 +1 @@ +7428675eac2c572aa3521df7af7e79f7cf1b6e8f8472e99c842dddf2f3c7ce77
\ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index d106f3f7064..8fe5a4c0011 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -30525,6 +30525,8 @@ CREATE INDEX index_ci_pipeline_schedules_on_owner_id_and_id_and_active ON ci_pip CREATE INDEX index_ci_pipeline_schedules_on_project_id ON ci_pipeline_schedules USING btree (project_id); +CREATE UNIQUE INDEX index_ci_pipeline_variables_on_id_convert_to_bigint ON ci_pipeline_variables USING btree (id_convert_to_bigint); + CREATE UNIQUE INDEX index_ci_pipeline_variables_on_pipeline_id_and_key ON ci_pipeline_variables USING btree (pipeline_id, key); CREATE INDEX index_ci_pipelines_config_on_pipeline_id ON ci_pipelines_config USING btree (pipeline_id); diff --git a/doc/administration/auth/atlassian.md b/doc/administration/auth/atlassian.md index 45617b9965c..27e33c85761 100644 --- a/doc/administration/auth/atlassian.md +++ b/doc/administration/auth/atlassian.md @@ -29,13 +29,13 @@ To enable the Atlassian OmniAuth provider for passwordless authentication you mu 1. On your GitLab server, open the configuration file: - For Omnibus GitLab installations: + For Linux package installations: ```shell sudo editor /etc/gitlab/gitlab.rb ``` - For installations from source: + For self-compiled installations: ```shell sudo -u git -H editor /home/git/gitlab/config/gitlab.yml @@ -47,7 +47,7 @@ To enable the Atlassian OmniAuth provider for passwordless authentication you mu GitLab account. 1. Add the provider configuration for Atlassian: - For Omnibus GitLab installations: + For Linux package installations: ```ruby gitlab_rails['omniauth_providers'] = [ @@ -61,7 +61,7 @@ To enable the Atlassian OmniAuth provider for passwordless authentication you mu ] ``` - For installations from source: + For self-compiled installations: ```yaml - { name: "atlassian_oauth2", @@ -76,8 +76,8 @@ To enable the Atlassian OmniAuth provider for passwordless authentication you mu 1. Save the configuration file. 1. For the changes to take effect: - - If you installed via Omnibus, [reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure). - - If you installed from source, [restart GitLab](../restart_gitlab.md#installations-from-source). + - If you installed using the Linux package, [reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure). + - If you self-compiled your installation, [restart GitLab](../restart_gitlab.md#installations-from-source). On the sign-in page there should now be an Atlassian icon below the regular sign in form. Select the icon to begin the authentication process. diff --git a/doc/administration/auth/cognito.md b/doc/administration/auth/cognito.md index cfac958e297..d821a1e9cfe 100644 --- a/doc/administration/auth/cognito.md +++ b/doc/administration/auth/cognito.md @@ -37,16 +37,14 @@ To enable AWS Cognito as an authentication provider, complete the following step 1. Save changes for the app client settings. 1. Under **Domain name**, include the AWS domain name for your AWS Cognito application. -1. Under **App Clients**, find your app client ID. Select **Show details* to display the app client secret. These values correspond to the OAuth 2.0 Client ID and Client Secret. Save these values. +1. Under **App Clients**, find your app client ID. Select **Show details** to display the app client secret. These values correspond to the OAuth 2.0 Client ID and Client Secret. Save these values. ## Configure GitLab 1. Configure the [common settings](../../integration/omniauth.md#configure-common-settings) to add `cognito` as a single sign-on provider. This enables Just-In-Time account provisioning for users who do not have an existing GitLab account. -1. On your GitLab server, open the configuration file. - - **For Omnibus installations** +1. On your GitLab server, open the configuration file. For Linux package installations: ```shell sudo editor /etc/gitlab/gitlab.rb diff --git a/doc/administration/auth/crowd.md b/doc/administration/auth/crowd.md index f89e1a00928..c4ebb9a5dee 100644 --- a/doc/administration/auth/crowd.md +++ b/doc/administration/auth/crowd.md @@ -26,19 +26,19 @@ this provider also allows Crowd authentication for Git-over-https requests. 1. On your GitLab server, open the configuration file. - **Omnibus:** + - Linux package installations: - ```shell - sudo editor /etc/gitlab/gitlab.rb - ``` + ```shell + sudo editor /etc/gitlab/gitlab.rb + ``` - **Source:** + - Self-compiled installations: - ```shell - cd /home/git/gitlab + ```shell + cd /home/git/gitlab - sudo -u git -H editor config/gitlab.yml - ``` + sudo -u git -H editor config/gitlab.yml + ``` 1. Configure the [common settings](../../integration/omniauth.md#configure-common-settings) to add `crowd` as a single sign-on provider. This enables Just-In-Time @@ -46,39 +46,39 @@ this provider also allows Crowd authentication for Git-over-https requests. 1. Add the provider configuration: - **Omnibus:** - - ```ruby - gitlab_rails['omniauth_providers'] = [ - { - name: "crowd", - # label: "Provider name", # optional label for login button, defaults to "Crowd" - args: { - crowd_server_url: "CROWD_SERVER_URL", - application_name: "YOUR_APP_NAME", - application_password: "YOUR_APP_PASSWORD" + - Linux package installations: + + ```ruby + gitlab_rails['omniauth_providers'] = [ + { + name: "crowd", + # label: "Provider name", # optional label for login button, defaults to "Crowd" + args: { + crowd_server_url: "CROWD_SERVER_URL", + application_name: "YOUR_APP_NAME", + application_password: "YOUR_APP_PASSWORD" + } } - } - ] - ``` + ] + ``` - **Source:** + - Self-compiled installations: - ```yaml - - { name: 'crowd', - # label: 'Provider name', # optional label for login button, defaults to "Crowd" - args: { - crowd_server_url: 'CROWD_SERVER_URL', - application_name: 'YOUR_APP_NAME', - application_password: 'YOUR_APP_PASSWORD' } } - ``` + ```yaml + - { name: 'crowd', + # label: 'Provider name', # optional label for login button, defaults to "Crowd" + args: { + crowd_server_url: 'CROWD_SERVER_URL', + application_name: 'YOUR_APP_NAME', + application_password: 'YOUR_APP_PASSWORD' } } + ``` 1. Change `CROWD_SERVER_URL` to the [base URL of your Crowd server](https://confluence.atlassian.com/crowdkb/how-to-change-the-crowd-base-url-245827278.html). 1. Change `YOUR_APP_NAME` to the application name from Crowd applications page. 1. Change `YOUR_APP_PASSWORD` to the application password you've set. 1. Save the configuration file. -1. [Reconfigure](../restart_gitlab.md#omnibus-gitlab-reconfigure) (Omnibus GitLab) or [restart](../restart_gitlab.md#installations-from-source) (source installations) for - the changes to take effect. +1. [Reconfigure](../restart_gitlab.md#omnibus-gitlab-reconfigure) (Linux package installations) or + [restart](../restart_gitlab.md#installations-from-source) (self-compiled installations) for the changes to take effect. On the sign in page there should now be a Crowd tab in the sign in form. diff --git a/doc/administration/auth/jwt.md b/doc/administration/auth/jwt.md index bdcd6fc89cc..0f2036bea04 100644 --- a/doc/administration/auth/jwt.md +++ b/doc/administration/auth/jwt.md @@ -12,13 +12,13 @@ JWT provides you with a secret key for you to use. 1. On your GitLab server, open the configuration file. - For Omnibus GitLab: + For Linux package installations: ```shell sudo editor /etc/gitlab/gitlab.rb ``` - For installations from source: + For self-compiled installations: ```shell cd /home/git/gitlab @@ -30,7 +30,7 @@ JWT provides you with a secret key for you to use. account provisioning for users who do not have an existing GitLab account. 1. Add the provider configuration. - For Omnibus GitLab: + For Linux package installations: ```ruby gitlab_rails['omniauth_providers'] = [ @@ -49,7 +49,7 @@ JWT provides you with a secret key for you to use. ] ``` - For installation from source: + For self-compiled installations: ```yaml - { name: 'jwt', @@ -75,9 +75,9 @@ JWT provides you with a secret key for you to use. 1. Change `YOUR_APP_SECRET` to the client secret and set `auth_url` to your redirect URL. 1. Save the configuration file. -1. For the changes to take effect: - - If you installed via Omnibus, [reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure). - - If you installed from source, [restart GitLab](../restart_gitlab.md#installations-from-source). +1. For changes to take effect, if you: + - Used the Linux package to install GitLab, [reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure). + - Self-compiled your GitLab installation, [restart GitLab](../restart_gitlab.md#installations-from-source). On the sign in page there should now be a JWT icon below the regular sign in form. Select the icon to begin the authentication process. JWT asks the user to diff --git a/doc/administration/auth/ldap/google_secure_ldap.md b/doc/administration/auth/ldap/google_secure_ldap.md index 042a65be500..8084633c3ba 100644 --- a/doc/administration/auth/ldap/google_secure_ldap.md +++ b/doc/administration/auth/ldap/google_secure_ldap.md @@ -72,7 +72,7 @@ values obtained during the LDAP client configuration earlier: - `cert`: The `.crt` file text from the downloaded certificate bundle - `key`: The `.key` file text from the downloaded certificate bundle -**For Omnibus installations** +For Linux package installations: 1. Edit `/etc/gitlab/gitlab.rb`: @@ -142,9 +142,7 @@ values obtained during the LDAP client configuration earlier: 1. Save the file and [reconfigure](../../restart_gitlab.md#omnibus-gitlab-reconfigure) GitLab for the changes to take effect. ---- - -**For installations from source** +For self-compiled installations: 1. Edit `config/gitlab.yml`: diff --git a/doc/administration/auth/ldap/index.md b/doc/administration/auth/ldap/index.md index efed9d76746..a4484da5940 100644 --- a/doc/administration/auth/ldap/index.md +++ b/doc/administration/auth/ldap/index.md @@ -1037,8 +1037,8 @@ For more information on synchronizing users and groups between LDAP and GitLab, ## Move from LDAP to SAML 1. [Configure SAML](../../../integration/saml.md). Add `auto_link_ldap_user` to: - - [`gitlab.rb` for Omnibus](../../../integration/saml.html?tab=Linux+package+%28Omnibus%29). - - [`values.yml` for Kubernetes](../../../integration/saml.html?tab=Helm+chart+%28Kubernetes%29). + - [`gitlab.rb` for Linux package installations](../../../integration/saml.html?tab=Linux+package+%28Omnibus%29). + - [`values.yml` for Helm chart installations](../../../integration/saml.html?tab=Helm+chart+%28Kubernetes%29). For more information, see the [initial settings for all providers](../../../integration/omniauth.md#configure-initial-settings). 1. Optional. [Disable the LDAP auth from the sign-in page](#disable-ldap-web-sign-in). diff --git a/doc/administration/auth/oidc.md b/doc/administration/auth/oidc.md index 106cc6c23eb..88c9a669441 100644 --- a/doc/administration/auth/oidc.md +++ b/doc/administration/auth/oidc.md @@ -16,7 +16,7 @@ The OpenID Connect provides you with a client's details and secret for you to us 1. On your GitLab server, open the configuration file. - For Omnibus GitLab: + For Linux package installations: ```shell sudo editor /etc/gitlab/gitlab.rb @@ -35,7 +35,7 @@ The OpenID Connect provides you with a client's details and secret for you to us 1. Add the provider configuration. - For Omnibus GitLab: + For Linux package installations: ```ruby gitlab_rails['omniauth_providers'] = [ @@ -63,7 +63,7 @@ The OpenID Connect provides you with a client's details and secret for you to us ] ``` - For Omnibus GitLab with multiple identity providers: + For Linux package installations with multiple identity providers: ```ruby { 'name' => 'openid_connect', @@ -108,7 +108,7 @@ The OpenID Connect provides you with a client's details and secret for you to us NOTE: For more information on using multiple identity providers with OIDC, see [issue 5992](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/5992). - For installation from source: + For self-compiled installations: ```yaml - { name: 'openid_connect', # do not change this parameter @@ -184,10 +184,10 @@ The OpenID Connect provides you with a client's details and secret for you to us - `jwks_uri` is the URL to the endpoint where the Token signer publishes its keys. 1. Save the configuration file. -1. For changes to take effect, if you installed GitLab: +1. For changes to take effect, if you: - - With Omnibus, [reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure). - - From source, [restart GitLab](../restart_gitlab.md#installations-from-source). + - Used the Linux package to install GitLab, [reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure). + - Self-compiled your GitLab installation, [restart GitLab](../restart_gitlab.md#installations-from-source). On the sign in page, you have an OpenID Connect option below the regular sign in form. Select this option to begin the authentication process. The OpenID Connect provider @@ -197,7 +197,7 @@ by the client. You are redirected to GitLab and signed in. ## Example configurations The following configurations illustrate how to set up OpenID with -different providers with Omnibus GitLab. +different providers when using the GitLab Linux package installation. ### Configure Google @@ -240,7 +240,7 @@ you need the following information: [Microsoft Quickstart Register an Application](https://learn.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app) documentation to obtain the tenant ID, client ID, and client secret for your app. -Example Omnibus configuration block: +Example configuration block for Linux package installations: ```ruby gitlab_rails['omniauth_providers'] = [ @@ -372,7 +372,7 @@ but `LocalAccounts` authenticates against local Active Directory accounts. Befor ``` 1. Configure the issuer URL with the custom policy used for `signup_signin`. For example, this is - the Omnibus configuration with a custom policy for `b2c_1a_signup_signin`: + the configuration with a custom policy for `b2c_1a_signup_signin` for Linux package installations: ```ruby gitlab_rails['omniauth_providers'] = [ @@ -432,7 +432,7 @@ HS256 or HS358) to sign tokens. Public key encryption algorithms are: 1. Select **Realm Settings > Tokens > Default Signature Algorithm**. 1. Configure the signature algorithm. -Example Omnibus configuration block: +Example configuration block for Linux package installations: ```ruby gitlab_rails['omniauth_providers'] = [ @@ -556,7 +556,7 @@ For your app, complete the following steps on Casdoor: See the [Casdoor documentation](https://casdoor.org/docs/integration/ruby/gitlab) for more details. -Example Omnibus GitLab configuration (file path: `/etc/gitlab/gitlab.rb`): +Example configuration for Linux package installations (file path: `/etc/gitlab/gitlab.rb`): ```ruby gitlab_rails['omniauth_providers'] = [ @@ -617,7 +617,7 @@ This is not compatible with [configuring users based on OIDC group membership](# The following example configurations show how to offer different levels of authentication, one option with 2FA and one without 2FA. -For Omnibus GitLab: +For Linux package installations: ```ruby gitlab_rails['omniauth_providers'] = [ @@ -668,7 +668,7 @@ gitlab_rails['omniauth_providers'] = [ ] ``` -For installation from source: +For self-compiled installations: ```yaml - { name: 'openid_connect', @@ -774,7 +774,7 @@ response to require users to be members of a certain group, configure GitLab to If you do not set `required_groups` or leave the setting empty, any user authenticated by the IdP through OIDC can use GitLab. -For Omnibus GitLab: +For Linux package installations: 1. Edit `/etc/gitlab/gitlab.rb`: @@ -808,7 +808,7 @@ For Omnibus GitLab: 1. Save the file and [reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect. -For installation from source: +For self-compiled installations: 1. Edit `/home/git/gitlab/config/gitlab.yml`: @@ -853,7 +853,7 @@ based on group membership, configure GitLab to identify: [external user](../../user/admin_area/external_users.md), using the `external_groups` setting. -For Omnibus GitLab: +For Linux package installations: 1. Edit `/etc/gitlab/gitlab.rb`: @@ -887,7 +887,7 @@ For Omnibus GitLab: 1. Save the file and [reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect. -For installation from source: +For self-compiled installations: 1. Edit `/home/git/gitlab/config/gitlab.yml`: @@ -930,7 +930,7 @@ response to assign users as administrator based on group membership, configure G - Which group memberships grant the user administrator access, using the `admin_groups` setting. -For Omnibus GitLab: +For Linux package installations: 1. Edit `/etc/gitlab/gitlab.rb`: @@ -964,7 +964,7 @@ For Omnibus GitLab: 1. Save the file and [reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect. -For installation from source: +For self-compiled installations: 1. Edit `/home/git/gitlab/config/gitlab.yml`: diff --git a/doc/administration/auth/smartcard.md b/doc/administration/auth/smartcard.md index 5b6d299f171..0fa585bace5 100644 --- a/doc/administration/auth/smartcard.md +++ b/doc/administration/auth/smartcard.md @@ -115,7 +115,7 @@ more information, see [the relevant issue](https://gitlab.com/gitlab-org/gitlab/ ## Configure GitLab for smartcard authentication -**For Omnibus installations** +For Linux package installations: 1. Edit `/etc/gitlab/gitlab.rb`: @@ -140,9 +140,7 @@ more information, see [the relevant issue](https://gitlab.com/gitlab-org/gitlab/ 1. Save the file and [reconfigure](../restart_gitlab.md#omnibus-gitlab-reconfigure) GitLab for the changes to take effect. ---- - -**For installations from source** +For self-compiled installations: 1. Configure NGINX to request a client side certificate @@ -237,7 +235,7 @@ more information, see [the relevant issue](https://gitlab.com/gitlab-org/gitlab/ ### Additional steps when using SAN extensions -**For Omnibus installations** +For Linux package installations: 1. Add to `/etc/gitlab/gitlab.rb`: @@ -248,7 +246,7 @@ more information, see [the relevant issue](https://gitlab.com/gitlab-org/gitlab/ 1. Save the file and [reconfigure](../restart_gitlab.md#omnibus-gitlab-reconfigure) GitLab for the changes to take effect. -**For installations from source** +For self-compiled installations: 1. Add the `san_extensions` line to `config/gitlab.yml` within the smartcard section: @@ -267,7 +265,7 @@ more information, see [the relevant issue](https://gitlab.com/gitlab-org/gitlab/ ### Additional steps when authenticating against an LDAP server -**For Omnibus installations** +For Linux package installations: 1. Edit `/etc/gitlab/gitlab.rb`: @@ -284,7 +282,7 @@ more information, see [the relevant issue](https://gitlab.com/gitlab-org/gitlab/ 1. Save the file and [reconfigure](../restart_gitlab.md#omnibus-gitlab-reconfigure) GitLab for the changes to take effect. -**For installations from source** +For self-compiled installations: 1. Edit `config/gitlab.yml`: @@ -304,7 +302,7 @@ more information, see [the relevant issue](https://gitlab.com/gitlab-org/gitlab/ ### Require browser session with smartcard sign-in for Git access -**For Omnibus installations** +For Linux package installations: 1. Edit `/etc/gitlab/gitlab.rb`: @@ -315,7 +313,7 @@ more information, see [the relevant issue](https://gitlab.com/gitlab-org/gitlab/ 1. Save the file and [reconfigure](../restart_gitlab.md#omnibus-gitlab-reconfigure) GitLab for the changes to take effect. -**For installations from source** +For self-compiled installations: 1. Edit `config/gitlab.yml`: diff --git a/doc/administration/clusters/kas.md b/doc/administration/clusters/kas.md index 6d6e8e5513c..cbd32dea95c 100644 --- a/doc/administration/clusters/kas.md +++ b/doc/administration/clusters/kas.md @@ -21,12 +21,12 @@ If you use self-managed GitLab, you must install an agent server or specify an e As a GitLab administrator, you can install the agent server: -- For [Omnibus installations](#for-omnibus). -- For [GitLab Helm Chart installations](#for-gitlab-helm-chart). +- For [Linux package installations](#for-linux-package-installations). +- For [GitLab Helm chart installations](#for-gitlab-helm-chart). -### For Omnibus +### For Linux package installations -You can enable the agent server for [Omnibus](https://docs.gitlab.com/omnibus/) package installations on a single node, or on multiple nodes at once. +You can enable the agent server for Linux package installations on a single node, or on multiple nodes at once. #### Enable on a single node @@ -167,7 +167,7 @@ service logs by running the following command: kubectl logs -f -l=app=kas -n <YOUR-GITLAB-NAMESPACE> ``` -In Omnibus GitLab, find the logs in `/var/log/gitlab/gitlab-kas/`. +In Linux package installations, find the logs in `/var/log/gitlab/gitlab-kas/`. You can also [troubleshoot issues with individual agents](../../user/clusters/agent/troubleshooting.md). @@ -212,7 +212,7 @@ When the agent server tries to connect to the GitLab API, the following error mi {"level":"error","time":"2021-08-16T14:56:47.289Z","msg":"GetAgentInfo()","correlation_id":"01FD7QE35RXXXX8R47WZFBAXTN","grpc_service":"gitlab.agent.reverse_tunnel.rpc.ReverseTunnel","grpc_method":"Connect","error":"Get \"https://gitlab.example.com/api/v4/internal/kubernetes/agent_info\": dial tcp 172.17.0.4:443: connect: connection refused"} ``` -To fix this issue for [Omnibus](https://docs.gitlab.com/omnibus/) package installations, +To fix this issue for Linux package installations, set the following parameter in `/etc/gitlab/gitlab.rb`. Replace `gitlab.example.com` with your GitLab instance's hostname: ```ruby diff --git a/doc/administration/operations/puma.md b/doc/administration/operations/puma.md index efc55a5fbc3..63f3c66622d 100644 --- a/doc/administration/operations/puma.md +++ b/doc/administration/operations/puma.md @@ -211,6 +211,71 @@ make Prometheus scrape them over HTTPS, and support for it is being discussed Hence, it is not technically possible to turn off this HTTP listener without losing Prometheus metrics. +### Using an encrypted SSL key + +> [Introduced](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/7799) in GitLab 16.1. + +Puma supports the use of an encrypted private SSL key, which can be +decrypted at runtime. The following instructions illustrate how to +configure this: + +1. Encrypt the key with a password if it is not already: + + ```shell + openssl rsa -aes256 -in /path/to/ssl-key.pem -out /path/to/encrypted-ssl-key.pem + ``` + + Enter in a password twice to write the encrypted file. In this + example, we use `some-password-here`. + +1. Create a script or executable that prints the password. For + example, create a basic script in + `/var/opt/gitlab/gitlab-rails/etc/puma-ssl-key-password` that echoes + the password: + + ```shell + #!/bin/sh + echo some-password-here + ``` + + Note that in production, you should avoid storing the password on + disk and use a secure mechanism for retrieving a password, such as + Vault. For example, the script might look like: + + ```shell + #!/bin/sh + export VAULT_ADDR=http://vault-password-distribution-point:8200 + export VAULT_TOKEN=<some token> + + echo "$(vault kv get -mount=secret puma-ssl-password)" + ``` + +1. Ensure the Puma process has sufficient permissions to execute the + script and to read the encrypted key: + + ```shell + chown git:git /var/opt/gitlab/gitlab-rails/etc/puma-ssl-key-password + chmod 770 /var/opt/gitlab/gitlab-rails/etc/puma-ssl-key-password + chmod 660 /path/to/encrypted-ssl-key.pem + ``` + +1. Edit `/etc/gitlab/gitlab.rb`, and replace `puma['ssl_certificate_key']` with the encrypted key and specify + `puma['ssl_key_password_command]`: + + ```ruby + puma['ssl_certificate_key'] = '/path/to/encrypted-ssl-key.pem' + puma['ssl_key_password_command'] = '/var/opt/gitlab/gitlab-rails/etc/puma-ssl-key-password' + ``` + +1. Reconfigure GitLab: + + ```shell + sudo gitlab-ctl reconfigure + ``` + +1. If GitLab comes up successfully, you should be able to remove the + unencrypted SSL key that was stored on the GitLab instance. + ## Switch from Unicorn to Puma NOTE: diff --git a/doc/administration/package_information/postgresql_versions.md b/doc/administration/package_information/postgresql_versions.md index c1e9f7320ea..44032883eb4 100644 --- a/doc/administration/package_information/postgresql_versions.md +++ b/doc/administration/package_information/postgresql_versions.md @@ -30,6 +30,7 @@ Read more about update policies and warnings in the PostgreSQL | GitLab version | PostgreSQL versions | Default version for fresh installs | Default version for upgrades | Notes | | -------------- | --------------------- | ---------------------------------- | ---------------------------- | ----- | +| 16.0 | 13.11 | 13.11 | 13.11 | | | 15.6 | 12.12, 13.8 | 13.8 | 12.12 | For upgrades, users can manually upgrade to 13.8 following the [upgrade documentation](https://docs.gitlab.com/omnibus/settings/database.html#gitlab-150-and-later). | | 15.0 | 12.10, 13.6 | 13.6 | 12.10 | For upgrades, users can manually upgrade to 13.6 following the [upgrade documentation](https://docs.gitlab.com/omnibus/settings/database.html#gitlab-150-and-later). | | 14.1 | 12.7, 13.3 | 12.7 | 12.7 | PostgreSQL 13 available for fresh installations if not using [Geo](../geo/index.md#requirements-for-running-geo) or [Patroni](../postgresql/index.md#postgresql-replication-and-failover-with-omnibus-gitlab). diff --git a/doc/user/profile/account/delete_account.md b/doc/user/profile/account/delete_account.md index 0b6aed01309..1ffe01d6e00 100644 --- a/doc/user/profile/account/delete_account.md +++ b/doc/user/profile/account/delete_account.md @@ -32,6 +32,8 @@ As a user, to delete your own account: NOTE: On GitLab.com, there is a seven day delay between a user deleting their own account and deletion of the user record. During this time, that user is [blocked](../../admin_area/moderate_users.md#block-a-user) and a new account with the same email address or username cannot be created. +Unblocking the account does not undo the deletion because the account will still be in the deletion queue, and there is no quick method to reverse this process. + ## Delete users and user contributions **(FREE SELF)** As an administrator, to delete a user account: diff --git a/lib/api/api.rb b/lib/api/api.rb index a7acd44e72a..5f7faa7eb7d 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -255,6 +255,7 @@ module API mount ::API::Metadata mount ::API::Metrics::Dashboard::Annotations mount ::API::Metrics::UserStarredDashboards + mount ::API::MlModelPackages mount ::API::Namespaces mount ::API::NpmGroupPackages mount ::API::NpmInstancePackages diff --git a/lib/api/ml_model_packages.rb b/lib/api/ml_model_packages.rb new file mode 100644 index 00000000000..fec72b03ffd --- /dev/null +++ b/lib/api/ml_model_packages.rb @@ -0,0 +1,111 @@ +# frozen_string_literal: true + +module API + class MlModelPackages < ::API::Base + include APIGuard + include ::API::Helpers::Authentication + + ML_MODEL_PACKAGES_REQUIREMENTS = { + package_name: API::NO_SLASH_URL_PART_REGEX, + file_name: API::NO_SLASH_URL_PART_REGEX + }.freeze + + ALLOWED_STATUSES = %w[default hidden].freeze + + feature_category :mlops + urgency :low + + after_validation do + require_packages_enabled! + authenticate_non_get! + + not_found! unless can?(current_user, :read_model_registry, user_project) + end + + authenticate_with do |accept| + accept.token_types(:personal_access_token, :deploy_token, :job_token) + .sent_through(:http_token) + end + + helpers do + include ::API::Helpers::PackagesHelpers + include ::API::Helpers::Packages::BasicAuthHelpers + + def project + authorized_user_project + end + + def max_file_size_exceeded? + project.actual_limits.exceeded?(:ml_model_max_file_size, params[:file].size) + end + end + + params do + requires :id, types: [String, Integer], desc: 'The ID or URL-encoded path of the project' + end + + resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do + namespace ':id/packages/ml_models' do + params do + requires :package_name, type: String, desc: 'Package name', regexp: Gitlab::Regex.ml_model_name_regex, + file_path: true + requires :package_version, type: String, desc: 'Package version', + regexp: Gitlab::Regex.ml_model_version_regex + requires :file_name, type: String, desc: 'Package file name', + regexp: Gitlab::Regex.ml_model_file_name_regex, file_path: true + optional :status, type: String, values: ALLOWED_STATUSES, desc: 'Package status' + end + namespace ':package_name/*package_version/:file_name', requirements: ML_MODEL_PACKAGES_REQUIREMENTS do + desc 'Workhorse authorize model package file' do + detail 'Introduced in GitLab 16.1' + success code: 200 + failure [ + { code: 401, message: 'Unauthorized' }, + { code: 403, message: 'Forbidden' }, + { code: 404, message: 'Not Found' } + ] + tags %w[ml_model_registry] + end + put 'authorize' do + authorize_workhorse!(subject: project, maximum_size: project.actual_limits.ml_model_max_file_size) + end + + desc 'Workhorse upload model package file' do + detail 'Introduced in GitLab 16.1' + success code: 201 + failure [ + { code: 401, message: 'Unauthorized' }, + { code: 403, message: 'Forbidden' }, + { code: 404, message: 'Not Found' } + ] + tags %w[ml_model_registry] + end + params do + requires :file, + type: ::API::Validations::Types::WorkhorseFile, + desc: 'The package file to be published (generated by Multipart middleware)', + documentation: { type: 'file' } + end + put do + authorize_upload!(project) + + bad_request!('File is too large') if max_file_size_exceeded? + + create_package_file_params = declared(params).merge(build: current_authenticated_job) + package_file = ::Packages::MlModel::CreatePackageFileService + .new(project, current_user, create_package_file_params) + .execute + + bad_request!('Package creation failed') unless package_file + + created! + rescue ObjectStorage::RemoteStoreError => e + Gitlab::ErrorTracking.track_exception(e, extra: { file_name: params[:file_name], project_id: project.id }) + + forbidden! + end + end + end + end + end +end diff --git a/lib/gitlab/regex.rb b/lib/gitlab/regex.rb index 79f12ee13f7..26ca9d2547c 100644 --- a/lib/gitlab/regex.rb +++ b/lib/gitlab/regex.rb @@ -623,6 +623,18 @@ module Gitlab def x509_subject_key_identifier_regex @x509_subject_key_identifier_regex ||= /\A(?:\h{2}:)*\h{2}\z/.freeze end + + def ml_model_version_regex + maven_version_regex + end + + def ml_model_name_regex + package_name_regex + end + + def ml_model_file_name_regex + maven_file_name_regex + end end end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 9a6218e486a..a65b08a5ede 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -11070,6 +11070,27 @@ msgstr "" msgid "CodeSuggestionsAlert|Get started with Code Suggestions, available for free during the beta period." msgstr "" +msgid "CodeSuggestionsSM|• Agree to the %{terms_link_start}GitLab Testing Agreement%{link_end}.%{br} • Acknowledge that GitLab will send data from the instance, including personal data, to Google for cloud hosting.%{br} We may also send data to %{ai_docs_link_start}third-party AI providers%{link_end} to provide this feature." +msgstr "" + +msgid "CodeSuggestionsSM|Code Suggestions" +msgstr "" + +msgid "CodeSuggestionsSM|Enable Code Suggestion for users of this GitLab instance." +msgstr "" + +msgid "CodeSuggestionsSM|Enter new personal access token" +msgstr "" + +msgid "CodeSuggestionsSM|Personal access token" +msgstr "" + +msgid "CodeSuggestionsSM|Turn on Code Suggestions for this instance. By turning on this feature, you:" +msgstr "" + +msgid "CodeSuggestionsSM|Your personal access token from GitLab.com. See the %{link_start}documentation%{link_end} for information on creating a personal access token." +msgstr "" + msgid "CodeSuggestions|%{link_start}What are code suggestions?%{link_end}" msgstr "" @@ -54137,6 +54158,9 @@ msgstr "" msgid "is read-only" msgstr "" +msgid "is required to enable Code Suggestions" +msgstr "" + msgid "is too long (%{current_value}). The maximum size is %{max_size}." msgstr "" diff --git a/scripts/review_apps/review-apps.sh b/scripts/review_apps/review-apps.sh index 6ebb0f61a04..660257b042a 100755 --- a/scripts/review_apps/review-apps.sh +++ b/scripts/review_apps/review-apps.sh @@ -133,8 +133,8 @@ function disable_sign_ups() { # We use this weird syntax because we need to pass a one-liner ruby command to a Kubernetes container via kubectl. read -r -d '' multiline_ruby_code <<RUBY user = User.find_by_username('root'); -puts 'Error: Could not find root user. Check that the database was properly seeded'; exit(1) unless user; -token = user.personal_access_tokens.create(scopes: [:api], name: 'Token to disable sign-ups'); +(puts 'Error: Could not find root user. Check that the database was properly seeded'; exit(1)) unless user; +token = user.personal_access_tokens.create(scopes: [:api], name: 'Token to disable sign-ups', expires_at: 30.days.from_now); token.set_token('${REVIEW_APPS_ROOT_TOKEN}'); begin; token.save!; diff --git a/spec/helpers/admin/application_settings/settings_helper_spec.rb b/spec/helpers/admin/application_settings/settings_helper_spec.rb index 9981e0d12bd..efffc224eb2 100644 --- a/spec/helpers/admin/application_settings/settings_helper_spec.rb +++ b/spec/helpers/admin/application_settings/settings_helper_spec.rb @@ -31,4 +31,18 @@ RSpec.describe Admin::ApplicationSettings::SettingsHelper do }) end end + + describe 'Code Suggestions for Self-Managed instances', feature_category: :code_suggestions do + describe '#code_suggestions_token_explanation' do + subject { helper.code_suggestions_token_explanation } + + it { is_expected.to include 'https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html#create-a-personal-access-token' } + end + + describe '#code_suggestions_agreement' do + subject { helper.code_suggestions_agreement } + + it { is_expected.to include 'https://about.gitlab.com/handbook/legal/testing-agreement/' } + end + end end diff --git a/spec/lib/sidebars/admin/menus/abuse_reports_menu_spec.rb b/spec/lib/sidebars/admin/menus/abuse_reports_menu_spec.rb index 5926852ff57..ef5b8055bec 100644 --- a/spec/lib/sidebars/admin/menus/abuse_reports_menu_spec.rb +++ b/spec/lib/sidebars/admin/menus/abuse_reports_menu_spec.rb @@ -11,7 +11,7 @@ RSpec.describe Sidebars::Admin::Menus::AbuseReportsMenu, feature_category: :navi it_behaves_like 'Admin menu without sub menus', active_routes: { controller: :abuse_reports } describe '#pill_count' do - let_it_be(:user) { create(:user, :admin) } + let(:user) { build_stubbed(:user, :admin) } let(:context) { Sidebars::Context.new(current_user: user, container: nil) } diff --git a/spec/lib/sidebars/admin/menus/monitoring_menu_spec.rb b/spec/lib/sidebars/admin/menus/monitoring_menu_spec.rb index 3bf43b9a251..4e1d56dc001 100644 --- a/spec/lib/sidebars/admin/menus/monitoring_menu_spec.rb +++ b/spec/lib/sidebars/admin/menus/monitoring_menu_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' RSpec.describe Sidebars::Admin::Menus::MonitoringMenu, feature_category: :navigation do - let_it_be(:user) { create(:user, :admin) } + let(:user) { build_stubbed(:user, :admin) } let(:context) { Sidebars::Context.new(current_user: user, container: nil) } let(:menu) { described_class.new(context) } diff --git a/spec/lib/sidebars/concerns/container_with_html_options_spec.rb b/spec/lib/sidebars/concerns/container_with_html_options_spec.rb index d95cdb9e0fe..588e89a80f7 100644 --- a/spec/lib/sidebars/concerns/container_with_html_options_spec.rb +++ b/spec/lib/sidebars/concerns/container_with_html_options_spec.rb @@ -2,7 +2,7 @@ require 'fast_spec_helper' -RSpec.describe Sidebars::Concerns::ContainerWithHtmlOptions do +RSpec.describe Sidebars::Concerns::ContainerWithHtmlOptions, feature_category: :navigation do subject do Class.new do include Sidebars::Concerns::ContainerWithHtmlOptions diff --git a/spec/lib/sidebars/concerns/link_with_html_options_spec.rb b/spec/lib/sidebars/concerns/link_with_html_options_spec.rb index f7e6701c37d..64f19ed9e98 100644 --- a/spec/lib/sidebars/concerns/link_with_html_options_spec.rb +++ b/spec/lib/sidebars/concerns/link_with_html_options_spec.rb @@ -2,7 +2,7 @@ require 'fast_spec_helper' -RSpec.describe Sidebars::Concerns::LinkWithHtmlOptions do +RSpec.describe Sidebars::Concerns::LinkWithHtmlOptions, feature_category: :navigation do let(:options) { {} } subject { Class.new { include Sidebars::Concerns::LinkWithHtmlOptions }.new } diff --git a/spec/lib/sidebars/groups/menus/ci_cd_menu_spec.rb b/spec/lib/sidebars/groups/menus/ci_cd_menu_spec.rb index 246df2e409b..2b9a4133f3f 100644 --- a/spec/lib/sidebars/groups/menus/ci_cd_menu_spec.rb +++ b/spec/lib/sidebars/groups/menus/ci_cd_menu_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Sidebars::Groups::Menus::CiCdMenu do +RSpec.describe Sidebars::Groups::Menus::CiCdMenu, feature_category: :navigation do let_it_be(:owner) { create(:user) } let_it_be(:root_group) do build(:group, :private).tap do |g| diff --git a/spec/lib/sidebars/groups/menus/observability_menu_spec.rb b/spec/lib/sidebars/groups/menus/observability_menu_spec.rb index 20af8ea00be..573760cddb6 100644 --- a/spec/lib/sidebars/groups/menus/observability_menu_spec.rb +++ b/spec/lib/sidebars/groups/menus/observability_menu_spec.rb @@ -2,9 +2,9 @@ require 'spec_helper' -RSpec.describe Sidebars::Groups::Menus::ObservabilityMenu do - let_it_be(:owner) { create(:user) } - let_it_be(:root_group) do +RSpec.describe Sidebars::Groups::Menus::ObservabilityMenu, feature_category: :navigation do + let(:owner) { build_stubbed(:user) } + let(:root_group) do build(:group, :private).tap do |g| g.add_owner(owner) end diff --git a/spec/lib/sidebars/groups/menus/settings_menu_spec.rb b/spec/lib/sidebars/groups/menus/settings_menu_spec.rb index bc30d7628af..8628696ebd8 100644 --- a/spec/lib/sidebars/groups/menus/settings_menu_spec.rb +++ b/spec/lib/sidebars/groups/menus/settings_menu_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Sidebars::Groups::Menus::SettingsMenu, :with_license do +RSpec.describe Sidebars::Groups::Menus::SettingsMenu, :with_license, feature_category: :navigation do let_it_be(:owner) { create(:user) } let_it_be_with_refind(:group) do diff --git a/spec/lib/sidebars/menu_item_spec.rb b/spec/lib/sidebars/menu_item_spec.rb index 84bc3430260..3ff5b80e5d9 100644 --- a/spec/lib/sidebars/menu_item_spec.rb +++ b/spec/lib/sidebars/menu_item_spec.rb @@ -2,7 +2,7 @@ require 'fast_spec_helper' -RSpec.describe Sidebars::MenuItem do +RSpec.describe Sidebars::MenuItem, feature_category: :navigation do let(:title) { 'foo' } let(:html_options) { {} } let(:menu_item) { described_class.new(title: title, active_routes: {}, link: '', container_html_options: html_options) } diff --git a/spec/lib/sidebars/projects/context_spec.rb b/spec/lib/sidebars/projects/context_spec.rb index 44578ae1583..bdf6439b85b 100644 --- a/spec/lib/sidebars/projects/context_spec.rb +++ b/spec/lib/sidebars/projects/context_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Sidebars::Projects::Context do +RSpec.describe Sidebars::Projects::Context, feature_category: :navigation do let(:project) { build(:project) } subject { described_class.new(current_user: nil, container: project) } diff --git a/spec/lib/sidebars/projects/menus/analytics_menu_spec.rb b/spec/lib/sidebars/projects/menus/analytics_menu_spec.rb index 878da747abe..45aa93bef1c 100644 --- a/spec/lib/sidebars/projects/menus/analytics_menu_spec.rb +++ b/spec/lib/sidebars/projects/menus/analytics_menu_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Sidebars::Projects::Menus::AnalyticsMenu do +RSpec.describe Sidebars::Projects::Menus::AnalyticsMenu, feature_category: :navigation do let_it_be_with_refind(:project) { create(:project, :repository) } let_it_be(:guest) do create(:user).tap { |u| project.add_guest(u) } diff --git a/spec/lib/sidebars/projects/menus/ci_cd_menu_spec.rb b/spec/lib/sidebars/projects/menus/ci_cd_menu_spec.rb index 6116fff792a..f6602bf2b46 100644 --- a/spec/lib/sidebars/projects/menus/ci_cd_menu_spec.rb +++ b/spec/lib/sidebars/projects/menus/ci_cd_menu_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Sidebars::Projects::Menus::CiCdMenu do +RSpec.describe Sidebars::Projects::Menus::CiCdMenu, feature_category: :navigation do let(:project) { build(:project) } let(:user) { project.first_owner } let(:can_view_pipeline_editor) { true } diff --git a/spec/lib/sidebars/projects/menus/confluence_menu_spec.rb b/spec/lib/sidebars/projects/menus/confluence_menu_spec.rb index 55c55b70a43..61ee16c3cf6 100644 --- a/spec/lib/sidebars/projects/menus/confluence_menu_spec.rb +++ b/spec/lib/sidebars/projects/menus/confluence_menu_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Sidebars::Projects::Menus::ConfluenceMenu do +RSpec.describe Sidebars::Projects::Menus::ConfluenceMenu, feature_category: :navigation do let_it_be_with_refind(:project) { create(:project, has_external_wiki: true) } let(:user) { project.first_owner } diff --git a/spec/lib/sidebars/projects/menus/external_issue_tracker_menu_spec.rb b/spec/lib/sidebars/projects/menus/external_issue_tracker_menu_spec.rb index 2033d40897e..dc7c2ec6ed8 100644 --- a/spec/lib/sidebars/projects/menus/external_issue_tracker_menu_spec.rb +++ b/spec/lib/sidebars/projects/menus/external_issue_tracker_menu_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Sidebars::Projects::Menus::ExternalIssueTrackerMenu do +RSpec.describe Sidebars::Projects::Menus::ExternalIssueTrackerMenu, feature_category: :navigation do let(:project) { build(:project) } let(:user) { project.first_owner } let(:jira_issues_integration_active) { false } diff --git a/spec/lib/sidebars/projects/menus/hidden_menu_spec.rb b/spec/lib/sidebars/projects/menus/hidden_menu_spec.rb index e64b0de9c62..a9e870934c4 100644 --- a/spec/lib/sidebars/projects/menus/hidden_menu_spec.rb +++ b/spec/lib/sidebars/projects/menus/hidden_menu_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Sidebars::Projects::Menus::HiddenMenu do +RSpec.describe Sidebars::Projects::Menus::HiddenMenu, feature_category: :navigation do let_it_be(:project) { create(:project, :repository) } let(:user) { project.first_owner } diff --git a/spec/lib/sidebars/projects/menus/issues_menu_spec.rb b/spec/lib/sidebars/projects/menus/issues_menu_spec.rb index 544cbcb956d..53d92d013a9 100644 --- a/spec/lib/sidebars/projects/menus/issues_menu_spec.rb +++ b/spec/lib/sidebars/projects/menus/issues_menu_spec.rb @@ -71,7 +71,7 @@ RSpec.describe Sidebars::Projects::Menus::IssuesMenu, feature_category: :navigat context 'when there are open issues' do it 'returns the number of open issues' do create_list(:issue, 2, :opened, project: project) - create(:issue, :closed, project: project) + build_stubbed(:issue, :closed, project: project) expect(subject.pill_count).to eq '2' end diff --git a/spec/lib/sidebars/projects/menus/monitor_menu_spec.rb b/spec/lib/sidebars/projects/menus/monitor_menu_spec.rb index 8bb7e8c8518..363822ee5e4 100644 --- a/spec/lib/sidebars/projects/menus/monitor_menu_spec.rb +++ b/spec/lib/sidebars/projects/menus/monitor_menu_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Sidebars::Projects::Menus::MonitorMenu do +RSpec.describe Sidebars::Projects::Menus::MonitorMenu, feature_category: :navigation do let_it_be_with_refind(:project) { create(:project) } let(:user) { project.first_owner } diff --git a/spec/lib/sidebars/projects/menus/settings_menu_spec.rb b/spec/lib/sidebars/projects/menus/settings_menu_spec.rb index 4be99892631..a60e46582f9 100644 --- a/spec/lib/sidebars/projects/menus/settings_menu_spec.rb +++ b/spec/lib/sidebars/projects/menus/settings_menu_spec.rb @@ -2,8 +2,8 @@ require 'spec_helper' -RSpec.describe Sidebars::Projects::Menus::SettingsMenu do - let_it_be(:project) { create(:project) } +RSpec.describe Sidebars::Projects::Menus::SettingsMenu, feature_category: :navigation do + let(:project) { build_stubbed(:project) } let(:user) { project.first_owner } let(:context) { Sidebars::Projects::Context.new(current_user: user, container: project) } diff --git a/spec/lib/sidebars/projects/menus/zentao_menu_spec.rb b/spec/lib/sidebars/projects/menus/zentao_menu_spec.rb index f0bce6b7ea5..749b0df7c08 100644 --- a/spec/lib/sidebars/projects/menus/zentao_menu_spec.rb +++ b/spec/lib/sidebars/projects/menus/zentao_menu_spec.rb @@ -2,6 +2,6 @@ require 'spec_helper' -RSpec.describe Sidebars::Projects::Menus::ZentaoMenu do +RSpec.describe Sidebars::Projects::Menus::ZentaoMenu, feature_category: :navigation do it_behaves_like 'ZenTao menu with CE version' end diff --git a/spec/lib/sidebars/projects/super_sidebar_panel_spec.rb b/spec/lib/sidebars/projects/super_sidebar_panel_spec.rb index bcc8f51901d..3fc6cd5083f 100644 --- a/spec/lib/sidebars/projects/super_sidebar_panel_spec.rb +++ b/spec/lib/sidebars/projects/super_sidebar_panel_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' RSpec.describe Sidebars::Projects::SuperSidebarPanel, feature_category: :navigation do - let_it_be(:project) { create(:project, :repository) } + let(:project) { build_stubbed(:project, :repository) } let(:user) { project.first_owner } let(:context) do diff --git a/spec/lib/sidebars/search/panel_spec.rb b/spec/lib/sidebars/search/panel_spec.rb index 30801ff800e..39c0f112793 100644 --- a/spec/lib/sidebars/search/panel_spec.rb +++ b/spec/lib/sidebars/search/panel_spec.rb @@ -3,8 +3,8 @@ require 'spec_helper' RSpec.describe Sidebars::Search::Panel, feature_category: :navigation do - let_it_be(:current_user) { create(:user) } - let_it_be(:user) { create(:user) } + let(:current_user) { build_stubbed(:user) } + let(:user) { build_stubbed(:user) } let(:context) { Sidebars::Context.new(current_user: current_user, container: user) } let(:panel) { described_class.new(context) } diff --git a/spec/lib/sidebars/user_profile/panel_spec.rb b/spec/lib/sidebars/user_profile/panel_spec.rb index c62c7f9fd96..a2bf490bc58 100644 --- a/spec/lib/sidebars/user_profile/panel_spec.rb +++ b/spec/lib/sidebars/user_profile/panel_spec.rb @@ -3,8 +3,8 @@ require 'spec_helper' RSpec.describe Sidebars::UserProfile::Panel, feature_category: :navigation do - let_it_be(:current_user) { create(:user) } - let_it_be(:user) { create(:user) } + let(:current_user) { build_stubbed(:user) } + let(:user) { build_stubbed(:user) } let(:context) { Sidebars::Context.new(current_user: current_user, container: user) } diff --git a/spec/lib/sidebars/user_settings/panel_spec.rb b/spec/lib/sidebars/user_settings/panel_spec.rb index 0c02bf77d0e..d574652188d 100644 --- a/spec/lib/sidebars/user_settings/panel_spec.rb +++ b/spec/lib/sidebars/user_settings/panel_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' RSpec.describe Sidebars::UserSettings::Panel, feature_category: :navigation do - let_it_be(:user) { create(:user) } + let(:user) { build_stubbed(:user) } let(:context) { Sidebars::Context.new(current_user: user, container: nil) } diff --git a/spec/lib/sidebars/your_work/menus/issues_menu_spec.rb b/spec/lib/sidebars/your_work/menus/issues_menu_spec.rb index a1206c0bc1c..2348054752f 100644 --- a/spec/lib/sidebars/your_work/menus/issues_menu_spec.rb +++ b/spec/lib/sidebars/your_work/menus/issues_menu_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' RSpec.describe Sidebars::YourWork::Menus::IssuesMenu, feature_category: :navigation do - let(:user) { create(:user) } + let(:user) { build_stubbed(:user) } let(:context) { Sidebars::Context.new(current_user: user, container: nil) } subject { described_class.new(context) } diff --git a/spec/lib/sidebars/your_work/menus/merge_requests_menu_spec.rb b/spec/lib/sidebars/your_work/menus/merge_requests_menu_spec.rb index 8941c11006e..d7d24bb55c8 100644 --- a/spec/lib/sidebars/your_work/menus/merge_requests_menu_spec.rb +++ b/spec/lib/sidebars/your_work/menus/merge_requests_menu_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' RSpec.describe Sidebars::YourWork::Menus::MergeRequestsMenu, feature_category: :navigation do - let_it_be(:user) { create(:user) } + let(:user) { build_stubbed(:user) } let(:context) { Sidebars::Context.new(current_user: user, container: nil) } diff --git a/spec/lib/sidebars/your_work/menus/todos_menu_spec.rb b/spec/lib/sidebars/your_work/menus/todos_menu_spec.rb index a8177a6a01b..d3b51645cca 100644 --- a/spec/lib/sidebars/your_work/menus/todos_menu_spec.rb +++ b/spec/lib/sidebars/your_work/menus/todos_menu_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' RSpec.describe Sidebars::YourWork::Menus::TodosMenu, feature_category: :navigation do - let(:user) { create(:user) } + let(:user) { build_stubbed(:user) } let(:context) { Sidebars::Context.new(current_user: user, container: nil) } subject { described_class.new(context) } diff --git a/spec/lib/sidebars/your_work/panel_spec.rb b/spec/lib/sidebars/your_work/panel_spec.rb index ae9c3aa18e6..65c2786a16d 100644 --- a/spec/lib/sidebars/your_work/panel_spec.rb +++ b/spec/lib/sidebars/your_work/panel_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' RSpec.describe Sidebars::YourWork::Panel, feature_category: :navigation do - let_it_be(:user) { create(:user) } + let(:user) { build_stubbed(:user) } let(:context) { Sidebars::Context.new(current_user: user, container: nil) } diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb index bac9b58056b..b0c7f8c6dfc 100644 --- a/spec/models/application_setting_spec.rb +++ b/spec/models/application_setting_spec.rb @@ -1650,4 +1650,29 @@ RSpec.describe ApplicationSetting, feature_category: :shared, type: :model do expect(setting.personal_access_tokens_disabled?).to eq(false) end end + + describe '#ai_access_token' do + context 'when `instance_level_code_suggestions_enabled` is true' do + before do + setting.instance_level_code_suggestions_enabled = true + end + + it { is_expected.not_to allow_value(nil).for(:ai_access_token) } + end + + context 'when `instance_level_code_suggestions_enabled` is false' do + before do + setting.instance_level_code_suggestions_enabled = false + end + + it { is_expected.to allow_value(nil).for(:ai_access_token) } + end + + it 'does not modify the token if it is unchanged in the form' do + setting.ai_access_token = 'foo' + setting.ai_access_token = ApplicationSettingMaskedAttrs::MASK + + expect(setting.ai_access_token).to eq('foo') + end + end end diff --git a/spec/requests/api/ml_model_packages_spec.rb b/spec/requests/api/ml_model_packages_spec.rb new file mode 100644 index 00000000000..9c19f522e46 --- /dev/null +++ b/spec/requests/api/ml_model_packages_spec.rb @@ -0,0 +1,200 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe ::API::MlModelPackages, feature_category: :mlops do + include HttpBasicAuthHelpers + include PackagesManagerApiSpecHelpers + include WorkhorseHelpers + using RSpec::Parameterized::TableSyntax + + include_context 'workhorse headers' + + let_it_be(:project, reload: true) { create(:project) } + let_it_be(:personal_access_token) { create(:personal_access_token) } + let_it_be(:job) { create(:ci_build, :running, user: personal_access_token.user, project: project) } + let_it_be(:deploy_token) { create(:deploy_token, read_package_registry: true, write_package_registry: true) } + let_it_be(:project_deploy_token) { create(:project_deploy_token, deploy_token: deploy_token, project: project) } + let_it_be(:another_project, reload: true) { create(:project) } + + let_it_be(:tokens) do + { + personal_access_token: personal_access_token.token, + deploy_token: deploy_token.token, + job_token: job.token + } + end + + let(:user) { personal_access_token.user } + let(:user_role) { :developer } + let(:member) { true } + let(:ci_build) { create(:ci_build, :running, user: user, project: project) } + let(:project_to_enable_ff) { project } + let(:headers) { {} } + + shared_context 'ml model authorize permissions table' do # rubocop:disable RSpec/ContextWording + # rubocop:disable Metrics/AbcSize + # :visibility, :user_role, :member, :token_type, :valid_token, :expected_status + def authorize_permissions_table + :public | :developer | true | :personal_access_token | true | :success + :public | :guest | true | :personal_access_token | true | :forbidden + :public | :developer | true | :personal_access_token | false | :unauthorized + :public | :guest | true | :personal_access_token | false | :unauthorized + :public | :developer | false | :personal_access_token | true | :forbidden + :public | :guest | false | :personal_access_token | true | :forbidden + :public | :developer | false | :personal_access_token | false | :unauthorized + :public | :guest | false | :personal_access_token | false | :unauthorized + :public | :anonymous | false | :personal_access_token | true | :unauthorized + :private | :developer | true | :personal_access_token | true | :success + :private | :guest | true | :personal_access_token | true | :forbidden + :private | :developer | true | :personal_access_token | false | :unauthorized + :private | :guest | true | :personal_access_token | false | :unauthorized + :private | :developer | false | :personal_access_token | true | :not_found + :private | :guest | false | :personal_access_token | true | :not_found + :private | :developer | false | :personal_access_token | false | :unauthorized + :private | :guest | false | :personal_access_token | false | :unauthorized + :private | :anonymous | false | :personal_access_token | true | :unauthorized + :public | :developer | true | :job_token | true | :success + :public | :guest | true | :job_token | true | :forbidden + :public | :developer | true | :job_token | false | :unauthorized + :public | :guest | true | :job_token | false | :unauthorized + :public | :developer | false | :job_token | true | :forbidden + :public | :guest | false | :job_token | true | :forbidden + :public | :developer | false | :job_token | false | :unauthorized + :public | :guest | false | :job_token | false | :unauthorized + :private | :developer | true | :job_token | true | :success + :private | :guest | true | :job_token | true | :forbidden + :private | :developer | true | :job_token | false | :unauthorized + :private | :guest | true | :job_token | false | :unauthorized + :private | :developer | false | :job_token | true | :not_found + :private | :guest | false | :job_token | true | :not_found + :private | :developer | false | :job_token | false | :unauthorized + :private | :guest | false | :job_token | false | :unauthorized + :public | :developer | true | :deploy_token | true | :success + :public | :developer | true | :deploy_token | false | :unauthorized + :private | :developer | true | :deploy_token | true | :success + :private | :developer | true | :deploy_token | false | :unauthorized + end + # rubocop:enable Metrics/AbcSize + end + + before do + project.send("add_#{user_role}", user) if member && user_role != :anonymous + end + + subject(:api_response) do + request + response + end + + describe 'PUT /api/v4/projects/:id/packages/ml_models/:package_name/:package_version/:file_name/authorize' do + include_context 'ml model authorize permissions table' + + let(:token) { tokens[:personal_access_token] } + let(:user_headers) { { 'HTTP_AUTHORIZATION' => token } } + let(:headers) { user_headers.merge(workhorse_headers) } + let(:request) { authorize_upload_file(headers) } + + describe 'user access' do + where(:visibility, :user_role, :member, :token_type, :valid_token, :expected_status) do + authorize_permissions_table + end + + with_them do + let(:token) { valid_token ? tokens[token_type] : 'invalid-token123' } + let(:user_headers) { user_role == :anonymous ? {} : { 'HTTP_AUTHORIZATION' => token } } + + before do + project.update_column(:visibility_level, Gitlab::VisibilityLevel.level_value(visibility.to_s)) + end + + it { is_expected.to have_gitlab_http_status(expected_status) } + end + + it_behaves_like 'Endpoint not found if read_model_registry not available' + end + + describe 'application security' do + where(:param_name, :param_value) do + :package_name | 'my-package/../' + :package_name | 'my-package%2f%2e%2e%2f' + :file_name | '../.ssh%2fauthorized_keys' + :file_name | '%2e%2e%2f.ssh%2fauthorized_keys' + end + + with_them do + let(:request) { authorize_upload_file(headers, param_name => param_value) } + + it 'rejects malicious request' do + is_expected.to have_gitlab_http_status(:bad_request) + end + end + end + end + + describe 'PUT /api/v4/projects/:id/packages/ml_models/:package_name/:package_version/:file_name' do + include_context 'ml model authorize permissions table' + + let_it_be(:file_name) { 'model.md5' } + + let(:token) { tokens[:personal_access_token] } + let(:user_headers) { { 'HTTP_AUTHORIZATION' => token } } + let(:headers) { user_headers.merge(workhorse_headers) } + let(:params) { { file: temp_file(file_name) } } + let(:file_key) { :file } + let(:send_rewritten_field) { true } + + let(:request) do + upload_file(headers) + end + + describe 'success' do + it 'creates a new package' do + expect { api_response }.to change { Packages::PackageFile.count }.by(1) + expect(api_response).to have_gitlab_http_status(:created) + end + end + + describe 'user access' do + where(:visibility, :user_role, :member, :token_type, :valid_token, :expected_status) do + authorize_permissions_table + end + + with_them do + let(:token) { valid_token ? tokens[token_type] : 'invalid-token123' } + let(:user_headers) { user_role == :anonymous ? {} : { 'HTTP_AUTHORIZATION' => token } } + + before do + project.update_column(:visibility_level, Gitlab::VisibilityLevel.level_value(visibility.to_s)) + end + + if params[:expected_status] == :success + it_behaves_like 'process ml model package upload' + else + it { is_expected.to have_gitlab_http_status(expected_status) } + end + end + + it_behaves_like 'Endpoint not found if read_model_registry not available' + end + end + + def authorize_upload_file(request_headers, package_name: 'mypackage', file_name: 'myfile.tar.gz') + url = "/projects/#{project.id}/packages/ml_models/#{package_name}/0.0.1/#{file_name}/authorize" + + put api(url), headers: request_headers + end + + def upload_file(request_headers, package_name: 'mypackage') + url = "/projects/#{project.id}/packages/ml_models/#{package_name}/0.0.1/#{file_name}" + + workhorse_finalize( + api(url), + method: :put, + file_key: file_key, + params: params, + headers: request_headers, + send_rewritten_field: send_rewritten_field + ) + end +end diff --git a/spec/services/merge_requests/mergeability/logger_spec.rb b/spec/services/merge_requests/mergeability/logger_spec.rb index 1f56b6bebdb..7863b69abf6 100644 --- a/spec/services/merge_requests/mergeability/logger_spec.rb +++ b/spec/services/merge_requests/mergeability/logger_spec.rb @@ -40,6 +40,37 @@ RSpec.describe MergeRequests::Mergeability::Logger, :request_store, feature_cate logger.commit end + context 'when block value responds to #success?' do + let(:success?) { true } + let(:check_result) { instance_double(Gitlab::MergeRequests::Mergeability::CheckResult, success?: success?) } + + let(:extra_data) do + { + 'mergeability.expensive_operation.successful.values' => [success?] + } + end + + shared_examples_for 'success state logger' do + it 'records operation success state' do + expect_next_instance_of(Gitlab::AppJsonLogger) do |app_logger| + expect(app_logger).to receive(:info).with(match(a_hash_including(loggable_data(**extra_data)))) + end + + expect(logger.instrument(mergeability_name: :expensive_operation) { check_result }).to eq(check_result) + + logger.commit + end + end + + it_behaves_like 'success state logger' + + context 'when not successful' do + let(:success?) { false } + + it_behaves_like 'success state logger' + end + end + context 'with multiple observations' do let(:operation_count) { 2 } diff --git a/spec/support/shared_examples/requests/api/ml_model_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/ml_model_packages_shared_examples.rb new file mode 100644 index 00000000000..81ff004779a --- /dev/null +++ b/spec/support/shared_examples/requests/api/ml_model_packages_shared_examples.rb @@ -0,0 +1,108 @@ +# frozen_string_literal: true + +RSpec.shared_examples 'Endpoint not found if read_model_registry not available' do + context 'when read_model_registry disabled for current project' do + before do + allow(Ability).to receive(:allowed?).and_call_original + allow(Ability).to receive(:allowed?) + .with(user, :read_model_registry, project) + .and_return(false) + end + + it "is not found" do + is_expected.to have_gitlab_http_status(:not_found) + end + end +end + +RSpec.shared_examples 'creates model experiments package files' do + it 'creates package files', :aggregate_failures do + expect { api_response } + .to change { project.packages.count }.by(1) + .and change { Packages::PackageFile.count }.by(1) + expect(api_response).to have_gitlab_http_status(:created) + + package_file = project.packages.last.package_files.reload.last + expect(package_file.file_name).to eq(file_name) + end + + it 'returns bad request if package creation fails' do + allow_next_instance_of(::Packages::MlModel::CreatePackageFileService) do |instance| + expect(instance).to receive(:execute).and_return(nil) + end + + expect(api_response).to have_gitlab_http_status(:bad_request) + end + + context 'when file is too large' do + it 'is bad request', :aggregate_failures do + allow_next_instance_of(UploadedFile) do |uploaded_file| + allow(uploaded_file).to receive(:size).and_return(project.actual_limits.ml_model_max_file_size + 1) + end + + expect(api_response).to have_gitlab_http_status(:bad_request) + end + end +end + +RSpec.shared_examples 'process ml model package upload' do + context 'with object storage disabled' do + before do + stub_package_file_object_storage(enabled: false) + end + + context 'without a file from workhorse' do + let(:send_rewritten_field) { false } + + it_behaves_like 'returning response status', :bad_request + end + + context 'with correct params' do + it_behaves_like 'package workhorse uploads' + it_behaves_like 'creates model experiments package files' + # To be reactivated with https://gitlab.com/gitlab-org/gitlab/-/issues/414270 + # it_behaves_like 'a package tracking event', '::API::MlModelPackages', 'push_package' + end + end + + context 'with object storage enabled' do + let(:tmp_object) do + fog_connection.directories.new(key: 'packages').files.create( # rubocop:disable Rails/SaveBang + key: "tmp/uploads/#{file_name}", + body: 'content' + ) + end + + let(:fog_file) { fog_to_uploaded_file(tmp_object) } + let(:params) { { file: fog_file, 'file.remote_id' => file_name } } + + context 'and direct upload enabled' do + let(:fog_connection) do + stub_package_file_object_storage(direct_upload: true) + end + + it_behaves_like 'creates model experiments package files' + + ['123123', '../../123123'].each do |remote_id| + context "with invalid remote_id: #{remote_id}" do + let(:params) do + { + file: fog_file, + 'file.remote_id' => remote_id + } + end + + it { is_expected.to have_gitlab_http_status(:forbidden) } + end + end + end + + context 'and direct upload disabled' do + let(:fog_connection) do + stub_package_file_object_storage(direct_upload: false) + end + + it_behaves_like 'creates model experiments package files' + end + end +end diff --git a/spec/views/admin/application_settings/_ai_access.html.haml_spec.rb b/spec/views/admin/application_settings/_ai_access.html.haml_spec.rb new file mode 100644 index 00000000000..e9e640f7cc6 --- /dev/null +++ b/spec/views/admin/application_settings/_ai_access.html.haml_spec.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'admin/application_settings/_ai_access.html.haml', feature_category: :code_suggestions do + let_it_be(:admin) { build_stubbed(:admin) } + let(:page) { Capybara::Node::Simple.new(rendered) } + + before do + allow(::Gitlab).to receive(:org_or_com?).and_return(false) # Will not render partial for .com or .org + assign(:application_setting, application_setting) + allow(view).to receive(:current_user) { admin } + allow(view).to receive(:expanded).and_return(true) + end + + context 'when ai_access_token is not set' do + let(:application_setting) { build(:application_setting) } + + it 'renders an empty password field' do + render + expect(rendered).to have_field('Personal access token', type: 'password') + expect(page.find_field('Personal access token').value).to be_blank + end + end + + context 'when ai_access_token is set' do + let(:application_setting) do + build(:application_setting, ai_access_token: 'ai_access_token', + instance_level_code_suggestions_enabled: true) + end + + it 'renders masked password field' do + render + expect(rendered).to have_field('Enter new personal access token', type: 'password') + expect(page.find_field('Enter new personal access token').value).to eq(ApplicationSettingMaskedAttrs::MASK) + end + end +end diff --git a/spec/views/admin/application_settings/general.html.haml_spec.rb b/spec/views/admin/application_settings/general.html.haml_spec.rb index dd49de8f880..861f3fffa83 100644 --- a/spec/views/admin/application_settings/general.html.haml_spec.rb +++ b/spec/views/admin/application_settings/general.html.haml_spec.rb @@ -110,4 +110,30 @@ RSpec.describe 'admin/application_settings/general.html.haml' do end end end + + describe 'instance-level code suggestions settings', feature_category: :code_suggestions do + before do + allow(::Gitlab).to receive(:org_or_com?).and_return(gitlab_org_or_com?) + + render + end + + context 'when on .com or .org' do + let(:gitlab_org_or_com?) { true } + + it 'does not render the form' do + expect(rendered).not_to have_field('application_setting_instance_level_code_suggestions_enabled') + expect(rendered).not_to have_field('application_setting_ai_access_token') + end + end + + context 'when not on .com and not on .org' do + let(:gitlab_org_or_com?) { false } + + it 'renders the form' do + expect(rendered).to have_field('application_setting_instance_level_code_suggestions_enabled') + expect(rendered).to have_field('application_setting_ai_access_token') + end + end + end end diff --git a/workhorse/internal/upstream/routes.go b/workhorse/internal/upstream/routes.go index c5af9dc3f00..dd1725a723e 100644 --- a/workhorse/internal/upstream/routes.go +++ b/workhorse/internal/upstream/routes.go @@ -279,6 +279,9 @@ func configureRoutes(u *upstream) { // Generic Packages Repository u.route("PUT", apiProjectPattern+`/packages/generic/`, requestBodyUploader), + // Ml Model Packages Repository + u.route("PUT", apiProjectPattern+`/packages/ml_models/`, requestBodyUploader), + // NuGet Artifact Repository u.route("PUT", apiProjectPattern+`/packages/nuget/`, mimeMultipartUploader), diff --git a/workhorse/upload_test.go b/workhorse/upload_test.go index c05af731797..8effe291979 100644 --- a/workhorse/upload_test.go +++ b/workhorse/upload_test.go @@ -580,6 +580,7 @@ func TestPackageFilesUpload(t *testing.T) { {"PUT", "/api/v4/projects/group%2Fproject/packages/conan/v1/files"}, {"PUT", "/api/v4/projects/group%2Fproject/packages/maven/v1/files"}, {"PUT", "/api/v4/projects/group%2Fproject/packages/generic/mypackage/0.0.1/myfile.tar.gz"}, + {"PUT", "/api/v4/projects/group%2Fproject/packages/ml_models/mymodel/0.0.1/myfile.tar.gz"}, {"PUT", "/api/v4/projects/group%2Fproject/packages/debian/libsample0_1.2.3~alpha2-1_amd64.deb"}, {"POST", "/api/v4/projects/group%2Fproject/packages/rubygems/api/v1/gems/sample.gem"}, {"POST", "/api/v4/projects/group%2Fproject/packages/rpm/sample-4.23.fc21.x86_64.rpm"}, |