Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-05-16 12:07:51 +00:00
parent f15ffb0170
commit 1c9afffa29
66 changed files with 3334 additions and 1587 deletions

View File

@ -765,3 +765,6 @@ Performance/ActiveRecordSubtransactionMethods:
Migration/BackgroundMigrationBaseClass: Migration/BackgroundMigrationBaseClass:
Enabled: false Enabled: false
Style/ClassAndModuleChildren:
Enabled: true

View File

@ -6,17 +6,6 @@
# Note that changes in the inspected code, or installation of new # Note that changes in the inspected code, or installation of new
# versions of RuboCop, may require this file to be generated again. # versions of RuboCop, may require this file to be generated again.
# Offense count: 1
Gitlab/PolicyRuleBoolean:
Exclude:
- 'ee/app/policies/ee/identity_provider_policy.rb'
# Configuration parameters: Include.
# Include: db/migrate/*.rb
Rails/CreateTableWithTimestamps:
Enabled: false
# Offense count: 354
# Configuration parameters: Include. # Configuration parameters: Include.
# Include: app/models/**/*.rb # Include: app/models/**/*.rb
Rails/HasManyOrHasOneDependent: Rails/HasManyOrHasOneDependent:
@ -26,10 +15,3 @@ Rails/HasManyOrHasOneDependent:
# Cop supports --auto-correct. # Cop supports --auto-correct.
Style/CaseLikeIf: Style/CaseLikeIf:
Enabled: false Enabled: false
# Offense count: 205
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle.
# SupportedStyles: compact, expanded
Style/EmptyMethod:
Enabled: false

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,4 @@
---
Gitlab/PolicyRuleBoolean:
Exclude:
- 'ee/app/policies/ee/identity_provider_policy.rb'

View File

@ -337,7 +337,7 @@ Layout/HashAlignment:
- 'ee/app/helpers/ee/feature_flags_helper.rb' - 'ee/app/helpers/ee/feature_flags_helper.rb'
- 'ee/app/helpers/ee/sorting_helper.rb' - 'ee/app/helpers/ee/sorting_helper.rb'
- 'ee/app/models/allowed_email_domain.rb' - 'ee/app/models/allowed_email_domain.rb'
- 'ee/app/models/ci/minutes/quota.rb' - 'ee/app/models/ci/minutes/usage.rb'
- 'ee/app/models/ee/application_setting.rb' - 'ee/app/models/ee/application_setting.rb'
- 'ee/app/models/elastic/reindexing_task.rb' - 'ee/app/models/elastic/reindexing_task.rb'
- 'ee/app/models/gitlab_subscriptions/features.rb' - 'ee/app/models/gitlab_subscriptions/features.rb'

View File

@ -0,0 +1,69 @@
---
Rails/CreateTableWithTimestamps:
# Offense count: 63
# Temporarily disabled due to too many offenses
Enabled: false
Exclude:
- 'db/migrate/20210305180331_create_ci_unit_tests.rb'
- 'db/migrate/20210305182855_create_ci_unit_test_failures.rb'
- 'db/migrate/20210317035357_create_dast_profiles_pipelines.rb'
- 'db/migrate/20210317104301_create_in_product_marketing_emails.rb'
- 'db/migrate/20210323125809_create_status_check_responses_table.rb'
- 'db/migrate/20210329191850_add_finding_signature_table.rb'
- 'db/migrate/20210411212813_add_clusters_integrations_prometheus.rb'
- 'db/migrate/20210423054022_create_dast_site_profiles_pipelines.rb'
- 'db/migrate/20210429032320_add_escalation_rules.rb'
- 'db/migrate/20210429131525_create_user_credit_card_validations.rb'
- 'db/migrate/20210511104929_add_epic_board_recent_visits_table.rb'
- 'db/migrate/20210512120122_add_pending_builds_table.rb'
- 'db/migrate/20210527194558_create_ci_job_token_project_scope_links.rb'
- 'db/migrate/20210601123341_add_running_builds_table.rb'
- 'db/migrate/20210602122213_add_upcoming_reconciliations.rb'
- 'db/migrate/20210604032738_create_dast_site_profiles_builds.rb'
- 'db/migrate/20210604051330_create_dast_scanner_profiles_builds.rb'
- 'db/migrate/20210604082145_create_external_status_checks_table.rb'
- 'db/migrate/20210713211008_create_banned_users.rb'
- 'db/migrate/20210729081739_create_project_topics.rb'
- 'db/migrate/20210729202143_create_incident_management_issuable_escalation_statuses.rb'
- 'db/migrate/20210730101609_create_analytics_cycle_analytics_stage_event_hashes.rb'
- 'db/migrate/20210809014850_create_agent_group_authorizations.rb'
- 'db/migrate/20210812171704_create_project_ci_feature_usages.rb'
- 'db/migrate/20210813101742_create_zentao_tracker_data.rb'
- 'db/migrate/20210813111909_create_ci_build_trace_metadata.rb'
- 'db/migrate/20210819185500_create_external_audit_event_destinations_table.rb'
- 'db/migrate/20210823172643_create_user_group_callout.rb'
- 'db/migrate/20210823213417_create_dependency_proxy_image_ttl_group_policies.rb'
- 'db/migrate/20210913010411_create_agent_project_authorizations.rb'
- 'db/migrate/20210922215740_create_issue_customer_relations_contacts.rb'
- 'db/migrate/20211004062942_create_coverage_fuzzing_corpuses.rb'
- 'db/migrate/20211004122540_create_member_tasks.rb'
- 'db/migrate/20211011004242_create_content_blocked_states.rb'
- 'db/migrate/20211011140930_create_ci_namespace_mirrors.rb'
- 'db/migrate/20211011140931_create_ci_project_mirrors.rb'
- 'db/migrate/20211011140932_create_namespaces_sync_events.rb'
- 'db/migrate/20211011141239_create_projects_sync_events.rb'
- 'db/migrate/20211028132247_create_packages_npm_metadata.rb'
- 'db/migrate/20211101132310_add_reindexing_queue.rb'
- 'db/migrate/20211101165656_create_upload_states.rb'
- 'db/migrate/20211110014701_create_agent_activity_events.rb'
- 'db/migrate/20211110092710_create_issue_emails.rb'
- 'db/migrate/20211111112425_create_merge_requests_compliance_violations.rb'
- 'db/migrate/20211115132613_create_incident_management_timeline_events.rb'
- 'db/migrate/20211117174209_create_vulnerability_reads.rb'
- 'db/migrate/20211119111006_create_job_artifact_states.rb'
- 'db/migrate/20211119154221_create_pages_deployment_states.rb'
- 'db/migrate/20211119195201_create_deployment_approvals.rb'
- 'db/migrate/20211201143042_create_lfs_object_states.rb'
- 'db/migrate/20211216220939_add_group_crm_settings.rb'
- 'db/migrate/20220110170953_create_ci_secure_files.rb'
- 'db/migrate/20220112205111_create_security_training_providers.rb'
- 'db/migrate/20220113125401_create_security_trainings.rb'
- 'db/migrate/20220120033115_create_alert_management_alert_metric_images.rb'
- 'db/migrate/20220204093120_create_analytics_cycle_analytics_aggregations.rb'
- 'db/migrate/20220211125954_create_related_epic_links.rb'
- 'db/migrate/20220216110023_create_saved_replies.rb'
- 'db/migrate/20220301175426_create_project_build_artifacts_size_refresh.rb'
- 'db/migrate/20220302110724_add_group_features_table.rb'
- 'db/migrate/20220314184009_create_protected_environment_approval_rules.rb'
- 'db/migrate/20220425120604_create_packages_cleanup_policies.rb'
- 'db/migrate/20220503102855_add_namespace_ci_cd_settings_table.rb'

View File

@ -0,0 +1,587 @@
---
# Cop supports --auto-correct.
Style/ClassAndModuleChildren:
Exclude:
- 'app/components/pajamas/toggle_component.rb'
- 'app/controllers/admin/abuse_reports_controller.rb'
- 'app/controllers/admin/application_controller.rb'
- 'app/controllers/admin/application_settings/appearances_controller.rb'
- 'app/controllers/admin/application_settings_controller.rb'
- 'app/controllers/admin/applications_controller.rb'
- 'app/controllers/admin/background_jobs_controller.rb'
- 'app/controllers/admin/background_migrations_controller.rb'
- 'app/controllers/admin/batched_jobs_controller.rb'
- 'app/controllers/admin/broadcast_messages_controller.rb'
- 'app/controllers/admin/ci/variables_controller.rb'
- 'app/controllers/admin/clusters/integrations_controller.rb'
- 'app/controllers/admin/clusters_controller.rb'
- 'app/controllers/admin/cohorts_controller.rb'
- 'app/controllers/admin/dashboard_controller.rb'
- 'app/controllers/admin/deploy_keys_controller.rb'
- 'app/controllers/admin/dev_ops_report_controller.rb'
- 'app/controllers/admin/gitaly_servers_controller.rb'
- 'app/controllers/admin/groups_controller.rb'
- 'app/controllers/admin/health_check_controller.rb'
- 'app/controllers/admin/hook_logs_controller.rb'
- 'app/controllers/admin/hooks_controller.rb'
- 'app/controllers/admin/identities_controller.rb'
- 'app/controllers/admin/impersonation_tokens_controller.rb'
- 'app/controllers/admin/impersonations_controller.rb'
- 'app/controllers/admin/instance_review_controller.rb'
- 'app/controllers/admin/integrations_controller.rb'
- 'app/controllers/admin/jobs_controller.rb'
- 'app/controllers/admin/keys_controller.rb'
- 'app/controllers/admin/labels_controller.rb'
- 'app/controllers/admin/plan_limits_controller.rb'
- 'app/controllers/admin/projects_controller.rb'
- 'app/controllers/admin/runner_projects_controller.rb'
- 'app/controllers/admin/runners_controller.rb'
- 'app/controllers/admin/sessions_controller.rb'
- 'app/controllers/admin/spam_logs_controller.rb'
- 'app/controllers/admin/system_info_controller.rb'
- 'app/controllers/admin/topics/avatars_controller.rb'
- 'app/controllers/admin/topics_controller.rb'
- 'app/controllers/admin/usage_trends_controller.rb'
- 'app/controllers/admin/users_controller.rb'
- 'app/controllers/admin/version_check_controller.rb'
- 'app/controllers/clusters/base_controller.rb'
- 'app/controllers/clusters/clusters_controller.rb'
- 'app/controllers/concerns/integrations/actions.rb'
- 'app/controllers/concerns/integrations/hooks_execution.rb'
- 'app/controllers/concerns/metrics/dashboard/prometheus_api_proxy.rb'
- 'app/controllers/concerns/snippets/blobs_actions.rb'
- 'app/controllers/concerns/snippets/send_blob.rb'
- 'app/controllers/concerns/spammable_actions/akismet_mark_as_spam_action.rb'
- 'app/controllers/concerns/spammable_actions/captcha_check/html_format_actions_support.rb'
- 'app/controllers/concerns/spammable_actions/captcha_check/json_format_actions_support.rb'
- 'app/controllers/concerns/spammable_actions/captcha_check/rest_api_actions_support.rb'
- 'app/controllers/dashboard/application_controller.rb'
- 'app/controllers/dashboard/groups_controller.rb'
- 'app/controllers/dashboard/labels_controller.rb'
- 'app/controllers/dashboard/milestones_controller.rb'
- 'app/controllers/dashboard/projects_controller.rb'
- 'app/controllers/dashboard/snippets_controller.rb'
- 'app/controllers/dashboard/todos_controller.rb'
- 'app/controllers/explore/application_controller.rb'
- 'app/controllers/explore/groups_controller.rb'
- 'app/controllers/explore/projects_controller.rb'
- 'app/controllers/explore/snippets_controller.rb'
- 'app/controllers/groups/application_controller.rb'
- 'app/controllers/groups/autocomplete_sources_controller.rb'
- 'app/controllers/groups/avatars_controller.rb'
- 'app/controllers/groups/boards_controller.rb'
- 'app/controllers/groups/clusters/integrations_controller.rb'
- 'app/controllers/groups/clusters_controller.rb'
- 'app/controllers/groups/crm/contacts_controller.rb'
- 'app/controllers/groups/crm/organizations_controller.rb'
- 'app/controllers/groups/dependency_proxy_auth_controller.rb'
- 'app/controllers/groups/dependency_proxy_for_containers_controller.rb'
- 'app/controllers/groups/deploy_tokens_controller.rb'
- 'app/controllers/groups/email_campaigns_controller.rb'
- 'app/controllers/groups/group_links_controller.rb'
- 'app/controllers/groups/group_members_controller.rb'
- 'app/controllers/groups/imports_controller.rb'
- 'app/controllers/groups/labels_controller.rb'
- 'app/controllers/groups/milestones_controller.rb'
- 'app/controllers/groups/runners_controller.rb'
- 'app/controllers/groups/uploads_controller.rb'
- 'app/controllers/import/available_namespaces_controller.rb'
- 'app/controllers/import/base_controller.rb'
- 'app/controllers/import/bitbucket_controller.rb'
- 'app/controllers/import/bitbucket_server_controller.rb'
- 'app/controllers/import/bulk_imports_controller.rb'
- 'app/controllers/import/fogbugz_controller.rb'
- 'app/controllers/import/gitea_controller.rb'
- 'app/controllers/import/github_controller.rb'
- 'app/controllers/import/gitlab_controller.rb'
- 'app/controllers/import/gitlab_groups_controller.rb'
- 'app/controllers/import/gitlab_projects_controller.rb'
- 'app/controllers/import/history_controller.rb'
- 'app/controllers/import/manifest_controller.rb'
- 'app/controllers/import/phabricator_controller.rb'
- 'app/controllers/import/url_controller.rb'
- 'app/controllers/jira_connect/app_descriptor_controller.rb'
- 'app/controllers/jira_connect/application_controller.rb'
- 'app/controllers/jira_connect/branches_controller.rb'
- 'app/controllers/jira_connect/events_controller.rb'
- 'app/controllers/jira_connect/installations_controller.rb'
- 'app/controllers/jira_connect/oauth_callbacks_controller.rb'
- 'app/controllers/jira_connect/subscriptions_controller.rb'
- 'app/controllers/jira_connect/users_controller.rb'
- 'app/controllers/ldap/omniauth_callbacks_controller.rb'
- 'app/controllers/oauth/applications_controller.rb'
- 'app/controllers/oauth/authorizations_controller.rb'
- 'app/controllers/oauth/authorized_applications_controller.rb'
- 'app/controllers/oauth/jira_dvcs/authorizations_controller.rb'
- 'app/controllers/oauth/token_info_controller.rb'
- 'app/controllers/oauth/tokens_controller.rb'
- 'app/controllers/profiles/accounts_controller.rb'
- 'app/controllers/profiles/active_sessions_controller.rb'
- 'app/controllers/profiles/application_controller.rb'
- 'app/controllers/profiles/avatars_controller.rb'
- 'app/controllers/profiles/chat_names_controller.rb'
- 'app/controllers/profiles/emails_controller.rb'
- 'app/controllers/profiles/gpg_keys_controller.rb'
- 'app/controllers/profiles/groups_controller.rb'
- 'app/controllers/profiles/keys_controller.rb'
- 'app/controllers/profiles/notifications_controller.rb'
- 'app/controllers/profiles/passwords_controller.rb'
- 'app/controllers/profiles/personal_access_tokens_controller.rb'
- 'app/controllers/profiles/preferences_controller.rb'
- 'app/controllers/profiles/two_factor_auths_controller.rb'
- 'app/controllers/profiles/u2f_registrations_controller.rb'
- 'app/controllers/profiles/webauthn_registrations_controller.rb'
- 'app/controllers/projects/alert_management_controller.rb'
- 'app/controllers/projects/analytics/cycle_analytics/stages_controller.rb'
- 'app/controllers/projects/analytics/cycle_analytics/summary_controller.rb'
- 'app/controllers/projects/analytics/cycle_analytics/value_streams_controller.rb'
- 'app/controllers/projects/application_controller.rb'
- 'app/controllers/projects/artifacts_controller.rb'
- 'app/controllers/projects/autocomplete_sources_controller.rb'
- 'app/controllers/projects/avatars_controller.rb'
- 'app/controllers/projects/badges_controller.rb'
- 'app/controllers/projects/blame_controller.rb'
- 'app/controllers/projects/blob_controller.rb'
- 'app/controllers/projects/boards_controller.rb'
- 'app/controllers/projects/branches_controller.rb'
- 'app/controllers/projects/build_artifacts_controller.rb'
- 'app/controllers/projects/builds_controller.rb'
- 'app/controllers/projects/ci/daily_build_group_report_results_controller.rb'
- 'app/controllers/projects/ci/lints_controller.rb'
- 'app/controllers/projects/ci/pipeline_editor_controller.rb'
- 'app/controllers/projects/ci/secure_files_controller.rb'
- 'app/controllers/projects/cluster_agents_controller.rb'
- 'app/controllers/projects/clusters/integrations_controller.rb'
- 'app/controllers/projects/clusters_controller.rb'
- 'app/controllers/projects/commit_controller.rb'
- 'app/controllers/projects/commits_controller.rb'
- 'app/controllers/projects/compare_controller.rb'
- 'app/controllers/projects/confluences_controller.rb'
- 'app/controllers/projects/cycle_analytics_controller.rb'
- 'app/controllers/projects/deploy_keys_controller.rb'
- 'app/controllers/projects/deploy_tokens_controller.rb'
- 'app/controllers/projects/deployments_controller.rb'
- 'app/controllers/projects/design_management/designs_controller.rb'
- 'app/controllers/projects/discussions_controller.rb'
- 'app/controllers/projects/environments/prometheus_api_controller.rb'
- 'app/controllers/projects/environments/sample_metrics_controller.rb'
- 'app/controllers/projects/environments_controller.rb'
- 'app/controllers/projects/error_tracking/base_controller.rb'
- 'app/controllers/projects/error_tracking_controller.rb'
- 'app/controllers/projects/feature_flags_clients_controller.rb'
- 'app/controllers/projects/feature_flags_controller.rb'
- 'app/controllers/projects/feature_flags_user_lists_controller.rb'
- 'app/controllers/projects/find_file_controller.rb'
- 'app/controllers/projects/forks_controller.rb'
- 'app/controllers/projects/google_cloud/base_controller.rb'
- 'app/controllers/projects/google_cloud/deployments_controller.rb'
- 'app/controllers/projects/google_cloud/gcp_regions_controller.rb'
- 'app/controllers/projects/google_cloud/revoke_oauth_controller.rb'
- 'app/controllers/projects/google_cloud/service_accounts_controller.rb'
- 'app/controllers/projects/google_cloud_controller.rb'
- 'app/controllers/projects/grafana_api_controller.rb'
- 'app/controllers/projects/graphs_controller.rb'
- 'app/controllers/projects/group_links_controller.rb'
- 'app/controllers/projects/hook_logs_controller.rb'
- 'app/controllers/projects/hooks_controller.rb'
- 'app/controllers/projects/imports_controller.rb'
- 'app/controllers/projects/incidents_controller.rb'
- 'app/controllers/projects/issues_controller.rb'
- 'app/controllers/projects/jobs_controller.rb'
- 'app/controllers/projects/labels_controller.rb'
- 'app/controllers/projects/learn_gitlab_controller.rb'
- 'app/controllers/projects/mattermosts_controller.rb'
- 'app/controllers/projects/merge_requests/application_controller.rb'
- 'app/controllers/projects/merge_requests/conflicts_controller.rb'
- 'app/controllers/projects/merge_requests/content_controller.rb'
- 'app/controllers/projects/merge_requests/creations_controller.rb'
- 'app/controllers/projects/merge_requests/diffs_controller.rb'
- 'app/controllers/projects/merge_requests/drafts_controller.rb'
- 'app/controllers/projects/merge_requests_controller.rb'
- 'app/controllers/projects/milestones_controller.rb'
- 'app/controllers/projects/mirrors_controller.rb'
- 'app/controllers/projects/network_controller.rb'
- 'app/controllers/projects/notes_controller.rb'
- 'app/controllers/projects/pages_controller.rb'
- 'app/controllers/projects/pages_domains_controller.rb'
- 'app/controllers/projects/pipeline_schedules_controller.rb'
- 'app/controllers/projects/pipelines_controller.rb'
- 'app/controllers/projects/pipelines_settings_controller.rb'
- 'app/controllers/projects/product_analytics_controller.rb'
- 'app/controllers/projects/project_members_controller.rb'
- 'app/controllers/projects/protected_branches_controller.rb'
- 'app/controllers/projects/protected_refs_controller.rb'
- 'app/controllers/projects/protected_tags_controller.rb'
- 'app/controllers/projects/raw_controller.rb'
- 'app/controllers/projects/redirect_controller.rb'
- 'app/controllers/projects/refs_controller.rb'
- 'app/controllers/projects/releases_controller.rb'
- 'app/controllers/projects/repositories_controller.rb'
- 'app/controllers/projects/runner_projects_controller.rb'
- 'app/controllers/projects/runners_controller.rb'
- 'app/controllers/projects/service_desk_controller.rb'
- 'app/controllers/projects/service_hook_logs_controller.rb'
- 'app/controllers/projects/service_ping_controller.rb'
- 'app/controllers/projects/services_controller.rb'
- 'app/controllers/projects/snippets/application_controller.rb'
- 'app/controllers/projects/snippets/blobs_controller.rb'
- 'app/controllers/projects/snippets_controller.rb'
- 'app/controllers/projects/starrers_controller.rb'
- 'app/controllers/projects/static_site_editor_controller.rb'
- 'app/controllers/projects/tags/releases_controller.rb'
- 'app/controllers/projects/tags_controller.rb'
- 'app/controllers/projects/templates_controller.rb'
- 'app/controllers/projects/terraform_controller.rb'
- 'app/controllers/projects/todos_controller.rb'
- 'app/controllers/projects/tree_controller.rb'
- 'app/controllers/projects/triggers_controller.rb'
- 'app/controllers/projects/uploads_controller.rb'
- 'app/controllers/projects/usage_quotas_controller.rb'
- 'app/controllers/projects/variables_controller.rb'
- 'app/controllers/projects/web_ide_schemas_controller.rb'
- 'app/controllers/projects/web_ide_terminals_controller.rb'
- 'app/controllers/projects/wikis_controller.rb'
- 'app/controllers/projects/work_items_controller.rb'
- 'app/controllers/snippets/application_controller.rb'
- 'app/controllers/snippets/blobs_controller.rb'
- 'app/controllers/snippets/notes_controller.rb'
- 'app/controllers/terraform/services_controller.rb'
- 'app/finders/admin/projects_finder.rb'
- 'app/finders/merge_request/metrics_finder.rb'
- 'app/finders/packages/package_file_finder.rb'
- 'app/finders/packages/tags_finder.rb'
- 'app/graphql/types/dependency_proxy/blob_type.rb'
- 'app/graphql/types/dependency_proxy/group_setting_type.rb'
- 'app/graphql/types/dependency_proxy/image_ttl_group_policy_type.rb'
- 'app/graphql/types/dependency_proxy/manifest_type.rb'
- 'app/graphql/types/dependency_proxy/manifest_type_enum.rb'
- 'app/graphql/types/namespace/package_settings_type.rb'
- 'app/graphql/types/namespace/shared_runners_setting_enum.rb'
- 'app/helpers/ci/triggers_helper.rb'
- 'app/helpers/groups/group_members_helper.rb'
- 'app/helpers/projects/alert_management_helper.rb'
- 'app/helpers/projects/cluster_agents_helper.rb'
- 'app/helpers/projects/error_tracking_helper.rb'
- 'app/helpers/projects/incidents_helper.rb'
- 'app/helpers/projects/project_members_helper.rb'
- 'app/helpers/projects/terraform_helper.rb'
- 'app/models/analytics/cycle_analytics/aggregation.rb'
- 'app/models/analytics/cycle_analytics/project_value_stream.rb'
- 'app/models/bulk_imports/configuration.rb'
- 'app/models/bulk_imports/entity.rb'
- 'app/models/bulk_imports/failure.rb'
- 'app/models/bulk_imports/tracker.rb'
- 'app/models/ci/build_pending_state.rb'
- 'app/models/ci/commit_with_pipeline.rb'
- 'app/models/customer_relations/contact.rb'
- 'app/models/customer_relations/issue_contact.rb'
- 'app/models/customer_relations/organization.rb'
- 'app/models/dependency_proxy/blob.rb'
- 'app/models/dependency_proxy/group_setting.rb'
- 'app/models/dependency_proxy/image_ttl_group_policy.rb'
- 'app/models/dependency_proxy/manifest.rb'
- 'app/models/dependency_proxy/registry.rb'
- 'app/models/error_tracking/client_key.rb'
- 'app/models/error_tracking/error.rb'
- 'app/models/error_tracking/error_event.rb'
- 'app/models/group/crm_settings.rb'
- 'app/models/instance_metadata/kas.rb'
- 'app/models/issue/email.rb'
- 'app/models/issue/metrics.rb'
- 'app/models/issues/csv_import.rb'
- 'app/models/loose_foreign_keys/deleted_record.rb'
- 'app/models/merge_request/cleanup_schedule.rb'
- 'app/models/merge_request/diff_commit_user.rb'
- 'app/models/merge_request/metrics.rb'
- 'app/models/namespace/admin_note.rb'
- 'app/models/namespace/aggregation_schedule.rb'
- 'app/models/namespace/package_setting.rb'
- 'app/models/namespace/root_storage_statistics.rb'
- 'app/models/namespaces/sync_event.rb'
- 'app/models/packages/build_info.rb'
- 'app/models/packages/conan/file_metadatum.rb'
- 'app/models/packages/conan/metadatum.rb'
- 'app/models/packages/debian/file_metadatum.rb'
- 'app/models/packages/debian/group_architecture.rb'
- 'app/models/packages/debian/group_component.rb'
- 'app/models/packages/debian/group_component_file.rb'
- 'app/models/packages/debian/group_distribution.rb'
- 'app/models/packages/debian/group_distribution_key.rb'
- 'app/models/packages/debian/project_architecture.rb'
- 'app/models/packages/debian/project_component.rb'
- 'app/models/packages/debian/project_component_file.rb'
- 'app/models/packages/debian/project_distribution.rb'
- 'app/models/packages/debian/project_distribution_key.rb'
- 'app/models/packages/debian/publication.rb'
- 'app/models/packages/dependency.rb'
- 'app/models/packages/dependency_link.rb'
- 'app/models/packages/event.rb'
- 'app/models/packages/maven/metadatum.rb'
- 'app/models/packages/npm/metadatum.rb'
- 'app/models/packages/nuget/dependency_link_metadatum.rb'
- 'app/models/packages/nuget/metadatum.rb'
- 'app/models/packages/package.rb'
- 'app/models/packages/package_file.rb'
- 'app/models/packages/package_file_build_info.rb'
- 'app/models/packages/pypi/metadatum.rb'
- 'app/models/packages/sem_ver.rb'
- 'app/models/packages/tag.rb'
- 'app/models/projects/sync_event.rb'
- 'app/models/protected_branch/merge_access_level.rb'
- 'app/models/protected_branch/push_access_level.rb'
- 'app/models/protected_tag/create_access_level.rb'
- 'app/policies/namespace/package_setting_policy.rb'
- 'app/policies/namespace/root_storage_statistics_policy.rb'
- 'app/policies/wiki_page/meta_policy.rb'
- 'app/serializers/acts_as_taggable_on/tag_entity.rb'
- 'app/serializers/acts_as_taggable_on/tag_serializer.rb'
- 'app/serializers/ci/lint/job_entity.rb'
- 'app/serializers/ci/lint/result_entity.rb'
- 'app/serializers/ci/lint/result_serializer.rb'
- 'app/serializers/ci/pipeline_entity.rb'
- 'app/serializers/import/base_provider_repo_entity.rb'
- 'app/serializers/import/bitbucket_provider_repo_entity.rb'
- 'app/serializers/import/bitbucket_server_provider_repo_entity.rb'
- 'app/serializers/import/bulk_import_entity.rb'
- 'app/serializers/import/fogbugz_provider_repo_entity.rb'
- 'app/serializers/import/githubish_provider_repo_entity.rb'
- 'app/serializers/import/gitlab_provider_repo_entity.rb'
- 'app/serializers/import/manifest_provider_repo_entity.rb'
- 'app/serializers/import/provider_repo_serializer.rb'
- 'app/serializers/jira_connect/app_data_serializer.rb'
- 'app/serializers/jira_connect/group_entity.rb'
- 'app/serializers/jira_connect/subscription_entity.rb'
- 'app/serializers/merge_requests/pipeline_entity.rb'
- 'app/services/projects/branches_by_mode_service.rb'
- 'app/services/repositories/base_service.rb'
- 'app/services/repositories/destroy_rollback_service.rb'
- 'app/services/repositories/destroy_service.rb'
- 'app/services/repositories/shell_destroy_service.rb'
- 'app/uploaders/dependency_proxy/file_uploader.rb'
- 'app/uploaders/packages/composer/cache_uploader.rb'
- 'app/uploaders/packages/debian/component_file_uploader.rb'
- 'app/uploaders/packages/debian/distribution_release_file_uploader.rb'
- 'app/uploaders/packages/package_file_uploader.rb'
- 'app/workers/merge_requests/delete_source_branch_worker.rb'
- 'app/workers/merge_requests/handle_assignees_change_worker.rb'
- 'app/workers/merge_requests/resolve_todos_worker.rb'
- 'config/initializers/active_record_data_types.rb'
- 'config/initializers/http_hostname_override.rb'
- 'config/initializers/httpclient_patch.rb'
- 'config/initializers/omniauth.rb'
- 'config/initializers/postgres_cte_as_materialized.rb'
- 'config/initializers/postgresql_cte.rb'
- 'config/initializers/rdoc_segfault_patch.rb'
- 'config/initializers/zz_metrics.rb'
- 'ee/app/controllers/admin/audit_log_reports_controller.rb'
- 'ee/app/controllers/admin/audit_logs_controller.rb'
- 'ee/app/controllers/admin/credentials_controller.rb'
- 'ee/app/controllers/admin/elasticsearch_controller.rb'
- 'ee/app/controllers/admin/emails_controller.rb'
- 'ee/app/controllers/admin/geo/application_controller.rb'
- 'ee/app/controllers/admin/geo/designs_controller.rb'
- 'ee/app/controllers/admin/geo/nodes_controller.rb'
- 'ee/app/controllers/admin/geo/projects_controller.rb'
- 'ee/app/controllers/admin/geo/replicables_controller.rb'
- 'ee/app/controllers/admin/geo/settings_controller.rb'
- 'ee/app/controllers/admin/licenses_controller.rb'
- 'ee/app/controllers/admin/push_rules_controller.rb'
- 'ee/app/controllers/admin/subscriptions_controller.rb'
- 'ee/app/controllers/admin/user_permission_exports_controller.rb'
- 'ee/app/controllers/concerns/registrations/apply_trial.rb'
- 'ee/app/controllers/concerns/registrations/create_group.rb'
- 'ee/app/controllers/concerns/registrations/create_project.rb'
- 'ee/app/controllers/concerns/registrations/verification.rb'
- 'ee/app/controllers/ee/profiles/accounts_controller.rb'
- 'ee/app/controllers/ee/profiles/preferences_controller.rb'
- 'ee/app/controllers/ee/projects/analytics/cycle_analytics/summary_controller.rb'
- 'ee/app/controllers/ee/projects/incidents_controller.rb'
- 'ee/app/controllers/groups/analytics/application_controller.rb'
- 'ee/app/controllers/groups/analytics/ci_cd_analytics_controller.rb'
- 'ee/app/controllers/groups/analytics/coverage_reports_controller.rb'
- 'ee/app/controllers/groups/analytics/cycle_analytics/value_streams_controller.rb'
- 'ee/app/controllers/groups/analytics/cycle_analytics_controller.rb'
- 'ee/app/controllers/groups/analytics/devops_adoption_controller.rb'
- 'ee/app/controllers/groups/analytics/productivity_analytics_controller.rb'
- 'ee/app/controllers/groups/analytics/repository_analytics_controller.rb'
- 'ee/app/controllers/groups/analytics/tasks_by_type_controller.rb'
- 'ee/app/controllers/groups/audit_events_controller.rb'
- 'ee/app/controllers/groups/billings_controller.rb'
- 'ee/app/controllers/groups/bulk_update_controller.rb'
- 'ee/app/controllers/groups/compliance_frameworks_controller.rb'
- 'ee/app/controllers/groups/contribution_analytics_controller.rb'
- 'ee/app/controllers/groups/epic_boards_controller.rb'
- 'ee/app/controllers/groups/epic_issues_controller.rb'
- 'ee/app/controllers/groups/epics/notes_controller.rb'
- 'ee/app/controllers/groups/epics_controller.rb'
- 'ee/app/controllers/groups/hooks_controller.rb'
- 'ee/app/controllers/groups/insights_controller.rb'
- 'ee/app/controllers/groups/issues_analytics_controller.rb'
- 'ee/app/controllers/groups/issues_controller.rb'
- 'ee/app/controllers/groups/iteration_cadences_controller.rb'
- 'ee/app/controllers/groups/iterations_controller.rb'
- 'ee/app/controllers/groups/ldap_group_links_controller.rb'
- 'ee/app/controllers/groups/ldap_settings_controller.rb'
- 'ee/app/controllers/groups/ldaps_controller.rb'
- 'ee/app/controllers/groups/merge_requests_controller.rb'
- 'ee/app/controllers/groups/omniauth_callbacks_controller.rb'
- 'ee/app/controllers/groups/push_rules_controller.rb'
- 'ee/app/controllers/groups/saml_providers_controller.rb'
- 'ee/app/controllers/groups/scim_oauth_controller.rb'
- 'ee/app/controllers/groups/seat_usage_controller.rb'
- 'ee/app/controllers/groups/security/compliance_dashboards_controller.rb'
- 'ee/app/controllers/groups/security/credentials_controller.rb'
- 'ee/app/controllers/groups/security/dashboard_controller.rb'
- 'ee/app/controllers/groups/security/discover_controller.rb'
- 'ee/app/controllers/groups/security/merge_commit_reports_controller.rb'
- 'ee/app/controllers/groups/sso_controller.rb'
- 'ee/app/controllers/groups/todos_controller.rb'
- 'ee/app/controllers/groups/usage_quotas_controller.rb'
- 'ee/app/controllers/groups/wikis_controller.rb'
- 'ee/app/controllers/oauth/geo_auth_controller.rb'
- 'ee/app/controllers/profiles/billings_controller.rb'
- 'ee/app/controllers/profiles/slacks_controller.rb'
- 'ee/app/controllers/profiles/usage_quotas_controller.rb'
- 'ee/app/controllers/projects/analytics/issues_analytics_controller.rb'
- 'ee/app/controllers/projects/analytics/merge_request_analytics_controller.rb'
- 'ee/app/controllers/projects/approver_groups_controller.rb'
- 'ee/app/controllers/projects/approvers_controller.rb'
- 'ee/app/controllers/projects/audit_events_controller.rb'
- 'ee/app/controllers/projects/insights_controller.rb'
- 'ee/app/controllers/projects/iteration_cadences_controller.rb'
- 'ee/app/controllers/projects/iterations_controller.rb'
- 'ee/app/controllers/projects/path_locks_controller.rb'
- 'ee/app/controllers/projects/protected_environments_controller.rb'
- 'ee/app/controllers/projects/push_rules_controller.rb'
- 'ee/app/controllers/projects/quality/test_cases_controller.rb'
- 'ee/app/controllers/projects/requirements_management/requirements_controller.rb'
- 'ee/app/controllers/projects/subscriptions_controller.rb'
- 'ee/app/controllers/projects/vulnerability_feedback_controller.rb'
- 'ee/app/finders/ee/group_members_finder.rb'
- 'ee/app/graphql/mutations/app_sec/fuzzing/coverage/corpus/create.rb'
- 'ee/app/helpers/ee/groups/analytics/cycle_analytics_helper.rb'
- 'ee/app/helpers/ee/groups/group_members_helper.rb'
- 'ee/app/helpers/ee/security_orchestration_helper.rb'
- 'ee/app/helpers/groups/ldap_sync_helper.rb'
- 'ee/app/helpers/groups/security_features_helper.rb'
- 'ee/app/helpers/groups/sso_helper.rb'
- 'ee/app/helpers/projects/on_demand_scans_helper.rb'
- 'ee/app/helpers/projects/security/api_fuzzing_configuration_helper.rb'
- 'ee/app/helpers/projects/security/dast_configuration_helper.rb'
- 'ee/app/helpers/projects/security/dast_profiles_helper.rb'
- 'ee/app/helpers/projects/security/discover_helper.rb'
- 'ee/app/helpers/projects/security/sast_configuration_helper.rb'
- 'ee/app/models/analytics/cycle_analytics/group_value_stream.rb'
- 'ee/app/models/analytics/devops_adoption.rb'
- 'ee/app/models/analytics/devops_adoption/enabled_namespace.rb'
- 'ee/app/models/analytics/devops_adoption/snapshot.rb'
- 'ee/app/models/analytics/issues_analytics.rb'
- 'ee/app/models/analytics/language_trend.rb'
- 'ee/app/models/analytics/language_trend/repository_language.rb'
- 'ee/app/models/concerns/geo/replicable_registry.rb'
- 'ee/app/models/concerns/geo/selective_sync.rb'
- 'ee/app/models/concerns/geo/syncable.rb'
- 'ee/app/models/dast/profile_schedule.rb'
- 'ee/app/models/ee/ci/job_artifact.rb'
- 'ee/app/models/ee/namespace/root_excess_storage_size.rb'
- 'ee/app/models/ee/namespace/root_storage_size.rb'
- 'ee/app/models/elastic/reindexing_slice.rb'
- 'ee/app/models/elastic/reindexing_subtask.rb'
- 'ee/app/models/elastic/reindexing_task.rb'
- 'ee/app/models/epic/metrics.rb'
- 'ee/app/models/epic/related_epic_link.rb'
- 'ee/app/models/geo/base_registry.rb'
- 'ee/app/models/geo/container_repository_registry.rb'
- 'ee/app/models/geo/deleted_project.rb'
- 'ee/app/models/geo/design_registry.rb'
- 'ee/app/models/geo/event_log_state.rb'
- 'ee/app/models/geo/group_wiki_repository_registry.rb'
- 'ee/app/models/geo/job_artifact_registry.rb'
- 'ee/app/models/geo/lfs_object_registry.rb'
- 'ee/app/models/geo/merge_request_diff_registry.rb'
- 'ee/app/models/geo/package_file_registry.rb'
- 'ee/app/models/geo/pages_deployment_registry.rb'
- 'ee/app/models/geo/project_registry.rb'
- 'ee/app/models/geo/push_user.rb'
- 'ee/app/models/geo/secondary_usage_data.rb'
- 'ee/app/models/geo/snippet_repository_registry.rb'
- 'ee/app/models/geo/terraform_state_version_registry.rb'
- 'ee/app/models/geo/upload_registry.rb'
- 'ee/app/models/protected_branch/required_code_owners_section.rb'
- 'ee/app/models/protected_branch/unprotect_access_level.rb'
- 'ee/app/models/protected_environment/deploy_access_level.rb'
- 'ee/app/serializers/vulnerabilities/feedback_entity.rb'
- 'ee/app/serializers/vulnerabilities/feedback_serializer.rb'
- 'ee/app/serializers/vulnerabilities/finding_diff_serializer.rb'
- 'ee/app/serializers/vulnerabilities/finding_entity.rb'
- 'ee/app/serializers/vulnerabilities/finding_reports_comparer_entity.rb'
- 'ee/app/serializers/vulnerabilities/finding_serializer.rb'
- 'ee/app/serializers/vulnerabilities/identifier_entity.rb'
- 'ee/app/serializers/vulnerabilities/request_entity.rb'
- 'ee/app/serializers/vulnerabilities/response_entity.rb'
- 'ee/app/serializers/vulnerabilities/scanner_entity.rb'
- 'ee/app/services/concerns/epics/related_epic_links/usage_data_helper.rb'
- 'ee/app/services/ee/projects/after_rename_service.rb'
- 'ee/app/services/ee/projects/disable_deploy_key_service.rb'
- 'ee/app/services/ee/projects/enable_deploy_key_service.rb'
- 'ee/app/services/ee/projects/update_pages_service.rb'
- 'ee/db/fixtures/development/20_burndown.rb'
- 'ee/db/fixtures/development/20_vulnerabilities.rb'
- 'ee/db/fixtures/development/21_dast_profiles.rb'
- 'ee/db/fixtures/development/30_customizable_cycle_analytics.rb'
- 'ee/db/fixtures/development/32_compliance_report_violations.rb'
- 'ee/db/fixtures/development/90_productivity_analytics.rb'
- 'ee/lib/ee/gitlab/analytics/cycle_analytics/aggregated/base_query_builder.rb'
- 'ee/lib/ee/gitlab/analytics/cycle_analytics/base_query_builder.rb'
- 'ee/lib/ee/gitlab/analytics/cycle_analytics/records_fetcher.rb'
- 'ee/lib/ee/gitlab/background_migration/recalculate_vulnerability_finding_signatures_for_findings.rb'
- 'ee/lib/ee/gitlab/throttle.rb'
- 'ee/lib/gitlab/path_locks_finder.rb'
- 'lib/api/error_tracking/client_keys.rb'
- 'lib/api/error_tracking/collector.rb'
- 'lib/api/error_tracking/project_settings.rb'
- 'lib/gitlab/background_migration/drop_invalid_vulnerabilities.rb'
- 'lib/gitlab/background_migration/recalculate_vulnerabilities_occurrences_uuid.rb'
- 'lib/gitlab/background_migration/remove_duplicate_vulnerabilities_findings.rb'
- 'lib/gitlab/background_migration/remove_occurrence_pipelines_and_duplicate_vulnerabilities_findings.rb'
- 'lib/gitlab/background_migration/update_jira_tracker_data_deployment_type_based_on_url.rb'
- 'lib/gitlab/ci/badge/base.rb'
- 'lib/gitlab/ci/badge/coverage/metadata.rb'
- 'lib/gitlab/ci/badge/coverage/report.rb'
- 'lib/gitlab/ci/badge/coverage/template.rb'
- 'lib/gitlab/ci/badge/metadata.rb'
- 'lib/gitlab/ci/badge/pipeline/metadata.rb'
- 'lib/gitlab/ci/badge/pipeline/status.rb'
- 'lib/gitlab/ci/badge/pipeline/template.rb'
- 'lib/gitlab/ci/badge/release/latest_release.rb'
- 'lib/gitlab/ci/badge/release/metadata.rb'
- 'lib/gitlab/ci/badge/release/template.rb'
- 'lib/gitlab/ci/badge/template.rb'
- 'lib/gitlab/ci/build/auto_retry.rb'
- 'lib/gitlab/ci/build/rules/rule.rb'
- 'lib/gitlab/ci/build/rules/rule/clause.rb'
- 'lib/gitlab/ci/build/rules/rule/clause/changes.rb'
- 'lib/gitlab/ci/build/rules/rule/clause/exists.rb'
- 'lib/gitlab/ci/build/rules/rule/clause/if.rb'
- 'lib/gitlab/ci/config/entry/include/rules/rule.rb'
- 'lib/gitlab/ci/config/entry/rules/rule.rb'
- 'lib/gitlab/ci/mask_secret.rb'
- 'lib/gitlab/ci/warnings.rb'
- 'lib/gitlab/config_helper.rb'
- 'lib/gitlab/instrumentation/elasticsearch_transport.rb'
- 'lib/gitlab/serverless/service.rb'
- 'lib/gitlab/usage_data_counters/base_counter.rb'
- 'lib/gitlab/usage_data_counters/ci_template_unique_counter.rb'
- 'lib/gitlab/usage_data_counters/cycle_analytics_counter.rb'
- 'lib/gitlab/usage_data_counters/designs_counter.rb'
- 'lib/gitlab/usage_data_counters/note_counter.rb'
- 'lib/gitlab/usage_data_counters/productivity_analytics_counter.rb'
- 'lib/gitlab/usage_data_counters/service_usage_data_counter.rb'
- 'lib/gitlab/usage_data_counters/snippet_counter.rb'
- 'lib/gitlab/usage_data_counters/source_code_counter.rb'
- 'lib/gitlab/usage_data_counters/wiki_page_counter.rb'
- 'lib/release_highlights/validator/entry.rb'
- 'qa/qa/page/component/project/templates.rb'
- 'scripts/perf/gc/print_gc_stats.rb'
- 'spec/support/inspect_squelch.rb'
- 'spec/support/matchers/markdown_matchers.rb'

View File

@ -0,0 +1,196 @@
---
# Cop supports --auto-correct.
Style/EmptyMethod:
# Offense count: 240
# Temporarily disabled due to too many offenses
Enabled: false
Exclude:
- 'app/controllers/admin/application_settings/appearances_controller.rb'
- 'app/controllers/admin/applications_controller.rb'
- 'app/controllers/admin/broadcast_messages_controller.rb'
- 'app/controllers/admin/deploy_keys_controller.rb'
- 'app/controllers/admin/hook_logs_controller.rb'
- 'app/controllers/admin/hooks_controller.rb'
- 'app/controllers/admin/identities_controller.rb'
- 'app/controllers/admin/labels_controller.rb'
- 'app/controllers/admin/runners_controller.rb'
- 'app/controllers/admin/topics_controller.rb'
- 'app/controllers/admin/usage_trends_controller.rb'
- 'app/controllers/admin/users_controller.rb'
- 'app/controllers/concerns/boards_actions.rb'
- 'app/controllers/groups/milestones_controller.rb'
- 'app/controllers/groups/runners_controller.rb'
- 'app/controllers/groups/settings/applications_controller.rb'
- 'app/controllers/groups/settings/ci_cd_controller.rb'
- 'app/controllers/groups/settings/packages_and_registries_controller.rb'
- 'app/controllers/help_controller.rb'
- 'app/controllers/import/bitbucket_server_controller.rb'
- 'app/controllers/import/fogbugz_controller.rb'
- 'app/controllers/import/manifest_controller.rb'
- 'app/controllers/import/phabricator_controller.rb'
- 'app/controllers/profiles/chat_names_controller.rb'
- 'app/controllers/profiles/passwords_controller.rb'
- 'app/controllers/profiles/preferences_controller.rb'
- 'app/controllers/profiles_controller.rb'
- 'app/controllers/projects/alert_management_controller.rb'
- 'app/controllers/projects/ci/lints_controller.rb'
- 'app/controllers/projects/ci/pipeline_editor_controller.rb'
- 'app/controllers/projects/ci/secure_files_controller.rb'
- 'app/controllers/projects/confluences_controller.rb'
- 'app/controllers/projects/deploy_keys_controller.rb'
- 'app/controllers/projects/environments_controller.rb'
- 'app/controllers/projects/feature_flags_controller.rb'
- 'app/controllers/projects/feature_flags_user_lists_controller.rb'
- 'app/controllers/projects/hook_logs_controller.rb'
- 'app/controllers/projects/import/jira_controller.rb'
- 'app/controllers/projects/imports_controller.rb'
- 'app/controllers/projects/incidents_controller.rb'
- 'app/controllers/projects/jobs_controller.rb'
- 'app/controllers/projects/labels_controller.rb'
- 'app/controllers/projects/learn_gitlab_controller.rb'
- 'app/controllers/projects/mattermosts_controller.rb'
- 'app/controllers/projects/pages_domains_controller.rb'
- 'app/controllers/projects/pipeline_schedules_controller.rb'
- 'app/controllers/projects/product_analytics_controller.rb'
- 'app/controllers/projects/runners_controller.rb'
- 'app/controllers/projects/services_controller.rb'
- 'app/controllers/projects/settings/packages_and_registries_controller.rb'
- 'app/controllers/projects/tags/releases_controller.rb'
- 'app/controllers/projects/terraform_controller.rb'
- 'app/controllers/projects/triggers_controller.rb'
- 'app/controllers/registrations/welcome_controller.rb'
- 'app/controllers/search_controller.rb'
- 'app/graphql/resolvers/concerns/caching_array_resolver.rb'
- 'app/helpers/namespace_storage_limit_alert_helper.rb'
- 'app/helpers/subscribable_banner_helper.rb'
- 'app/helpers/users/callouts_helper.rb'
- 'app/models/ci/bridge.rb'
- 'app/models/concerns/cross_database_modification.rb'
- 'app/models/concerns/reactive_caching.rb'
- 'app/models/concerns/relative_positioning.rb'
- 'app/models/hooks/web_hook.rb'
- 'app/models/integrations/hangouts_chat.rb'
- 'app/models/integrations/microsoft_teams.rb'
- 'app/models/integrations/unify_circuit.rb'
- 'app/models/integrations/webex_teams.rb'
- 'app/models/wiki.rb'
- 'app/services/auto_merge/base_service.rb'
- 'app/services/award_emojis/destroy_service.rb'
- 'app/services/issuable_base_service.rb'
- 'app/services/issues/reopen_service.rb'
- 'app/services/projects/transfer_service.rb'
- 'app/workers/authorized_projects_worker.rb'
- 'app/workers/namespaces/root_statistics_worker.rb'
- 'db/migrate/20210420012444_change_web_hook_events_default.rb'
- 'db/migrate/20210507191949_add_remove_on_issue_close_to_labels.rb'
- 'db/migrate/20210729123101_confirm_security_bot.rb'
- 'db/migrate/20211012134316_clean_up_migrate_merge_request_diff_commit_users.rb'
- 'db/post_migrate/20210511095658_schedule_migrate_project_taggings_context_from_tags_to_topics.rb'
- 'db/post_migrate/20210730170823_schedule_security_setting_creation.rb'
- 'db/post_migrate/20210823132600_remove_duplicate_dast_site_tokens.rb'
- 'db/post_migrate/20210826171758_initialize_throttle_unauthenticated_api_columns.rb'
- 'db/post_migrate/20211028100843_delete_issue_merge_request_taggings_records.rb'
- 'db/post_migrate/20220324032250_migrate_shimo_confluence_service_category.rb'
- 'db/post_migrate/20220412143552_consume_remaining_encrypt_integration_property_jobs.rb'
- 'db/post_migrate/20220425121435_backfill_integrations_enable_ssl_verification.rb'
- 'ee/app/controllers/admin/emails_controller.rb'
- 'ee/app/controllers/admin/geo/designs_controller.rb'
- 'ee/app/controllers/admin/geo/settings_controller.rb'
- 'ee/app/controllers/admin/push_rules_controller.rb'
- 'ee/app/controllers/groups/analytics/ci_cd_analytics_controller.rb'
- 'ee/app/controllers/groups/analytics/cycle_analytics_controller.rb'
- 'ee/app/controllers/groups/analytics/devops_adoption_controller.rb'
- 'ee/app/controllers/groups/compliance_frameworks_controller.rb'
- 'ee/app/controllers/groups/feature_discovery_moments_controller.rb'
- 'ee/app/controllers/groups/hooks_controller.rb'
- 'ee/app/controllers/groups/ldap_group_links_controller.rb'
- 'ee/app/controllers/groups/push_rules_controller.rb'
- 'ee/app/controllers/projects/analytics/code_reviews_controller.rb'
- 'ee/app/controllers/projects/analytics/merge_request_analytics_controller.rb'
- 'ee/app/controllers/projects/incident_management/escalation_policies_controller.rb'
- 'ee/app/controllers/projects/incident_management/oncall_schedules_controller.rb'
- 'ee/app/controllers/projects/on_demand_scans_controller.rb'
- 'ee/app/controllers/projects/security/api_fuzzing_configuration_controller.rb'
- 'ee/app/controllers/projects/security/corpus_management_controller.rb'
- 'ee/app/controllers/projects/security/dast_configuration_controller.rb'
- 'ee/app/controllers/projects/security/dast_profiles_controller.rb'
- 'ee/app/controllers/projects/security/dast_scanner_profiles_controller.rb'
- 'ee/app/controllers/projects/security/dast_site_profiles_controller.rb'
- 'ee/app/controllers/projects/security/sast_configuration_controller.rb'
- 'ee/app/controllers/projects/settings/slacks_controller.rb'
- 'ee/app/controllers/registrations/company_controller.rb'
- 'ee/app/controllers/registrations/verification_controller.rb'
- 'ee/app/controllers/subscriptions/groups_controller.rb'
- 'ee/app/controllers/trial_registrations_controller.rb'
- 'ee/app/controllers/trials_controller.rb'
- 'ee/app/experiments/cart_abandonment_modal_experiment.rb'
- 'ee/app/models/ee/epic.rb'
- 'ee/app/services/feature_flag_issues/destroy_service.rb'
- 'ee/db/geo/migrate/20170906174622_remove_duplicates_from_project_registry.rb'
- 'lib/api/helpers/packages/conan/api_helpers.rb'
- 'lib/api/helpers/projects_helpers.rb'
- 'lib/api/projects_relation_builder.rb'
- 'lib/backup/task.rb'
- 'lib/banzai/filter/inline_embeds_filter.rb'
- 'lib/feature.rb'
- 'lib/gitlab/alert_management/payload/base.rb'
- 'lib/gitlab/background_migration/backfill_iteration_cadence_id_for_boards.rb'
- 'lib/gitlab/background_migration/create_security_setting.rb'
- 'lib/gitlab/background_migration/drop_invalid_remediations.rb'
- 'lib/gitlab/background_migration/fix_incorrect_max_seats_used.rb'
- 'lib/gitlab/background_migration/migrate_approver_to_approval_rules.rb'
- 'lib/gitlab/background_migration/migrate_approver_to_approval_rules_check_progress.rb'
- 'lib/gitlab/background_migration/migrate_approver_to_approval_rules_in_batch.rb'
- 'lib/gitlab/background_migration/migrate_job_artifact_registry_to_ssf.rb'
- 'lib/gitlab/background_migration/migrate_requirements_to_work_items.rb'
- 'lib/gitlab/background_migration/recalculate_vulnerability_finding_signatures_for_findings.rb'
- 'lib/gitlab/background_migration/update_vulnerability_occurrences_location.rb'
- 'lib/gitlab/ci/config/entry/need.rb'
- 'lib/gitlab/ci/config/entry/rules/rule.rb'
- 'lib/gitlab/ci/limit.rb'
- 'lib/gitlab/ci/pipeline/chain/validate/after_config.rb'
- 'lib/gitlab/config/entry/node.rb'
- 'lib/gitlab/config/entry/simplifiable.rb'
- 'lib/gitlab/email/message/in_product_marketing/experience.rb'
- 'lib/gitlab/empty_search_results.rb'
- 'lib/gitlab/git_access.rb'
- 'lib/gitlab/import_export/json/ndjson_writer.rb'
- 'lib/gitlab/null_request_store.rb'
- 'lib/gitlab/usage_data_non_sql_metrics.rb'
- 'lib/mattermost/session.rb'
- 'qa/qa/resource/clusters/agent.rb'
- 'qa/qa/resource/clusters/agent_token.rb'
- 'qa/qa/resource/job.rb'
- 'qa/qa/resource/package.rb'
- 'qa/qa/resource/registry_repository.rb'
- 'qa/qa/resource/runner.rb'
- 'qa/qa/service/cluster_provider/k3d.rb'
- 'qa/qa/service/cluster_provider/k3s.rb'
- 'qa/qa/service/cluster_provider/minikube.rb'
- 'spec/controllers/concerns/check_rate_limit_spec.rb'
- 'spec/controllers/concerns/issuable_actions_spec.rb'
- 'spec/initializers/forbid_sidekiq_in_transactions_spec.rb'
- 'spec/lib/api/helpers/rate_limiter_spec.rb'
- 'spec/lib/gitlab/database/load_balancing/sidekiq_client_middleware_spec.rb'
- 'spec/lib/gitlab/database/load_balancing/sidekiq_server_middleware_spec.rb'
- 'spec/lib/gitlab/database/migration_helpers/restrict_gitlab_schema_spec.rb'
- 'spec/lib/gitlab/database/migrations/background_migration_helpers_spec.rb'
- 'spec/lib/gitlab/database/partitioning/sliding_list_strategy_spec.rb'
- 'spec/lib/gitlab/database/postgresql_adapter/dump_schema_versions_mixin_spec.rb'
- 'spec/lib/gitlab/database/postgresql_database_tasks/load_schema_versions_mixin_spec.rb'
- 'spec/lib/gitlab/git/rugged_impl/use_rugged_spec.rb'
- 'spec/lib/gitlab/repository_archive_rate_limiter_spec.rb'
- 'spec/lib/gitlab/repository_cache_adapter_spec.rb'
- 'spec/lib/gitlab/sidekiq_middleware/client_metrics_spec.rb'
- 'spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/client_spec.rb'
- 'spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/server_spec.rb'
- 'spec/lib/gitlab/sidekiq_middleware/instrumentation_logger_spec.rb'
- 'spec/lib/gitlab/sidekiq_middleware/query_analyzer_spec.rb'
- 'spec/lib/gitlab/sidekiq_middleware/server_metrics_spec.rb'
- 'spec/lib/gitlab/sidekiq_middleware/worker_context/client_spec.rb'
- 'spec/lib/gitlab/sidekiq_middleware/worker_context/server_spec.rb'
- 'spec/lib/gitlab/ssh_public_key_spec.rb'
- 'spec/lib/gitlab/utils/delegator_override/validator_spec.rb'
- 'spec/lib/gitlab/utils/delegator_override_spec.rb'
- 'spec/lib/gitlab/utils/override_spec.rb'
- 'spec/workers/concerns/waitable_worker_spec.rb'

View File

@ -0,0 +1,21 @@
import axios from '../lib/utils/axios_utils';
import { buildApiUrl } from './api_utils';
const JIRA_CONNECT_SUBSCRIPTIONS_PATH = '/api/:version/integrations/jira_connect/subscriptions';
export function addJiraConnectSubscription(namespacePath, { jwt, accessToken }) {
const url = buildApiUrl(JIRA_CONNECT_SUBSCRIPTIONS_PATH);
return axios.post(
url,
{
jwt,
namespace_path: namespacePath,
},
{
headers: {
Authorization: `Bearer ${accessToken}`, // eslint-disable-line @gitlab/require-i18n-strings
},
},
);
}

View File

@ -371,7 +371,7 @@ export default {
events.push(TRACKING_MULTIPLE_FILES_MODE); events.push(TRACKING_MULTIPLE_FILES_MODE);
} }
queueRedisHllEvents(events); queueRedisHllEvents(events, { verifyCap: true });
this.subscribeToVirtualScrollingEvents(); this.subscribeToVirtualScrollingEvents();
}, },

View File

@ -379,6 +379,53 @@ export default {
:class="hasBodyClasses.contentByHash" :class="hasBodyClasses.contentByHash"
data-testid="content-area" data-testid="content-area"
> >
<gl-alert
v-if="!showLoadingIcon && file.conflict_type"
variant="danger"
:dismissible="false"
data-testid="conflictsAlert"
>
{{ $options.CONFLICT_TEXT[file.conflict_type] }}
<template v-if="!canMerge">
{{ __('Ask someone with write access to resolve it.') }}
</template>
<gl-sprintf
v-else-if="conflictResolutionPath"
:message="
__(
'You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}.',
)
"
>
<template #gitlabLink="{ content }">
<gl-button
:href="conflictResolutionPath"
variant="link"
class="gl-vertical-align-text-bottom"
>{{ content }}</gl-button
>
</template>
<template #resolveLocally="{ content }">
<gl-button
variant="link"
class="gl-vertical-align-text-bottom js-check-out-modal-trigger"
>{{ content }}</gl-button
>
</template>
</gl-sprintf>
<gl-sprintf
v-else
:message="__('You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}.')"
>
<template #resolveLocally="{ content }">
<gl-button
variant="link"
class="gl-vertical-align-text-bottom js-check-out-modal-trigger"
>{{ content }}</gl-button
>
</template>
</gl-sprintf>
</gl-alert>
<gl-loading-icon <gl-loading-icon
v-if="showLoadingIcon" v-if="showLoadingIcon"
size="sm" size="sm"
@ -402,53 +449,6 @@ export default {
<div v-else v-safe-html="errorMessage" class="nothing-here-block"></div> <div v-else v-safe-html="errorMessage" class="nothing-here-block"></div>
</div> </div>
<template v-else> <template v-else>
<gl-alert
v-if="file.conflict_type"
variant="danger"
:dismissible="false"
data-testid="conflictsAlert"
>
{{ $options.CONFLICT_TEXT[file.conflict_type] }}
<template v-if="!canMerge">
{{ __('Ask someone with write access to resolve it.') }}
</template>
<gl-sprintf
v-else-if="conflictResolutionPath"
:message="
__(
'You can %{gitlabLinkStart}resolve conflicts on GitLab%{gitlabLinkEnd} or %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}.',
)
"
>
<template #gitlabLink="{ content }">
<gl-button
:href="conflictResolutionPath"
variant="link"
class="gl-vertical-align-text-bottom"
>{{ content }}</gl-button
>
</template>
<template #resolveLocally="{ content }">
<gl-button
variant="link"
class="gl-vertical-align-text-bottom js-check-out-modal-trigger"
>{{ content }}</gl-button
>
</template>
</gl-sprintf>
<gl-sprintf
v-else
:message="__('You can %{resolveLocallyStart}resolve it locally%{resolveLocallyEnd}.')"
>
<template #resolveLocally="{ content }">
<gl-button
variant="link"
class="gl-vertical-align-text-bottom js-check-out-modal-trigger"
>{{ content }}</gl-button
>
</template>
</gl-sprintf>
</gl-alert>
<div <div
v-if="showWarning" v-if="showWarning"
class="collapsed-file-warning gl-p-7 gl-bg-orange-50 gl-text-center gl-rounded-bottom-left-base gl-rounded-bottom-right-base" class="collapsed-file-warning gl-p-7 gl-bg-orange-50 gl-text-center gl-rounded-bottom-left-base gl-rounded-bottom-right-base"

View File

@ -102,6 +102,8 @@ export const CONFLICT_MARKER_THEIR = 'conflict_marker_their';
// Tracking events // Tracking events
export const DEFER_DURATION = 750; export const DEFER_DURATION = 750;
export const TRACKING_CAP_KEY = 'code_review_events_dispatched';
export const TRACKING_CAP_LENGTH = 86400000; // 24 hours
export const TRACKING_CLICK_DIFF_VIEW_SETTING = 'i_code_review_click_diff_view_setting'; export const TRACKING_CLICK_DIFF_VIEW_SETTING = 'i_code_review_click_diff_view_setting';
export const TRACKING_DIFF_VIEW_INLINE = 'i_code_review_diff_view_inline'; export const TRACKING_DIFF_VIEW_INLINE = 'i_code_review_diff_view_inline';

View File

@ -1,13 +1,31 @@
import { delay } from 'lodash'; import { delay } from 'lodash';
import api from '~/api'; import api from '~/api';
import { DEFER_DURATION } from '../constants'; import { DEFER_DURATION, TRACKING_CAP_KEY, TRACKING_CAP_LENGTH } from '../constants';
function trackRedisHllUserEvent(event, deferDuration = 0) { function shouldDispatchEvent() {
const timestamp = parseInt(localStorage.getItem(TRACKING_CAP_KEY), 10);
if (Number.isNaN(timestamp)) {
return true;
}
return timestamp + TRACKING_CAP_LENGTH < Date.now();
}
export function dispatchRedisHllUserEvent(event, deferDuration = 0) {
delay(() => api.trackRedisHllUserEvent(event), deferDuration); delay(() => api.trackRedisHllUserEvent(event), deferDuration);
} }
export function queueRedisHllEvents(events) { export function queueRedisHllEvents(events, { verifyCap = false } = {}) {
if (verifyCap) {
if (!shouldDispatchEvent()) {
return;
}
localStorage.setItem(TRACKING_CAP_KEY, Date.now());
}
events.forEach((event, index) => { events.forEach((event, index) => {
trackRedisHllUserEvent(event, DEFER_DURATION * (index + 1)); dispatchRedisHllUserEvent(event, DEFER_DURATION * (index + 1));
}); });
} }

View File

@ -1,20 +1,30 @@
<script> <script>
import { mapActions } from 'vuex';
import { GlButton } from '@gitlab/ui'; import { GlButton } from '@gitlab/ui';
import { helpPagePath } from '~/helpers/help_page_helper';
import { addSubscription } from '~/jira_connect/subscriptions/api'; import { addSubscription } from '~/jira_connect/subscriptions/api';
import { persistAlert, reloadPage } from '~/jira_connect/subscriptions/utils'; import { persistAlert, reloadPage } from '~/jira_connect/subscriptions/utils';
import { s__ } from '~/locale'; import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import GroupItemName from '../group_item_name.vue'; import GroupItemName from '../group_item_name.vue';
import {
INTEGRATIONS_DOC_LINK,
I18N_ADD_SUBSCRIPTION_SUCCESS_ALERT_TITLE,
I18N_ADD_SUBSCRIPTION_SUCCESS_ALERT_MESSAGE,
I18N_ADD_SUBSCRIPTIONS_ERROR_MESSAGE,
} from '../../constants';
export default { export default {
components: { components: {
GlButton, GlButton,
GroupItemName, GroupItemName,
}, },
mixins: [glFeatureFlagMixin()],
inject: { inject: {
addSubscriptionsPath: { addSubscriptionsPath: {
default: '', default: '',
}, },
subscriptionsPath: {
default: '',
},
}, },
props: { props: {
group: { group: {
@ -32,31 +42,41 @@ export default {
isLoading: false, isLoading: false,
}; };
}, },
computed: {
oauthEnabled() {
return this.glFeatures.jiraConnectOauth;
},
},
methods: { methods: {
onClick() { ...mapActions(['addSubscription']),
async onClick() {
if (this.oauthEnabled) {
this.isLoading = true;
await this.addSubscription({
namespacePath: this.group.full_path,
subscriptionsPath: this.subscriptionsPath,
});
this.isLoading = false;
} else {
this.deprecatedAddSubscription();
}
},
deprecatedAddSubscription() {
this.isLoading = true; this.isLoading = true;
addSubscription(this.addSubscriptionsPath, this.group.full_path) addSubscription(this.addSubscriptionsPath, this.group.full_path)
.then(() => { .then(() => {
persistAlert({ persistAlert({
title: s__('Integrations|Namespace successfully linked'), title: I18N_ADD_SUBSCRIPTION_SUCCESS_ALERT_TITLE,
message: s__( message: I18N_ADD_SUBSCRIPTION_SUCCESS_ALERT_MESSAGE,
'Integrations|You should now see GitLab.com activity inside your Jira Cloud issues. %{linkStart}Learn more%{linkEnd}', linkUrl: INTEGRATIONS_DOC_LINK,
),
linkUrl: helpPagePath('integration/jira_development_panel.html', {
anchor: 'use-the-integration',
}),
variant: 'success', variant: 'success',
}); });
reloadPage(); reloadPage();
}) })
.catch((error) => { .catch((error) => {
this.$emit( this.$emit('error', error?.response?.data?.error || I18N_ADD_SUBSCRIPTIONS_ERROR_MESSAGE);
'error',
error?.response?.data?.error ||
s__('Integrations|Failed to link namespace. Please try again.'),
);
this.isLoading = false; this.isLoading = false;
}); });
}, },

View File

@ -34,12 +34,8 @@ export default {
default: '', default: '',
}, },
}, },
data() {
return {
user: null,
};
},
computed: { computed: {
...mapState(['currentUser']),
...mapState(['alert', 'subscriptions']), ...mapState(['alert', 'subscriptions']),
shouldShowAlert() { shouldShowAlert() {
return Boolean(this.alert?.message); return Boolean(this.alert?.message);
@ -48,7 +44,11 @@ export default {
return !isEmpty(this.subscriptions); return !isEmpty(this.subscriptions);
}, },
userSignedIn() { userSignedIn() {
return Boolean(!this.usersPath || this.user); if (this.isOauthEnabled) {
return Boolean(this.currentUser);
}
return Boolean(!this.usersPath);
}, },
isOauthEnabled() { isOauthEnabled() {
return this.glFeatures.jiraConnectOauth; return this.glFeatures.jiraConnectOauth;
@ -85,8 +85,7 @@ export default {
const { linkUrl, title, message, variant } = retrieveAlert() || {}; const { linkUrl, title, message, variant } = retrieveAlert() || {};
this.setAlert({ linkUrl, title, message, variant }); this.setAlert({ linkUrl, title, message, variant });
}, },
onSignInOauth(user) { onSignInOauth() {
this.user = user;
this.fetchSubscriptionsOauth(); this.fetchSubscriptionsOauth();
}, },
onSignInError() { onSignInError() {
@ -123,7 +122,11 @@ export default {
</template> </template>
</gl-alert> </gl-alert>
<user-link :user-signed-in="userSignedIn" :has-subscriptions="hasSubscriptions" :user="user" /> <user-link
:user-signed-in="userSignedIn"
:has-subscriptions="hasSubscriptions"
:user="currentUser"
/>
<div class="gl-layout-w-limited gl-mx-auto gl-px-5 gl-mb-7"> <div class="gl-layout-w-limited gl-mx-auto gl-px-5 gl-mb-7">
<sign-in-page <sign-in-page

View File

@ -1,4 +1,5 @@
<script> <script>
import { mapActions, mapMutations } from 'vuex';
import { GlButton } from '@gitlab/ui'; import { GlButton } from '@gitlab/ui';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import { import {
@ -8,8 +9,8 @@ import {
} from '~/jira_connect/subscriptions/constants'; } from '~/jira_connect/subscriptions/constants';
import { setUrlParams } from '~/lib/utils/url_utility'; import { setUrlParams } from '~/lib/utils/url_utility';
import AccessorUtilities from '~/lib/utils/accessor'; import AccessorUtilities from '~/lib/utils/accessor';
import { getCurrentUser } from '~/rest_api';
import { createCodeVerifier, createCodeChallenge } from '../pkce'; import { createCodeVerifier, createCodeChallenge } from '../pkce';
import { SET_ACCESS_TOKEN } from '../store/mutation_types';
export default { export default {
components: { components: {
@ -31,6 +32,10 @@ export default {
window.removeEventListener('message', this.handleWindowMessage); window.removeEventListener('message', this.handleWindowMessage);
}, },
methods: { methods: {
...mapActions(['loadCurrentUser']),
...mapMutations({
setAccessToken: SET_ACCESS_TOKEN,
}),
async startOAuthFlow() { async startOAuthFlow() {
this.loading = true; this.loading = true;
@ -58,7 +63,6 @@ export default {
async handleWindowMessage(event) { async handleWindowMessage(event) {
if (window.origin !== event.origin) { if (window.origin !== event.origin) {
this.loading = false; this.loading = false;
this.handleError();
return; return;
} }
@ -74,8 +78,10 @@ export default {
const code = event.data?.code; const code = event.data?.code;
try { try {
const accessToken = await this.getOAuthToken(code); const accessToken = await this.getOAuthToken(code);
await this.loadCurrentUser(accessToken);
await this.loadUser(accessToken); this.setAccessToken(accessToken);
this.$emit('sign-in');
} catch (e) { } catch (e) {
this.handleError(); this.handleError();
} finally { } finally {
@ -98,13 +104,6 @@ export default {
return data.access_token; return data.access_token;
}, },
async loadUser(accessToken) {
const { data } = await getCurrentUser({
headers: { Authorization: `Bearer ${accessToken}` },
});
this.$emit('sign-in', data);
},
}, },
i18n: { i18n: {
defaultButtonText: I18N_DEFAULT_SIGN_IN_BUTTON_TEXT, defaultButtonText: I18N_DEFAULT_SIGN_IN_BUTTON_TEXT,

View File

@ -1,4 +1,5 @@
import { s__ } from '~/locale'; import { s__ } from '~/locale';
import { helpPagePath } from '~/helpers/help_page_helper';
export const DEFAULT_GROUPS_PER_PAGE = 10; export const DEFAULT_GROUPS_PER_PAGE = 10;
export const ALERT_LOCALSTORAGE_KEY = 'gitlab_alert'; export const ALERT_LOCALSTORAGE_KEY = 'gitlab_alert';
@ -11,6 +12,19 @@ export const I18N_DEFAULT_SIGN_IN_ERROR_MESSAGE = s__('Integrations|Failed to si
export const I18N_DEFAULT_SUBSCRIPTIONS_ERROR_MESSAGE = s__( export const I18N_DEFAULT_SUBSCRIPTIONS_ERROR_MESSAGE = s__(
'Integrations|Failed to load subscriptions.', 'Integrations|Failed to load subscriptions.',
); );
export const I18N_ADD_SUBSCRIPTION_SUCCESS_ALERT_TITLE = s__(
'Integrations|Namespace successfully linked',
);
export const I18N_ADD_SUBSCRIPTION_SUCCESS_ALERT_MESSAGE = s__(
'Integrations|You should now see GitLab.com activity inside your Jira Cloud issues. %{linkStart}Learn more%{linkEnd}',
);
export const INTEGRATIONS_DOC_LINK = helpPagePath('integration/jira_development_panel', {
anchor: 'use-the-integration',
});
export const I18N_ADD_SUBSCRIPTIONS_ERROR_MESSAGE = s__(
'Integrations|Failed to link namespace. Please try again.',
);
const OAUTH_WINDOW_SIZE = 800; const OAUTH_WINDOW_SIZE = 800;
export const OAUTH_WINDOW_OPTIONS = [ export const OAUTH_WINDOW_OPTIONS = [

View File

@ -29,7 +29,7 @@ export default {
<sign-in-gitlab-com <sign-in-gitlab-com
v-else v-else
:has-subscriptions="hasSubscriptions" :has-subscriptions="hasSubscriptions"
@sign-in-oauth="$emit('sign-in-oauth', $event)" @sign-in-oauth="$emit('sign-in-oauth')"
@error="$emit('error', $event)" @error="$emit('error', $event)"
/> />
</template> </template>

View File

@ -1,10 +1,22 @@
import { fetchSubscriptions as fetchSubscriptionsREST } from '~/jira_connect/subscriptions/api'; import { fetchSubscriptions as fetchSubscriptionsREST } from '~/jira_connect/subscriptions/api';
import { I18N_DEFAULT_SUBSCRIPTIONS_ERROR_MESSAGE } from '../constants'; import { getCurrentUser } from '~/rest_api';
import { addJiraConnectSubscription } from '~/api/integrations_api';
import {
I18N_ADD_SUBSCRIPTION_SUCCESS_ALERT_TITLE,
I18N_ADD_SUBSCRIPTION_SUCCESS_ALERT_MESSAGE,
INTEGRATIONS_DOC_LINK,
I18N_DEFAULT_SUBSCRIPTIONS_ERROR_MESSAGE,
} from '../constants';
import { getJwt } from '../utils';
import { import {
SET_SUBSCRIPTIONS, SET_SUBSCRIPTIONS,
SET_SUBSCRIPTIONS_LOADING, SET_SUBSCRIPTIONS_LOADING,
SET_SUBSCRIPTIONS_ERROR, SET_SUBSCRIPTIONS_ERROR,
ADD_SUBSCRIPTION_LOADING,
ADD_SUBSCRIPTION_ERROR,
SET_ALERT, SET_ALERT,
SET_CURRENT_USER,
SET_CURRENT_USER_ERROR,
} from './mutation_types'; } from './mutation_types';
export const fetchSubscriptions = async ({ commit }, subscriptionsPath) => { export const fetchSubscriptions = async ({ commit }, subscriptionsPath) => {
@ -20,3 +32,42 @@ export const fetchSubscriptions = async ({ commit }, subscriptionsPath) => {
commit(SET_SUBSCRIPTIONS_LOADING, false); commit(SET_SUBSCRIPTIONS_LOADING, false);
} }
}; };
export const loadCurrentUser = async ({ commit }, accessToken) => {
try {
const { data: user } = await getCurrentUser({
headers: { Authorization: `Bearer ${accessToken}` },
});
commit(SET_CURRENT_USER, user);
} catch (e) {
commit(SET_CURRENT_USER_ERROR, e);
}
};
export const addSubscription = async (
{ commit, state, dispatch },
{ namespacePath, subscriptionsPath },
) => {
try {
commit(ADD_SUBSCRIPTION_LOADING, true);
await addJiraConnectSubscription(namespacePath, {
jwt: await getJwt(),
accessToken: state.accessToken,
});
commit(SET_ALERT, {
title: I18N_ADD_SUBSCRIPTION_SUCCESS_ALERT_TITLE,
message: I18N_ADD_SUBSCRIPTION_SUCCESS_ALERT_MESSAGE,
linkUrl: INTEGRATIONS_DOC_LINK,
variant: 'success',
});
dispatch('fetchSubscriptions', subscriptionsPath);
} catch (e) {
commit(ADD_SUBSCRIPTION_ERROR, e);
} finally {
commit(ADD_SUBSCRIPTION_LOADING, false);
}
};

View File

@ -1,4 +1,13 @@
export const SET_ALERT = 'SET_ALERT'; export const SET_ALERT = 'SET_ALERT';
export const SET_SUBSCRIPTIONS = 'SET_SUBSCRIPTIONS'; export const SET_SUBSCRIPTIONS = 'SET_SUBSCRIPTIONS';
export const SET_SUBSCRIPTIONS_LOADING = 'SET_SUBSCRIPTIONS_LOADING'; export const SET_SUBSCRIPTIONS_LOADING = 'SET_SUBSCRIPTIONS_LOADING';
export const SET_SUBSCRIPTIONS_ERROR = 'SET_SUBSCRIPTIONS_ERROR'; export const SET_SUBSCRIPTIONS_ERROR = 'SET_SUBSCRIPTIONS_ERROR';
export const ADD_SUBSCRIPTION_LOADING = 'ADD_SUBSCRIPTION_LOADING';
export const ADD_SUBSCRIPTION_ERROR = 'ADD_SUBSCRIPTION_ERROR';
export const SET_CURRENT_USER = 'SET_CURRENT_USER';
export const SET_CURRENT_USER_ERROR = 'SET_CURRENT_USER_ERROR';
export const SET_ACCESS_TOKEN = 'SET_ACCESS_TOKEN';

View File

@ -3,12 +3,18 @@ import {
SET_SUBSCRIPTIONS, SET_SUBSCRIPTIONS,
SET_SUBSCRIPTIONS_LOADING, SET_SUBSCRIPTIONS_LOADING,
SET_SUBSCRIPTIONS_ERROR, SET_SUBSCRIPTIONS_ERROR,
ADD_SUBSCRIPTION_LOADING,
ADD_SUBSCRIPTION_ERROR,
SET_CURRENT_USER,
SET_CURRENT_USER_ERROR,
SET_ACCESS_TOKEN,
} from './mutation_types'; } from './mutation_types';
export default { export default {
[SET_ALERT](state, { title, message, variant, linkUrl } = {}) { [SET_ALERT](state, { title, message, variant, linkUrl } = {}) {
state.alert = { title, message, variant, linkUrl }; state.alert = { title, message, variant, linkUrl };
}, },
[SET_SUBSCRIPTIONS](state, subscriptions = []) { [SET_SUBSCRIPTIONS](state, subscriptions = []) {
state.subscriptions = subscriptions; state.subscriptions = subscriptions;
}, },
@ -18,4 +24,22 @@ export default {
[SET_SUBSCRIPTIONS_ERROR](state, subscriptionsError) { [SET_SUBSCRIPTIONS_ERROR](state, subscriptionsError) {
state.subscriptionsError = subscriptionsError; state.subscriptionsError = subscriptionsError;
}, },
[ADD_SUBSCRIPTION_LOADING](state, loading) {
state.addSubscriptionLoading = loading;
},
[ADD_SUBSCRIPTION_ERROR](state, error) {
state.addSubscriptionError = error;
},
[SET_CURRENT_USER](state, currentUser) {
state.currentUser = currentUser;
},
[SET_CURRENT_USER_ERROR](state, currentUserError) {
state.currentUserError = currentUserError;
},
[SET_ACCESS_TOKEN](state, accessToken) {
state.accessToken = accessToken;
},
}; };

View File

@ -1,8 +1,17 @@
export default function createState({ subscriptions = [], subscriptionsLoading = false } = {}) { export default function createState({ subscriptions = [], subscriptionsLoading = false } = {}) {
return { return {
alert: undefined, alert: undefined,
subscriptions, subscriptions,
subscriptionsLoading, subscriptionsLoading,
subscriptionsError: false, subscriptionsError: false,
addSubscriptionLoading: false,
addSubscriptionError: false,
currentUser: null,
currentUserError: null,
accessToken: null,
}; };
} }

View File

@ -60,15 +60,15 @@ export default {
<div <div
class="gl-display-flex gl-align-items-center gl-pl-4 gl-rounded-base gl-mr-3" class="gl-display-flex gl-align-items-center gl-pl-4 gl-rounded-base gl-mr-3"
:class="{ :class="{
'gl-bg-orange-50': blocksMerge, 'gl-bg-orange-50': blocksMerge && !allResolved,
'gl-bg-gray-50': !blocksMerge, 'gl-bg-gray-50': !blocksMerge || allResolved,
'gl-pr-4': allResolved, 'gl-pr-4': allResolved,
'gl-pr-2': !allResolved, 'gl-pr-2': !allResolved,
}" }"
data-testid="discussions-counter-text" data-testid="discussions-counter-text"
> >
<template v-if="allResolved"> <template v-if="allResolved">
{{ __('All threads resolved') }} {{ __('All threads resolved!') }}
</template> </template>
<template v-else> <template v-else>
{{ n__('%d unresolved thread', '%d unresolved threads', unresolvedDiscussionsCount) }} {{ n__('%d unresolved thread', '%d unresolved threads', unresolvedDiscussionsCount) }}

View File

@ -44,9 +44,3 @@ $header-height: 40px;
height: calc(100% - #{$header-height}); height: calc(100% - #{$header-height});
max-width: 1000px; max-width: 1000px;
} }
// needed for external_link
svg.s16 {
width: 16px;
height: 16px;
}

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
# Concern for handling deprecation arguments. # Concern for handling deprecation arguments.
# https://docs.gitlab.com/ee/development/api_graphql_styleguide.html#deprecating-fields-and-enum-values # https://docs.gitlab.com/ee/development/api_graphql_styleguide.html#deprecating-schema-items
module GitlabStyleDeprecations module GitlabStyleDeprecations
extend ActiveSupport::Concern extend ActiveSupport::Concern
@ -11,7 +11,7 @@ module GitlabStyleDeprecations
def gitlab_deprecation(kwargs) def gitlab_deprecation(kwargs)
if kwargs[:deprecation_reason].present? if kwargs[:deprecation_reason].present?
raise ArgumentError, 'Use `deprecated` property instead of `deprecation_reason`. ' \ raise ArgumentError, 'Use `deprecated` property instead of `deprecation_reason`. ' \
'See https://docs.gitlab.com/ee/development/api_graphql_styleguide.html#deprecating-fields-arguments-and-enum-values' 'See https://docs.gitlab.com/ee/development/api_graphql_styleguide.html#deprecating-schema-items'
end end
deprecation = ::Gitlab::Graphql::Deprecation.parse(kwargs.delete(:deprecated)) deprecation = ::Gitlab::Graphql::Deprecation.parse(kwargs.delete(:deprecated))

View File

@ -88,7 +88,7 @@ module NamespacesHelper
}.to_json }.to_json
end end
def pipeline_usage_quota_app_data(namespace) def pipeline_usage_app_data(namespace)
{ {
namespace_actual_plan_name: namespace.actual_plan_name, namespace_actual_plan_name: namespace.actual_plan_name,
namespace_path: namespace.full_path, namespace_path: namespace.full_path,

View File

@ -11,7 +11,7 @@
= s_('JiraService|You can now close this window and%{br}return to the GitLab for Jira application.').html_safe % { br: '<br>'.html_safe } = s_('JiraService|You can now close this window and%{br}return to the GitLab for Jira application.').html_safe % { br: '<br>'.html_safe }
- if @jira_app_link - if @jira_app_link
%p= external_link s_('Integrations|Return to GitLab for Jira'), @jira_app_link, class: 'gl-button btn btn-confirm' %p= link_to s_('Integrations|Return to GitLab for Jira'), @jira_app_link, class: 'gl-button btn btn-confirm'
%p= link_to _('Sign out'), destroy_user_session_path, method: :post %p= link_to _('Sign out'), destroy_user_session_path, method: :post

View File

@ -31,11 +31,11 @@
= sprite_icon('chevron-double-lg-left') = sprite_icon('chevron-double-lg-left')
.detail-page-header-actions.js-issuable-actions{ class: "#{'gl-align-self-start is-merge-request' if updated_mr_header_enabled}" } .detail-page-header-actions.js-issuable-actions{ class: "#{'gl-align-self-start is-merge-request' if updated_mr_header_enabled}" }
- if @merge_request.source_project
= render 'projects/merge_requests/code_dropdown'
- if can_update_merge_request - if can_update_merge_request
= link_to _('Edit'), edit_project_merge_request_path(@project, @merge_request), class: "gl-display-none gl-md-display-block btn gl-button btn-default btn-grouped js-issuable-edit", data: { qa_selector: "edit_button" } = link_to _('Edit'), edit_project_merge_request_path(@project, @merge_request), class: "gl-display-none gl-md-display-block btn gl-button btn-default btn-grouped js-issuable-edit", data: { qa_selector: "edit_button" }
- if @merge_request.source_project
= render 'projects/merge_requests/code_dropdown'
- if current_user - if current_user
= render 'projects/merge_requests/close_reopen_draft_report_toggle' = render 'projects/merge_requests/close_reopen_draft_report_toggle'

View File

@ -21,7 +21,7 @@
.text-secondary .text-secondary
= sprite_icon("rocket", size: 12) = sprite_icon("rocket", size: 12)
= _("Release") = _("Release")
= link_to release.name, project_release_path(@project, release.tag), class: 'gl-text-blue-600!' = link_to release.name, project_releases_path(@project, anchor: release.tag), class: 'gl-text-blue-600!'
- if tag.message.present? - if tag.message.present?
%pre.wrap %pre.wrap

View File

@ -33,6 +33,7 @@
- credential_management - credential_management
- database - database
- dataops - dataops
- dedicated
- delivery - delivery
- dependency_firewall - dependency_firewall
- dependency_proxy - dependency_proxy
@ -52,7 +53,6 @@
- experimentation_conversion - experimentation_conversion
- experimentation_expansion - experimentation_expansion
- feature_flags - feature_flags
- free_user_caps_conversion
- five_minute_production_app - five_minute_production_app
- fulfillment_platform - fulfillment_platform
- fuzz_testing - fuzz_testing
@ -62,7 +62,6 @@
- gitlab_docs - gitlab_docs
- global_search - global_search
- helm_chart_registry - helm_chart_registry
- horse
- importers - importers
- incident_management - incident_management
- infrastructure_as_code - infrastructure_as_code
@ -101,6 +100,7 @@
- redis - redis
- release_evidence - release_evidence
- release_orchestration - release_orchestration
- remote_development
- requirements_management - requirements_management
- review_apps - review_apps
- runbooks - runbooks

View File

@ -0,0 +1,8 @@
---
name: delayed_project_import_schedule_worker
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/87438
rollout_issue_url: https://gitlab.com/gitlab-com/gl-infra/scalability/-/issues/1681
milestone: '15.0'
type: development
group: group::scalability
default_enabled: false

View File

@ -0,0 +1,16 @@
- name: "Jaeger integration removed in GitLab 15.0" # The headline announcing the removal. i.e. "`CI_PROJECT_CONFIG_PATH` removed in Gitlab 14.0"
announcement_milestone: "14.7" # The milestone when this feature was first announced as deprecated.
announcement_date: "2022-01-22"
removal_milestone: "15.0" # The milestone when this feature is being removed.
removal_date: "2022-05-22" # This should almost always be the 22nd of a month (YYYY-MM-DD), the date of the milestone release when this feature will be removed.
breaking_change: true # Change to true if this removal is a breaking change.
reporter: kbychu # GitLab username of the person reporting the removal
body: | # Do not modify this line, instead modify the lines below.
Tracing in GitLab is an integration with Jaeger, an open-source end-to-end distributed tracing system. GitLab users could previously navigate to their Jaeger instance to gain insight into the performance of a deployed application, tracking each function or microservice that handles a given request. Tracing in GitLab was deprecated in GitLab 14.7, and removed in 15.0. To track work on a possible replacement, see the issue for [Opstrace integration with GitLab](https://gitlab.com/groups/gitlab-org/-/epics/6976).
# The following items are not published on the docs page, but may be used in the future.
stage: Monitor # (optional - may be required in the future) String value of the stage that the feature was created in. e.g., Growth
tiers: [Free] # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate]
issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/346540 # (optional) This is a link to the deprecation issue in GitLab
documentation_url: https://docs.gitlab.com/ee/operations/tracing.html # (optional) This is a link to the current documentation page
image_url: # (optional) This is a link to a thumbnail image depicting the feature
video_url: # (optional) Use the youtube thumbnail URL with the structure of https://img.youtube.com/vi/UNIQUEID/hqdefault.jpg

View File

@ -561,7 +561,7 @@ This same warning is displayed when:
The default value for `allow_failure` is: The default value for `allow_failure` is:
- `true` for [manual jobs](../jobs/job_control.md#create-a-job-that-must-be-run-manually). - `true` for [manual jobs](../jobs/job_control.md#create-a-job-that-must-be-run-manually).
- `false` for manual jobs that also use [`rules`](#rules). - `false` for jobs that use `when: manual` inside [`rules`](#rules).
- `false` in all other cases. - `false` in all other cases.
**Keyword type**: Job keyword. You can use it only as part of a job. **Keyword type**: Job keyword. You can use it only as part of a job.

View File

@ -97,7 +97,7 @@ discussed in [Nullable fields](#nullable-fields).
Fields that use the [`feature_flag` property](#feature_flag-property) and the flag is disabled by default are exempt Fields that use the [`feature_flag` property](#feature_flag-property) and the flag is disabled by default are exempt
from the deprecation process, and can be removed at any time without notice. from the deprecation process, and can be removed at any time without notice.
See the [deprecating fields, arguments, and enum values](#deprecating-fields-arguments-and-enum-values) section for how to deprecate items. See the [deprecating schema items](#deprecating-schema-items) section for how to deprecate items.
## Global IDs ## Global IDs
@ -540,18 +540,18 @@ def foo
end end
``` ```
## Deprecating fields, arguments, and enum values ## Deprecating schema items
The GitLab GraphQL API is versionless, which means we maintain backwards The GitLab GraphQL API is versionless, which means we maintain backwards
compatibility with older versions of the API with every change. compatibility with older versions of the API with every change.
Rather than removing fields, arguments, or [enum values](#enums), they Rather than removing fields, arguments, [enum values](#enums), or [mutations](#mutations),
must be _deprecated_ instead. they must be _deprecated_ instead.
The deprecated parts of the schema can then be removed in a future release The deprecated parts of the schema can then be removed in a future release
in accordance with the [GitLab deprecation process](../api/graphql/index.md#deprecation-and-removal-process). in accordance with the [GitLab deprecation process](../api/graphql/index.md#deprecation-and-removal-process).
Fields, arguments, and enum values are deprecated using the `deprecated` property. Fields, arguments, enum values, and mutations are deprecated using the `deprecated` property.
The value of the property is a `Hash` of: The value of the property is a `Hash` of:
- `reason` - Reason for the deprecation. - `reason` - Reason for the deprecation.
@ -748,7 +748,7 @@ end
``` ```
Enum values can be deprecated using the Enum values can be deprecated using the
[`deprecated` keyword](#deprecating-fields-arguments-and-enum-values). [`deprecated` keyword](#deprecating-schema-items).
### Defining GraphQL enums dynamically from Rails enums ### Defining GraphQL enums dynamically from Rails enums
@ -1713,7 +1713,7 @@ mount_aliased_mutation 'BarMutation', Mutations::FooMutation
``` ```
This allows us to rename a mutation and continue to support the old name, This allows us to rename a mutation and continue to support the old name,
when coupled with the [`deprecated`](#deprecating-fields-arguments-and-enum-values) when coupled with the [`deprecated`](#deprecating-schema-items)
argument. argument.
Example: Example:

View File

@ -85,7 +85,7 @@ voters to agree.
When features are deprecated and removed, update the related documentation. When features are deprecated and removed, update the related documentation.
API documentation follows these guidelines, but the GraphQL docs use API documentation follows these guidelines, but the GraphQL docs use
a [separate process](../api_graphql_styleguide.md#deprecating-fields-arguments-and-enum-values). a [separate process](../api_graphql_styleguide.md#deprecating-schema-items).
### Deprecate a page or topic ### Deprecate a page or topic

View File

@ -132,7 +132,7 @@ GitLab database. [Read more about this requirement, and troubleshooting](postgre
| `plpgsql` | 11.7 | | `plpgsql` | 11.7 |
NOTE: NOTE:
Support for [PostgreSQL 9.6 and 10 was removed in GitLab 13.0](https://about.gitlab.com/releases/2020/05/22/gitlab-13-0-released/#postgresql-11-is-now-the-minimum-required-version-to-install-gitlab) so that GitLab can benefit from PostgreSQL 11 improvements, such as partitioning. For the schedule of transitioning to PostgreSQL 12, see [the related epic](https://gitlab.com/groups/gitlab-org/-/epics/2184). Support for [PostgreSQL 9.6 and 10 was removed in GitLab 13.0](https://about.gitlab.com/releases/2020/05/22/gitlab-13-0-released/#postgresql-11-is-now-the-minimum-required-version-to-install-gitlab) so that GitLab can benefit from PostgreSQL 11 improvements, such as partitioning.
#### Additional requirements for GitLab Geo #### Additional requirements for GitLab Geo

View File

@ -405,6 +405,27 @@ Incremental backups can also be created from [an untarred backup](#skipping-tar-
sudo gitlab-backup create INCREMENTAL=yes SKIP=tar sudo gitlab-backup create INCREMENTAL=yes SKIP=tar
``` ```
#### Back up specific repository storages
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/86896) in GitLab 15.0.
When using [multiple repository storages](../administration/repository_storage_paths.md),
repositories from specific repository storages can be backed up separately
using the `REPOSITORIES_STORAGES` option. The option accepts a comma-separated list of
storage names.
For example, for Omnibus GitLab installations:
```shell
sudo gitlab-backup create REPOSITORIES_STORAGES=storage1,storage2
```
For example, for installations from source:
```shell
sudo -u git -H bundle exec rake gitlab:backup:create REPOSITORIES_STORAGES=storage1,storage2
```
#### Uploading backups to a remote (cloud) storage #### Uploading backups to a remote (cloud) storage
You can let the backup script upload (using the [Fog library](http://fog.io/)) You can let the backup script upload (using the [Fog library](http://fog.io/))
@ -1217,6 +1238,27 @@ For installations from source:
sudo -u git -H bundle exec rake gitlab:backup:restore BACKUP=timestamp_of_backup SKIP=db,uploads RAILS_ENV=production sudo -u git -H bundle exec rake gitlab:backup:restore BACKUP=timestamp_of_backup SKIP=db,uploads RAILS_ENV=production
``` ```
#### Restore specific repository storages
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/86896) in GitLab 15.0.
When using [multiple repository storages](../administration/repository_storage_paths.md),
repositories from specific repository storages can be restored separately
using the `REPOSITORIES_STORAGES` option. The option accepts a comma-separated list of
storage names.
For example, for Omnibus GitLab installations:
```shell
sudo gitlab-backup restore BACKUP=timestamp_of_backup REPOSITORIES_STORAGES=storage1,storage2
```
For example, for installations from source:
```shell
sudo -u git -H bundle exec rake gitlab:backup:restore BACKUP=timestamp_of_backup REPOSITORIES_STORAGES=storage1,storage2
```
## Alternative backup strategies ## Alternative backup strategies
If your GitLab instance contains a lot of Git repository data, you may find the If your GitLab instance contains a lot of Git repository data, you may find the

View File

@ -233,6 +233,16 @@ The permissions model for GraphQL is being updated. After 15.0, users with the G
The issue for this removal is [GitLab-#350682](https://gitlab.com/gitlab-org/gitlab/-/issues/350682) The issue for this removal is [GitLab-#350682](https://gitlab.com/gitlab-org/gitlab/-/issues/350682)
### Jaeger integration removed in GitLab 15.0
WARNING:
This feature was changed or removed in 15.0
as a [breaking change](https://docs.gitlab.com/ee/development/contributing/#breaking-changes).
Before updating GitLab, review the details carefully to determine if you need to make any
changes to your code, settings, or workflow.
Tracing in GitLab is an integration with Jaeger, an open-source end-to-end distributed tracing system. GitLab users could previously navigate to their Jaeger instance to gain insight into the performance of a deployed application, tracking each function or microservice that handles a given request. Tracing in GitLab was deprecated in GitLab 14.7, and removed in 15.0. To track work on a possible replacement, see the issue for [Opstrace integration with GitLab](https://gitlab.com/groups/gitlab-org/-/epics/6976).
### Legacy Geo Admin UI routes ### Legacy Geo Admin UI routes
In GitLab 13.0, we introduced new project and design replication details routes in the Geo Admin UI. These routes are `/admin/geo/replication/projects` and `/admin/geo/replication/designs`. We kept the legacy routes and redirected them to the new routes. These legacy routes `/admin/geo/projects` and `/admin/geo/designs` have been removed in GitLab 15.0. Please update any bookmarks or scripts that may use the legacy routes. In GitLab 13.0, we introduced new project and design replication details routes in the Geo Admin UI. These routes are `/admin/geo/replication/projects` and `/admin/geo/replication/designs`. We kept the legacy routes and redirected them to the new routes. These legacy routes `/admin/geo/projects` and `/admin/geo/designs` have been removed in GitLab 15.0. Please update any bookmarks or scripts that may use the legacy routes.

View File

@ -243,8 +243,8 @@ table.supported-languages ul {
<td>C#</td> <td>C#</td>
</tr> </tr>
<tr> <tr>
<td rowspan="3">Python</td> <td rowspan="4">Python</td>
<td rowspan="3">3.9</td> <td rowspan="4">3.9</td>
<td><a href="https://setuptools.readthedocs.io/en/latest/">setuptools</a></td> <td><a href="https://setuptools.readthedocs.io/en/latest/">setuptools</a></td>
<td><code>setup.py</code></td> <td><code>setup.py</code></td>
<td><a href="https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium">Gemnasium</a></td> <td><a href="https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium">Gemnasium</a></td>
@ -273,6 +273,12 @@ table.supported-languages ul {
<td><a href="https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium">Gemnasium</a></td> <td><a href="https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium">Gemnasium</a></td>
<td>N</td> <td>N</td>
</tr> </tr>
<tr>
<td><a href="https://python-poetry.org/">Poetry</a><sup><b><a href="#notes-regarding-supported-languages-and-package-managers-4">4</a></b></sup></td>
<td><code>poetry.lock</code></td>
<td><a href="https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium">Gemnasium</a></td>
<td>N</td>
</tr>
<tr> <tr>
<td>Scala</td> <td>Scala</td>
<td>N/A</td> <td>N/A</td>
@ -312,6 +318,14 @@ table.supported-languages ul {
Support for <a href="https://www.scala-sbt.org/">sbt</a> 1.3 and above was added in GitLab 13.9. Support for <a href="https://www.scala-sbt.org/">sbt</a> 1.3 and above was added in GitLab 13.9.
</p> </p>
</li> </li>
<li>
<a id="notes-regarding-supported-languages-and-package-managers-4"></a>
<p>
Support for <a href="https://python-poetry.org/">Poetry</a> projects with a <code>poetry.lock</code> file was [added in GitLab 15.0](https://gitlab.com/gitlab-org/gitlab/-/issues/7006).
Support for projects without a <code>poetry.lock</code> file is tracked in issue:
<a href="https://gitlab.com/gitlab-org/gitlab/-/issues/32774">Poetry's pyproject.toml support for dependency scanning.</a>
</p>
</li>
</ol> </ol>
<!-- markdownlint-enable MD044 --> <!-- markdownlint-enable MD044 -->
@ -328,13 +342,13 @@ The following package managers use lockfiles that GitLab analyzers are capable o
| Package Manager | Supported File Format Versions | Tested Versions | | Package Manager | Supported File Format Versions | Tested Versions |
| ------ | ------ | ------ | | ------ | ------ | ------ |
| Bundler | N/A | [1.17.3](https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium/-/blob/master/qa/fixtures/ruby-bundler/main/Gemfile.lock#L118), [2.1.4](https://gitlab.com/gitlab-org/security-products/tests/ruby-bundler/-/blob/bundler2-FREEZE/Gemfile.lock#L118) | | Bundler | N/A | [1.17.3](https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium/-/blob/master/qa/fixtures/ruby-bundler/default/Gemfile.lock#L118), [2.1.4](https://gitlab.com/gitlab-org/security-products/tests/ruby-bundler/-/blob/bundler2-FREEZE/Gemfile.lock#L118) |
| Composer | N/A | [1.x](https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium/-/blob/master/qa/fixtures/php-composer/main/composer.lock) | | Composer | N/A | [1.x](https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium/-/blob/master/qa/fixtures/php-composer/default/composer.lock) |
| Conan | 0.4 | [1.x](https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium/-/blob/master/qa/fixtures/c-conan/main/conan.lock) | | Conan | 0.4 | [1.x](https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium/-/blob/master/qa/fixtures/c-conan/default/conan.lock) |
| Go | N/A | [1.x](https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium/-/blob/master/qa/fixtures/go-modules/main/go.sum) | | Go | N/A | [1.x](https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium/-/blob/master/qa/fixtures/go-modules/default/go.sum) |
| NuGet | v1 | [4.9](https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium/-/blob/master/qa/fixtures/csharp-nuget-dotnetcore/main/src/web.api/packages.lock.json#L2) | | NuGet | v1 | [4.9](https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium/-/blob/master/qa/fixtures/csharp-nuget-dotnetcore/default/src/web.api/packages.lock.json#L2) |j
| npm | v1, v2 | [6.x](https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium/-/blob/master/qa/fixtures/js-npm/main/package-lock.json#L4), [7.x](https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium/-/blob/master/qa/fixtures/js-npm/lockfileVersion2/package-lock.json#L4) | | npm | v1, v2 | [6.x](https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium/-/blob/master/qa/fixtures/js-npm/default/package-lock.json#L4), [7.x](https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium/-/blob/master/qa/fixtures/js-npm/lockfileVersion2/package-lock.json#L4) |
| yarn | v1 | [1.x](https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium/-/blob/master/qa/fixtures/js-yarn/main/yarn.lock#L2) | | yarn | v1 | [1.x](https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium/-/blob/master/qa/fixtures/js-yarn/default/yarn.lock#L2) |
#### Obtaining dependency information by running a package manager to generate a parsable file #### Obtaining dependency information by running a package manager to generate a parsable file
@ -413,8 +427,14 @@ If you've run into problems while scanning multiple files, please contribute a c
#### Python #### Python
We only execute one installation in the directory where a requirements file has been detected, such as `requirements.txt` or any We only execute one installation in the directory where either a requirements file or a lock file has been detected. Dependencies are only analyzed by `gemnasium-python` for the first file that is detected. Files are searched for in the following order:
variation of this file (for example, `requirements.pip` or `requires.txt`).
1. `requirements.txt`, `requirements.pip`, or `requires.txt` for projects using Pip.
1. `Pipfile` or `Pipfile.lock` for projects using Pipenv.
1. `poetry.lock` for projects using Poetry.
1. `setup.py` for project using Setuptools.
The search begins with the root directory and then continues with subdirectories if no builds are found in the root directory. Consequently a Poetry lock file in the root directory would be detected before a Pipenv file in a subdirectory.
#### Java and Scala #### Java and Scala
@ -448,15 +468,13 @@ From GitLab 14.8 the `gemnasium` analyzer scans supported JavaScript projects fo
The analyzer for these languages supports multiple lockfiles. The analyzer for these languages supports multiple lockfiles.
### Support for additional languages #### Support for additional languages
Support for additional languages, dependency managers, and dependency files are tracked in the following issues: Support for additional languages, dependency managers, and dependency files are tracked in the following issues:
| Package Managers | Languages | Supported files | Scan tools | Issue | | Package Managers | Languages | Supported files | Scan tools | Issue |
| ------------------- | --------- | --------------- | ---------- | ----- | | ------------------- | --------- | --------------- | ---------- | ----- |
| [Poetry](https://python-poetry.org/) | Python | `poetry.lock` | [Gemnasium](https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium) | [GitLab#7006](https://gitlab.com/gitlab-org/gitlab/-/issues/7006) | | [Poetry](https://python-poetry.org/) | Python | `pyproject.toml` | [Gemnasium](https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium) | [GitLab#32774](https://gitlab.com/gitlab-org/gitlab/-/issues/32774) |
For workarounds, see the [Troubleshooting section](#troubleshooting).
## Contribute your scanner ## Contribute your scanner
@ -1088,9 +1106,8 @@ Generally, the approach is the following:
1. Add [`dependencies: [<your-converter-job>]`](../../../ci/yaml/index.md#dependencies) 1. Add [`dependencies: [<your-converter-job>]`](../../../ci/yaml/index.md#dependencies)
to your `dependency_scanning` job to make use of the converted definitions files. to your `dependency_scanning` job to make use of the converted definitions files.
For example, the unsupported `poetry.lock` file can be For example, Poetry projects that _only_ have a `pyproject.toml`
[converted](https://python-poetry.org/docs/cli/#export) file can generate the `poetry.lock` file as follows.
to the supported `requirements.txt` as follows.
```yaml ```yaml
include: include:
@ -1099,15 +1116,11 @@ include:
stages: stages:
- test - test
variables:
PIP_REQUIREMENTS_FILE: "requirements-converted.txt"
gemnasium-python-dependency_scanning: gemnasium-python-dependency_scanning:
# Work around https://gitlab.com/gitlab-org/gitlab/-/issues/7006 # Work around https://gitlab.com/gitlab-org/gitlab/-/issues/32774
before_script: before_script:
- pip install poetry # Or via another method: https://python-poetry.org/docs/#installation - pip install "poetry>=1,<2" # Or via another method: https://python-poetry.org/docs/#installation
- poetry export --output="$PIP_REQUIREMENTS_FILE" - poetry update --lock # Generates the lock file to be analyzed.
- rm poetry.lock pyproject.toml
``` ```
### `Error response from daemon: error processing tar file: docker-tar: relocation error` ### `Error response from daemon: error processing tar file: docker-tar: relocation error`

View File

@ -714,6 +714,17 @@ module API
end end
end end
desc 'Start a task to recalculate repository size for a project' do
detail 'This feature was introduced in GitLab 15.0.'
end
post ':id/repository_size', feature_category: :source_code_management do
authorize_admin_project
user_project.repository.expire_statistics_caches
::Projects::UpdateStatisticsService.new(user_project, nil, statistics: [:repository_size, :lfs_objects_size]).execute
end
desc 'Transfer a project to a new namespace' desc 'Transfer a project to a new namespace'
params do params do
requires :namespace, type: String, desc: 'The ID or path of the new namespace' requires :namespace, type: String, desc: 'The ID or path of the new namespace'

View File

@ -9,6 +9,11 @@ module Backup
# if some of these files are still there, we don't need them in the backup # if some of these files are still there, we don't need them in the backup
LEGACY_PAGES_TMP_PATH = '@pages.tmp' LEGACY_PAGES_TMP_PATH = '@pages.tmp'
LIST_ENVS = {
skipped: 'SKIP',
repositories_storages: 'REPOSITORIES_STORAGES'
}.freeze
TaskDefinition = Struct.new( TaskDefinition = Struct.new(
:enabled, # `true` if the task can be used. Treated as `true` when not specified. :enabled, # `true` if the task can be used. Treated as `true` when not specified.
:human_name, # Name of the task used for logging. :human_name, # Name of the task used for logging.
@ -32,7 +37,7 @@ module Backup
Feature.enabled?(:incremental_repository_backup) && Feature.enabled?(:incremental_repository_backup) &&
Gitlab::Utils.to_boolean(ENV['INCREMENTAL'], default: false) Gitlab::Utils.to_boolean(ENV['INCREMENTAL'], default: false)
@definitions = definitions || build_definitions @definitions = definitions
end end
def create def create
@ -43,7 +48,9 @@ module Backup
update_backup_information update_backup_information
end end
@definitions.keys.each do |task_name| build_backup_information
definitions.keys.each do |task_name|
run_create_task(task_name) run_create_task(task_name)
end end
@ -65,10 +72,10 @@ module Backup
end end
def run_create_task(task_name) def run_create_task(task_name)
definition = @definitions[task_name]
build_backup_information build_backup_information
definition = definitions[task_name]
unless definition.enabled? unless definition.enabled?
puts_time "Dumping #{definition.human_name} ... ".color(:blue) + "[DISABLED]".color(:cyan) puts_time "Dumping #{definition.human_name} ... ".color(:blue) + "[DISABLED]".color(:cyan)
return return
@ -92,7 +99,7 @@ module Backup
read_backup_information read_backup_information
verify_backup_version verify_backup_version
@definitions.keys.each do |task_name| definitions.keys.each do |task_name|
run_restore_task(task_name) if !skipped?(task_name) && enabled_task?(task_name) run_restore_task(task_name) if !skipped?(task_name) && enabled_task?(task_name)
end end
@ -111,7 +118,9 @@ module Backup
end end
def run_restore_task(task_name) def run_restore_task(task_name)
definition = @definitions[task_name] read_backup_information
definition = definitions[task_name]
unless definition.enabled? unless definition.enabled?
puts_time "Restoring #{definition.human_name} ... ".color(:blue) + "[DISABLED]".color(:cyan) puts_time "Restoring #{definition.human_name} ... ".color(:blue) + "[DISABLED]".color(:cyan)
@ -143,6 +152,10 @@ module Backup
private private
def definitions
@definitions ||= build_definitions
end
def build_definitions def build_definitions
{ {
'db' => TaskDefinition.new( 'db' => TaskDefinition.new(
@ -212,7 +225,7 @@ module Backup
max_storage_concurrency = ENV['GITLAB_BACKUP_MAX_STORAGE_CONCURRENCY'].presence max_storage_concurrency = ENV['GITLAB_BACKUP_MAX_STORAGE_CONCURRENCY'].presence
strategy = Backup::GitalyBackup.new(progress, incremental: incremental?, max_parallelism: max_concurrency, storage_parallelism: max_storage_concurrency) strategy = Backup::GitalyBackup.new(progress, incremental: incremental?, max_parallelism: max_concurrency, storage_parallelism: max_storage_concurrency)
Repositories.new(progress, strategy: strategy) Repositories.new(progress, strategy: strategy, storages: repositories_storages)
end end
def build_files_task(app_files_dir, excludes: []) def build_files_task(app_files_dir, excludes: [])
@ -245,7 +258,8 @@ module Backup
gitlab_version: Gitlab::VERSION, gitlab_version: Gitlab::VERSION,
tar_version: tar_version, tar_version: tar_version,
installation_type: Gitlab::INSTALLATION_TYPE, installation_type: Gitlab::INSTALLATION_TYPE,
skipped: ENV["SKIP"] skipped: ENV['SKIP'],
repositories_storages: ENV['REPOSITORIES_STORAGES']
} }
end end
@ -256,7 +270,9 @@ module Backup
backup_created_at: Time.zone.now, backup_created_at: Time.zone.now,
gitlab_version: Gitlab::VERSION, gitlab_version: Gitlab::VERSION,
tar_version: tar_version, tar_version: tar_version,
installation_type: Gitlab::INSTALLATION_TYPE installation_type: Gitlab::INSTALLATION_TYPE,
skipped: list_env(:skipped).join(','),
repositories_storages: list_env(:repositories_storages).join(',')
) )
end end
@ -309,7 +325,7 @@ module Backup
puts_time "Deleting tar staging files ... ".color(:blue) puts_time "Deleting tar staging files ... ".color(:blue)
remove_backup_path(MANIFEST_NAME) remove_backup_path(MANIFEST_NAME)
@definitions.each do |_, definition| definitions.each do |_, definition|
remove_backup_path(definition.cleanup_path || definition.destination_path) remove_backup_path(definition.cleanup_path || definition.destination_path)
end end
@ -443,12 +459,26 @@ module Backup
end end
def skipped?(item) def skipped?(item)
ENV.fetch('SKIP', '').include?(item) || skipped.include?(item)
backup_information[:skipped] && backup_information[:skipped].include?(item) end
def skipped
@skipped ||= list_env(:skipped)
end
def repositories_storages
@repositories_storages ||= list_env(:repositories_storages)
end
def list_env(name)
list = ENV.fetch(LIST_ENVS[name], '').split(',')
list += backup_information[name].split(',') if backup_information[name]
list.uniq!
list
end end
def enabled_task?(task_name) def enabled_task?(task_name)
@definitions[task_name].enabled? definitions[task_name].enabled?
end end
def backup_file?(file) def backup_file?(file)
@ -503,7 +533,7 @@ module Backup
end end
def backup_contents def backup_contents
[MANIFEST_NAME] + @definitions.reject do |name, definition| [MANIFEST_NAME] + definitions.reject do |name, definition|
skipped?(name) || !enabled_task?(name) || skipped?(name) || !enabled_task?(name) ||
(definition.destination_optional && !File.exist?(File.join(backup_path, definition.destination_path))) (definition.destination_optional && !File.exist?(File.join(backup_path, definition.destination_path)))
end.values.map(&:destination_path) end.values.map(&:destination_path)

View File

@ -6,10 +6,11 @@ module Backup
class Repositories < Task class Repositories < Task
extend ::Gitlab::Utils::Override extend ::Gitlab::Utils::Override
def initialize(progress, strategy:) def initialize(progress, strategy:, storages: [])
super(progress) super(progress)
@strategy = strategy @strategy = strategy
@storages = storages
end end
override :dump override :dump
@ -35,7 +36,7 @@ module Backup
private private
attr_reader :strategy attr_reader :strategy, :storages
def enqueue_consecutive def enqueue_consecutive
enqueue_consecutive_projects enqueue_consecutive_projects
@ -49,7 +50,7 @@ module Backup
end end
def enqueue_consecutive_snippets def enqueue_consecutive_snippets
Snippet.find_each(batch_size: 1000) { |snippet| enqueue_snippet(snippet) } snippet_relation.find_each(batch_size: 1000) { |snippet| enqueue_snippet(snippet) }
end end
def enqueue_project(project) def enqueue_project(project)
@ -63,7 +64,15 @@ module Backup
end end
def project_relation def project_relation
Project.includes(:route, :group, namespace: :owner) scope = Project.includes(:route, :group, namespace: :owner)
scope = scope.id_in(ProjectRepository.for_repository_storage(storages).select(:project_id)) if storages.any?
scope
end
def snippet_relation
scope = Snippet.all
scope = scope.id_in(SnippetRepository.for_repository_storage(storages).select(:snippet_id)) if storages.any?
scope
end end
def restore_object_pools def restore_object_pools
@ -88,7 +97,7 @@ module Backup
def cleanup_snippets_without_repositories def cleanup_snippets_without_repositories
invalid_snippets = [] invalid_snippets = []
Snippet.find_each(batch_size: 1000).each do |snippet| snippet_relation.find_each(batch_size: 1000).each do |snippet|
response = Snippets::RepositoryValidationService.new(nil, snippet).execute response = Snippets::RepositoryValidationService.new(nil, snippet).execute
next if response.success? next if response.success?

View File

@ -120,6 +120,7 @@ gemnasium-maven-dependency_scanning:
- '{Pipfile,*/Pipfile,*/*/Pipfile}' - '{Pipfile,*/Pipfile,*/*/Pipfile}'
- '{requires.txt,*/requires.txt,*/*/requires.txt}' - '{requires.txt,*/requires.txt,*/*/requires.txt}'
- '{setup.py,*/setup.py,*/*/setup.py}' - '{setup.py,*/setup.py,*/*/setup.py}'
- '{poetry.lock,*/poetry.lock,*/*/poetry.lock}'
gemnasium-python-dependency_scanning: gemnasium-python-dependency_scanning:
extends: extends:

View File

@ -3705,7 +3705,7 @@ msgstr ""
msgid "All protected branches" msgid "All protected branches"
msgstr "" msgstr ""
msgid "All threads resolved" msgid "All threads resolved!"
msgstr "" msgstr ""
msgid "All users must accept the Terms of Service and Privacy Policy to access GitLab" msgid "All users must accept the Terms of Service and Privacy Policy to access GitLab"
@ -11318,6 +11318,12 @@ msgstr ""
msgid "DastProfiles|Branch missing" msgid "DastProfiles|Branch missing"
msgstr "" msgstr ""
msgid "DastProfiles|Change scanner profile"
msgstr ""
msgid "DastProfiles|Change site profile"
msgstr ""
msgid "DastProfiles|Choose a scan method" msgid "DastProfiles|Choose a scan method"
msgstr "" msgstr ""
@ -11432,9 +11438,6 @@ msgstr ""
msgid "DastProfiles|No scanner profile selected" msgid "DastProfiles|No scanner profile selected"
msgstr "" msgstr ""
msgid "DastProfiles|No scanner profile selected."
msgstr ""
msgid "DastProfiles|No scanner profiles created yet" msgid "DastProfiles|No scanner profiles created yet"
msgstr "" msgstr ""
@ -11489,9 +11492,6 @@ msgstr ""
msgid "DastProfiles|Scanner name" msgid "DastProfiles|Scanner name"
msgstr "" msgstr ""
msgid "DastProfiles|Scanner profile"
msgstr ""
msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}." msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
msgstr "" msgstr ""
@ -39027,18 +39027,12 @@ msgstr ""
msgid "ThreatMonitoring|Alerts" msgid "ThreatMonitoring|Alerts"
msgstr "" msgstr ""
msgid "ThreatMonitoring|All Environments"
msgstr ""
msgid "ThreatMonitoring|Date and time" msgid "ThreatMonitoring|Date and time"
msgstr "" msgstr ""
msgid "ThreatMonitoring|Dismissed" msgid "ThreatMonitoring|Dismissed"
msgstr "" msgstr ""
msgid "ThreatMonitoring|Environment"
msgstr ""
msgid "ThreatMonitoring|Events" msgid "ThreatMonitoring|Events"
msgstr "" msgstr ""
@ -39066,9 +39060,6 @@ msgstr ""
msgid "ThreatMonitoring|Resolved" msgid "ThreatMonitoring|Resolved"
msgstr "" msgstr ""
msgid "ThreatMonitoring|Something went wrong, unable to fetch environments"
msgstr ""
msgid "ThreatMonitoring|Status" msgid "ThreatMonitoring|Status"
msgstr "" msgstr ""

View File

@ -74,7 +74,11 @@ module QA
end end
def has_linked_pipeline?(title: nil) def has_linked_pipeline?(title: nil)
title ? find_linked_pipeline_by_title(title) : has_element?(:linked_pipeline_container) # If the pipeline page has loaded linked pipelines should appear, but it can take a little while,
# especially on busier environments.
retry_until(reload: true, message: 'Waiting for linked pipeline to appear') do
title ? find_linked_pipeline_by_title(title) : has_element?(:linked_pipeline_container)
end
end end
alias_method :has_child_pipeline?, :has_linked_pipeline? alias_method :has_child_pipeline?, :has_linked_pipeline?

View File

@ -44,7 +44,7 @@ module QA
end end
it( it(
'creates a multi-project pipeline', 'creates a multi-project pipeline with artifact download',
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/358064' testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/358064'
) do ) do
Page::Project::Pipeline::Show.perform do |show| Page::Project::Pipeline::Show.perform do |show|
@ -78,7 +78,10 @@ module QA
job1: job1:
stage: test stage: test
tags: ["#{executor}"] tags: ["#{executor}"]
script: echo 'done' script: echo 'done' > output.txt
artifacts:
paths:
- output.txt
staging: staging:
stage: deploy stage: deploy
@ -96,7 +99,12 @@ module QA
"#{downstream_job_name}": "#{downstream_job_name}":
stage: test stage: test
tags: ["#{executor}"] tags: ["#{executor}"]
script: echo 'done' needs:
- project: #{upstream_project.path_with_namespace}
job: job1
ref: main
artifacts: true
script: cat output.txt
YAML YAML
} }
end end

View File

@ -60,7 +60,14 @@ module QA
child_job: child_job:
stage: test stage: test
tags: ["#{project.name}"] tags: ["#{project.name}"]
script: echo "Child job done!" needs:
- project: #{project.path_with_namespace}
job: job1
ref: main
artifacts: true
script:
- cat output.txt
- echo "Child job done!"
YAML YAML
} }
@ -84,18 +91,28 @@ module QA
file_path: '.gitlab-ci.yml', file_path: '.gitlab-ci.yml',
content: <<~YAML content: <<~YAML
stages: stages:
- build
- test - test
- deploy - deploy
default:
tags: ["#{project.name}"]
job1: job1:
stage: build
script: echo "build success" > output.txt
artifacts:
paths:
- output.txt
job2:
stage: test stage: test
trigger: trigger:
include: ".child-ci.yml" include: ".child-ci.yml"
strategy: depend strategy: depend
job2: job3:
stage: deploy stage: deploy
tags: ["#{project.name}"]
script: echo "parent deploy done" script: echo "parent deploy done"
YAML YAML

View File

@ -5,33 +5,76 @@ module RuboCop
module Gitlab module Gitlab
# Cop that enforces use of namespaced classes in order to better identify # Cop that enforces use of namespaced classes in order to better identify
# high level domains within the codebase. # high level domains within the codebase.
#
# @example # @example
# # bad # # bad
# class MyClass # class MyClass
# end # end
# #
# module Gitlab
# class MyClass
# end
# end
#
# class Gitlab::MyClass
# end
#
# # good # # good
# module MyDomain # module MyDomain
# class MyClass # class MyClass
# end # end
# end # end
#
# module Gitlab
# module MyDomain
# class MyClass
# end
# end
# end
#
# class Gitlab::MyDomain::MyClass
# end
class NamespacedClass < RuboCop::Cop::Cop class NamespacedClass < RuboCop::Cop::Cop
MSG = 'Classes must be declared inside a module indicating a product domain namespace. For more info: https://gitlab.com/gitlab-org/gitlab/-/issues/212156' MSG = 'Classes must be declared inside a module indicating a product domain namespace. For more info: https://gitlab.com/gitlab-org/gitlab/-/issues/212156'
def_node_matcher :compact_namespaced_class?, <<~PATTERN # These namespaces are considered top-level semantically.
(class (const (const ...) ...) ...) # Note: Nested namespace like Foo::Bar are also supported.
PATTERN PSEUDO_TOPLEVEL = %w[Gitlab]
.map { _1.split('::') }.freeze
def on_module(node) def on_module(node)
@namespaced = true add_potential_domain_namespace(node)
end end
def on_class(node) def on_class(node)
return if @namespaced # Add potential namespaces from compact definitions like `class Foo::Bar`.
# Remove class name because it's not a domain namespace.
add_potential_domain_namespace(node) { _1.pop }
add_offense(node) unless compact_namespaced_class?(node) add_offense(node) if domain_namespaces.none?
end
private
def domain_namespaces
@domain_namespaces ||= []
end
def add_potential_domain_namespace(node)
return if domain_namespaces.any?
identifiers = identifiers_for(node)
yield(identifiers) if block_given?
PSEUDO_TOPLEVEL.each do |namespaces|
identifiers.shift(namespaces.size) if namespaces == identifiers.first(namespaces.size)
end
domain_namespaces.concat(identifiers)
end
def identifiers_for(node)
node.identifier.source.sub(/^::/, '').split('::')
end end
end end
end end

View File

@ -239,7 +239,7 @@ RSpec.describe 'Merge request > User posts diff notes', :js do
def should_allow_dismissing_a_comment(line_holder, diff_side = nil) def should_allow_dismissing_a_comment(line_holder, diff_side = nil)
write_comment_on_line(line_holder, diff_side) write_comment_on_line(line_holder, diff_side)
accept_gl_confirm(s_('Notes|Are you sure you want to cancel creating this comment?')) do accept_gl_confirm(s_('Notes|Are you sure you want to cancel creating this comment?'), button_text: _('Discard changes')) do
find('.js-close-discussion-note-form').click find('.js-close-discussion-note-form').click
end end

View File

@ -1,11 +1,15 @@
import api from '~/api'; import api from '~/api';
import { DEFER_DURATION } from '~/diffs/constants'; import { DEFER_DURATION, TRACKING_CAP_KEY, TRACKING_CAP_LENGTH } from '~/diffs/constants';
import { queueRedisHllEvents } from '~/diffs/utils/queue_events'; import { queueRedisHllEvents } from '~/diffs/utils/queue_events';
jest.mock('~/api', () => ({ jest.mock('~/api', () => ({
trackRedisHllUserEvent: jest.fn(), trackRedisHllUserEvent: jest.fn(),
})); }));
beforeAll(() => {
localStorage.clear();
});
describe('diffs events queue', () => { describe('diffs events queue', () => {
describe('queueRedisHllEvents', () => { describe('queueRedisHllEvents', () => {
it('does not dispatch the event immediately', () => { it('does not dispatch the event immediately', () => {
@ -17,6 +21,7 @@ describe('diffs events queue', () => {
queueRedisHllEvents(['know_event']); queueRedisHllEvents(['know_event']);
jest.advanceTimersByTime(DEFER_DURATION + 1); jest.advanceTimersByTime(DEFER_DURATION + 1);
expect(api.trackRedisHllUserEvent).toHaveBeenCalled(); expect(api.trackRedisHllUserEvent).toHaveBeenCalled();
expect(localStorage.getItem(TRACKING_CAP_KEY)).toBe(null);
}); });
it('increase defer duration based on the provided events count', () => { it('increase defer duration based on the provided events count', () => {
@ -32,5 +37,35 @@ describe('diffs events queue', () => {
deferDuration *= index + 1; deferDuration *= index + 1;
}); });
}); });
describe('with tracking cap verification', () => {
const currentTimestamp = Date.now();
beforeEach(() => {
localStorage.clear();
});
it('dispatches the event if cap value is not found', () => {
queueRedisHllEvents(['know_event'], { verifyCap: true });
jest.advanceTimersByTime(DEFER_DURATION + 1);
expect(api.trackRedisHllUserEvent).toHaveBeenCalled();
expect(localStorage.getItem(TRACKING_CAP_KEY)).toBe(currentTimestamp.toString());
});
it('dispatches the event if cap value is less than limit', () => {
localStorage.setItem(TRACKING_CAP_KEY, 1);
queueRedisHllEvents(['know_event'], { verifyCap: true });
jest.advanceTimersByTime(DEFER_DURATION + 1);
expect(api.trackRedisHllUserEvent).toHaveBeenCalled();
expect(localStorage.getItem(TRACKING_CAP_KEY)).toBe(currentTimestamp.toString());
});
it('does not dispatch the event if cap value is greater than limit', () => {
localStorage.setItem(TRACKING_CAP_KEY, currentTimestamp - (TRACKING_CAP_LENGTH + 1));
queueRedisHllEvents(['know_event'], { verifyCap: true });
jest.advanceTimersByTime(DEFER_DURATION + 1);
expect(api.trackRedisHllUserEvent).toHaveBeenCalled();
});
});
}); });
}); });

View File

@ -7,21 +7,35 @@ import * as JiraConnectApi from '~/jira_connect/subscriptions/api';
import GroupItemName from '~/jira_connect/subscriptions/components/group_item_name.vue'; import GroupItemName from '~/jira_connect/subscriptions/components/group_item_name.vue';
import GroupsListItem from '~/jira_connect/subscriptions/components/add_namespace_modal/groups_list_item.vue'; import GroupsListItem from '~/jira_connect/subscriptions/components/add_namespace_modal/groups_list_item.vue';
import { persistAlert, reloadPage } from '~/jira_connect/subscriptions/utils'; import { persistAlert, reloadPage } from '~/jira_connect/subscriptions/utils';
import {
I18N_ADD_SUBSCRIPTION_SUCCESS_ALERT_TITLE,
I18N_ADD_SUBSCRIPTION_SUCCESS_ALERT_MESSAGE,
INTEGRATIONS_DOC_LINK,
} from '~/jira_connect/subscriptions/constants';
import createStore from '~/jira_connect/subscriptions/store';
import { mockGroup1 } from '../../mock_data'; import { mockGroup1 } from '../../mock_data';
jest.mock('~/jira_connect/subscriptions/utils'); jest.mock('~/jira_connect/subscriptions/utils');
describe('GroupsListItem', () => { describe('GroupsListItem', () => {
let wrapper; let wrapper;
let store;
const mockAddSubscriptionsPath = '/addSubscriptionsPath'; const mockAddSubscriptionsPath = '/addSubscriptionsPath';
const createComponent = ({ mountFn = shallowMount } = {}) => { const createComponent = ({ mountFn = shallowMount, provide } = {}) => {
store = createStore();
jest.spyOn(store, 'dispatch').mockImplementation();
wrapper = mountFn(GroupsListItem, { wrapper = mountFn(GroupsListItem, {
store,
propsData: { propsData: {
group: mockGroup1, group: mockGroup1,
}, },
provide: { provide: {
addSubscriptionsPath: mockAddSubscriptionsPath, addSubscriptionsPath: mockAddSubscriptionsPath,
...provide,
}, },
}); });
}; };
@ -51,65 +65,88 @@ describe('GroupsListItem', () => {
}); });
describe('on Link button click', () => { describe('on Link button click', () => {
let addSubscriptionSpy; describe('when jiraConnectOauth feature flag is disabled', () => {
let addSubscriptionSpy;
beforeEach(() => {
createComponent({ mountFn: mount });
addSubscriptionSpy = jest.spyOn(JiraConnectApi, 'addSubscription').mockResolvedValue();
});
it('sets button to loading and sends request', async () => {
expect(findLinkButton().props('loading')).toBe(false);
clickLinkButton();
await nextTick();
expect(findLinkButton().props('loading')).toBe(true);
await waitForPromises();
expect(addSubscriptionSpy).toHaveBeenCalledWith(
mockAddSubscriptionsPath,
mockGroup1.full_path,
);
expect(persistAlert).toHaveBeenCalledWith({
linkUrl: '/help/integration/jira_development_panel.html#use-the-integration',
message:
'You should now see GitLab.com activity inside your Jira Cloud issues. %{linkStart}Learn more%{linkEnd}',
title: 'Namespace successfully linked',
variant: 'success',
});
});
describe('when request is successful', () => {
it('reloads the page', async () => {
clickLinkButton();
await waitForPromises();
expect(reloadPage).toHaveBeenCalled();
});
});
describe('when request has errors', () => {
const mockErrorMessage = 'error message';
const mockError = { response: { data: { error: mockErrorMessage } } };
beforeEach(() => { beforeEach(() => {
addSubscriptionSpy = jest createComponent({ mountFn: mount });
.spyOn(JiraConnectApi, 'addSubscription')
.mockRejectedValue(mockError); addSubscriptionSpy = jest.spyOn(JiraConnectApi, 'addSubscription').mockResolvedValue();
}); });
it('emits `error` event', async () => { it('sets button to loading and sends request', async () => {
clickLinkButton(); expect(findLinkButton().props('loading')).toBe(false);
clickLinkButton();
await nextTick();
expect(findLinkButton().props('loading')).toBe(true);
await waitForPromises(); await waitForPromises();
expect(reloadPage).not.toHaveBeenCalled(); expect(addSubscriptionSpy).toHaveBeenCalledWith(
expect(wrapper.emitted('error')[0][0]).toBe(mockErrorMessage); mockAddSubscriptionsPath,
mockGroup1.full_path,
);
expect(persistAlert).toHaveBeenCalledWith({
linkUrl: INTEGRATIONS_DOC_LINK,
message: I18N_ADD_SUBSCRIPTION_SUCCESS_ALERT_MESSAGE,
title: I18N_ADD_SUBSCRIPTION_SUCCESS_ALERT_TITLE,
variant: 'success',
});
});
describe('when request is successful', () => {
it('reloads the page', async () => {
clickLinkButton();
await waitForPromises();
expect(reloadPage).toHaveBeenCalled();
});
});
describe('when request has errors', () => {
const mockErrorMessage = 'error message';
const mockError = { response: { data: { error: mockErrorMessage } } };
beforeEach(() => {
addSubscriptionSpy = jest
.spyOn(JiraConnectApi, 'addSubscription')
.mockRejectedValue(mockError);
});
it('emits `error` event', async () => {
clickLinkButton();
await waitForPromises();
expect(reloadPage).not.toHaveBeenCalled();
expect(wrapper.emitted('error')[0][0]).toBe(mockErrorMessage);
});
});
});
describe('when jiraConnectOauth feature flag is enabled', () => {
const mockSubscriptionsPath = '/subscriptions';
beforeEach(() => {
createComponent({
mountFn: mount,
provide: {
subscriptionsPath: mockSubscriptionsPath,
glFeatures: { jiraConnectOauth: true },
},
});
});
it('dispatches `addSubscription` action', async () => {
clickLinkButton();
await nextTick();
expect(store.dispatch).toHaveBeenCalledWith('addSubscription', {
namespacePath: mockGroup1.full_path,
subscriptionsPath: mockSubscriptionsPath,
});
}); });
}); });
}); });

View File

@ -161,32 +161,6 @@ describe('JiraConnectApp', () => {
}); });
describe('when user signed out', () => { describe('when user signed out', () => {
describe('when sign in page emits `sign-in-oauth` event', () => {
const mockUser = { name: 'test' };
beforeEach(async () => {
createComponent({
provide: {
usersPath: '/mock',
},
});
findSignInPage().vm.$emit('sign-in-oauth', mockUser);
await nextTick();
});
it('hides sign in page and renders subscriptions page', () => {
expect(findSignInPage().exists()).toBe(false);
expect(findSubscriptionsPage().exists()).toBe(true);
});
it('sets correct UserLink props', () => {
expect(findUserLink().props()).toMatchObject({
user: mockUser,
userSignedIn: true,
});
});
});
describe('when sign in page emits `error` event', () => { describe('when sign in page emits `error` event', () => {
beforeEach(async () => { beforeEach(async () => {
createComponent({ createComponent({

View File

@ -12,6 +12,8 @@ import waitForPromises from 'helpers/wait_for_promises';
import httpStatus from '~/lib/utils/http_status'; import httpStatus from '~/lib/utils/http_status';
import AccessorUtilities from '~/lib/utils/accessor'; import AccessorUtilities from '~/lib/utils/accessor';
import { getCurrentUser } from '~/rest_api'; import { getCurrentUser } from '~/rest_api';
import createStore from '~/jira_connect/subscriptions/store';
import { SET_ACCESS_TOKEN } from '~/jira_connect/subscriptions/store/mutation_types';
jest.mock('~/lib/utils/accessor'); jest.mock('~/lib/utils/accessor');
jest.mock('~/jira_connect/subscriptions/utils'); jest.mock('~/jira_connect/subscriptions/utils');
@ -31,9 +33,15 @@ const mockOauthMetadata = {
describe('SignInOauthButton', () => { describe('SignInOauthButton', () => {
let wrapper; let wrapper;
let mockAxios; let mockAxios;
let store;
const createComponent = ({ slots } = {}) => { const createComponent = ({ slots } = {}) => {
store = createStore();
jest.spyOn(store, 'dispatch').mockImplementation();
jest.spyOn(store, 'commit').mockImplementation();
wrapper = shallowMount(SignInOauthButton, { wrapper = shallowMount(SignInOauthButton, {
store,
slots, slots,
provide: { provide: {
oauthMetadata: mockOauthMetadata, oauthMetadata: mockOauthMetadata,
@ -117,10 +125,6 @@ describe('SignInOauthButton', () => {
await waitForPromises(); await waitForPromises();
}); });
it('emits `error` event', () => {
expect(wrapper.emitted('error')).toBeTruthy();
});
it('does not emit `sign-in` event', () => { it('does not emit `sign-in` event', () => {
expect(wrapper.emitted('sign-in')).toBeFalsy(); expect(wrapper.emitted('sign-in')).toBeFalsy();
}); });
@ -164,25 +168,25 @@ describe('SignInOauthButton', () => {
}); });
}); });
it('executes GET request to fetch user data', () => { it('dispatches loadCurrentUser action', () => {
expect(getCurrentUser).toHaveBeenCalledWith({ expect(store.dispatch).toHaveBeenCalledWith('loadCurrentUser', mockAccessToken);
headers: { Authorization: `Bearer ${mockAccessToken}` }, });
});
it('commits SET_ACCESS_TOKEN mutation with correct access token', () => {
expect(store.commit).toHaveBeenCalledWith(SET_ACCESS_TOKEN, mockAccessToken);
}); });
it('emits `sign-in` event with user data', () => { it('emits `sign-in` event with user data', () => {
expect(wrapper.emitted('sign-in')[0]).toEqual([mockUser]); expect(wrapper.emitted('sign-in')[0]).toBeTruthy();
}); });
}); });
describe('when API requests fail', () => { describe('when API requests fail', () => {
beforeEach(async () => { beforeEach(async () => {
jest.spyOn(axios, 'post'); jest.spyOn(axios, 'post');
jest.spyOn(axios, 'get');
mockAxios mockAxios
.onPost(mockOauthMetadata.oauth_token_url) .onPost(mockOauthMetadata.oauth_token_url)
.replyOnce(httpStatus.INTERNAL_SERVER_ERROR, { access_token: mockAccessToken }); .replyOnce(httpStatus.INTERNAL_SERVER_ERROR);
mockAxios.onGet('/api/v4/user').replyOnce(httpStatus.INTERNAL_SERVER_ERROR, mockUser);
window.dispatchEvent(new MessageEvent('message', mockEvent)); window.dispatchEvent(new MessageEvent('message', mockEvent));
@ -190,7 +194,7 @@ describe('SignInOauthButton', () => {
}); });
it('emits `error` event', () => { it('emits `error` event', () => {
expect(wrapper.emitted('error')).toBeTruthy(); expect(wrapper.emitted('error')[0]).toEqual([]);
}); });
it('does not emit `sign-in` event', () => { it('does not emit `sign-in` event', () => {

View File

@ -74,8 +74,8 @@ describe('SignInPage', () => {
describe('when sign-in-oauth event is emitted', () => { describe('when sign-in-oauth event is emitted', () => {
it('emits another sign-in-oauth event', () => { it('emits another sign-in-oauth event', () => {
findSignInGitlabCom().vm.$emit('sign-in-oauth', 'test'); findSignInGitlabCom().vm.$emit('sign-in-oauth');
expect(wrapper.emitted('sign-in-oauth')[0][0]).toBe('test'); expect(wrapper.emitted('sign-in-oauth')[0]).toEqual([]);
}); });
}); });
}); });

View File

@ -1,10 +1,22 @@
import testAction from 'helpers/vuex_action_helper'; import testAction from 'helpers/vuex_action_helper';
import * as types from '~/jira_connect/subscriptions/store/mutation_types'; import * as types from '~/jira_connect/subscriptions/store/mutation_types';
import { fetchSubscriptions } from '~/jira_connect/subscriptions/store/actions'; import {
fetchSubscriptions,
loadCurrentUser,
addSubscription,
} from '~/jira_connect/subscriptions/store/actions';
import state from '~/jira_connect/subscriptions/store/state'; import state from '~/jira_connect/subscriptions/store/state';
import * as api from '~/jira_connect/subscriptions/api'; import * as api from '~/jira_connect/subscriptions/api';
import { I18N_DEFAULT_SUBSCRIPTIONS_ERROR_MESSAGE } from '~/jira_connect/subscriptions/constants'; import * as userApi from '~/api/user_api';
import * as integrationsApi from '~/api/integrations_api';
import {
I18N_DEFAULT_SUBSCRIPTIONS_ERROR_MESSAGE,
I18N_ADD_SUBSCRIPTION_SUCCESS_ALERT_TITLE,
I18N_ADD_SUBSCRIPTION_SUCCESS_ALERT_MESSAGE,
INTEGRATIONS_DOC_LINK,
} from '~/jira_connect/subscriptions/constants';
import * as utils from '~/jira_connect/subscriptions/utils';
describe('JiraConnect actions', () => { describe('JiraConnect actions', () => {
let mockedState; let mockedState;
@ -60,4 +72,101 @@ describe('JiraConnect actions', () => {
}); });
}); });
}); });
describe('loadCurrentUser', () => {
const mockAccessToken = 'abcd1234';
describe('when API request succeeds', () => {
it('commits the SET_ACCESS_TOKEN and SET_CURRENT_USER mutations', async () => {
const mockUser = { name: 'root' };
jest.spyOn(userApi, 'getCurrentUser').mockResolvedValue({ data: mockUser });
await testAction(
loadCurrentUser,
mockAccessToken,
mockedState,
[{ type: types.SET_CURRENT_USER, payload: mockUser }],
[],
);
expect(userApi.getCurrentUser).toHaveBeenCalledWith({
headers: { Authorization: `Bearer ${mockAccessToken}` },
});
});
});
describe('when API request fails', () => {
it('commits the SET_CURRENT_USER_ERROR mutation', async () => {
jest.spyOn(userApi, 'getCurrentUser').mockRejectedValue();
await testAction(
loadCurrentUser,
mockAccessToken,
mockedState,
[{ type: types.SET_CURRENT_USER_ERROR }],
[],
);
});
});
});
describe('addSubscription', () => {
const mockNamespace = 'gitlab-org/gitlab';
const mockSubscriptionsPath = '/subscriptions';
beforeEach(() => {
jest.spyOn(utils, 'getJwt').mockReturnValue('1234');
});
describe('when API request succeeds', () => {
it('commits the SET_ACCESS_TOKEN and SET_CURRENT_USER mutations', async () => {
jest
.spyOn(integrationsApi, 'addJiraConnectSubscription')
.mockResolvedValue({ success: true });
await testAction(
addSubscription,
{ namespacePath: mockNamespace, subscriptionsPath: mockSubscriptionsPath },
mockedState,
[
{ type: types.ADD_SUBSCRIPTION_LOADING, payload: true },
{
type: types.SET_ALERT,
payload: {
title: I18N_ADD_SUBSCRIPTION_SUCCESS_ALERT_TITLE,
message: I18N_ADD_SUBSCRIPTION_SUCCESS_ALERT_MESSAGE,
linkUrl: INTEGRATIONS_DOC_LINK,
variant: 'success',
},
},
{ type: types.ADD_SUBSCRIPTION_LOADING, payload: false },
],
[{ type: 'fetchSubscriptions', payload: mockSubscriptionsPath }],
);
expect(integrationsApi.addJiraConnectSubscription).toHaveBeenCalledWith(mockNamespace, {
accessToken: null,
jwt: '1234',
});
});
});
describe('when API request fails', () => {
it('commits the SET_CURRENT_USER_ERROR mutation', async () => {
jest.spyOn(integrationsApi, 'addJiraConnectSubscription').mockRejectedValue();
await testAction(
addSubscription,
mockNamespace,
mockedState,
[
{ type: types.ADD_SUBSCRIPTION_LOADING, payload: true },
{ type: types.ADD_SUBSCRIPTION_ERROR },
{ type: types.ADD_SUBSCRIPTION_LOADING, payload: false },
],
[],
);
});
});
});
}); });

View File

@ -26,14 +26,6 @@ describe('JiraConnect store mutations', () => {
}); });
}); });
describe('SET_SUBSCRIPTIONS_LOADING', () => {
it('sets subscriptions loading flag', () => {
mutations.SET_SUBSCRIPTIONS_LOADING(localState, true);
expect(localState.subscriptionsLoading).toBe(true);
});
});
describe('SET_SUBSCRIPTIONS', () => { describe('SET_SUBSCRIPTIONS', () => {
it('sets subscriptions loading flag', () => { it('sets subscriptions loading flag', () => {
const mockSubscriptions = [{ name: 'test' }]; const mockSubscriptions = [{ name: 'test' }];
@ -42,4 +34,62 @@ describe('JiraConnect store mutations', () => {
expect(localState.subscriptions).toBe(mockSubscriptions); expect(localState.subscriptions).toBe(mockSubscriptions);
}); });
}); });
describe('SET_SUBSCRIPTIONS_LOADING', () => {
it('sets subscriptions loading flag', () => {
mutations.SET_SUBSCRIPTIONS_LOADING(localState, true);
expect(localState.subscriptionsLoading).toBe(true);
});
});
describe('SET_SUBSCRIPTIONS_ERROR', () => {
it('sets subscriptions error', () => {
mutations.SET_SUBSCRIPTIONS_ERROR(localState, true);
expect(localState.subscriptionsError).toBe(true);
});
});
describe('ADD_SUBSCRIPTION_LOADING', () => {
it('sets addSubscriptionLoading', () => {
mutations.ADD_SUBSCRIPTION_LOADING(localState, true);
expect(localState.addSubscriptionLoading).toBe(true);
});
});
describe('ADD_SUBSCRIPTION_ERROR', () => {
it('sets addSubscriptionError', () => {
mutations.ADD_SUBSCRIPTION_ERROR(localState, true);
expect(localState.addSubscriptionError).toBe(true);
});
});
describe('SET_CURRENT_USER', () => {
it('sets currentUser', () => {
const mockUser = { name: 'root' };
mutations.SET_CURRENT_USER(localState, mockUser);
expect(localState.currentUser).toBe(mockUser);
});
});
describe('SET_CURRENT_USER_ERROR', () => {
it('sets currentUserError', () => {
mutations.SET_CURRENT_USER_ERROR(localState, true);
expect(localState.currentUserError).toBe(true);
});
});
describe('SET_ACCESS_TOKEN', () => {
it('sets accessToken', () => {
const mockAccessToken = 'asdf1234';
mutations.SET_ACCESS_TOKEN(localState, mockAccessToken);
expect(localState.accessToken).toBe(mockAccessToken);
});
});
}); });

View File

@ -88,6 +88,7 @@ describe('DiscussionCounter component', () => {
'changes background color to $color if blocksMerge is $blocksMerge', 'changes background color to $color if blocksMerge is $blocksMerge',
({ blocksMerge, color }) => { ({ blocksMerge, color }) => {
updateStore(); updateStore();
store.state.unresolvedDiscussionsCount = 1;
wrapper = shallowMount(DiscussionCounter, { store, propsData: { blocksMerge } }); wrapper = shallowMount(DiscussionCounter, { store, propsData: { blocksMerge } });
expect(wrapper.find('[data-testid="discussions-counter-text"]').classes()).toContain(color); expect(wrapper.find('[data-testid="discussions-counter-text"]').classes()).toContain(color);

View File

@ -269,9 +269,9 @@ RSpec.describe NamespacesHelper do
end end
end end
describe '#pipeline_usage_quota_app_data' do describe '#pipeline_usage_app_data' do
it 'returns a hash with necessary data for the frontend' do it 'returns a hash with necessary data for the frontend' do
expect(helper.pipeline_usage_quota_app_data(user_group)).to eql({ expect(helper.pipeline_usage_app_data(user_group)).to eql({
namespace_actual_plan_name: user_group.actual_plan_name, namespace_actual_plan_name: user_group.actual_plan_name,
namespace_path: user_group.full_path, namespace_path: user_group.full_path,
namespace_id: user_group.id, namespace_id: user_group.id,

View File

@ -853,6 +853,7 @@ RSpec.describe Backup::Manager do
] ]
) )
allow(File).to receive(:exist?).with(File.join(Gitlab.config.backup.path, 'backup_information.yml')).and_return(true) allow(File).to receive(:exist?).with(File.join(Gitlab.config.backup.path, 'backup_information.yml')).and_return(true)
stub_env('SKIP', 'something')
end end
after do after do
@ -872,7 +873,7 @@ RSpec.describe Backup::Manager do
backup_created_at: backup_time, backup_created_at: backup_time,
full_backup_id: full_backup_id, full_backup_id: full_backup_id,
gitlab_version: Gitlab::VERSION, gitlab_version: Gitlab::VERSION,
skipped: 'tar' skipped: 'something,tar'
) )
end end

View File

@ -5,13 +5,15 @@ require 'spec_helper'
RSpec.describe Backup::Repositories do RSpec.describe Backup::Repositories do
let(:progress) { spy(:stdout) } let(:progress) { spy(:stdout) }
let(:strategy) { spy(:strategy) } let(:strategy) { spy(:strategy) }
let(:storages) { [] }
let(:destination) { 'repositories' } let(:destination) { 'repositories' }
let(:backup_id) { 'backup_id' } let(:backup_id) { 'backup_id' }
subject do subject do
described_class.new( described_class.new(
progress, progress,
strategy: strategy strategy: strategy,
storages: storages
) )
end end
@ -67,17 +69,50 @@ RSpec.describe Backup::Repositories do
end.count end.count
create_list(:project, 2, :repository) create_list(:project, 2, :repository)
create_list(:snippet, 2, :repository)
expect do expect do
subject.dump(destination, backup_id) subject.dump(destination, backup_id)
end.not_to exceed_query_limit(control_count) end.not_to exceed_query_limit(control_count)
end end
describe 'storages' do
let(:storages) { %w{default} }
let_it_be(:project) { create(:project, :repository) }
before do
stub_storage_settings('test_second_storage' => {
'gitaly_address' => Gitlab.config.repositories.storages.default.gitaly_address,
'path' => TestEnv::SECOND_STORAGE_PATH
})
end
it 'calls enqueue for all repositories on the specified storage', :aggregate_failures do
excluded_project = create(:project, :repository, repository_storage: 'test_second_storage')
excluded_project_snippet = create(:project_snippet, :repository, project: excluded_project)
excluded_project_snippet.track_snippet_repository('test_second_storage')
excluded_personal_snippet = create(:personal_snippet, :repository, author: excluded_project.first_owner)
excluded_personal_snippet.track_snippet_repository('test_second_storage')
subject.dump(destination, backup_id)
expect(strategy).to have_received(:start).with(:create, destination, backup_id: backup_id)
expect(strategy).not_to have_received(:enqueue).with(excluded_project, Gitlab::GlRepository::PROJECT)
expect(strategy).not_to have_received(:enqueue).with(excluded_project_snippet, Gitlab::GlRepository::SNIPPET)
expect(strategy).not_to have_received(:enqueue).with(excluded_personal_snippet, Gitlab::GlRepository::SNIPPET)
expect(strategy).to have_received(:enqueue).with(project, Gitlab::GlRepository::PROJECT)
expect(strategy).to have_received(:enqueue).with(project, Gitlab::GlRepository::WIKI)
expect(strategy).to have_received(:enqueue).with(project, Gitlab::GlRepository::DESIGN)
expect(strategy).to have_received(:finish!)
end
end
end end
describe '#restore' do describe '#restore' do
let_it_be(:project) { create(:project) } let_it_be(:project) { create(:project, :repository) }
let_it_be(:personal_snippet) { create(:personal_snippet, author: project.first_owner) } let_it_be(:personal_snippet) { create(:personal_snippet, :repository, author: project.first_owner) }
let_it_be(:project_snippet) { create(:project_snippet, project: project, author: project.first_owner) } let_it_be(:project_snippet) { create(:project_snippet, :repository, project: project, author: project.first_owner) }
it 'calls enqueue for each repository type', :aggregate_failures do it 'calls enqueue for each repository type', :aggregate_failures do
subject.restore(destination) subject.restore(destination)
@ -116,9 +151,6 @@ RSpec.describe Backup::Repositories do
context 'cleanup snippets' do context 'cleanup snippets' do
before do before do
create(:snippet_repository, snippet: personal_snippet)
create(:snippet_repository, snippet: project_snippet)
error_response = ServiceResponse.error(message: "Repository has more than one branch") error_response = ServiceResponse.error(message: "Repository has more than one branch")
allow(Snippets::RepositoryValidationService).to receive_message_chain(:new, :execute).and_return(error_response) allow(Snippets::RepositoryValidationService).to receive_message_chain(:new, :execute).and_return(error_response)
end end
@ -146,5 +178,35 @@ RSpec.describe Backup::Repositories do
expect(gitlab_shell.repository_exists?(shard_name, path)).to eq false expect(gitlab_shell.repository_exists?(shard_name, path)).to eq false
end end
end end
context 'storages' do
let(:storages) { %w{default} }
before do
stub_storage_settings('test_second_storage' => {
'gitaly_address' => Gitlab.config.repositories.storages.default.gitaly_address,
'path' => TestEnv::SECOND_STORAGE_PATH
})
end
it 'calls enqueue for all repositories on the specified storage', :aggregate_failures do
excluded_project = create(:project, :repository, repository_storage: 'test_second_storage')
excluded_project_snippet = create(:project_snippet, :repository, project: excluded_project)
excluded_project_snippet.track_snippet_repository('test_second_storage')
excluded_personal_snippet = create(:personal_snippet, :repository, author: excluded_project.first_owner)
excluded_personal_snippet.track_snippet_repository('test_second_storage')
subject.restore(destination)
expect(strategy).to have_received(:start).with(:restore, destination)
expect(strategy).not_to have_received(:enqueue).with(excluded_project, Gitlab::GlRepository::PROJECT)
expect(strategy).not_to have_received(:enqueue).with(excluded_project_snippet, Gitlab::GlRepository::SNIPPET)
expect(strategy).not_to have_received(:enqueue).with(excluded_personal_snippet, Gitlab::GlRepository::SNIPPET)
expect(strategy).to have_received(:enqueue).with(project, Gitlab::GlRepository::PROJECT)
expect(strategy).to have_received(:enqueue).with(project, Gitlab::GlRepository::WIKI)
expect(strategy).to have_received(:enqueue).with(project, Gitlab::GlRepository::DESIGN)
expect(strategy).to have_received(:finish!)
end
end
end end
end end

View File

@ -4510,6 +4510,43 @@ RSpec.describe API::Projects do
end end
end end
describe 'POST /projects/:id/repository_size' do
let(:update_statistics_service) { Projects::UpdateStatisticsService.new(project, nil, statistics: [:repository_size, :lfs_objects_size]) }
before do
allow(Projects::UpdateStatisticsService).to receive(:new).with(project, nil, statistics: [:repository_size, :lfs_objects_size]).and_return(update_statistics_service)
end
context 'when authenticated as owner' do
it 'starts the housekeeping process' do
expect(update_statistics_service).to receive(:execute).once
post api("/projects/#{project.id}/repository_size", user)
expect(response).to have_gitlab_http_status(:created)
end
end
context 'when authenticated as developer' do
before do
project_member
end
it 'returns forbidden error' do
post api("/projects/#{project.id}/repository_size", user3)
expect(response).to have_gitlab_http_status(:forbidden)
end
end
context 'when unauthenticated' do
it 'returns authentication error' do
post api("/projects/#{project.id}/repository_size")
expect(response).to have_gitlab_http_status(:unauthorized)
end
end
end
describe 'PUT /projects/:id/transfer' do describe 'PUT /projects/:id/transfer' do
context 'when authenticated as owner' do context 'when authenticated as owner' do
let(:group) { create :group } let(:group) { create :group }

View File

@ -1,72 +1,125 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'fast_spec_helper' require 'fast_spec_helper'
require 'rubocop/rspec/support'
require_relative '../../../../rubocop/cop/gitlab/namespaced_class' require_relative '../../../../rubocop/cop/gitlab/namespaced_class'
RSpec.describe RuboCop::Cop::Gitlab::NamespacedClass do RSpec.describe RuboCop::Cop::Gitlab::NamespacedClass do
subject(:cop) { described_class.new } subject(:cop) { described_class.new }
it 'flags a class definition without namespace' do shared_examples 'enforces namespaced classes' do
expect_offense(<<~SOURCE) def namespaced(code)
class MyClass return code unless namespace
^^^^^^^^^^^^^ #{described_class::MSG}
end
SOURCE
end
it 'flags a class definition with inheritance without namespace' do <<~SOURCE
expect_offense(<<~SOURCE) module #{namespace}
class MyClass < ApplicationRecord #{code}
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #{described_class::MSG}
def some_method
true
end end
end SOURCE
SOURCE end
end
it 'does not flag the class definition with namespace in separate lines' do it 'flags a class definition without additional namespace' do
expect_no_offenses(<<~SOURCE) expect_offense(namespaced(<<~SOURCE))
module MyModule class MyClass
^^^^^^^^^^^^^ #{described_class::MSG}
end
SOURCE
end
it 'flags a compact class definition without additional namespace' do
expect_offense(<<~SOURCE, namespace: namespace)
class %{namespace}::MyClass
^{namespace}^^^^^^^^^^^^^^^ #{described_class::MSG}
end
SOURCE
end
it 'flags a class definition with inheritance without additional namespace' do
expect_offense(namespaced(<<~SOURCE))
class MyClass < ApplicationRecord class MyClass < ApplicationRecord
end ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #{described_class::MSG}
def some_method
class MyOtherClass true
def other_method
1 + 1
end end
end end
end SOURCE
SOURCE end
end
it 'does not flag the class definition with nested namespace in separate lines' do it 'does not flag the class definition with namespace in separate lines' do
expect_no_offenses(<<~SOURCE) expect_no_offenses(namespaced(<<~SOURCE))
module TopLevelModule module MyModule
module NestedModule class MyClass < ApplicationRecord
end
class MyOtherClass
def other_method
1 + 1
end
end
end
SOURCE
end
it 'does not flag the class definition with nested namespace in separate lines' do
expect_no_offenses(namespaced(<<~SOURCE))
module TopLevelModule
module NestedModule
class MyClass
end
end
end
SOURCE
end
it 'does not flag the class definition nested inside namespaced class' do
expect_no_offenses(namespaced(<<~SOURCE))
module TopLevelModule
class TopLevelClass
class MyClass
end
end
end
SOURCE
end
it 'does not flag the class definition nested inside compact namespace' do
expect_no_offenses(<<~SOURCE)
module #{namespace}::TopLevelModule
class MyClass class MyClass
end end
end end
end SOURCE
SOURCE end
end
it 'does not flag the class definition nested inside namespaced class' do it 'does not flag a compact namespaced class definition' do
expect_no_offenses(<<~SOURCE) expect_no_offenses(namespaced(<<~SOURCE))
module TopLevelModule class MyModule::MyClass < ApplicationRecord
class TopLevelClass
class MyClass
end
end end
end SOURCE
SOURCE end
it 'does not flag a truly compact namespaced class definition' do
expect_no_offenses(<<~SOURCE, namespace: namespace)
class %{namespace}::MyModule::MyClass < ApplicationRecord
end
SOURCE
end
end end
it 'does not flag a compact namespaced class definition' do context 'without top-level namespace' do
expect_no_offenses(<<~SOURCE) let(:namespace) { nil }
class MyModule::MyClass < ApplicationRecord
end it_behaves_like 'enforces namespaced classes'
SOURCE end
context 'with Gitlab namespace' do
let(:namespace) { 'Gitlab' }
it_behaves_like 'enforces namespaced classes'
end
context 'with ::Gitlab namespace' do
let(:namespace) { '::Gitlab' }
it_behaves_like 'enforces namespaced classes'
end end
end end

View File

@ -6,7 +6,7 @@ RSpec.shared_examples 'Gitlab-style deprecations' do
expect { subject(deprecation_reason: 'foo') }.to raise_error( expect { subject(deprecation_reason: 'foo') }.to raise_error(
ArgumentError, ArgumentError,
'Use `deprecated` property instead of `deprecation_reason`. ' \ 'Use `deprecated` property instead of `deprecation_reason`. ' \
'See https://docs.gitlab.com/ee/development/api_graphql_styleguide.html#deprecating-fields-arguments-and-enum-values' 'See https://docs.gitlab.com/ee/development/api_graphql_styleguide.html#deprecating-schema-items'
) )
end end

View File

@ -377,21 +377,6 @@ RSpec.describe 'gitlab:app namespace rake task', :delete do
expect(tar_lines).to include(a_string_matching(repo_name)) expect(tar_lines).to include(a_string_matching(repo_name))
end end
end end
def move_repository_to_secondary(record)
Gitlab::GitalyClient::StorageSettings.allow_disk_access do
default_shard_legacy_path = Gitlab.config.repositories.storages.default.legacy_disk_path
secondary_legacy_path = Gitlab.config.repositories.storages[second_storage_name].legacy_disk_path
dst_dir = File.join(secondary_legacy_path, File.dirname(record.disk_path))
FileUtils.mkdir_p(dst_dir) unless Dir.exist?(dst_dir)
FileUtils.mv(
File.join(default_shard_legacy_path, record.disk_path + '.git'),
File.join(secondary_legacy_path, record.disk_path + '.git')
)
end
end
end end
context 'no concurrency' do context 'no concurrency' do
@ -405,6 +390,66 @@ RSpec.describe 'gitlab:app namespace rake task', :delete do
it_behaves_like 'includes repositories in all repository storages' it_behaves_like 'includes repositories in all repository storages'
end end
context 'REPOSITORIES_STORAGES set' do
before do
stub_env('REPOSITORIES_STORAGES', default_storage_name)
end
it 'includes repositories in default repository storage', :aggregate_failures do
project_a = create(:project, :repository)
project_snippet_a = create(:project_snippet, :repository, project: project_a, author: project_a.first_owner)
project_b = create(:project, :repository, repository_storage: second_storage_name)
project_snippet_b = create(:project_snippet, :repository, project: project_b, author: project_b.first_owner)
project_snippet_b.snippet_repository.update!(shard: project_b.project_repository.shard)
create(:wiki_page, container: project_a)
create(:design, :with_file, issue: create(:issue, project: project_a))
move_repository_to_secondary(project_b)
move_repository_to_secondary(project_snippet_b)
expect { run_rake_task('gitlab:backup:create') }.to output.to_stdout_from_any_process
tar_contents, exit_status = Gitlab::Popen.popen(
%W{tar -tvf #{backup_tar} repositories}
)
tar_lines = tar_contents.lines.grep(/\.bundle/)
expect(exit_status).to eq(0)
[
"#{project_a.disk_path}/.+/001.bundle",
"#{project_a.disk_path}.wiki/.+/001.bundle",
"#{project_a.disk_path}.design/.+/001.bundle",
"#{project_snippet_a.disk_path}/.+/001.bundle"
].each do |repo_name|
expect(tar_lines).to include(a_string_matching(repo_name))
end
[
"#{project_b.disk_path}/.+/001.bundle",
"#{project_snippet_b.disk_path}/.+/001.bundle"
].each do |repo_name|
expect(tar_lines).not_to include(a_string_matching(repo_name))
end
end
end
def move_repository_to_secondary(record)
Gitlab::GitalyClient::StorageSettings.allow_disk_access do
default_shard_legacy_path = Gitlab.config.repositories.storages.default.legacy_disk_path
secondary_legacy_path = Gitlab.config.repositories.storages[second_storage_name].legacy_disk_path
dst_dir = File.join(secondary_legacy_path, File.dirname(record.disk_path))
FileUtils.mkdir_p(dst_dir) unless Dir.exist?(dst_dir)
FileUtils.mv(
File.join(default_shard_legacy_path, record.disk_path + '.git'),
File.join(secondary_legacy_path, record.disk_path + '.git')
)
end
end
end end
context 'concurrency settings' do context 'concurrency settings' do
@ -420,7 +465,7 @@ RSpec.describe 'gitlab:app namespace rake task', :delete do
stub_env('GITLAB_BACKUP_MAX_STORAGE_CONCURRENCY', 2) stub_env('GITLAB_BACKUP_MAX_STORAGE_CONCURRENCY', 2)
expect(::Backup::Repositories).to receive(:new) expect(::Backup::Repositories).to receive(:new)
.with(anything, strategy: anything) .with(anything, strategy: anything, storages: [])
.and_call_original .and_call_original
expect(::Backup::GitalyBackup).to receive(:new).with(anything, max_parallelism: 5, storage_parallelism: 2, incremental: false).and_call_original expect(::Backup::GitalyBackup).to receive(:new).with(anything, max_parallelism: 5, storage_parallelism: 2, incremental: false).and_call_original

View File

@ -27,7 +27,7 @@ RSpec.describe 'projects/tags/index.html.haml' do
it 'renders links to the Releases page for tags associated with a release' do it 'renders links to the Releases page for tags associated with a release' do
render render
expect(rendered).to have_link(release.name, href: project_release_path(project, release.tag)) expect(rendered).to have_link(release.name, href: project_releases_path(project, anchor: release.tag))
end end
context 'when the most recent build for a tag has artifacts' do context 'when the most recent build for a tag has artifacts' do

View File

@ -383,7 +383,7 @@ RSpec.describe 'Every Sidekiq worker' do
'ProjectDailyStatisticsWorker' => 3, 'ProjectDailyStatisticsWorker' => 3,
'ProjectDestroyWorker' => 3, 'ProjectDestroyWorker' => 3,
'ProjectExportWorker' => false, 'ProjectExportWorker' => false,
'ProjectImportScheduleWorker' => false, 'ProjectImportScheduleWorker' => 1,
'ProjectScheduleBulkRepositoryShardMovesWorker' => 3, 'ProjectScheduleBulkRepositoryShardMovesWorker' => 3,
'ProjectServiceWorker' => 3, 'ProjectServiceWorker' => 3,
'ProjectTemplateExportWorker' => false, 'ProjectTemplateExportWorker' => false,