Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
		
							parent
							
								
									f1ce233e6a
								
							
						
					
					
						commit
						bb0d99269b
					
				|  | @ -1103,3 +1103,10 @@ RSpec/UselessDynamicDefinition: | |||
|   Exclude: | ||||
|     - 'spec/factories/**/*' | ||||
|     - 'ee/spec/factories/**/*' | ||||
| 
 | ||||
| Database/AvoidUsingPluckWithoutLimit: | ||||
|   Enabled: true | ||||
|   Exclude: | ||||
|     - 'spec/**/*.rb' | ||||
|     - 'ee/spec/**/*.rb' | ||||
|     - 'qa/qa/specs/**/*.rb' | ||||
|  |  | |||
|  | @ -0,0 +1,167 @@ | |||
| --- | ||||
| Database/AvoidUsingPluckWithoutLimit: | ||||
|   Details: grace period | ||||
|   Exclude: | ||||
|     - app/finders/groups/projects_requiring_authorizations_refresh/base.rb | ||||
|     - app/finders/issuables/label_filter.rb | ||||
|     - app/finders/projects/members/effective_access_level_finder.rb | ||||
|     - app/models/application_record.rb | ||||
|     - app/models/ci/build.rb | ||||
|     - app/models/ci/build_trace_chunks/fog.rb | ||||
|     - app/models/ci/build_trace_chunks/redis_base.rb | ||||
|     - app/models/ci/group_variable.rb | ||||
|     - app/models/ci/job_artifact.rb | ||||
|     - app/models/ci/job_token/allowlist.rb | ||||
|     - app/models/ci/pipeline.rb | ||||
|     - app/models/ci/runner.rb | ||||
|     - app/models/ci/runner_manager_build.rb | ||||
|     - app/models/commit_collection.rb | ||||
|     - app/models/commit_signatures/gpg_signature.rb | ||||
|     - app/models/commit_status.rb | ||||
|     - app/models/concerns/cascading_namespace_setting_attribute.rb | ||||
|     - app/models/concerns/commit_signature.rb | ||||
|     - app/models/concerns/integrations/reset_secret_fields.rb | ||||
|     - app/models/concerns/issuable.rb | ||||
|     - app/models/concerns/packages/debian/distribution.rb | ||||
|     - app/models/concerns/subquery.rb | ||||
|     - app/models/concerns/taggable_queries.rb | ||||
|     - app/models/customer_relations/contact.rb | ||||
|     - app/models/customer_relations/issue_contact.rb | ||||
|     - app/models/environment.rb | ||||
|     - app/models/event_collection.rb | ||||
|     - app/models/group.rb | ||||
|     - app/models/incident_management/timeline_event_tag.rb | ||||
|     - app/models/integration.rb | ||||
|     - app/models/integrations/base_chat_notification.rb | ||||
|     - app/models/integrations/slack_workspace/api_scope.rb | ||||
|     - app/models/integrations/slack_workspace/integration_api_scope.rb | ||||
|     - app/models/issue.rb | ||||
|     - app/models/label.rb | ||||
|     - app/models/loose_foreign_keys/deleted_record.rb | ||||
|     - app/models/member.rb | ||||
|     - app/models/members/last_group_owner_assigner.rb | ||||
|     - app/models/merge_request.rb | ||||
|     - app/models/merge_request_diff.rb | ||||
|     - app/models/merge_requests_closing_issues.rb | ||||
|     - app/models/namespace.rb | ||||
|     - app/models/namespaces/traversal/linear.rb | ||||
|     - app/models/namespaces/traversal/recursive.rb | ||||
|     - app/models/note.rb | ||||
|     - app/models/packages/build_info.rb | ||||
|     - app/models/packages/dependency.rb | ||||
|     - app/models/packages/maven/metadatum.rb | ||||
|     - app/models/packages/package.rb | ||||
|     - app/models/pages_deployment.rb | ||||
|     - app/models/postgresql/replication_slot.rb | ||||
|     - app/models/project.rb | ||||
|     - app/models/project_authorizations/changes.rb | ||||
|     - app/models/project_team.rb | ||||
|     - app/models/prometheus_alert.rb | ||||
|     - app/models/protected_branch.rb | ||||
|     - app/models/remote_mirror.rb | ||||
|     - app/models/shard.rb | ||||
|     - app/models/slack_integration.rb | ||||
|     - app/models/todo.rb | ||||
|     - app/models/uploads/fog.rb | ||||
|     - app/models/user.rb | ||||
|     - app/models/users/group_visit.rb | ||||
|     - app/models/users/project_visit.rb | ||||
|     - app/models/x509_certificate.rb | ||||
|     - app/services/authorized_project_update/project_recalculate_service.rb | ||||
|     - app/services/boards/base_items_list_service.rb | ||||
|     - app/services/branches/delete_merged_service.rb | ||||
|     - app/services/bulk_imports/batched_relation_export_service.rb | ||||
|     - app/services/ci/job_artifacts/bulk_delete_by_project_service.rb | ||||
|     - app/services/ci/pipeline_creation/cancel_redundant_pipelines_service.rb | ||||
|     - app/services/ci/pipeline_processing/atomic_processing_service/status_collection.rb | ||||
|     - app/services/ci/queue/pending_builds_strategy.rb | ||||
|     - app/services/ci/refs/enqueue_pipelines_to_unlock_service.rb | ||||
|     - app/services/ci/runners/reconcile_existing_runner_versions_service.rb | ||||
|     - app/services/ci/unlock_pipeline_service.rb | ||||
|     - app/services/groups/autocomplete_service.rb | ||||
|     - app/services/groups/destroy_service.rb | ||||
|     - app/services/groups/transfer_service.rb | ||||
|     - app/services/issues/referenced_merge_requests_service.rb | ||||
|     - app/services/labels/available_labels_service.rb | ||||
|     - app/services/labels/promote_service.rb | ||||
|     - app/services/labels/transfer_service.rb | ||||
|     - app/services/merge_requests/push_options_handler_service.rb | ||||
|     - app/services/merge_requests/pushed_branches_service.rb | ||||
|     - app/services/packages/cleanup/execute_policy_service.rb | ||||
|     - app/services/projects/slack_application_install_service.rb | ||||
|     - app/services/projects/unlink_fork_service.rb | ||||
|     - ee/app/finders/ee/issuables/label_filter.rb | ||||
|     - ee/app/finders/ee/merge_requests_finder.rb | ||||
|     - ee/app/finders/groups_with_templates_finder.rb | ||||
|     - ee/app/finders/namespaces/billed_users_finder.rb | ||||
|     - ee/app/finders/namespaces/free_user_cap/users_finder.rb | ||||
|     - ee/app/finders/namespaces/free_user_cap/users_without_added_members_finder.rb | ||||
|     - ee/app/models/ai/ai_resource/concerns/noteable.rb | ||||
|     - ee/app/models/allowed_email_domain.rb | ||||
|     - ee/app/models/analytics/issues_analytics.rb | ||||
|     - ee/app/models/approval_wrapped_rule.rb | ||||
|     - ee/app/models/concerns/audit_events/streaming/streamable_event_type_filter.rb | ||||
|     - ee/app/models/concerns/ee/ci/artifactable.rb | ||||
|     - ee/app/models/concerns/ee/issuable_link.rb | ||||
|     - ee/app/models/concerns/elasticsearch_indexed_container.rb | ||||
|     - ee/app/models/concerns/geo/verifiable_model.rb | ||||
|     - ee/app/models/dast_scanner_profile.rb | ||||
|     - ee/app/models/dast_site_profile.rb | ||||
|     - ee/app/models/ee/ci/daily_build_group_report_result.rb | ||||
|     - ee/app/models/ee/ci/pipeline.rb | ||||
|     - ee/app/models/ee/design_management/repository.rb | ||||
|     - ee/app/models/ee/epic.rb | ||||
|     - ee/app/models/ee/group.rb | ||||
|     - ee/app/models/ee/group_group_link.rb | ||||
|     - ee/app/models/ee/group_member.rb | ||||
|     - ee/app/models/ee/label.rb | ||||
|     - ee/app/models/ee/merge_request.rb | ||||
|     - ee/app/models/ee/personal_access_token.rb | ||||
|     - ee/app/models/ee/project.rb | ||||
|     - ee/app/models/ee/project_authorization.rb | ||||
|     - ee/app/models/ee/project_group_link.rb | ||||
|     - ee/app/models/ee/projects/wiki_repository.rb | ||||
|     - ee/app/models/ee/uploads/local.rb | ||||
|     - ee/app/models/ee/user.rb | ||||
|     - ee/app/models/embedding/application_record.rb | ||||
|     - ee/app/models/geo/base_registry.rb | ||||
|     - ee/app/models/geo/container_repository_registry.rb | ||||
|     - ee/app/models/geo/lfs_object_registry.rb | ||||
|     - ee/app/models/geo/tracking_base.rb | ||||
|     - ee/app/models/geo/upload_registry.rb | ||||
|     - ee/app/models/gitlab_subscriptions/user_add_on_assignment.rb | ||||
|     - ee/app/models/incident_management/oncall_rotation.rb | ||||
|     - ee/app/models/instance_security_dashboard.rb | ||||
|     - ee/app/models/issuables_analytics.rb | ||||
|     - ee/app/models/iteration.rb | ||||
|     - ee/app/models/merge_requests/external_status_check.rb | ||||
|     - ee/app/models/protected_environment.rb | ||||
|     - ee/app/models/security/finding.rb | ||||
|     - ee/app/models/security/scan.rb | ||||
|     - ee/app/models/security/scan_result_policy_violation.rb | ||||
|     - ee/app/models/software_license.rb | ||||
|     - ee/app/models/vulnerabilities/finding.rb | ||||
|     - ee/app/models/vulnerabilities/read.rb | ||||
|     - ee/app/services/analytics/cycle_analytics/consistency_check_service.rb | ||||
|     - ee/app/services/analytics/cycle_analytics/data_loader_service.rb | ||||
|     - ee/app/services/approval_rules/params_filtering_service.rb | ||||
|     - ee/app/services/click_house/data_ingestion/ci_finished_builds_sync_service.rb | ||||
|     - ee/app/services/ee/groups/destroy_service.rb | ||||
|     - ee/app/services/ee/search/global_service.rb | ||||
|     - ee/app/services/epics/update_dates_service.rb | ||||
|     - ee/app/services/gitlab_subscriptions/preview_billable_user_change_service.rb | ||||
|     - ee/app/services/security/merge_request_security_report_generation_service.rb | ||||
|     - ee/app/services/security/scan_result_policies/sync_any_merge_request_rules_service.rb | ||||
|     - ee/app/services/security/scan_result_policies/update_approvals_service.rb | ||||
|     - ee/app/services/security/security_orchestration_policies/default_branch_updation_check_service.rb | ||||
|     - ee/app/services/security/security_orchestration_policies/fetch_policy_approvers_service.rb | ||||
|     - ee/app/services/security/security_orchestration_policies/policy_branches_service.rb | ||||
|     - ee/app/services/security/security_orchestration_policies/process_scan_result_policy_service.rb | ||||
|     - ee/app/services/security/security_orchestration_policies/protected_branches_deletion_check_service.rb | ||||
|     - ee/app/services/security/security_orchestration_policies/protected_branches_push_service.rb | ||||
|     - ee/app/services/security/security_orchestration_policies/rule_schedule_service.rb | ||||
|     - ee/app/services/security/security_orchestration_policies/validate_policy_service.rb | ||||
|     - ee/app/services/security/sync_license_scanning_rules_service.rb | ||||
|     - ee/app/services/vulnerabilities/bulk_dismiss_service.rb | ||||
|     - ee/app/services/vulnerability_exports/export_service.rb | ||||
|     - ee/lib/ee/gitlab/background_migration/migrate_approver_to_approval_rules.rb | ||||
|  | @ -2281,7 +2281,6 @@ Layout/LineLength: | |||
|     - 'ee/spec/views/admin/dashboard/index.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/compliance_management/compliance_framework/_project_settings.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/groups/security/discover/show.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/layouts/header/_current_user_dropdown.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/operations/environments.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/projects/security/discover/show.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/shared/_mirror_status.html.haml_spec.rb' | ||||
|  |  | |||
|  | @ -205,7 +205,6 @@ RSpec/FactoryBot/AvoidCreate: | |||
|     - 'ee/spec/views/groups/settings/reporting/show.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/layouts/application.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/layouts/group.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/layouts/header/_current_user_dropdown.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/layouts/header/_read_only_banner.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/layouts/project.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/projects/edit.html.haml_spec.rb' | ||||
|  |  | |||
|  | @ -1445,7 +1445,6 @@ RSpec/FeatureCategory: | |||
|     - 'ee/spec/views/groups/settings/reporting/show.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/layouts/application.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/layouts/checkout.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/layouts/header/_current_user_dropdown.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/layouts/header/_ee_subscribable_banner.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/layouts/header/_read_only_banner.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/operations/environments.html.haml_spec.rb' | ||||
|  | @ -4061,8 +4060,6 @@ RSpec/FeatureCategory: | |||
|     - 'spec/lib/gitlab/usage/metrics/instrumentations/gitlab_for_jira_app_direct_installations_count_metric_spec.rb' | ||||
|     - 'spec/lib/gitlab/usage/metrics/instrumentations/gitlab_for_jira_app_proxy_installations_count_metric_spec.rb' | ||||
|     - 'spec/lib/gitlab/usage/metrics/instrumentations/hostname_metric_spec.rb' | ||||
|     - 'spec/lib/gitlab/usage/metrics/instrumentations/in_product_marketing_email_cta_clicked_metric_spec.rb' | ||||
|     - 'spec/lib/gitlab/usage/metrics/instrumentations/in_product_marketing_email_sent_metric_spec.rb' | ||||
|     - 'spec/lib/gitlab/usage/metrics/instrumentations/jira_imports_total_imported_issues_count_metric_spec.rb' | ||||
|     - 'spec/lib/gitlab/usage/metrics/instrumentations/merge_request_widget_extension_metric_spec.rb' | ||||
|     - 'spec/lib/gitlab/usage/metrics/instrumentations/numbers_metric_spec.rb' | ||||
|  |  | |||
|  | @ -1188,7 +1188,6 @@ RSpec/NamedSubject: | |||
|     - 'ee/spec/validators/user_existence_validator_spec.rb' | ||||
|     - 'ee/spec/validators/user_id_existence_validator_spec.rb' | ||||
|     - 'ee/spec/views/devise/registrations/new.html.haml_spec.rb' | ||||
|     - 'ee/spec/views/layouts/header/_current_user_dropdown.html.haml_spec.rb' | ||||
|     - 'ee/spec/workers/active_user_count_threshold_worker_spec.rb' | ||||
|     - 'ee/spec/workers/admin_emails_worker_spec.rb' | ||||
|     - 'ee/spec/workers/app_sec/dast/profile_schedule_worker_spec.rb' | ||||
|  | @ -3528,7 +3527,6 @@ RSpec/NamedSubject: | |||
|     - 'spec/services/users/activity_service_spec.rb' | ||||
|     - 'spec/services/users/approve_service_spec.rb' | ||||
|     - 'spec/services/users/assigned_issues_count_service_spec.rb' | ||||
|     - 'spec/services/users/in_product_marketing_email_records_spec.rb' | ||||
|     - 'spec/services/users/keys_count_service_spec.rb' | ||||
|     - 'spec/services/users/reject_service_spec.rb' | ||||
|     - 'spec/services/users/saved_replies/create_service_spec.rb' | ||||
|  |  | |||
|  | @ -30,8 +30,8 @@ export const initHomeOrganizationSetting = () => { | |||
|           block: true, | ||||
|           label: s__('Organization|Home organization'), | ||||
|           description: s__('Organization|Choose what organization you want to see by default.'), | ||||
|           inputName: 'home_organization', | ||||
|           inputId: 'home_organization', | ||||
|           inputName: 'user[home_organization_id]', | ||||
|           inputId: 'user_home_organization_id', | ||||
|           initialSelection, | ||||
|           toggleClass: 'gl-form-input-xl', | ||||
|         }, | ||||
|  |  | |||
|  | @ -22,12 +22,17 @@ export default class PersistentUserCallout { | |||
| 
 | ||||
|   init() { | ||||
|     const followLink = this.container.querySelector('.js-follow-link'); | ||||
|     const closeAndFollowLink = this.container.querySelector('.js-close-and-follow-link'); | ||||
| 
 | ||||
|     if (this.closeButtons.length) { | ||||
|       this.handleCloseButtonCallout(); | ||||
|     } else if (followLink) { | ||||
|       this.handleFollowLinkCallout(followLink); | ||||
|     } | ||||
| 
 | ||||
|     if (closeAndFollowLink) { | ||||
|       this.handleFollowLinkCallout(closeAndFollowLink); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   handleCloseButtonCallout() { | ||||
|  |  | |||
|  | @ -4,7 +4,6 @@ const PERSISTENT_USER_CALLOUTS = [ | |||
|   '.js-recovery-settings-callout', | ||||
|   '.js-users-over-license-callout', | ||||
|   '.js-admin-licensed-user-count-threshold', | ||||
|   '.js-buy-pipeline-minutes-notification-callout', | ||||
|   '.js-token-expiry-callout', | ||||
|   '.js-registration-enabled-callout', | ||||
|   '.js-new-user-signups-cap-reached', | ||||
|  |  | |||
|  | @ -53,6 +53,9 @@ export default { | |||
|     projectKey: { | ||||
|       default: '', | ||||
|     }, | ||||
|     reopenIssueOnExternalParticipantNote: { | ||||
|       default: false, | ||||
|     }, | ||||
|     addExternalParticipantsFromCc: { | ||||
|       default: false, | ||||
|     }, | ||||
|  | @ -115,6 +118,7 @@ export default { | |||
|       fileTemplateProjectId, | ||||
|       outgoingName, | ||||
|       projectKey, | ||||
|       reopenIssueOnExternalParticipantNote, | ||||
|       addExternalParticipantsFromCc, | ||||
|     }) { | ||||
|       this.isTemplateSaving = true; | ||||
|  | @ -123,6 +127,7 @@ export default { | |||
|         issue_template_key: selectedTemplate, | ||||
|         outgoing_name: outgoingName, | ||||
|         project_key: projectKey, | ||||
|         reopen_issue_on_external_participant_note: reopenIssueOnExternalParticipantNote, | ||||
|         add_external_participants_from_cc: addExternalParticipantsFromCc, | ||||
|         service_desk_enabled: this.isEnabled, | ||||
|         file_template_project_id: fileTemplateProjectId, | ||||
|  | @ -195,6 +200,7 @@ export default { | |||
|       :initial-selected-file-template-project-id="selectedFileTemplateProjectId" | ||||
|       :initial-outgoing-name="outgoingName" | ||||
|       :initial-project-key="projectKey" | ||||
|       :initial-reopen-issue-on-external-participant-note="reopenIssueOnExternalParticipantNote" | ||||
|       :initial-add-external-participants-from-cc="addExternalParticipantsFromCc" | ||||
|       :templates="templates" | ||||
|       :is-template-saving="isTemplateSaving" | ||||
|  |  | |||
|  | @ -23,6 +23,12 @@ export default { | |||
|     issueTrackerEnableMessage: __( | ||||
|       'To use Service Desk in this project, you must %{linkStart}activate the issue tracker%{linkEnd}.', | ||||
|     ), | ||||
|     reopenIssueOnExternalParticipantNote: { | ||||
|       label: s__('ServiceDesk|Reopen issues when an external participant comments'), | ||||
|       help: s__( | ||||
|         'ServiceDesk|This also adds an internal comment that mentions the assignees of the issue.', | ||||
|       ), | ||||
|     }, | ||||
|     addExternalParticipantsFromCc: { | ||||
|       label: s__('ServiceDesk|Add external participants from the %{codeStart}Cc%{codeEnd} header'), | ||||
|       help: s__( | ||||
|  | @ -91,6 +97,11 @@ export default { | |||
|       required: false, | ||||
|       default: '', | ||||
|     }, | ||||
|     initialReopenIssueOnExternalParticipantNote: { | ||||
|       type: Boolean, | ||||
|       required: false, | ||||
|       default: false, | ||||
|     }, | ||||
|     initialAddExternalParticipantsFromCc: { | ||||
|       type: Boolean, | ||||
|       required: false, | ||||
|  | @ -113,6 +124,7 @@ export default { | |||
|       selectedFileTemplateProjectId: this.initialSelectedFileTemplateProjectId, | ||||
|       outgoingName: this.initialOutgoingName || __('GitLab Support Bot'), | ||||
|       projectKey: this.initialProjectKey, | ||||
|       reopenIssueOnExternalParticipantNote: this.initialReopenIssueOnExternalParticipantNote, | ||||
|       addExternalParticipantsFromCc: this.initialAddExternalParticipantsFromCc, | ||||
|       searchTerm: '', | ||||
|       projectKeyError: null, | ||||
|  | @ -156,6 +168,7 @@ export default { | |||
|         selectedTemplate: this.selectedTemplate, | ||||
|         outgoingName: this.outgoingName, | ||||
|         projectKey: this.projectKey, | ||||
|         reopenIssueOnExternalParticipantNote: this.reopenIssueOnExternalParticipantNote, | ||||
|         addExternalParticipantsFromCc: this.addExternalParticipantsFromCc, | ||||
|         fileTemplateProjectId: this.selectedFileTemplateProjectId, | ||||
|       }); | ||||
|  | @ -322,10 +335,23 @@ export default { | |||
|           </template> | ||||
|         </gl-form-group> | ||||
| 
 | ||||
|         <gl-form-checkbox | ||||
|           v-model="reopenIssueOnExternalParticipantNote" | ||||
|           :disabled="!isIssueTrackerEnabled" | ||||
|           data-testid="reopen-issue-on-external-participant-note" | ||||
|         > | ||||
|           {{ $options.i18n.reopenIssueOnExternalParticipantNote.label }} | ||||
| 
 | ||||
|           <template #help> | ||||
|             {{ $options.i18n.reopenIssueOnExternalParticipantNote.help }} | ||||
|           </template> | ||||
|         </gl-form-checkbox> | ||||
| 
 | ||||
|         <gl-form-checkbox | ||||
|           v-if="showAddExternalParticipantsFromCC" | ||||
|           v-model="addExternalParticipantsFromCc" | ||||
|           :disabled="!isIssueTrackerEnabled" | ||||
|           data-testid="add-external-participants-from-cc" | ||||
|         > | ||||
|           <gl-sprintf :message="$options.i18n.addExternalParticipantsFromCc.label"> | ||||
|             <template #code="{ content }"> | ||||
|  |  | |||
|  | @ -21,6 +21,7 @@ export default () => { | |||
|     incomingEmail, | ||||
|     outgoingName, | ||||
|     projectKey, | ||||
|     reopenIssueOnExternalParticipantNote, | ||||
|     addExternalParticipantsFromCc, | ||||
|     selectedTemplate, | ||||
|     selectedFileTemplateProjectId, | ||||
|  | @ -40,6 +41,7 @@ export default () => { | |||
|       isIssueTrackerEnabled: parseBoolean(issueTrackerEnabled), | ||||
|       outgoingName, | ||||
|       projectKey, | ||||
|       reopenIssueOnExternalParticipantNote: parseBoolean(reopenIssueOnExternalParticipantNote), | ||||
|       addExternalParticipantsFromCc: parseBoolean(addExternalParticipantsFromCc), | ||||
|       selectedTemplate, | ||||
|       selectedFileTemplateProjectId: parseInt(selectedFileTemplateProjectId, 10) || null, | ||||
|  |  | |||
|  | @ -31,13 +31,14 @@ const fetchData = (projectPath, path, ref, offset, refType) => { | |||
| 
 | ||||
|   fetchedBatches.push(offset); | ||||
| 
 | ||||
|   const encodePathFunc = gon.features.encodingLogsTree ? encodeURI : encodeURIComponent; | ||||
|   const url = joinPaths( | ||||
|     gon.relative_url_root || '/', | ||||
|     projectPath, | ||||
|     '/-/refs/', | ||||
|     encodeURIComponent(ref), | ||||
|     encodePathFunc(ref), | ||||
|     '/logs_tree/', | ||||
|     encodeURIComponent(removeLeadingSlash(path)), | ||||
|     encodePathFunc(removeLeadingSlash(path)), | ||||
|   ); | ||||
| 
 | ||||
|   return axios | ||||
|  |  | |||
|  | @ -1,30 +1,41 @@ | |||
| import setHighlightClass from 'ee_else_ce/search/highlight_blob_search_result'; | ||||
| import { queryToObject } from '~/lib/utils/url_utility'; | ||||
| import syntaxHighlight from '~/syntax_highlight'; | ||||
| import { initSidebar, sidebarInitState } from './sidebar'; | ||||
| import { initSidebar } from './sidebar'; | ||||
| import { initSearchSort } from './sort'; | ||||
| import createStore from './store'; | ||||
| import { initTopbar } from './topbar'; | ||||
| import { initBlobRefSwitcher } from './under_topbar'; | ||||
| 
 | ||||
| const topbarInitState = () => { | ||||
|   const el = document.getElementById('js-search-topbar'); | ||||
| const sidebarInitState = () => { | ||||
|   const el = document.getElementById('js-search-sidebar'); | ||||
|   if (!el) return {}; | ||||
|   const { defaultBranchName } = el.dataset; | ||||
|   return { defaultBranchName }; | ||||
| 
 | ||||
|   const { navigationJson, searchType, groupInitialJson, projectInitialJson } = el.dataset; | ||||
| 
 | ||||
|   const navigationJsonParsed = JSON.parse(navigationJson); | ||||
|   const groupInitialJsonParsed = JSON.parse(groupInitialJson); | ||||
|   const projectInitialJsonParsed = JSON.parse(projectInitialJson); | ||||
| 
 | ||||
|   return { navigationJsonParsed, searchType, groupInitialJsonParsed, projectInitialJsonParsed }; | ||||
| }; | ||||
| 
 | ||||
| export const initSearchApp = () => { | ||||
|   syntaxHighlight(document.querySelectorAll('.js-search-results')); | ||||
|   const query = queryToObject(window.location.search, { gatherArrays: true }); | ||||
|   const { navigationJsonParsed: navigation, searchType } = sidebarInitState() || {}; | ||||
|   const { defaultBranchName } = topbarInitState() || {}; | ||||
|   const { | ||||
|     navigationJsonParsed: navigation, | ||||
|     searchType, | ||||
|     groupInitialJsonParsed: groupInitialJson, | ||||
|     projectInitialJsonParsed: projectInitialJson, | ||||
|   } = sidebarInitState() || {}; | ||||
| 
 | ||||
|   const store = createStore({ | ||||
|     query, | ||||
|     navigation, | ||||
|     searchType, | ||||
|     defaultBranchName, | ||||
|     groupInitialJson, | ||||
|     projectInitialJson, | ||||
|   }); | ||||
| 
 | ||||
|   initTopbar(store); | ||||
|  |  | |||
|  | @ -0,0 +1,19 @@ | |||
| <script> | ||||
| import GroupFilter from './group_filter.vue'; | ||||
| import ProjectFilter from './project_filter.vue'; | ||||
| 
 | ||||
| export default { | ||||
|   name: 'AllScopesStartFilters', | ||||
|   components: { | ||||
|     GroupFilter, | ||||
|     ProjectFilter, | ||||
|   }, | ||||
| }; | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|   <div class="gl-px-5 gl-pt-6"> | ||||
|     <group-filter class="gl-mb-5" /> | ||||
|     <project-filter class="gl-mb-5" /> | ||||
|   </div> | ||||
| </template> | ||||
|  | @ -25,6 +25,7 @@ import NotesFilters from './notes_filters.vue'; | |||
| import CommitsFilters from './commits_filters.vue'; | ||||
| import MilestonesFilters from './milestones_filters.vue'; | ||||
| import WikiBlobsFilters from './wiki_blobs_filters.vue'; | ||||
| import AllScopesStartFilters from './all_scopes_start_filters.vue'; | ||||
| 
 | ||||
| export default { | ||||
|   name: 'GlobalSearchSidebar', | ||||
|  | @ -40,8 +41,16 @@ export default { | |||
|     DomElementListener, | ||||
|     CommitsFilters, | ||||
|     MilestonesFilters, | ||||
|     AllScopesStartFilters, | ||||
|   }, | ||||
|   mixins: [glFeatureFlagsMixin()], | ||||
|   props: { | ||||
|     headerText: { | ||||
|       required: false, | ||||
|       type: String, | ||||
|       default: '', | ||||
|     }, | ||||
|   }, | ||||
|   computed: { | ||||
|     ...mapState(['searchType']), | ||||
|     ...mapGetters(['currentScope']), | ||||
|  | @ -82,6 +91,13 @@ export default { | |||
|   <section> | ||||
|     <dom-element-listener selector="#js-open-mobile-filters" @click="toggleFiltersFromSidebar" /> | ||||
|     <sidebar-portal> | ||||
|       <all-scopes-start-filters /> | ||||
|       <div | ||||
|         v-if="headerText" | ||||
|         class="gl-px-5 gl-pt-3 gl-pb-2 gl-m-0 gl-reset-line-height gl-font-weight-bold gl-font-sm super-sidebar-context-header" | ||||
|       > | ||||
|         {{ headerText }} | ||||
|       </div> | ||||
|       <scope-sidebar-navigation /> | ||||
|       <issues-filters v-if="showIssuesFilters" /> | ||||
|       <merge-requests-filters v-if="showMergeRequestFilters" /> | ||||
|  |  | |||
|  | @ -2,34 +2,27 @@ | |||
| import { isEmpty } from 'lodash'; | ||||
| // eslint-disable-next-line no-restricted-imports | ||||
| import { mapState, mapActions, mapGetters } from 'vuex'; | ||||
| import { s__ } from '~/locale'; | ||||
| import { visitUrl, setUrlParams } from '~/lib/utils/url_utility'; | ||||
| import { ANY_OPTION, GROUP_DATA, PROJECT_DATA } from '../constants'; | ||||
| import SearchableDropdown from './searchable_dropdown.vue'; | ||||
| 
 | ||||
| export default { | ||||
|   name: 'GroupFilter', | ||||
|   i18n: { | ||||
|     groupFieldLabel: s__('GlobalSearch|Group'), | ||||
|   }, | ||||
|   components: { | ||||
|     SearchableDropdown, | ||||
|   }, | ||||
|   props: { | ||||
|     groupInitialJson: { | ||||
|       type: Object, | ||||
|       required: false, | ||||
|       default: () => ({}), | ||||
|     }, | ||||
|     labelId: { | ||||
|       type: String, | ||||
|       required: false, | ||||
|       default: 'labelId', | ||||
|     }, | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       search: '', | ||||
|       labelId: 'group-filter-dropdown-id', | ||||
|     }; | ||||
|   }, | ||||
|   computed: { | ||||
|     ...mapState(['query', 'groups', 'fetchingGroups']), | ||||
|     ...mapState(['query', 'groups', 'fetchingGroups', 'groupInitialJson', 'useSidebarNavigation']), | ||||
|     ...mapGetters(['frequentGroups', 'currentScope']), | ||||
|     selectedGroup() { | ||||
|       return isEmpty(this.groupInitialJson) ? ANY_OPTION : this.groupInitialJson; | ||||
|  | @ -73,6 +66,10 @@ export default { | |||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|   <div> | ||||
|     <h5 :id="labelId" class="gl-mt-0 gl-mb-5 gl-font-sm"> | ||||
|       {{ $options.i18n.groupFieldLabel }} | ||||
|     </h5> | ||||
|     <searchable-dropdown | ||||
|       data-testid="group-filter" | ||||
|       :header-text="$options.GROUP_DATA.headerText" | ||||
|  | @ -86,4 +83,5 @@ export default { | |||
|       @first-open="firstLoad" | ||||
|       @change="handleGroupChange" | ||||
|     /> | ||||
|   </div> | ||||
| </template> | ||||
|  | @ -2,34 +2,33 @@ | |||
| import { isEmpty } from 'lodash'; | ||||
| // eslint-disable-next-line no-restricted-imports | ||||
| import { mapState, mapActions, mapGetters } from 'vuex'; | ||||
| import { s__ } from '~/locale'; | ||||
| import { visitUrl, setUrlParams } from '~/lib/utils/url_utility'; | ||||
| import { ANY_OPTION, GROUP_DATA, PROJECT_DATA } from '../constants'; | ||||
| import { ANY_OPTION, GROUP_DATA, PROJECT_DATA } from '~/search/sidebar/constants'; | ||||
| import SearchableDropdown from './searchable_dropdown.vue'; | ||||
| 
 | ||||
| export default { | ||||
|   name: 'ProjectFilter', | ||||
|   i18n: { | ||||
|     projectFieldLabel: s__('GlobalSearch|Project'), | ||||
|   }, | ||||
|   components: { | ||||
|     SearchableDropdown, | ||||
|   }, | ||||
|   props: { | ||||
|     projectInitialJson: { | ||||
|       type: Object, | ||||
|       required: false, | ||||
|       default: () => null, | ||||
|     }, | ||||
|     labelId: { | ||||
|       type: String, | ||||
|       required: false, | ||||
|       default: '', | ||||
|     }, | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       search: '', | ||||
|       labelId: 'projects-filter-dropdown-id', | ||||
|     }; | ||||
|   }, | ||||
|   computed: { | ||||
|     ...mapState(['query', 'projects', 'fetchingProjects']), | ||||
|     ...mapState([ | ||||
|       'query', | ||||
|       'projects', | ||||
|       'fetchingProjects', | ||||
|       'projectInitialJson', | ||||
|       'useSidebarNavigation', | ||||
|     ]), | ||||
|     ...mapGetters(['frequentProjects', 'currentScope']), | ||||
|     selectedProject() { | ||||
|       return isEmpty(this.projectInitialJson) ? ANY_OPTION : this.projectInitialJson; | ||||
|  | @ -74,6 +73,10 @@ export default { | |||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|   <div> | ||||
|     <h5 :id="labelId" class="gl-mt-0 gl-mb-5 gl-font-sm"> | ||||
|       {{ $options.i18n.projectFieldLabel }} | ||||
|     </h5> | ||||
|     <searchable-dropdown | ||||
|       data-testid="project-filter" | ||||
|       :header-text="$options.PROJECT_DATA.headerText" | ||||
|  | @ -87,4 +90,5 @@ export default { | |||
|       @first-open="firstLoad" | ||||
|       @change="handleProjectChange" | ||||
|     /> | ||||
|   </div> | ||||
| </template> | ||||
|  | @ -2,6 +2,7 @@ | |||
| // eslint-disable-next-line no-restricted-imports | ||||
| import { mapActions, mapState, mapGetters } from 'vuex'; | ||||
| import { s__ } from '~/locale'; | ||||
| import eventHub from '~/super_sidebar/event_hub'; | ||||
| import NavItem from '~/super_sidebar/components/nav_item.vue'; | ||||
| import { NAV_LINK_DEFAULT_CLASSES, NAV_LINK_COUNT_DEFAULT_CLASSES } from '../constants'; | ||||
| 
 | ||||
|  | @ -18,6 +19,8 @@ export default { | |||
|     ...mapGetters(['navigationItems']), | ||||
|   }, | ||||
|   created() { | ||||
|     eventHub.$emit('toggle-menu-header', false); | ||||
| 
 | ||||
|     if (this.urlQuery?.search) { | ||||
|       this.fetchSidebarCount(); | ||||
|     } | ||||
|  |  | |||
|  | @ -1,3 +1,5 @@ | |||
| import { __ } from '~/locale'; | ||||
| 
 | ||||
| export const SCOPE_ISSUES = 'issues'; | ||||
| export const SCOPE_MERGE_REQUESTS = 'merge_requests'; | ||||
| export const SCOPE_BLOB = 'blobs'; | ||||
|  | @ -26,3 +28,23 @@ export const TRACKING_LABEL_RESET = 'Reset Filters'; | |||
| export const SEARCH_TYPE_BASIC = 'basic'; | ||||
| export const SEARCH_TYPE_ADVANCED = 'advanced'; | ||||
| export const SEARCH_TYPE_ZOEKT = 'zoekt'; | ||||
| 
 | ||||
| export const ANY_OPTION = { | ||||
|   id: null, | ||||
|   name: __('Any'), | ||||
|   name_with_namespace: __('Any'), | ||||
| }; | ||||
| 
 | ||||
| export const GROUP_DATA = { | ||||
|   headerText: __('Filter results by group'), | ||||
|   queryParam: 'group_id', | ||||
|   name: 'name', | ||||
|   fullName: 'full_name', | ||||
| }; | ||||
| 
 | ||||
| export const PROJECT_DATA = { | ||||
|   headerText: __('Filter results by project'), | ||||
|   queryParam: 'project_id', | ||||
|   name: 'name', | ||||
|   fullName: 'name_with_namespace', | ||||
| }; | ||||
|  |  | |||
|  | @ -4,27 +4,23 @@ import GlobalSearchSidebar from './components/app.vue'; | |||
| 
 | ||||
| Vue.use(Translate); | ||||
| 
 | ||||
| export const sidebarInitState = () => { | ||||
|   const el = document.getElementById('js-search-sidebar'); | ||||
|   if (!el) return {}; | ||||
| 
 | ||||
|   const { navigationJson, searchType } = el.dataset; | ||||
| 
 | ||||
|   const navigationJsonParsed = JSON.parse(navigationJson); | ||||
| 
 | ||||
|   return { navigationJsonParsed, searchType }; | ||||
| }; | ||||
| 
 | ||||
| export const initSidebar = (store) => { | ||||
|   const el = document.getElementById('js-search-sidebar'); | ||||
|   const hederEl = document.getElementById('super-sidebar-context-header'); | ||||
|   const headerText = hederEl.innerText; | ||||
| 
 | ||||
|   if (!el) return false; | ||||
| 
 | ||||
|   return new Vue({ | ||||
|     el, | ||||
|     name: 'GlobalSearchSidebar', | ||||
|     store, | ||||
|     render(createElement) { | ||||
|       return createElement(GlobalSearchSidebar); | ||||
|       return createElement(GlobalSearchSidebar, { | ||||
|         props: { | ||||
|           headerText, | ||||
|         }, | ||||
|       }); | ||||
|     }, | ||||
|   }); | ||||
| }; | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| import { cloneDeep } from 'lodash'; | ||||
| import { GROUPS_LOCAL_STORAGE_KEY, PROJECTS_LOCAL_STORAGE_KEY } from './constants'; | ||||
| 
 | ||||
| const createState = ({ query, navigation, defaultBranchName, searchType }) => ({ | ||||
| const createState = ({ query, navigation, searchType, groupInitialJson, projectInitialJson }) => ({ | ||||
|   urlQuery: cloneDeep(query), | ||||
|   query, | ||||
|   groups: [], | ||||
|  | @ -21,7 +21,8 @@ const createState = ({ query, navigation, defaultBranchName, searchType }) => ({ | |||
|   }, | ||||
|   searchLabelString: '', | ||||
|   searchType, | ||||
|   defaultBranchName, | ||||
|   groupInitialJson, | ||||
|   projectInitialJson, | ||||
| }); | ||||
| 
 | ||||
| export default createState; | ||||
|  |  | |||
|  | @ -3,13 +3,10 @@ import { GlSearchBoxByType, GlButton } from '@gitlab/ui'; | |||
| // eslint-disable-next-line no-restricted-imports | ||||
| import { mapState, mapActions } from 'vuex'; | ||||
| import { s__ } from '~/locale'; | ||||
| import { parseBoolean } from '~/lib/utils/common_utils'; | ||||
| import MarkdownDrawer from '~/vue_shared/components/markdown_drawer/markdown_drawer.vue'; | ||||
| import { ZOEKT_SEARCH_TYPE, ADVANCED_SEARCH_TYPE } from '~/search/store/constants'; | ||||
| import { SYNTAX_OPTIONS_ADVANCED_DOCUMENT, SYNTAX_OPTIONS_ZOEKT_DOCUMENT } from '../constants'; | ||||
| import SearchTypeIndicator from './search_type_indicator.vue'; | ||||
| import GroupFilter from './group_filter.vue'; | ||||
| import ProjectFilter from './project_filter.vue'; | ||||
| 
 | ||||
| export default { | ||||
|   name: 'GlobalSearchTopbar', | ||||
|  | @ -23,22 +20,10 @@ export default { | |||
|   components: { | ||||
|     GlButton, | ||||
|     GlSearchBoxByType, | ||||
|     GroupFilter, | ||||
|     ProjectFilter, | ||||
|     MarkdownDrawer, | ||||
|     SearchTypeIndicator, | ||||
|   }, | ||||
|   props: { | ||||
|     groupInitialJson: { | ||||
|       type: Object, | ||||
|       required: false, | ||||
|       default: () => ({}), | ||||
|     }, | ||||
|     projectInitialJson: { | ||||
|       type: Object, | ||||
|       required: false, | ||||
|       default: () => ({}), | ||||
|     }, | ||||
|     defaultBranchName: { | ||||
|       type: String, | ||||
|       required: false, | ||||
|  | @ -55,9 +40,6 @@ export default { | |||
|         this.setQuery({ key: 'search', value }); | ||||
|       }, | ||||
|     }, | ||||
|     showFilters() { | ||||
|       return !parseBoolean(this.query.snippets); | ||||
|     }, | ||||
|     showSyntaxOptions() { | ||||
|       return ( | ||||
|         (this.searchType === ZOEKT_SEARCH_TYPE || this.searchType === ADVANCED_SEARCH_TYPE) && | ||||
|  | @ -103,31 +85,17 @@ export default { | |||
|       </template> | ||||
|       <search-type-indicator /> | ||||
|     </div> | ||||
|     <div class="search-page-form gl-lg-display-flex gl-flex-direction-row gl-align-items-flex-end"> | ||||
|       <div class="gl-flex-grow-1 gl-lg-mb-0 gl-lg-mr-2"> | ||||
|     <div class="search-page-form gl-lg-display-flex gl-flex-direction-column"> | ||||
|       <div class="gl-lg-display-flex gl-flex-direction-row gl-align-items-flex-start"> | ||||
|         <div class="gl-flex-grow-1 gl-pb-8 gl-lg-mb-0 gl-lg-mr-2"> | ||||
|           <gl-search-box-by-type | ||||
|             id="dashboard_search" | ||||
|             v-model="search" | ||||
|             name="search" | ||||
|             :placeholder="$options.i18n.searchPlaceholder" | ||||
|           @submit="applyQuery" | ||||
|             @keydown.enter.stop.prevent="applyQuery" | ||||
|           /> | ||||
|         </div> | ||||
|       <div v-if="showFilters" class="gl-mb-4 gl-lg-mb-0 gl-lg-mx-3 gl-min-w-20"> | ||||
|         <label id="groupfilterDropdown" class="gl-display-block gl-mb-1 gl-md-pb-2">{{ | ||||
|           $options.i18n.groupFieldLabel | ||||
|         }}</label> | ||||
|         <group-filter label-id="groupfilterDropdown" :group-initial-json="groupInitialJson" /> | ||||
|       </div> | ||||
|       <div v-if="showFilters" class="gl-mb-4 gl-lg-mb-0 gl-lg-ml-3 gl-min-w-20"> | ||||
|         <label id="projectfilterDropdown" class="gl-display-block gl-mb-1 gl-md-pb-2">{{ | ||||
|           $options.i18n.projectFieldLabel | ||||
|         }}</label> | ||||
|         <project-filter | ||||
|           label-id="projectfilterDropdown" | ||||
|           :project-initial-json="projectInitialJson" | ||||
|         /> | ||||
|       </div> | ||||
|     </div> | ||||
|   </section> | ||||
|  |  | |||
|  | @ -1,25 +1,3 @@ | |||
| import { __ } from '~/locale'; | ||||
| 
 | ||||
| export const ANY_OPTION = Object.freeze({ | ||||
|   id: null, | ||||
|   name: __('Any'), | ||||
|   name_with_namespace: __('Any'), | ||||
| }); | ||||
| 
 | ||||
| export const GROUP_DATA = { | ||||
|   headerText: __('Filter results by group'), | ||||
|   queryParam: 'group_id', | ||||
|   name: 'name', | ||||
|   fullName: 'full_name', | ||||
| }; | ||||
| 
 | ||||
| export const PROJECT_DATA = { | ||||
|   headerText: __('Filter results by project'), | ||||
|   queryParam: 'project_id', | ||||
|   name: 'name', | ||||
|   fullName: 'name_with_namespace', | ||||
| }; | ||||
| 
 | ||||
| export const SYNTAX_OPTIONS_ADVANCED_DOCUMENT = 'drawers/drawers/advanced_search_syntax.md'; | ||||
| export const SYNTAX_OPTIONS_ZOEKT_DOCUMENT = 'drawers/drawers/exact_code_search_syntax.md'; | ||||
| 
 | ||||
|  |  | |||
|  | @ -11,19 +11,16 @@ export const initTopbar = (store) => { | |||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   const { groupInitialJson, projectInitialJson } = el.dataset; | ||||
| 
 | ||||
|   const groupInitialJsonParsed = JSON.parse(groupInitialJson); | ||||
|   const projectInitialJsonParsed = JSON.parse(projectInitialJson); | ||||
|   const { defaultBranchName } = el.dataset; | ||||
| 
 | ||||
|   return new Vue({ | ||||
|     el, | ||||
|     name: 'GlobalSearchTopbar', | ||||
|     store, | ||||
|     render(createElement) { | ||||
|       return createElement(GlobalSearchTopbar, { | ||||
|         props: { | ||||
|           groupInitialJson: groupInitialJsonParsed, | ||||
|           projectInitialJson: projectInitialJsonParsed, | ||||
|           defaultBranchName, | ||||
|         }, | ||||
|       }); | ||||
|     }, | ||||
|  |  | |||
|  | @ -14,6 +14,7 @@ export const initBlobRefSwitcher = () => { | |||
| 
 | ||||
|   return new Vue({ | ||||
|     el, | ||||
|     name: 'GlobalSearchUnderTopbar', | ||||
|     render(createElement) { | ||||
|       return createElement(RefSelector, { | ||||
|         props: { | ||||
|  |  | |||
|  | @ -7,6 +7,7 @@ import { TAB_KEY_CODE } from '~/lib/utils/keycodes'; | |||
| import { keysFor, TOGGLE_SUPER_SIDEBAR } from '~/behaviors/shortcuts/keybindings'; | ||||
| import { __, s__ } from '~/locale'; | ||||
| import Tracking from '~/tracking'; | ||||
| import eventHub from '../event_hub'; | ||||
| import { | ||||
|   sidebarState, | ||||
|   SUPER_SIDEBAR_PEEK_STATE_CLOSED as STATE_CLOSED, | ||||
|  | @ -58,6 +59,7 @@ export default { | |||
|       showPeekHint: false, | ||||
|       isMouseover: false, | ||||
|       breakpoint: null, | ||||
|       showSuperSidebarContextHeader: true, | ||||
|     }; | ||||
|   }, | ||||
|   computed: { | ||||
|  | @ -96,6 +98,7 @@ export default { | |||
|   mounted() { | ||||
|     this.setupFocusTrapListener(); | ||||
|     Mousetrap.bind(keysFor(TOGGLE_SUPER_SIDEBAR), this.toggleSidebar); | ||||
|     eventHub.$on('toggle-menu-header', this.onToggleMenuHeader); | ||||
|   }, | ||||
|   beforeDestroy() { | ||||
|     document.removeEventListener('keydown', this.focusTrap); | ||||
|  | @ -166,6 +169,9 @@ export default { | |||
|         event.preventDefault(); | ||||
|       } | ||||
|     }, | ||||
|     onToggleMenuHeader(forceState) { | ||||
|       this.showSuperSidebarContextHeader = forceState; | ||||
|     }, | ||||
|   }, | ||||
| }; | ||||
| </script> | ||||
|  | @ -207,6 +213,7 @@ export default { | |||
|       > | ||||
|         <scroll-scrim class="gl-flex-grow-1" data-testid="nav-container"> | ||||
|           <div | ||||
|             v-if="showSuperSidebarContextHeader" | ||||
|             id="super-sidebar-context-header" | ||||
|             class="gl-px-5 gl-pt-3 gl-pb-2 gl-m-0 gl-reset-line-height gl-font-weight-bold gl-font-sm super-sidebar-context-header" | ||||
|           > | ||||
|  |  | |||
|  | @ -92,8 +92,7 @@ | |||
|   @include transition(background-color, border-color, color, box-shadow); | ||||
| } | ||||
| 
 | ||||
| .dropdown-menu-toggle, | ||||
| .header-user-avatar { | ||||
| .dropdown-menu-toggle { | ||||
|   @include transition(border-color); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -810,14 +810,6 @@ | |||
|   .navbar-gitlab { | ||||
|     li.dropdown { | ||||
|       position: static; | ||||
| 
 | ||||
|       &.user-counter { | ||||
|         margin-left: 8px !important; | ||||
| 
 | ||||
|         > a { | ||||
|           padding: 0 4px !important; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|  |  | |||
|  | @ -98,12 +98,6 @@ | |||
|   .container-fluid { | ||||
|     padding: 0; | ||||
| 
 | ||||
|     .user-counter { | ||||
|       svg { | ||||
|         margin-right: 3px; | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     .navbar-nav { | ||||
|       @include media-breakpoint-down(xs) { | ||||
|         display: flex; | ||||
|  | @ -120,12 +114,6 @@ | |||
|     } | ||||
| 
 | ||||
|     .nav > li { | ||||
|       &.header-user { | ||||
|         @include media-breakpoint-down(xs) { | ||||
|           padding-left: 10px; | ||||
|         } | ||||
|       } | ||||
| 
 | ||||
|       > a { | ||||
|         will-change: color; | ||||
|         margin: 4px 0; | ||||
|  | @ -137,38 +125,11 @@ | |||
|             padding: 0; | ||||
|           } | ||||
|         } | ||||
| 
 | ||||
|         &.header-user-dropdown-toggle { | ||||
|           margin-left: 2px; | ||||
| 
 | ||||
|           .header-user-avatar { | ||||
|             margin-right: 0; | ||||
|           } | ||||
|         } | ||||
|       } | ||||
| 
 | ||||
|       .header-new-dropdown-toggle { | ||||
|         margin-right: 0; | ||||
|       } | ||||
| 
 | ||||
|       .impersonated-user, | ||||
|       .impersonated-user:hover { | ||||
|         margin-right: 1px; | ||||
|         background-color: $white; | ||||
|         border-top-right-radius: 0; | ||||
|         border-bottom-right-radius: 0; | ||||
|       } | ||||
| 
 | ||||
|       .impersonation-btn, | ||||
|       .impersonation-btn:hover { | ||||
|         background-color: $white; | ||||
|         border-top-left-radius: 0; | ||||
|         border-bottom-left-radius: 0; | ||||
| 
 | ||||
|         svg { | ||||
|           color: $orange-500; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | @ -223,10 +184,6 @@ | |||
|     top: -1px; | ||||
|     font-size: 10px; | ||||
|   } | ||||
| 
 | ||||
|   .impersonation i { | ||||
|     color: $red-500; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .caret-down, | ||||
|  | @ -238,7 +195,6 @@ | |||
|   fill: currentColor; | ||||
| } | ||||
| 
 | ||||
| .header-user .dropdown-menu, | ||||
| .header-new .dropdown-menu { | ||||
|   margin-top: $dropdown-vertical-offset; | ||||
| } | ||||
|  | @ -304,44 +260,6 @@ | |||
|       } | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   .header-user-dropdown-toggle { | ||||
|     text-align: center; | ||||
|   } | ||||
| 
 | ||||
|   .header-user-avatar { | ||||
|     float: none; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .header-user { | ||||
|   &.show .dropdown-menu { | ||||
|     margin-top: 4px; | ||||
|     color: var(--gl-text-color, $gl-text-color); | ||||
|     left: auto; | ||||
|     max-height: $dropdown-max-height-lg; | ||||
| 
 | ||||
|     .user-status { | ||||
|       max-width: 240px; | ||||
|     } | ||||
| 
 | ||||
|     svg { | ||||
|       vertical-align: text-top; | ||||
|     } | ||||
| 
 | ||||
|     a.ci-minutes-emoji gl-emoji, | ||||
|     a.trial-link gl-emoji { | ||||
|       font-size: $gl-font-size; | ||||
|       vertical-align: baseline; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .header-user-avatar { | ||||
|   float: left; | ||||
|   margin-right: 5px; | ||||
|   border-radius: 50%; | ||||
|   border: 1px solid $gray-normal; | ||||
| } | ||||
| 
 | ||||
| .notification-dot { | ||||
|  |  | |||
|  | @ -3,7 +3,6 @@ | |||
| module Explore | ||||
|   class CatalogController < Explore::ApplicationController | ||||
|     feature_category :pipeline_composition | ||||
|     before_action :check_feature_flag | ||||
|     before_action :check_resource_access, only: :show | ||||
| 
 | ||||
|     def show; end | ||||
|  | @ -14,10 +13,6 @@ module Explore | |||
| 
 | ||||
|     private | ||||
| 
 | ||||
|     def check_feature_flag | ||||
|       render_404 unless Feature.enabled?(:global_ci_catalog, current_user) | ||||
|     end | ||||
| 
 | ||||
|     def check_resource_access | ||||
|       render_404 unless catalog_resource.present? | ||||
|     end | ||||
|  |  | |||
|  | @ -41,6 +41,7 @@ class Profiles::PreferencesController < Profiles::ApplicationController | |||
|       :color_scheme_id, | ||||
|       :diffs_deletion_color, | ||||
|       :diffs_addition_color, | ||||
|       :home_organization_id, | ||||
|       :layout, | ||||
|       :dashboard, | ||||
|       :project_view, | ||||
|  |  | |||
|  | @ -52,6 +52,7 @@ class Projects::BlobController < Projects::ApplicationController | |||
|     push_frontend_feature_flag(:blob_blame_info, @project) | ||||
|     push_frontend_feature_flag(:highlight_js_worker, @project) | ||||
|     push_frontend_feature_flag(:explain_code_chat, current_user) | ||||
|     push_frontend_feature_flag(:encoding_logs_tree) | ||||
|     push_licensed_feature(:file_locks) if @project.licensed_feature_available?(:file_locks) | ||||
|   end | ||||
| 
 | ||||
|  |  | |||
|  | @ -29,7 +29,13 @@ class Projects::ServiceDeskController < Projects::ApplicationController | |||
|   end | ||||
| 
 | ||||
|   def allowed_update_attributes | ||||
|     %i[issue_template_key outgoing_name project_key add_external_participants_from_cc] | ||||
|     %i[ | ||||
|       issue_template_key | ||||
|       outgoing_name | ||||
|       project_key | ||||
|       reopen_issue_on_external_participant_note | ||||
|       add_external_participants_from_cc | ||||
|     ] | ||||
|   end | ||||
| 
 | ||||
|   def service_desk_attributes | ||||
|  | @ -42,6 +48,7 @@ class Projects::ServiceDeskController < Projects::ApplicationController | |||
|       template_file_missing: service_desk_settings&.issue_template_missing?, | ||||
|       outgoing_name: service_desk_settings&.outgoing_name, | ||||
|       project_key: service_desk_settings&.project_key, | ||||
|       reopen_issue_on_external_participant_note: service_desk_settings&.reopen_issue_on_external_participant_note, | ||||
|       add_external_participants_from_cc: service_desk_settings&.add_external_participants_from_cc | ||||
|     } | ||||
|   end | ||||
|  |  | |||
|  | @ -22,6 +22,7 @@ class Projects::TreeController < Projects::ApplicationController | |||
|     push_frontend_feature_flag(:blob_blame_info, @project) | ||||
|     push_frontend_feature_flag(:highlight_js_worker, @project) | ||||
|     push_frontend_feature_flag(:explain_code_chat, current_user) | ||||
|     push_frontend_feature_flag(:encoding_logs_tree) | ||||
|     push_licensed_feature(:file_locks) if @project.licensed_feature_available?(:file_locks) | ||||
|   end | ||||
| 
 | ||||
|  |  | |||
|  | @ -43,6 +43,7 @@ class ProjectsController < Projects::ApplicationController | |||
|     push_frontend_feature_flag(:remove_monitor_metrics, @project) | ||||
|     push_frontend_feature_flag(:explain_code_chat, current_user) | ||||
|     push_frontend_feature_flag(:issue_email_participants, @project) | ||||
|     push_frontend_feature_flag(:encoding_logs_tree) | ||||
|     # TODO: We need to remove the FF eventually when we rollout page_specific_styles | ||||
|     push_frontend_feature_flag(:page_specific_styles, current_user) | ||||
|     push_licensed_feature(:file_locks) if @project.present? && @project.licensed_feature_available?(:file_locks) | ||||
|  |  | |||
|  | @ -0,0 +1,30 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| module Resolvers | ||||
|   module Namespaces | ||||
|     class WorkItemStateCountsResolver < WorkItemsResolver | ||||
|       type Types::WorkItemStateCountsType, null: true | ||||
| 
 | ||||
|       def ready?(**args) | ||||
|         # The search filter is not supported for work times at the namespace level. | ||||
|         # See https://gitlab.com/gitlab-org/gitlab/-/work_items/393126 | ||||
|         if args[:search] | ||||
|           raise Gitlab::Graphql::Errors::ArgumentError, | ||||
|             'Searching is not available for work items at the namespace level yet' | ||||
|         end | ||||
| 
 | ||||
|         super | ||||
|       end | ||||
| 
 | ||||
|       def resolve(**args) | ||||
|         return if resource_parent.nil? | ||||
| 
 | ||||
|         Gitlab::IssuablesCountForState.new( | ||||
|           finder(args), | ||||
|           resource_parent, | ||||
|           store_in_redis_cache: true | ||||
|         ) | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  | @ -0,0 +1,16 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| module Resolvers | ||||
|   class WorkItemStateCountsResolver < WorkItemsResolver | ||||
|     type Types::WorkItemStateCountsType, null: true | ||||
| 
 | ||||
|     def resolve(**args) | ||||
|       return if resource_parent.nil? | ||||
| 
 | ||||
|       work_item_finder = finder(prepare_finder_params(args)) | ||||
|       work_item_finder.parent_param = resource_parent | ||||
| 
 | ||||
|       Gitlab::IssuablesCountForState.new(work_item_finder, resource_parent) | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  | @ -275,6 +275,14 @@ module Types | |||
|           description: 'Find a work item by IID directly associated with the group. Returns `null` if the ' \ | ||||
|                        '`namespace_level_work_items` feature flag is disabled.' | ||||
| 
 | ||||
|     field :work_item_state_counts, | ||||
|           Types::WorkItemStateCountsType, | ||||
|           null: true, | ||||
|           alpha: { milestone: '16.7' }, | ||||
|           description: 'Counts of work items by state for the namespace. Returns `null` if the ' \ | ||||
|                        '`namespace_level_work_items` feature flag is disabled.', | ||||
|           resolver: Resolvers::Namespaces::WorkItemStateCountsResolver | ||||
| 
 | ||||
|     field :autocomplete_users, | ||||
|           null: true, | ||||
|           resolver: Resolvers::AutocompleteUsersResolver, | ||||
|  |  | |||
|  | @ -254,6 +254,13 @@ module Types | |||
|       extras: [:lookahead], | ||||
|       resolver: Resolvers::WorkItemsResolver | ||||
| 
 | ||||
|     field :work_item_state_counts, | ||||
|       Types::WorkItemStateCountsType, | ||||
|       null: true, | ||||
|       alpha: { milestone: '16.7' }, | ||||
|       description: 'Counts of work items by state for the project.', | ||||
|       resolver: Resolvers::WorkItemStateCountsResolver | ||||
| 
 | ||||
|     field :issue_status_counts, | ||||
|       Types::IssueStatusCountsType, | ||||
|       null: true, | ||||
|  |  | |||
|  | @ -0,0 +1,25 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| module Types | ||||
|   # rubocop: disable Graphql/AuthorizeTypes -- Parent node applies authorization | ||||
|   class WorkItemStateCountsType < BaseObject | ||||
|     graphql_name 'WorkItemStateCountsType' | ||||
|     description 'Represents total number of work items for the represented states' | ||||
| 
 | ||||
|     field :all, | ||||
|       GraphQL::Types::Int, | ||||
|       null: true, | ||||
|       description: 'Number of work items for the project or group.' | ||||
| 
 | ||||
|     field :closed, | ||||
|       GraphQL::Types::Int, | ||||
|       null: true, | ||||
|       description: 'Number of work items with state CLOSED for the project or group.' | ||||
| 
 | ||||
|     field :opened, | ||||
|       GraphQL::Types::Int, | ||||
|       null: true, | ||||
|       description: 'Number of work items with state OPENED for the project or group.' | ||||
|   end | ||||
|   # rubocop: enable Graphql/AuthorizeTypes | ||||
| end | ||||
|  | @ -315,8 +315,8 @@ module ApplicationHelper | |||
|     class_names << 'issue-boards-page gl-overflow-auto' if current_controller?(:boards) | ||||
|     class_names << 'epic-boards-page gl-overflow-auto' if current_controller?(:epic_boards) | ||||
|     class_names << 'with-performance-bar' if performance_bar_enabled? | ||||
|     class_names << 'with-header' if !show_super_sidebar? || !current_user | ||||
|     class_names << 'with-top-bar' if show_super_sidebar? && !@hide_top_bar_padding | ||||
|     class_names << 'with-header' unless current_user | ||||
|     class_names << 'with-top-bar' unless @hide_top_bar_padding | ||||
|     class_names << system_message_class | ||||
| 
 | ||||
|     class_names | ||||
|  |  | |||
|  | @ -3,18 +3,6 @@ | |||
| module DashboardHelper | ||||
|   include IconsHelper | ||||
| 
 | ||||
|   def assigned_issues_dashboard_path | ||||
|     issues_dashboard_path(assignee_username: current_user.username) | ||||
|   end | ||||
| 
 | ||||
|   def assigned_mrs_dashboard_path | ||||
|     merge_requests_dashboard_path(assignee_username: current_user.username) | ||||
|   end | ||||
| 
 | ||||
|   def reviewer_mrs_dashboard_path | ||||
|     merge_requests_dashboard_path(reviewer_username: current_user.username) | ||||
|   end | ||||
| 
 | ||||
|   def has_start_trial? | ||||
|     false | ||||
|   end | ||||
|  |  | |||
|  | @ -149,16 +149,6 @@ module IssuablesHelper | |||
|     end | ||||
|   end | ||||
| 
 | ||||
|   def assigned_open_issues_count_text | ||||
|     count = assigned_issuables_count(:issues) | ||||
| 
 | ||||
|     if count > User::MAX_LIMIT_FOR_ASSIGNEED_ISSUES_COUNT - 1 | ||||
|       "#{count - 1}+" | ||||
|     else | ||||
|       count.to_s | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   def issuable_reference(issuable) | ||||
|     @show_full_reference ? issuable.to_reference(full: true) : issuable.to_reference(@group || @project) | ||||
|   end | ||||
|  |  | |||
|  | @ -40,14 +40,6 @@ module NavHelper | |||
|     end | ||||
|   end | ||||
| 
 | ||||
|   def user_dropdown_class | ||||
|     class_names = [] | ||||
|     class_names << 'header-user-dropdown-toggle' | ||||
|     class_names << 'impersonated-user' if session[:impersonator_id] | ||||
| 
 | ||||
|     class_names | ||||
|   end | ||||
| 
 | ||||
|   def page_has_markdown? | ||||
|     current_path?('projects/merge_requests#show') || | ||||
|       current_path?('projects/merge_requests/conflicts#show') || | ||||
|  |  | |||
|  | @ -51,8 +51,7 @@ module Organizations | |||
| 
 | ||||
|     def home_organization_setting_app_data | ||||
|       { | ||||
|         # TODO: use real setting - https://gitlab.com/gitlab-org/gitlab/-/issues/428668 | ||||
|         initial_selection: 1 | ||||
|         initial_selection: current_user.home_organization_id | ||||
|       }.to_json | ||||
|     end | ||||
| 
 | ||||
|  |  | |||
|  | @ -5,10 +5,6 @@ module TodosHelper | |||
|     @todos_pending_count ||= current_user.todos_pending_count | ||||
|   end | ||||
| 
 | ||||
|   def todos_count_format(count) | ||||
|     count > 99 ? '99+' : count.to_s | ||||
|   end | ||||
| 
 | ||||
|   def todos_done_count | ||||
|     @todos_done_count ||= current_user.todos_done_count | ||||
|   end | ||||
|  |  | |||
|  | @ -758,6 +758,11 @@ class Issue < ApplicationRecord | |||
|     Gitlab::HookData::IssueBuilder.new(self).build | ||||
|   end | ||||
| 
 | ||||
|   override :gfm_reference | ||||
|   def gfm_reference(from = nil) | ||||
|     "#{work_item_type_with_default.name.underscore} #{to_reference(from)}" | ||||
|   end | ||||
| 
 | ||||
|   private | ||||
| 
 | ||||
|   def project_level_readable_by?(user) | ||||
|  |  | |||
|  | @ -400,6 +400,7 @@ class User < MainClusterwide::ApplicationRecord | |||
|     :pinned_nav_items, :pinned_nav_items=, | ||||
|     :achievements_enabled, :achievements_enabled=, | ||||
|     :enabled_following, :enabled_following=, | ||||
|     :home_organization, :home_organization_id, :home_organization_id=, | ||||
|     to: :user_preference | ||||
| 
 | ||||
|   delegate :path, to: :namespace, allow_nil: true, prefix: true | ||||
|  | @ -613,6 +614,10 @@ class User < MainClusterwide::ApplicationRecord | |||
| 
 | ||||
|   strip_attributes! :name | ||||
| 
 | ||||
|   def user_belongs_to_organization?(organization) | ||||
|     organization_users.exists?(organization: organization) | ||||
|   end | ||||
| 
 | ||||
|   def preferred_language | ||||
|     read_attribute('preferred_language').presence || Gitlab::CurrentSettings.default_preferred_language | ||||
|   end | ||||
|  |  | |||
|  | @ -10,6 +10,7 @@ class UserPreference < MainClusterwide::ApplicationRecord | |||
|   TIME_DISPLAY_FORMATS = { system: 0, non_iso_format: 1, iso_format: 2 }.freeze | ||||
| 
 | ||||
|   belongs_to :user | ||||
|   belongs_to :home_organization, class_name: "Organizations::Organization", optional: true | ||||
| 
 | ||||
|   scope :with_user, -> { joins(:user) } | ||||
|   scope :gitpod_enabled, -> { where(gitpod_enabled: true) } | ||||
|  | @ -30,6 +31,8 @@ class UserPreference < MainClusterwide::ApplicationRecord | |||
| 
 | ||||
|   validates :time_display_format, inclusion: { in: TIME_DISPLAY_FORMATS.values }, presence: true | ||||
| 
 | ||||
|   validate :user_belongs_to_home_organization, if: :home_organization_changed? | ||||
| 
 | ||||
|   # 2023-06-22 is after 16.1 release and during 16.2 release https://docs.gitlab.com/ee/development/database/avoiding_downtime_in_migrations.html#ignoring-the-column-release-m | ||||
|   ignore_columns :use_legacy_web_ide, remove_with: '16.2', remove_after: '2023-06-22' | ||||
| 
 | ||||
|  | @ -52,6 +55,16 @@ class UserPreference < MainClusterwide::ApplicationRecord | |||
|     end | ||||
|   end | ||||
| 
 | ||||
|   def user_belongs_to_home_organization | ||||
|     # If we don't ignore the default organization id below then all users need to have their corresponding entry | ||||
|     # with default organization id as organization id in the `organization_users` table. | ||||
|     # Otherwise, the user won't be able to the default organization as the home organization. | ||||
|     return if home_organization_id == Organizations::Organization::DEFAULT_ORGANIZATION_ID | ||||
|     return if user.user_belongs_to_organization?(home_organization_id) | ||||
| 
 | ||||
|     errors.add(:user, _("is not part of the given organization")) | ||||
|   end | ||||
| 
 | ||||
|   def set_notes_filter(filter_id, issuable) | ||||
|     # No need to update the column if the value is already set. | ||||
|     if filter_id && NOTES_FILTERS.value?(filter_id) | ||||
|  |  | |||
|  | @ -12,8 +12,8 @@ module Groups | |||
|         noteable_owner + | ||||
|         participants_in_noteable + | ||||
|         all_members + | ||||
|         groups + | ||||
|         group_hierarchy_users | ||||
|         group_hierarchy_users + | ||||
|         groups | ||||
| 
 | ||||
|       render_participants_as_hash(participants.uniq) | ||||
|     end | ||||
|  |  | |||
|  | @ -35,8 +35,6 @@ module MergeRequests | |||
|       result = maybe_rebase!(**result) | ||||
|       result = maybe_merge!(**result) | ||||
| 
 | ||||
|       update_merge_request!(merge_request, result) | ||||
| 
 | ||||
|       ServiceResponse.success(payload: result) | ||||
|     rescue CreateRefError => error | ||||
|       ServiceResponse.error(message: error.message) | ||||
|  | @ -118,10 +116,6 @@ module MergeRequests | |||
|       ).compact | ||||
|     end | ||||
| 
 | ||||
|     def update_merge_request!(merge_request, result) | ||||
|       # overridden in EE | ||||
|     end | ||||
| 
 | ||||
|     def safe_gitaly_operation | ||||
|       yield | ||||
|     rescue Gitlab::Git::PreReceiveError, Gitlab::Git::CommandError, ArgumentError => error | ||||
|  |  | |||
|  | @ -1,50 +0,0 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| module Namespaces | ||||
|   class InProductMarketingEmailsService | ||||
|     TRACKS = { | ||||
|       create: { | ||||
|         interval_days: [1, 5, 10], | ||||
|         completed_actions: [:created], | ||||
|         incomplete_actions: [:git_write] | ||||
|       }, | ||||
|       team_short: { | ||||
|         interval_days: [1], | ||||
|         completed_actions: [:git_write], | ||||
|         incomplete_actions: [:user_added] | ||||
|       }, | ||||
|       trial_short: { | ||||
|         interval_days: [2], | ||||
|         completed_actions: [:git_write], | ||||
|         incomplete_actions: [:trial_started] | ||||
|       }, | ||||
|       admin_verify: { | ||||
|         interval_days: [3], | ||||
|         completed_actions: [:git_write], | ||||
|         incomplete_actions: [:pipeline_created] | ||||
|       }, | ||||
|       verify: { | ||||
|         interval_days: [4, 8, 13], | ||||
|         completed_actions: [:git_write], | ||||
|         incomplete_actions: [:pipeline_created] | ||||
|       }, | ||||
|       trial: { | ||||
|         interval_days: [1, 5, 10], | ||||
|         completed_actions: [:git_write, :pipeline_created], | ||||
|         incomplete_actions: [:trial_started] | ||||
|       }, | ||||
|       team: { | ||||
|         interval_days: [1, 5, 10], | ||||
|         completed_actions: [:git_write, :pipeline_created, :trial_started], | ||||
|         incomplete_actions: [:user_added] | ||||
|       } | ||||
|     }.freeze | ||||
| 
 | ||||
|     def self.email_count_for_track(track) | ||||
|       interval_days = TRACKS.dig(track.to_sym, :interval_days) | ||||
|       interval_days&.count || 0 | ||||
|     end | ||||
|   end | ||||
| end | ||||
| 
 | ||||
| Namespaces::InProductMarketingEmailsService.prepend_mod | ||||
|  | @ -1,26 +0,0 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| module Users | ||||
|   class InProductMarketingEmailRecords | ||||
|     attr_reader :records | ||||
| 
 | ||||
|     def initialize | ||||
|       @records = [] | ||||
|     end | ||||
| 
 | ||||
|     def save! | ||||
|       Users::InProductMarketingEmail.bulk_insert!(@records) | ||||
|       @records = [] | ||||
|     end | ||||
| 
 | ||||
|     def add(user, track: nil, series: nil) | ||||
|       @records << Users::InProductMarketingEmail.new( | ||||
|         user: user, | ||||
|         track: track, | ||||
|         series: series, | ||||
|         created_at: Time.zone.now, | ||||
|         updated_at: Time.zone.now | ||||
|       ) | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  | @ -62,7 +62,7 @@ | |||
| 
 | ||||
|   = yield :page_specific_javascripts | ||||
| 
 | ||||
|   = webpack_bundle_tag 'super_sidebar' if show_super_sidebar? | ||||
|   = webpack_bundle_tag 'super_sidebar' | ||||
| 
 | ||||
|   = webpack_controller_bundle_tags | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,51 +0,0 @@ | |||
| - return unless current_user | ||||
| 
 | ||||
| %ul | ||||
|   %li.current-user | ||||
|     - if current_user_menu?(:profile) | ||||
|       = link_to current_user, class: 'gl-line-height-20!', data: { user: current_user.username, testid: 'user-profile-link', track_action: "click_link", track_label: "user_profile", track_property: "navigation_top" } do | ||||
|         = render 'layouts/header/current_user_dropdown_item' | ||||
|     - else | ||||
|       .gl-py-3.gl-px-4 | ||||
|         = render 'layouts/header/current_user_dropdown_item' | ||||
|   %li.divider | ||||
|   - if can?(current_user, :update_user_status, current_user) | ||||
|     %li | ||||
|       = render Pajamas::ButtonComponent.new(button_options: { class: 'menu-item js-set-status-modal-trigger' }) do | ||||
|         - if current_user.status&.busy? || current_user.status&.customized? | ||||
|           = s_('SetStatusModal|Edit status') | ||||
|         - else | ||||
|           = s_('SetStatusModal|Set status') | ||||
|   = dispensable_render_if_exists 'layouts/header/start_trial' | ||||
|   - if current_user_menu?(:settings) | ||||
|     %li | ||||
|       = link_to s_("CurrentUser|Edit profile"), profile_path, data: { testid: 'edit_profile_link', track_action: "click_link", track_label: "user_edit_profile", track_property: "navigation_top" } | ||||
|     %li | ||||
|       = link_to s_("CurrentUser|Preferences"), profile_preferences_path, data: { track_action: "click_link", track_label: "user_preferences", track_property: "navigation_top" } | ||||
|   = render_if_exists 'layouts/header/buy_pipeline_minutes', project: @project, namespace: @group | ||||
| 
 | ||||
|   - if current_user_menu?(:help) | ||||
|     %li.divider.d-md-none | ||||
|     %li.d-md-none | ||||
|       = link_to _("Help"), help_path, data: {track_action: 'click_link', track_label: 'help', track_property: 'navigation_top'} | ||||
|     %li.d-md-none | ||||
|       = link_to _("Support"), support_url, data: {track_action: 'click_link', track_label: 'support', track_property: 'navigation_top'} | ||||
|     %li.d-md-none | ||||
|       = render 'shared/help_dropdown_forum_link' | ||||
|   %li.d-md-none | ||||
|     = link_to _("Submit feedback"), Gitlab::Utils.append_path(promo_url, "submit-feedback"), data: {track_action: 'click_link', track_label: 'submit_feedback', track_property: 'navigation_top'} | ||||
|   - if current_user_menu?(:help) || current_user_menu?(:settings) || current_user_menu?(:profile) | ||||
|     %li.d-md-none | ||||
|       = render 'shared/user_dropdown_contributing_link' | ||||
|   = render 'shared/user_dropdown_instance_review' | ||||
|   - if Gitlab.com_but_not_canary? | ||||
|     %li.d-md-none | ||||
|       = link_to _("Switch to GitLab Next"), Gitlab::Saas.canary_toggle_com_url, data: { track_action: "click_link", track_label: "switch_to_canary", track_property: "navigation_top" } | ||||
| 
 | ||||
|   %li.divider | ||||
|   .js-new-nav-toggle{ data: { enabled: show_super_sidebar?.to_s, endpoint: profile_preferences_path} } | ||||
| 
 | ||||
|   - if current_user_menu?(:sign_out) | ||||
|     %li.divider | ||||
|     %li | ||||
|       = link_to _("Sign out"), destroy_user_session_path, method: :post, class: "sign-out-link", data: { testid: 'sign_out_link', track_action: "click_link", track_label: "user_sign_out", track_property: "navigation_top" } | ||||
|  | @ -1,12 +0,0 @@ | |||
| .gl-font-weight-bold | ||||
|   = current_user.name | ||||
|   - if current_user.status&.busy? | ||||
|     = render Pajamas::BadgeComponent.new(s_('UserProfile|Busy'), size: 'sm', variant: 'warning') | ||||
| = current_user.to_reference | ||||
| - if current_user.status | ||||
|   .user-status.d-flex.align-items-center.gl-mt-2.gl-mr-0.gl-font-sm.has-tooltip{ title: current_user.status.message_html, data: { html: 'true', placement: 'bottom' } } | ||||
|     - if current_user.status.customized? | ||||
|       .user-status-emoji.d-flex.align-items-center | ||||
|         = emoji_icon current_user.status.emoji | ||||
|     %span.user-status-message.str-truncated | ||||
|       = current_user.status.message_html.html_safe | ||||
|  | @ -1,6 +1,3 @@ | |||
| - has_impersonation_link = header_link?(:admin_impersonation) | ||||
| - user_status_data = user_status_properties(current_user) | ||||
| 
 | ||||
| %header.navbar.navbar-gitlab.navbar-expand-sm.js-navbar.legacy-top-bar{ data: { testid: 'navbar' } } | ||||
|   %a.gl-sr-only.gl-accessibility{ href: "#content-body" } Skip to content | ||||
|   .container-fluid | ||||
|  | @ -12,64 +9,6 @@ | |||
|         %ul.nav.navbar-nav.gl-w-full.gl-align-items-center.gl-justify-content-end | ||||
|           - if current_user | ||||
|             = render 'layouts/header/new_dropdown', class: 'gl-display-none gl-sm-display-block gl-white-space-nowrap gl-text-right' | ||||
|           - if header_link?(:issues) | ||||
|             = nav_link(path: 'dashboard#issues', html_options: { class: "user-counter" }) do | ||||
|               = link_to assigned_issues_dashboard_path, title: _('Issues'), class: 'dashboard-shortcuts-issues js-prefetch-document', aria: { label: _('Issues') }, | ||||
|                 data: { testid: 'issues_shortcut_button', toggle: 'tooltip', placement: 'bottom', | ||||
|                 track_label: 'main_navigation', | ||||
|                 track_action: 'click_issues_link', | ||||
|                 track_property: 'navigation_top', | ||||
|                 container: 'body' } do | ||||
|                 = sprite_icon('issues') | ||||
|                 - issues_count = assigned_issuables_count(:issues) | ||||
|                 = gl_badge_tag({ size: :sm, variant: :success }, { class: "gl-ml-n2 #{'gl-display-none' if issues_count == 0}", "aria-label": n_("%d assigned issue", "%d assigned issues", issues_count) % issues_count }) do | ||||
|                   = assigned_open_issues_count_text | ||||
|           - if header_link?(:merge_requests) | ||||
|             = nav_link(path: 'dashboard#merge_requests', html_options: { class: "user-counter dropdown" }) do | ||||
|               - top_level_link = assigned_mrs_dashboard_path | ||||
|               = link_to top_level_link, class: 'dashboard-shortcuts-merge_requests has-tooltip', title: _('Merge requests'), aria: { label: _('Merge requests') }, | ||||
|                 data: { testid: 'merge_requests_shortcut_button', | ||||
|                 toggle: "dropdown", | ||||
|                 placement: 'bottom', | ||||
|                 track_label: 'merge_requests_menu', | ||||
|                 track_action: 'click_dropdown', | ||||
|                 track_property: 'navigation_top', | ||||
|                 container: 'body' } do | ||||
|                 = sprite_icon('git-merge') | ||||
|                 = gl_badge_tag({ size: :sm, variant: :warning }, { class: "js-merge-requests-count gl-ml-n2 #{'gl-display-none' if user_merge_requests_counts[:total] == 0}", "aria-label": n_("%d merge request", "%d merge requests", user_merge_requests_counts[:total]) % user_merge_requests_counts[:total] }) do | ||||
|                   = number_with_delimiter(user_merge_requests_counts[:total]) | ||||
|                 = sprite_icon('chevron-down', css_class: 'caret-down gl-mx-0!') | ||||
|               .dropdown-menu.dropdown-menu-right | ||||
|                 %ul | ||||
|                   %li.dropdown-header | ||||
|                     = _('Merge requests') | ||||
|                   %li | ||||
|                     = link_to assigned_mrs_dashboard_path, | ||||
|                       class: 'gl-display-flex! gl-align-items-center js-prefetch-document', | ||||
|                       data: {track_action: 'click_link', track_label: 'merge_requests_assigned', track_property: 'navigation_top'} do | ||||
|                       = _('Assigned') | ||||
|                       = gl_badge_tag({ variant: :neutral, size: :sm }, { class: "js-assigned-mr-count gl-ml-auto" }) do | ||||
|                         = user_merge_requests_counts[:assigned] | ||||
|                   %li | ||||
|                     = link_to reviewer_mrs_dashboard_path, | ||||
|                       class: 'dashboard-shortcuts-review_requests gl-display-flex! gl-align-items-center js-prefetch-document', | ||||
|                       data: {track_action: 'click_link', track_label: 'merge_requests_to_review', track_property: 'navigation_top'} do | ||||
|                       = _('Review requests') | ||||
|                       = gl_badge_tag({ variant: :neutral, size: :sm }, { class: "js-reviewer-mr-count gl-ml-auto" }) do | ||||
|                         = user_merge_requests_counts[:review_requested] | ||||
|           - if header_link?(:todos) | ||||
|             = nav_link(controller: 'dashboard/todos', html_options: { class: "user-counter" }) do | ||||
|               = link_to dashboard_todos_path, title: _('To-Do List'), aria: { label: _('To-Do List') }, class: 'shortcuts-todos js-prefetch-document', | ||||
|                 data: { testid: 'todos-shortcut-button', toggle: 'tooltip', placement: 'bottom', | ||||
|                 track_label: 'main_navigation', | ||||
|                 track_action: 'click_to_do_link', | ||||
|                 track_property: 'navigation_top', | ||||
|                 container: 'body' } do | ||||
|                 = sprite_icon('todo-done') | ||||
|                 -# The todos' counter badge's visibility is being toggled by adding or removing the .hidden class in Js. | ||||
|                 -# We'll eventually migrate to .gl-display-none: https://gitlab.com/gitlab-org/gitlab/-/issues/351792. | ||||
|                 = gl_badge_tag({ size: :sm, variant: :info }, { class: "js-todos-count gl-ml-n2 #{'hidden' if todos_pending_count == 0}", "aria-label": _("Todos count") }) do | ||||
|                   = todos_count_format(todos_pending_count) | ||||
|           %li.nav-item.header-help.dropdown.d-none.d-md-block | ||||
|             = link_to help_path, class: 'header-help-dropdown-toggle gl-relative', data: { toggle: "dropdown", track_action: 'click_question_mark_link', track_label: 'main_navigation', track_property: 'navigation_top' } do | ||||
|               %span.gl-sr-only | ||||
|  | @ -79,27 +18,6 @@ | |||
|               = sprite_icon('chevron-down', css_class: 'caret-down') | ||||
|             .dropdown-menu.dropdown-menu-right | ||||
|               = render 'layouts/header/help_dropdown' | ||||
|           - if header_link?(:user_dropdown) | ||||
|             %li.nav-item.header-user.js-nav-user-dropdown.dropdown{ data: { testid: 'user-dropdown' }, class: ('mr-0' if has_impersonation_link) } | ||||
|               = link_to current_user, class: user_dropdown_class, data: { toggle: "dropdown", track_label: "profile_dropdown", track_action: "click_dropdown", track_property: "navigation_top" } do | ||||
|                 = render Pajamas::AvatarComponent.new(current_user, size: 24, class: 'header-user-avatar', avatar_options: { data: { testid: 'user-avatar-content' } }) | ||||
|                 = render_if_exists 'layouts/header/user_notification_dot', project: project, namespace: group | ||||
|                 = sprite_icon('chevron-down', css_class: 'caret-down') | ||||
|               .dropdown-menu.dropdown-menu-right | ||||
|                 = render 'layouts/header/current_user_dropdown' | ||||
|           - if has_impersonation_link | ||||
|             %li.nav-item.impersonation.ml-0 | ||||
|               = render Pajamas::ButtonComponent.new(href: admin_impersonation_path, icon: 'incognito', button_options: { title: _('Stop impersonation'), class: 'impersonation-btn', aria: { label: _('Stop impersonation') }, data: { method: :delete, toggle: 'tooltip', placement: 'bottom', container: 'body', testid: 'stop_impersonation_btn' } }) | ||||
|           - if header_link?(:sign_in) | ||||
|             - if allow_signup? | ||||
|               %li.nav-item | ||||
|                 = render Pajamas::ButtonComponent.new(href: new_user_registration_path) do | ||||
|                   = _('Register') | ||||
|             %li.nav-item{ class: 'gl-flex-grow-0! gl-flex-basis-half!' } | ||||
|               = link_to _('Sign in'), new_session_path(:user, redirect_to_referer: 'yes') | ||||
| 
 | ||||
| - if display_whats_new? | ||||
|   #whats-new-app{ data: { version_digest: whats_new_version_digest } } | ||||
| 
 | ||||
| - if can?(current_user, :update_user_status, current_user) | ||||
|   .js-set-status-modal-wrapper{ data: user_status_data } | ||||
|  |  | |||
|  | @ -1,7 +1,6 @@ | |||
| - @breadcrumb_title = _("Help") | ||||
| - page_title    _("Help") | ||||
| - header_title  _("Help"), help_path | ||||
| - if show_super_sidebar? | ||||
|   - @force_desktop_expanded_sidebar = true | ||||
| - @force_desktop_expanded_sidebar = true | ||||
| 
 | ||||
| = render template: "layouts/application" | ||||
|  |  | |||
|  | @ -1,7 +1,6 @@ | |||
| - page_title    _("Search") | ||||
| - header_title  _("Search"), search_path | ||||
| - add_page_specific_style 'page_bundles/search' | ||||
| - if show_super_sidebar? | ||||
|   - @force_desktop_expanded_sidebar = true | ||||
| - @force_desktop_expanded_sidebar = true | ||||
| 
 | ||||
| = render template: "layouts/application" | ||||
|  |  | |||
|  | @ -18,6 +18,7 @@ | |||
|         selected_file_template_project_id: "#{@project.service_desk_setting&.file_template_project_id}", | ||||
|         outgoing_name: "#{@project.service_desk_setting&.outgoing_name}", | ||||
|         project_key: "#{@project.service_desk_setting&.project_key}", | ||||
|         reopen_issue_on_external_participant_note: "#{@project.service_desk_setting&.reopen_issue_on_external_participant_note}", | ||||
|         add_external_participants_from_cc: "#{@project.service_desk_setting&.add_external_participants_from_cc}", | ||||
|         templates: available_service_desk_templates_for(@project), | ||||
|         public_project: "#{@project.public?}", | ||||
|  |  | |||
|  | @ -8,9 +8,7 @@ | |||
| - elsif @search_objects.blank? | ||||
|   = render partial: "search/results/empty" | ||||
| - else | ||||
|   - statusBarClass = !show_super_sidebar? ? 'gl-lg-pl-5' : '' | ||||
| 
 | ||||
|   .section{ class: statusBarClass } | ||||
|   .section | ||||
|     - if @scope == 'commits' | ||||
|       %ul.content-list.commit-list | ||||
|         = render partial: "search/results/commit", collection: @search_objects | ||||
|  |  | |||
|  | @ -1,7 +1,4 @@ | |||
| - statusBarClass = !show_super_sidebar? ? 'gl-lg-pl-5' : '' | ||||
| - statusBarClass = statusBarClass + ' gl-lg-display-none' if @search_objects.to_a.empty? | ||||
| 
 | ||||
| .section{ class: statusBarClass } | ||||
| .section{ class: ('gl-lg-display-none' if @search_objects.to_a.empty?) } | ||||
|   .search-results-status | ||||
|     .gl-display-flex.gl-flex-direction-column | ||||
|       .gl-p-5.gl-display-flex.gl-flex-wrap | ||||
|  |  | |||
|  | @ -16,8 +16,8 @@ | |||
|     - page_description(_("%{count} %{scope} for term '%{term}'") % { count: @search_results.formatted_count(@scope), scope: @scope, term: @search_term }) | ||||
|   - page_card_attributes("Namespace" => @group&.full_path, "Project" => @project&.full_path) | ||||
| 
 | ||||
| #js-search-topbar{ data: { "group-initial-json": group_attributes.to_json, "project-initial-json": project_attributes.to_json, "default-branch-name": @project&.default_branch } } | ||||
| #js-search-topbar{ data: { "default-branch-name": @project&.default_branch } } | ||||
| .results.gl-lg-display-flex.gl-mt-0 | ||||
|   #js-search-sidebar{ data: { navigation_json: search_navigation_json, search_type: search_service.search_type } } | ||||
|   #js-search-sidebar{ data: { navigation_json: search_navigation_json, search_type: search_service.search_type, group_initial_json: group_attributes.to_json, project_initial_json: project_attributes.to_json, } } | ||||
|   - if @search_term | ||||
|     = render 'search/results' | ||||
|  |  | |||
|  | @ -1,2 +1 @@ | |||
| - current_href = Feature.enabled?(:global_ci_catalog, @user) ? href : nil | ||||
| = render Pajamas::BadgeComponent.new(s_('CiCatalog|CI/CD catalog resource'), variant: 'info', icon: 'catalog-checkmark', class: css_class, href: current_href) | ||||
| = render Pajamas::BadgeComponent.new(s_('CiCatalog|CI/CD catalog resource'), variant: 'info', icon: 'catalog-checkmark', class: css_class, href: href) | ||||
|  |  | |||
|  | @ -19,4 +19,4 @@ | |||
| 
 | ||||
|   %p | ||||
|     = s_("Runners|Tags control which type of jobs a runner can handle. By tagging a runner, you make sure shared runners only handle the jobs they are equipped to run.") | ||||
|     = link_to _("Learn more."), help_page_path("ci/runners/configure_runners", anchor: "use-tags-to-control-which-jobs-a-runner-can-run"), target: '_blank' | ||||
|     = link_to _("Learn more."), help_page_path("ci/runners/configure_runners", anchor: "how-the-runner-uses-tags"), target: '_blank' | ||||
|  |  | |||
|  | @ -1,8 +1,8 @@ | |||
| --- | ||||
| name: global_ci_catalog  | ||||
| introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/133885 | ||||
| rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/427940  | ||||
| milestone: '16.6' | ||||
| name: encoding_logs_tree | ||||
| introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/136323 | ||||
| rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/432559 | ||||
| milestone: '16.7' | ||||
| type: development | ||||
| group: group::pipeline authoring  | ||||
| group: group::source code | ||||
| default_enabled: false | ||||
|  | @ -0,0 +1,8 @@ | |||
| --- | ||||
| name: product_analytics_beta_optin | ||||
| introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/138469 | ||||
| rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/433344 | ||||
| milestone: '16.7' | ||||
| type: development | ||||
| group: group::product analytics | ||||
| default_enabled: false | ||||
|  | @ -187,6 +187,8 @@ | |||
|   - 1 | ||||
| - - compliance_management_pending_status_check | ||||
|   - 1 | ||||
| - - compliance_management_standards_adherence_export_mailer | ||||
|   - 1 | ||||
| - - compliance_management_standards_gitlab_at_least_two_approvals | ||||
|   - 1 | ||||
| - - compliance_management_standards_gitlab_base | ||||
|  |  | |||
|  | @ -4,3 +4,4 @@ description: Backfill missing vulnerability dimissal information as a result of | |||
| feature_category: vulnerability_management | ||||
| introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/126253 | ||||
| milestone: '16.2' | ||||
| finalized_by: '20231207220935' | ||||
|  |  | |||
|  | @ -1,6 +1,9 @@ | |||
| --- | ||||
| migration_job_name: BackfillRootStorageStatisticsForkStorageSizes | ||||
| description: Backfill the public_forks_storage_size, internal_forks_storage_size, and private_forks_storage_size columns on the namespace_root_storage_statistics table | ||||
| description: Backfill the public_forks_storage_size, internal_forks_storage_size, | ||||
|   and private_forks_storage_size columns on the namespace_root_storage_statistics | ||||
|   table | ||||
| feature_category: consumables_cost_management | ||||
| introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/120916 | ||||
| milestone: '16.1' | ||||
| finalized_by: '20231207221036' | ||||
|  |  | |||
|  | @ -0,0 +1,10 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| class AddHomeOrganizationIdToUserPreferences < Gitlab::Database::Migration[2.2] | ||||
|   enable_lock_retries! | ||||
|   milestone '16.7' | ||||
| 
 | ||||
|   def change | ||||
|     add_column(:user_preferences, :home_organization_id, :bigint, null: true) | ||||
|   end | ||||
| end | ||||
|  | @ -0,0 +1,16 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| class AddHomeOrganizationIdIndexToUserPreferences < Gitlab::Database::Migration[2.2] | ||||
|   disable_ddl_transaction! | ||||
|   milestone '16.7' | ||||
| 
 | ||||
|   INDEX = 'index_user_preferences_on_home_organization_id' | ||||
| 
 | ||||
|   def up | ||||
|     add_concurrent_index(:user_preferences, :home_organization_id, name: INDEX) | ||||
|   end | ||||
| 
 | ||||
|   def down | ||||
|     remove_concurrent_index_by_name(:user_preferences, name: INDEX) | ||||
|   end | ||||
| end | ||||
|  | @ -0,0 +1,16 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| class AddHomeOrganizationIdFkToUserPreferences < Gitlab::Database::Migration[2.2] | ||||
|   disable_ddl_transaction! | ||||
|   milestone '16.7' | ||||
| 
 | ||||
|   def up | ||||
|     add_concurrent_foreign_key(:user_preferences, :organizations, column: :home_organization_id, on_delete: :nullify) | ||||
|   end | ||||
| 
 | ||||
|   def down | ||||
|     with_lock_retries do | ||||
|       remove_foreign_key_if_exists :user_preferences, column: :home_organization_id | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  | @ -0,0 +1,21 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| class FinalizeBackfillMissingVulnerabilityDismissalDetails < Gitlab::Database::Migration[2.2] | ||||
|   milestone '16.7' | ||||
| 
 | ||||
|   disable_ddl_transaction! | ||||
| 
 | ||||
|   restrict_gitlab_migration gitlab_schema: :gitlab_main | ||||
| 
 | ||||
|   def up | ||||
|     ensure_batched_background_migration_is_finished( | ||||
|       job_class_name: 'BackfillMissingVulnerabilityDismissalDetails', | ||||
|       table_name: :vulnerabilities, | ||||
|       column_name: :id, | ||||
|       job_arguments: [], | ||||
|       finalize: true | ||||
|     ) | ||||
|   end | ||||
| 
 | ||||
|   def down; end | ||||
| end | ||||
|  | @ -0,0 +1,21 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| class FinalizeBackfillRootStorageStatisticsForkStorageSizes < Gitlab::Database::Migration[2.2] | ||||
|   milestone '16.7' | ||||
| 
 | ||||
|   disable_ddl_transaction! | ||||
| 
 | ||||
|   restrict_gitlab_migration gitlab_schema: :gitlab_main | ||||
| 
 | ||||
|   def up | ||||
|     ensure_batched_background_migration_is_finished( | ||||
|       job_class_name: 'BackfillRootStorageStatisticsForkStorageSizes', | ||||
|       table_name: :namespace_root_storage_statistics, | ||||
|       column_name: :namespace_id, | ||||
|       job_arguments: [], | ||||
|       finalize: true | ||||
|     ) | ||||
|   end | ||||
| 
 | ||||
|   def down; end | ||||
| end | ||||
|  | @ -0,0 +1 @@ | |||
| 624bef2f1f8ebd81bdc49c0007a72d77bad34db215dfee01101d976000964e28 | ||||
|  | @ -0,0 +1 @@ | |||
| 4d4539b21d0f9ea2ade7a1223953f2aea36c4432e5f3b042266e98d71f6a9a48 | ||||
|  | @ -0,0 +1 @@ | |||
| 7078ee3b40cd12e32c0d8f2cc1e55e19b4352dac8d2c708b617a2ff03e979c3a | ||||
|  | @ -0,0 +1 @@ | |||
| 644dfd3c7371feff5431900510e25fc2dc0c661c7ee9142bd26431c10d929416 | ||||
|  | @ -0,0 +1 @@ | |||
| 5afbc4c287ce349c58ab70e2c8b44c833f075fe114cf2af2a29aaf4247053d82 | ||||
|  | @ -24663,6 +24663,7 @@ CREATE TABLE user_preferences ( | |||
|     enabled_zoekt boolean DEFAULT true NOT NULL, | ||||
|     keyboard_shortcuts_enabled boolean DEFAULT true NOT NULL, | ||||
|     time_display_format smallint DEFAULT 0 NOT NULL, | ||||
|     home_organization_id bigint, | ||||
|     CONSTRAINT check_89bf269f41 CHECK ((char_length(diffs_deletion_color) <= 7)), | ||||
|     CONSTRAINT check_d07ccd35f7 CHECK ((char_length(diffs_addition_color) <= 7)) | ||||
| ); | ||||
|  | @ -34896,6 +34897,8 @@ CREATE INDEX index_user_phone_validations_on_dial_code_phone_number ON user_phon | |||
| 
 | ||||
| CREATE INDEX index_user_preferences_on_gitpod_enabled ON user_preferences USING btree (gitpod_enabled); | ||||
| 
 | ||||
| CREATE INDEX index_user_preferences_on_home_organization_id ON user_preferences USING btree (home_organization_id); | ||||
| 
 | ||||
| CREATE UNIQUE INDEX index_user_preferences_on_user_id ON user_preferences USING btree (user_id); | ||||
| 
 | ||||
| CREATE INDEX index_user_project_callouts_on_project_id ON user_project_callouts USING btree (project_id); | ||||
|  | @ -38158,6 +38161,9 @@ ALTER TABLE ONLY gitlab_subscriptions | |||
| ALTER TABLE ONLY abuse_events | ||||
|     ADD CONSTRAINT fk_e5ce49c215 FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL; | ||||
| 
 | ||||
| ALTER TABLE ONLY user_preferences | ||||
|     ADD CONSTRAINT fk_e5e029c10b FOREIGN KEY (home_organization_id) REFERENCES organizations(id) ON DELETE SET NULL; | ||||
| 
 | ||||
| ALTER TABLE ONLY merge_requests | ||||
|     ADD CONSTRAINT fk_e719a85f8a FOREIGN KEY (author_id) REFERENCES users(id) ON DELETE SET NULL; | ||||
| 
 | ||||
|  |  | |||
|  | @ -1249,8 +1249,15 @@ By default, Gitaly doesn't sign commits made using GitLab UI. For example, commi | |||
| - Web IDE. | ||||
| - Merge requests. | ||||
| 
 | ||||
| You can configure Gitaly to sign commits made with the GitLab UI. The commits show as unverified and signed by an unknown | ||||
| user. Support for improvements is proposed in [issue 19185](https://gitlab.com/gitlab-org/gitlab/-/issues/19185). | ||||
| You can configure Gitaly to sign commits made with the GitLab UI. | ||||
| 
 | ||||
| By default, Gitaly sets the author of a commit as the committer. In this case, | ||||
| it is harder to [Verify commits locally](../../user/project/repository/signed_commits/ssh.md#verify-commits-locally) | ||||
| because the signature belongs to neither the author nor the committer of the commit. | ||||
| 
 | ||||
| You can configure Gitaly to reflect that a commit has been committed by your instance by | ||||
| setting `committer_email` and `committer_name`. For example, on GitLab.com these configuration options are | ||||
| set to `noreply@gitlab.com` and `GitLab`. | ||||
| 
 | ||||
| Configure Gitaly to sign commits made with the GitLab UI in one of two ways: | ||||
| 
 | ||||
|  | @ -1281,7 +1288,10 @@ Configure Gitaly to sign commits made with the GitLab UI in one of two ways: | |||
|       # ... | ||||
|       git: { | ||||
|         # ... | ||||
|         committer_name: 'Your Instance', | ||||
|         committer_email: 'noreply@yourinstance.com', | ||||
|         signing_key: '/etc/gitlab/gitaly/signing_key.gpg', | ||||
|         # ... | ||||
|       }, | ||||
|    } | ||||
|    ``` | ||||
|  | @ -1310,6 +1320,8 @@ Configure Gitaly to sign commits made with the GitLab UI in one of two ways: | |||
| 
 | ||||
|    ```toml | ||||
|    [git] | ||||
|    committer_name = "Your Instance" | ||||
|    committer_email = "noreply@yourinstance.com" | ||||
|    signing_key = "/etc/gitlab/gitaly/signing_key.gpg" | ||||
|    ``` | ||||
| 
 | ||||
|  |  | |||
|  | @ -435,7 +435,7 @@ You can change the maximum time a job can run before it times out: | |||
| 
 | ||||
| - At the project-level in the [project's CI/CD settings](../ci/pipelines/settings.md#set-a-limit-for-how-long-jobs-can-run) | ||||
|   for a given project. This limit must be between 10 minutes and 1 month. | ||||
| - At the [runner level](../ci/runners/configure_runners.md#set-maximum-job-timeout-for-a-runner). | ||||
| - At the [runner level](../ci/runners/configure_runners.md#set-the-maximum-job-timeout). | ||||
|   This limit must be 10 minutes or longer. | ||||
| 
 | ||||
| ### Maximum number of deployment jobs in a pipeline | ||||
|  |  | |||
|  | @ -108,13 +108,24 @@ Notify.test_email(u.email, "Test email for #{u.name}", 'Test email').deliver_now | |||
| ## Disable database statement timeout | ||||
| 
 | ||||
| You can disable the PostgreSQL statement timeout for the current Rails console | ||||
| session by running: | ||||
| session. | ||||
| 
 | ||||
| In GitLab 15.11 and earlier, to disable the database statement timeout, run: | ||||
| 
 | ||||
| ```ruby | ||||
| ActiveRecord::Base.connection.execute('SET statement_timeout TO 0') | ||||
| ``` | ||||
| 
 | ||||
| This change only affects the current Rails console session and is | ||||
| In GitLab 16.0 and later, [GitLab uses two database connections by default](../../update/versions/gitlab_16_changes.md#1600). To disable the database statement timeout, run: | ||||
| 
 | ||||
| ```ruby | ||||
| ActiveRecord::Base.connection.execute('SET statement_timeout TO 0') | ||||
| Ci::ApplicationRecord.connection.execute('SET statement_timeout TO 0') | ||||
| ``` | ||||
| 
 | ||||
| Instances running GitLab 16.0 and later reconfigured to use a single database connection should disable the database statement timeout using the code for GitLab 15.11 and earlier. | ||||
| 
 | ||||
| Disabling the database statement timeout affects only the current Rails console session and is | ||||
| not persisted in the GitLab production environment or in the next Rails | ||||
| console session. | ||||
| 
 | ||||
|  |  | |||
|  | @ -109,8 +109,7 @@ Prerequisites: | |||
| If you need support for namespace in the URL path to remove the requirement for wildcard DNS: | ||||
| 
 | ||||
| 1. Enable the GitLab Pages flag for this feature by adding | ||||
|    `gitlab_pages["namespace_in_path"] = true` to `gitlab.rb`. For more information, | ||||
|    see [Use environment variables](#use-environment-variables). | ||||
|    `gitlab_pages["namespace_in_path"] = true` to `/etc/gitlab/gitlab.rb`. | ||||
| 1. In your DNS provider, add entries for `example.com` and `projects.example.com`. | ||||
|    In both lines, replace `example.com` with your domain name, and `192.0.0.0` with | ||||
|    the IPv4 version of your IP address. The entries look like this: | ||||
|  | @ -214,8 +213,7 @@ Prerequisites: | |||
| 
 | ||||
|    pages_nginx['enable'] = true | ||||
| 
 | ||||
|    # Set this feature flag to enable this feature | ||||
|    # For more information, see https://docs.gitlab.com/ee/administration/pages/index.html#use-environment-variables | ||||
|    # Set this flag to enable this feature | ||||
|    gitlab_pages["namespace_in_path"] = true | ||||
|    ``` | ||||
| 
 | ||||
|  | @ -304,8 +302,7 @@ daemon doesn't listen to the outside world: | |||
|    pages_nginx['enable'] = true | ||||
|    pages_nginx['redirect_http_to_https'] = true | ||||
| 
 | ||||
|    # Set this feature flag to enable this feature | ||||
|    # For more information, see https://docs.gitlab.com/ee/administration/pages/index.html#use-environment-variables | ||||
|    # Set this flag to enable this feature | ||||
|    gitlab_pages["namespace_in_path"] = true | ||||
|    ``` | ||||
| 
 | ||||
|  |  | |||
|  | @ -20281,6 +20281,31 @@ Returns [`WorkItem`](#workitem). | |||
| | ---- | ---- | ----------- | | ||||
| | <a id="groupworkitemiid"></a>`iid` | [`String!`](#string) | IID of the work item. | | ||||
| 
 | ||||
| ##### `Group.workItemStateCounts` | ||||
| 
 | ||||
| Counts of work items by state for the namespace. Returns `null` if the `namespace_level_work_items` feature flag is disabled. | ||||
| 
 | ||||
| WARNING: | ||||
| **Introduced** in 16.7. | ||||
| This feature is an Experiment. It can be changed or removed at any time. | ||||
| 
 | ||||
| Returns [`WorkItemStateCountsType`](#workitemstatecountstype). | ||||
| 
 | ||||
| ###### Arguments | ||||
| 
 | ||||
| | Name | Type | Description | | ||||
| | ---- | ---- | ----------- | | ||||
| | <a id="groupworkitemstatecountsauthorusername"></a>`authorUsername` **{warning-solid}** | [`String`](#string) | **Introduced** in 15.9. This feature is an Experiment. It can be changed or removed at any time. Filter work items by author username. | | ||||
| | <a id="groupworkitemstatecountsiid"></a>`iid` | [`String`](#string) | IID of the work item. For example, "1". | | ||||
| | <a id="groupworkitemstatecountsiids"></a>`iids` | [`[String!]`](#string) | List of IIDs of work items. For example, `["1", "2"]`. | | ||||
| | <a id="groupworkitemstatecountsin"></a>`in` | [`[IssuableSearchableField!]`](#issuablesearchablefield) | Specify the fields to perform the search in. Defaults to `[TITLE, DESCRIPTION]`. Requires the `search` argument.'. | | ||||
| | <a id="groupworkitemstatecountsrequirementlegacywidget"></a>`requirementLegacyWidget` **{warning-solid}** | [`RequirementLegacyFilterInput`](#requirementlegacyfilterinput) | **Deprecated** in 15.9. Use work item IID filter instead. | | ||||
| | <a id="groupworkitemstatecountssearch"></a>`search` | [`String`](#string) | Search query for title or description. | | ||||
| | <a id="groupworkitemstatecountssort"></a>`sort` | [`WorkItemSort`](#workitemsort) | Sort work items by criteria. | | ||||
| | <a id="groupworkitemstatecountsstate"></a>`state` | [`IssuableState`](#issuablestate) | Current state of the work item. | | ||||
| | <a id="groupworkitemstatecountsstatuswidget"></a>`statusWidget` | [`StatusFilterInput`](#statusfilterinput) | Input for status widget filter. Ignored if `work_items_mvc_2` is disabled. | | ||||
| | <a id="groupworkitemstatecountstypes"></a>`types` | [`[IssueType!]`](#issuetype) | Filter work items by the given work item types. | | ||||
| 
 | ||||
| ##### `Group.workItemTypes` | ||||
| 
 | ||||
| Work item types available to the group. | ||||
|  | @ -25587,6 +25612,31 @@ Returns [`VulnerabilitySeveritiesCount`](#vulnerabilityseveritiescount). | |||
| | <a id="projectvulnerabilityseveritiescountseverity"></a>`severity` | [`[VulnerabilitySeverity!]`](#vulnerabilityseverity) | Filter vulnerabilities by severity. | | ||||
| | <a id="projectvulnerabilityseveritiescountstate"></a>`state` | [`[VulnerabilityState!]`](#vulnerabilitystate) | Filter vulnerabilities by state. | | ||||
| 
 | ||||
| ##### `Project.workItemStateCounts` | ||||
| 
 | ||||
| Counts of work items by state for the project. | ||||
| 
 | ||||
| WARNING: | ||||
| **Introduced** in 16.7. | ||||
| This feature is an Experiment. It can be changed or removed at any time. | ||||
| 
 | ||||
| Returns [`WorkItemStateCountsType`](#workitemstatecountstype). | ||||
| 
 | ||||
| ###### Arguments | ||||
| 
 | ||||
| | Name | Type | Description | | ||||
| | ---- | ---- | ----------- | | ||||
| | <a id="projectworkitemstatecountsauthorusername"></a>`authorUsername` **{warning-solid}** | [`String`](#string) | **Introduced** in 15.9. This feature is an Experiment. It can be changed or removed at any time. Filter work items by author username. | | ||||
| | <a id="projectworkitemstatecountsiid"></a>`iid` | [`String`](#string) | IID of the work item. For example, "1". | | ||||
| | <a id="projectworkitemstatecountsiids"></a>`iids` | [`[String!]`](#string) | List of IIDs of work items. For example, `["1", "2"]`. | | ||||
| | <a id="projectworkitemstatecountsin"></a>`in` | [`[IssuableSearchableField!]`](#issuablesearchablefield) | Specify the fields to perform the search in. Defaults to `[TITLE, DESCRIPTION]`. Requires the `search` argument.'. | | ||||
| | <a id="projectworkitemstatecountsrequirementlegacywidget"></a>`requirementLegacyWidget` **{warning-solid}** | [`RequirementLegacyFilterInput`](#requirementlegacyfilterinput) | **Deprecated** in 15.9. Use work item IID filter instead. | | ||||
| | <a id="projectworkitemstatecountssearch"></a>`search` | [`String`](#string) | Search query for title or description. | | ||||
| | <a id="projectworkitemstatecountssort"></a>`sort` | [`WorkItemSort`](#workitemsort) | Sort work items by criteria. | | ||||
| | <a id="projectworkitemstatecountsstate"></a>`state` | [`IssuableState`](#issuablestate) | Current state of the work item. | | ||||
| | <a id="projectworkitemstatecountsstatuswidget"></a>`statusWidget` | [`StatusFilterInput`](#statusfilterinput) | Input for status widget filter. Ignored if `work_items_mvc_2` is disabled. | | ||||
| | <a id="projectworkitemstatecountstypes"></a>`types` | [`[IssueType!]`](#issuetype) | Filter work items by the given work item types. | | ||||
| 
 | ||||
| ##### `Project.workItemTypes` | ||||
| 
 | ||||
| Work item types available to the project. | ||||
|  | @ -28684,6 +28734,18 @@ Check permissions for the current user on a work item. | |||
| | <a id="workitempermissionssetworkitemmetadata"></a>`setWorkItemMetadata` | [`Boolean!`](#boolean) | If `true`, the user can perform `set_work_item_metadata` on this resource. | | ||||
| | <a id="workitempermissionsupdateworkitem"></a>`updateWorkItem` | [`Boolean!`](#boolean) | If `true`, the user can perform `update_work_item` on this resource. | | ||||
| 
 | ||||
| ### `WorkItemStateCountsType` | ||||
| 
 | ||||
| Represents total number of work items for the represented states. | ||||
| 
 | ||||
| #### Fields | ||||
| 
 | ||||
| | Name | Type | Description | | ||||
| | ---- | ---- | ----------- | | ||||
| | <a id="workitemstatecountstypeall"></a>`all` | [`Int`](#int) | Number of work items for the project or group. | | ||||
| | <a id="workitemstatecountstypeclosed"></a>`closed` | [`Int`](#int) | Number of work items with state CLOSED for the project or group. | | ||||
| | <a id="workitemstatecountstypeopened"></a>`opened` | [`Int`](#int) | Number of work items with state OPENED for the project or group. | | ||||
| 
 | ||||
| ### `WorkItemType` | ||||
| 
 | ||||
| #### Fields | ||||
|  |  | |||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -45,7 +45,7 @@ can't link to files outside it. | |||
| 
 | ||||
| To ensure maximum availability of the cache, do one or more of the following: | ||||
| 
 | ||||
| - [Tag your runners](../runners/configure_runners.md#use-tags-to-control-which-jobs-a-runner-can-run) and use the tag on jobs | ||||
| - [Tag your runners](../runners/configure_runners.md#control-jobs-that-a-runner-can-run) and use the tag on jobs | ||||
|   that share the cache. | ||||
| - [Use runners that are only available to a particular project](../runners/runners_scope.md#prevent-a-project-runner-from-being-enabled-for-other-projects). | ||||
| - [Use a `key`](../yaml/index.md#cachekey) that fits your workflow. For example, | ||||
|  |  | |||
|  | @ -216,6 +216,17 @@ If you disable [catalog resource setting](#set-a-component-project-as-a-catalog- | |||
| the component project and all versions are removed from the catalog. To publish it again, | ||||
| you must re-enable the setting and release a new version. | ||||
| 
 | ||||
| ### Unpublish a component project | ||||
| 
 | ||||
| To remove a component project from the catalog, turn off the [**CI/CD Catalog resource**](#set-a-component-project-as-a-catalog-resource) toggle. | ||||
| in the project settings. | ||||
| 
 | ||||
| WARNING: | ||||
| This action destroys the metadata about the component project and its versions published | ||||
| in the catalog. The project and its repository still exist, but are not visible in the catalog. | ||||
| 
 | ||||
| To publish the component project in the catalog again, you need to [publish a new release](#publish-a-new-release). | ||||
| 
 | ||||
| ## Best practices | ||||
| 
 | ||||
| This section describes some best practices for creating high quality component projects. | ||||
|  |  | |||
|  | @ -466,7 +466,7 @@ Some key details about runners: | |||
| 
 | ||||
| - Runners can be [configured](../runners/runners_scope.md) to be shared across an instance, | ||||
|   a group, or dedicated to a single project. | ||||
| - You can use the [`tags` keyword](../runners/configure_runners.md#use-tags-to-control-which-jobs-a-runner-can-run) | ||||
| - You can use the [`tags` keyword](../runners/configure_runners.md#control-jobs-that-a-runner-can-run) | ||||
|   for finer control, and associate runners with specific jobs. For example, you can use a tag for jobs that | ||||
|   require dedicated, more powerful, or specific hardware. | ||||
| - GitLab has [autoscaling for runners](https://docs.gitlab.com/runner/configuration/autoscale.html). | ||||
|  |  | |||
|  | @ -482,7 +482,7 @@ Some key details about runners: | |||
| 
 | ||||
| - Runners can be [configured](../runners/runners_scope.md) to be shared across an instance, | ||||
|   a group, or dedicated to a single project. | ||||
| - You can use the [`tags` keyword](../runners/configure_runners.md#use-tags-to-control-which-jobs-a-runner-can-run) | ||||
| - You can use the [`tags` keyword](../runners/configure_runners.md#control-jobs-that-a-runner-can-run) | ||||
|   for finer control, and associate runners with specific jobs. For example, you can use a tag for jobs that | ||||
|   require dedicated, more powerful, or specific hardware. | ||||
| - GitLab has [autoscaling for runners](https://docs.gitlab.com/runner/configuration/autoscale.html). | ||||
|  |  | |||
|  | @ -225,7 +225,7 @@ You can define how long a job can run before it times out. | |||
| 
 | ||||
| Jobs that exceed the timeout are marked as failed. | ||||
| 
 | ||||
| You can override this value [for individual runners](../runners/configure_runners.md#set-maximum-job-timeout-for-a-runner). | ||||
| You can override this value [for individual runners](../runners/configure_runners.md#set-the-maximum-job-timeout). | ||||
| 
 | ||||
| ## Pipeline badges | ||||
| 
 | ||||
|  |  | |||
|  | @ -11,53 +11,79 @@ If you have installed your own runners, you can configure and secure them in Git | |||
| If you need to configure runners on the machine where you installed GitLab Runner, see | ||||
| [the GitLab Runner documentation](https://docs.gitlab.com/runner/configuration/). | ||||
| 
 | ||||
| ## Manually clear the runner cache | ||||
| ## Set the maximum job timeout | ||||
| 
 | ||||
| Read [clearing the cache](../caching/index.md#clearing-the-cache). | ||||
| You can specify a maximum job timeout for each runner to prevent projects | ||||
| with longer job timeouts from using the runner. The maximum job timeout is | ||||
| used of it is shorter than the job timeout defined in the project. | ||||
| 
 | ||||
| ## Set maximum job timeout for a runner | ||||
| ### For a shared runner | ||||
| 
 | ||||
| For each runner, you can specify a *maximum job timeout*. This timeout, | ||||
| if smaller than the [project defined timeout](../pipelines/settings.md#set-a-limit-for-how-long-jobs-can-run), takes precedence. | ||||
| Prerequisites: | ||||
| 
 | ||||
| This feature can be used to prevent your shared runner from being overwhelmed | ||||
| by a project that has jobs with a long timeout (for example, one week). | ||||
| - You must be an administrator. | ||||
| 
 | ||||
| On GitLab.com, you cannot override the job timeout for shared runners and must use the [project defined timeout](../pipelines/settings.md#set-a-limit-for-how-long-jobs-can-run). | ||||
| On GitLab.com, you cannot override the job timeout for shared runners and must use the [project defined timeout](../pipelines/settings.md#set-a-limit-for-how-long-jobs-can-run) instead. | ||||
| 
 | ||||
| To set the maximum job timeout: | ||||
| 
 | ||||
| 1. On the left sidebar, at the bottom, select **Admin Area**. | ||||
| 1. Select **CI/CD > Runners**. | ||||
| 1. To the right of the runner, you want to edit, select **Edit** (**{pencil}**). | ||||
| 1. In the **Maximum job timeout** field, enter a value in seconds. The minimum amount is 600 seconds (10 minutes). | ||||
| 1. Select **Save changes**. | ||||
| 
 | ||||
| ### For a group runner | ||||
| 
 | ||||
| Prerequisites: | ||||
| 
 | ||||
| - You must have the Owner role for the group. | ||||
| 
 | ||||
| To set the maximum job timeout: | ||||
| 
 | ||||
| 1. On the left sidebar, select **Search or go to** and find your group. | ||||
| 1. Select **Build > Runners**. | ||||
| 1. To the right of the runner you want to edit, select **Edit** (**{pencil}**). | ||||
| 1. In the **Maximum job timeout** field, enter a value in seconds. The minimum amount is 600 seconds (10 minutes). | ||||
| 1. Select **Save changes**. | ||||
| 
 | ||||
| ### For a project runner | ||||
| 
 | ||||
| Prerequisites: | ||||
| 
 | ||||
| - You must have the Owner role for the project. | ||||
| 
 | ||||
| To set the maximum job timeout: | ||||
| 
 | ||||
| 1. On the left sidebar, select **Search or go to** and find your project. | ||||
| 1. Select **Settings > CI/CD**. | ||||
| 1. Expand **Runners**. | ||||
| 1. Select your project runner to edit the settings. | ||||
| 1. Enter a value under **Maximum job timeout**. Must be 10 minutes or more. If not | ||||
|    defined, the [project's job timeout setting](../pipelines/settings.md#set-a-limit-for-how-long-jobs-can-run) | ||||
|    is used. | ||||
| 1. To the right of the runner you want to edit, select **Edit** (**{pencil}**). | ||||
| 1. In the **Maximum job timeout** field, enter a value in seconds. The minimum amount is 600 seconds (10 minutes). If not defined, the [job timeout for the project](../pipelines/settings.md#set-a-limit-for-how-long-jobs-can-run) is used instead. | ||||
| 1. Select **Save changes**. | ||||
| 
 | ||||
| How this feature works: | ||||
| ## How maximum job timeout works | ||||
| 
 | ||||
| **Example 1 - Runner timeout bigger than project timeout** | ||||
| 
 | ||||
| 1. You set the _maximum job timeout_ for a runner to 24 hours | ||||
| 1. You set the _CI/CD Timeout_ for a project to **2 hours** | ||||
| 1. You start a job | ||||
| 1. The job, if running longer, times out after **2 hours** | ||||
| 1. You set the _maximum job timeout_ for a runner to 24 hours. | ||||
| 1. You set the _CI/CD Timeout_ for a project to **2 hours**. | ||||
| 1. You start a job. | ||||
| 1. The job, if running longer, times out after **2 hours**. | ||||
| 
 | ||||
| **Example 2 - Runner timeout not configured** | ||||
| 
 | ||||
| 1. You remove the _maximum job timeout_ configuration from a runner | ||||
| 1. You set the _CI/CD Timeout_ for a project to **2 hours** | ||||
| 1. You start a job | ||||
| 1. The job, if running longer, times out after **2 hours** | ||||
| 1. You remove the _maximum job timeout_ configuration from a runner. | ||||
| 1. You set the _CI/CD Timeout_ for a project to **2 hours**. | ||||
| 1. You start a job. | ||||
| 1. The job, if running longer, times out after **2 hours**. | ||||
| 
 | ||||
| **Example 3 - Runner timeout smaller than project timeout** | ||||
| 
 | ||||
| 1. You set the _maximum job timeout_ for a runner to **30 minutes** | ||||
| 1. You set the _CI/CD Timeout_ for a project to 2 hours | ||||
| 1. You start a job | ||||
| 1. The job, if running longer, times out after **30 minutes** | ||||
| 1. You set the _maximum job timeout_ for a runner to **30 minutes**. | ||||
| 1. You set the _CI/CD Timeout_ for a project to 2 hours. | ||||
| 1. You start a job. | ||||
| 1. The job, if running longer, times out after **30 minutes**. | ||||
| 
 | ||||
| ## Set `script` and `after_script` timeouts | ||||
| 
 | ||||
|  | @ -66,7 +92,7 @@ How this feature works: | |||
| To control the amount of time `script` and `after_script` runs before it terminates, you can set specify a timeout. | ||||
| 
 | ||||
| For example, you can specify a timeout to terminate a long-running `script` early, so that artifacts and caches can still be uploaded | ||||
| before the [job timeout](#set-maximum-job-timeout-for-a-runner) is exceeded. | ||||
| before the job timeout is exceeded. | ||||
| 
 | ||||
| - To set a timeout for `script`, use the job variable `RUNNER_SCRIPT_TIMEOUT`. | ||||
| - To set a timeout for `after_script`, and override the default of 5 minutes, use the job variable `RUNNER_AFTER_SCRIPT_TIMEOUT`. | ||||
|  | @ -105,26 +131,11 @@ of shared runners on large GitLab instances. This ensures that you | |||
| control access to your GitLab instances and secure [runner executors](https://docs.gitlab.com/runner/executors/). | ||||
| 
 | ||||
| If certain executors run a job, the file system, the code the runner executes, | ||||
| and the runner authentication token may be exposed. This means that anyone that runs jobs | ||||
| and the runner authentication token may be exposed. This means that anyone who runs jobs | ||||
| on a _shared runner_ can access another user's code that runs on the runner. | ||||
| Users with access to the runner authentication token can use it to create a clone of | ||||
| a runner and submit false jobs in a vector attack. For more information, see [Security Considerations](https://docs.gitlab.com/runner/security/). | ||||
| 
 | ||||
| ### Prevent runners from revealing sensitive information | ||||
| 
 | ||||
| To ensure runners don't reveal sensitive information, you can configure them to only run jobs | ||||
| on [protected branches](../../user/project/protected_branches.md), or jobs that have [protected tags](../../user/project/protected_tags.md). | ||||
| 
 | ||||
| To prevent runners from revealing sensitive information: | ||||
| 
 | ||||
| 1. On the left sidebar, select **Search or go to** and find your project. | ||||
| 1. Select **Settings > CI/CD**. | ||||
| 1. Expand **Runners**. | ||||
| 1. Find the runner you want to protect or unprotect. Make sure the runner is enabled. | ||||
| 1. Select **Edit** (**{pencil}**). | ||||
| 1. Select the **Protected** checkbox. | ||||
| 1. Select **Save changes**. | ||||
| 
 | ||||
| ### Using shared runners in forked projects | ||||
| 
 | ||||
| When a project is forked, the job settings related to jobs are copied. If you have shared runners | ||||
|  | @ -179,37 +190,129 @@ To reset the runner authentication token: | |||
|    - [Create a project runner](runners_scope.md#create-a-project-runner-with-a-runner-authentication-token). | ||||
| 1. Optional. To verify that the previous runner authentication token has been revoked, use the [Runners API](../../api/runners.md#verify-authentication-for-a-registered-runner). | ||||
| 
 | ||||
| ## Use tags to control which jobs a runner can run | ||||
| ### Automatically rotate runner authentication tokens | ||||
| 
 | ||||
| You can use [tags](../yaml/index.md#tags) to ensure that runners only run the jobs they are equipped | ||||
| to run. For example, you can specify the `rails` tag for runners that have the dependencies to run | ||||
| You can specify an interval for runner authentication tokens to rotate. | ||||
| This rotation helps ensure the security of the tokens assigned to your runners. | ||||
| 
 | ||||
| Prerequisites: | ||||
| 
 | ||||
| - Runners must use [GitLab Runner 15.3 or later](https://docs.gitlab.com/runner/#gitlab-runner-versions). | ||||
| - You must be an administrator. | ||||
| 
 | ||||
| To automatically rotate runner authentication tokens: | ||||
| 
 | ||||
| 1. On the left sidebar, at the bottom, select **Admin Area**. | ||||
| 1. On the left sidebar, select **Settings > CI/CD**. | ||||
| 1. Expand **Continuous Integration and Deployment** | ||||
| 1. Set a **Runners expiration** time for runners, leave empty for no expiration. | ||||
| 1. Select **Save changes**. | ||||
| 
 | ||||
| Before the interval expires, runners automatically request a new runner authentication token. | ||||
| 
 | ||||
| ## Prevent runners from revealing sensitive information | ||||
| 
 | ||||
| To ensure runners don't reveal sensitive information, you can configure them to only run jobs | ||||
| on [protected branches](../../user/project/protected_branches.md), or jobs that have [protected tags](../../user/project/protected_tags.md). | ||||
| 
 | ||||
| ### For a shared runner | ||||
| 
 | ||||
| Prerequisites: | ||||
| 
 | ||||
| - You must be an administrator. | ||||
| 
 | ||||
| 1. On the left sidebar, at the bottom, select **Admin Area**. | ||||
| 1. Select **CI/CD > Runners**. | ||||
| 1. To the right of the runner you want to protect, select **Edit** (**{pencil}**). | ||||
| 1. Select the **Protected** checkbox. | ||||
| 1. Select **Save changes**. | ||||
| 
 | ||||
| ### For a group runner | ||||
| 
 | ||||
| Prerequisites: | ||||
| 
 | ||||
| - You must have the Owner role for the group. | ||||
| 
 | ||||
| 1. On the left sidebar, select **Search or go to** and find your group. | ||||
| 1. Select **Build > Runners**. | ||||
| 1. To the right of the runner you want to protect, select **Edit** (**{pencil}**). | ||||
| 1. Select the **Protected** checkbox. | ||||
| 1. Select **Save changes**. | ||||
| 
 | ||||
| ### For a project runner | ||||
| 
 | ||||
| Prerequisites: | ||||
| 
 | ||||
| - You must have the Owner role for the project. | ||||
| 
 | ||||
| 1. On the left sidebar, select **Search or go to** and find your project. | ||||
| 1. Select **Settings > CI/CD**. | ||||
| 1. Expand **Runners**. | ||||
| 1. To the right of the runner you want to protect, select **Edit** (**{pencil}**). | ||||
| 1. Select the **Protected** checkbox. | ||||
| 1. Select **Save changes**. | ||||
| 
 | ||||
| ## Control jobs that a runner can run | ||||
| 
 | ||||
| You can use [tags](../yaml/index.md#tags) to control the jobs a runner can run. | ||||
| For example, you can specify the `rails` tag for runners that have the dependencies to run | ||||
| Rails test suites. | ||||
| 
 | ||||
| GitLab CI/CD tags are different to Git tags. GitLab CI/CD tags are associated with runners. | ||||
| Git tags are associated with commits. | ||||
| 
 | ||||
| ### Set a runner to run untagged jobs | ||||
| ### For a shared runner | ||||
| 
 | ||||
| When you [register a runner](https://docs.gitlab.com/runner/register/), its default behavior is to **only pick** | ||||
| [tagged jobs](../yaml/index.md#tags). | ||||
| To change this, you must have the Owner role for the project. | ||||
| Prerequisites: | ||||
| 
 | ||||
| To make a runner pick untagged jobs: | ||||
| - You must be an administrator. | ||||
| 
 | ||||
| To set the maximum job timeout: | ||||
| 
 | ||||
| 1. On the left sidebar, at the bottom, select **Admin Area**. | ||||
| 1. Select **CI/CD > Runners**. | ||||
| 1. To the right of the runner you want to edit, select **Edit** (**{pencil}**). | ||||
| 1. Set the runner to run tagged or untagged jobs: | ||||
|    - To run tagged jobs, in the **Tags** field, enter the job tags separated with a comma. For example, `macos`, `rails`. | ||||
|    - To run untagged jobs, select the **Run untagged jobs** checkbox. | ||||
| 1. Select **Save changes**. | ||||
| 
 | ||||
| ### For a group runner | ||||
| 
 | ||||
| Prerequisites: | ||||
| 
 | ||||
| - You must have the Owner role for the group. | ||||
| 
 | ||||
| To set the maximum job timeout: | ||||
| 
 | ||||
| 1. On the left sidebar, select **Search or go to** and find your group. | ||||
| 1. Select **Build > Runners**. | ||||
| 1. To the right of the runner you want to edit, select **Edit** (**{pencil}**). | ||||
| 1. Set the runner to run tagged or untagged jobs: | ||||
|    - To run tagged jobs, in the **Tags** field, enter the job tags separated with a comma. For example, `macos`, `ruby`. | ||||
|    - To run untagged jobs, select the **Run untagged jobs** checkbox. | ||||
| 1. Select **Save changes**. | ||||
| 
 | ||||
| ### For a project runner | ||||
| 
 | ||||
| Prerequisites: | ||||
| 
 | ||||
| - You must have the Owner role for the project. | ||||
| 
 | ||||
| To set a runner to run tagged jobs: | ||||
| 
 | ||||
| 1. On the left sidebar, select **Search or go to** and find your project. | ||||
| 1. Select **Settings > CI/CD**. | ||||
| 1. Expand **Runners**. | ||||
| 1. Find the runner you want to pick untagged jobs and make sure it's enabled. | ||||
| 1. Select **Edit** (**{pencil}**). | ||||
| 1. Select the **Run untagged jobs** checkbox. | ||||
| 1. To the right of the runner you want to edit, select **Edit** (**{pencil}**). | ||||
| 1. Set the runner to run tagged or untagged jobs: | ||||
|    - To run tagged jobs, in the **Tags** field, enter the job tags separated with a comma. For example, `macos`, `ruby`. | ||||
|    - To run untagged jobs, select the **Run untagged jobs** checkbox. | ||||
| 1. Select **Save changes**. | ||||
| 
 | ||||
| NOTE: | ||||
| The runner tags list cannot be empty when it's not allowed to pick untagged jobs. | ||||
| ### How the runner uses tags | ||||
| 
 | ||||
| Below are some example scenarios of different variations. | ||||
| 
 | ||||
| ### Runner runs only tagged jobs | ||||
| #### Runner runs only tagged jobs | ||||
| 
 | ||||
| The following examples illustrate the potential impact of the runner being set | ||||
| to run only tagged jobs. | ||||
|  | @ -229,7 +332,7 @@ Example 3: | |||
| 1. The runner is configured to run only tagged jobs and has the `docker` tag. | ||||
| 1. A job that has no tags defined is executed and stuck. | ||||
| 
 | ||||
| ### Runner is allowed to run untagged jobs | ||||
| #### Runner is allowed to run untagged jobs | ||||
| 
 | ||||
| The following examples illustrate the potential impact of the runner being set | ||||
| to run tagged and untagged jobs. | ||||
|  | @ -246,7 +349,7 @@ Example 2: | |||
| 1. A job that has no tags defined is executed and run. | ||||
| 1. A second job that has a `docker` tag defined is stuck. | ||||
| 
 | ||||
| ### A runner and a job have multiple tags | ||||
| #### A runner and a job have multiple tags | ||||
| 
 | ||||
| The selection logic that matches the job and runner is based on the list of `tags` | ||||
| defined in the job. | ||||
|  | @ -273,7 +376,9 @@ Example 3: | |||
| 
 | ||||
| You can use tags to run different jobs on different platforms. For | ||||
| example, if you have an OS X runner with tag `osx` and a Windows runner with tag | ||||
| `windows`, you can run a job on each platform: | ||||
| `windows`, you can run a job on each platform. | ||||
| 
 | ||||
| Update the `tags` field in the `config.toml`: | ||||
| 
 | ||||
| ```yaml | ||||
| windows job: | ||||
|  | @ -295,7 +400,7 @@ osx job: | |||
| 
 | ||||
| > Introduced in [GitLab 14.1](https://gitlab.com/gitlab-org/gitlab/-/issues/35742). | ||||
| 
 | ||||
| You can use [CI/CD variables](../variables/index.md) with `tags` for dynamic runner selection: | ||||
| In the `.gitlab-ci.yml` file, use [CI/CD variables](../variables/index.md) with `tags` for dynamic runner selection: | ||||
| 
 | ||||
| ```yaml | ||||
| variables: | ||||
|  | @ -928,22 +1033,3 @@ No manual intervention should be required, and no running jobs should be affecte | |||
| 
 | ||||
| If you need to manually update the runner authentication token, you can run a | ||||
| command to [reset the token](https://docs.gitlab.com/runner/commands/#gitlab-runner-reset-token). | ||||
| 
 | ||||
| ### Automatically rotate runner authentication tokens | ||||
| 
 | ||||
| You can specify an interval for runner authentication tokens to rotate. | ||||
| This rotation helps ensure the security of the tokens assigned to your runners. | ||||
| 
 | ||||
| Prerequisites: | ||||
| 
 | ||||
| - Ensure your runners are using [GitLab Runner 15.3 or later](https://docs.gitlab.com/runner/#gitlab-runner-versions). | ||||
| 
 | ||||
| To automatically rotate runner authentication tokens: | ||||
| 
 | ||||
| 1. On the left sidebar, at the bottom, select **Admin Area**. | ||||
| 1. On the left sidebar, select **Settings > CI/CD**. | ||||
| 1. Expand **Continuous Integration and Deployment** | ||||
| 1. Set a **Runners expiration** time for runners, leave empty for no expiration. | ||||
| 1. Select **Save**. | ||||
| 
 | ||||
| Before the interval expires, runners automatically request a new runner authentication token. | ||||
|  |  | |||
|  | @ -20,7 +20,7 @@ For more information about the cost factor applied to the machine type based on | |||
| The number of minutes you can use on these runners depends on the [maximum number of units of compute](../pipelines/cicd_minutes.md) | ||||
| in your [subscription plan](https://about.gitlab.com/pricing/). | ||||
| 
 | ||||
| [Untagged](../../ci/runners/configure_runners.md#use-tags-to-control-which-jobs-a-runner-can-run) jobs automatically run in containers | ||||
| [Untagged](../../ci/runners/configure_runners.md#control-jobs-that-a-runner-can-run) jobs automatically run in containers | ||||
| on the `small` Linux runners. | ||||
| 
 | ||||
| The objective is to make 90% of CI/CD jobs start executing in 120 seconds or less. The error rate should be less than 0.5%. | ||||
|  |  | |||
|  | @ -4568,7 +4568,7 @@ In this example, only runners with *both* the `ruby` and `postgres` tags can run | |||
| 
 | ||||
| **Related topics**: | ||||
| 
 | ||||
| - [Use tags to control which jobs a runner can run](../runners/configure_runners.md#use-tags-to-control-which-jobs-a-runner-can-run). | ||||
| - [Use tags to control which jobs a runner can run](../runners/configure_runners.md#control-jobs-that-a-runner-can-run). | ||||
| - [Select different runner tags for each parallel matrix job](../jobs/job_control.md#select-different-runner-tags-for-each-parallel-matrix-job). | ||||
| 
 | ||||
| ### `timeout` | ||||
|  | @ -4579,7 +4579,7 @@ Use `timeout` to configure a timeout for a specific job. If the job runs for lon | |||
| than the timeout, the job fails. | ||||
| 
 | ||||
| The job-level timeout can be longer than the [project-level timeout](../pipelines/settings.md#set-a-limit-for-how-long-jobs-can-run), | ||||
| but can't be longer than the [runner's timeout](../runners/configure_runners.md#set-maximum-job-timeout-for-a-runner). | ||||
| but can't be longer than the [runner's timeout](../runners/configure_runners.md#set-the-maximum-job-timeout). | ||||
| 
 | ||||
| **Keyword type**: Job keyword. You can use it only as part of a job or in the | ||||
| [`default` section](#default). | ||||
|  |  | |||
|  | @ -38,8 +38,8 @@ For more information on: | |||
|    gitlab_rails['omniauth_block_auto_created_users'] = false | ||||
|    ``` | ||||
| 
 | ||||
| 1. Optional. You can automatically link SAML users with existing GitLab users if their | ||||
|    email addresses match by adding the following setting in `/etc/gitlab/gitlab.rb`: | ||||
| 1. Optional. You should automatically link a first-time SAML sign-in with existing GitLab users if their | ||||
|    email addresses match. To do this, add the following setting in `/etc/gitlab/gitlab.rb`: | ||||
| 
 | ||||
|    ```ruby | ||||
|    gitlab_rails['omniauth_auto_link_saml_user'] = true | ||||
|  | @ -728,6 +728,9 @@ On GitLab.com, Microsoft Azure/Entra ID attributes are introduced | |||
| [with a flag](../administration/feature_flags.md) named `saml_microsoft_attribute_names`. | ||||
| On GitLab.com, this feature is unavailable but can be configured by GitLab.com administrators only. | ||||
| 
 | ||||
| NOTE: | ||||
| The attributes are case-sensitive. | ||||
| 
 | ||||
| | Field           | Supported default keys                                                                                                                                                         | | ||||
| |-----------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | ||||
| | Email (required)| `email`, `mail`, `http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress`, `http://schemas.microsoft.com/ws/2008/06/identity/claims/emailaddress`                  | | ||||
|  |  | |||
|  | @ -90,7 +90,7 @@ To create a project runner: | |||
| 1. Expand the **Runners** section. | ||||
| 1. Select **New project runner**. | ||||
| 1. Select your operating system. | ||||
| 1. In the **Tags** section, select the **Run untagged** checkbox. [Tags](../../ci/runners/configure_runners.md#use-tags-to-control-which-jobs-a-runner-can-run) specify which jobs | ||||
| 1. In the **Tags** section, select the **Run untagged** checkbox. [Tags](../../ci/runners/configure_runners.md#control-jobs-that-a-runner-can-run) specify which jobs | ||||
|    the runner can run and are optional. | ||||
| 1. Select **Create runner**. | ||||
| 1. Follow the on-screen instructions to register the runner from the command line. When prompted: | ||||
|  |  | |||
|  | @ -541,6 +541,10 @@ by this issue. | |||
|   [throw errors on startup](../../install/docker.md#threaderror-cant-create-thread-operation-not-permitted). | ||||
| - Starting with 16.0, GitLab self-managed installations now have two database connections by default, instead of one. This change doubles the number of PostgreSQL connections. It makes self-managed versions of GitLab behave similarly to GitLab.com, and is a step toward enabling a separate database for CI features for self-managed versions of GitLab. Before upgrading to 16.0, determine if you need to [increase max connections for PostgreSQL](https://docs.gitlab.com/omnibus/settings/database.html#configuring-multiple-database-connections). | ||||
|   - This change applies to installation methods with Linux packages (Omnibus), GitLab Helm chart, GitLab Operator, GitLab Docker images, and self-compiled installations. | ||||
|   - The second database connection can be disabled: | ||||
|     - [Linux package and Docker installations](https://docs.gitlab.com/omnibus/settings/database.html#configuring-multiple-database-connections). | ||||
|     - [Helm chart and GitLab Operator installations](https://docs.gitlab.com/charts/charts/globals.html#configure-multiple-database-connections). | ||||
|     - [Self-compiled installations](../../install/installation.md#configure-gitlab-db-settings). | ||||
| - Container registry using Azure storage might be empty with zero tags. You can fix this by following the [breaking change instructions](../deprecations.md#azure-storage-driver-defaults-to-the-correct-root-prefix). | ||||
| 
 | ||||
| ### Linux package installations | ||||
|  |  | |||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
		Reference in New Issue